mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat: add qwencoder as co-author (#207)
* init Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix shell tool regex pattern for git commit messages Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -79,6 +79,12 @@ export interface TelemetrySettings {
|
|||||||
outfile?: string;
|
outfile?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GitCoAuthorSettings {
|
||||||
|
enabled?: boolean;
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GeminiCLIExtension {
|
export interface GeminiCLIExtension {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
@@ -164,6 +170,7 @@ export interface ConfigParameters {
|
|||||||
contextFileName?: string | string[];
|
contextFileName?: string | string[];
|
||||||
accessibility?: AccessibilitySettings;
|
accessibility?: AccessibilitySettings;
|
||||||
telemetry?: TelemetrySettings;
|
telemetry?: TelemetrySettings;
|
||||||
|
gitCoAuthor?: GitCoAuthorSettings;
|
||||||
usageStatisticsEnabled?: boolean;
|
usageStatisticsEnabled?: boolean;
|
||||||
fileFiltering?: {
|
fileFiltering?: {
|
||||||
respectGitIgnore?: boolean;
|
respectGitIgnore?: boolean;
|
||||||
@@ -227,6 +234,7 @@ export class Config {
|
|||||||
private readonly showMemoryUsage: boolean;
|
private readonly showMemoryUsage: boolean;
|
||||||
private readonly accessibility: AccessibilitySettings;
|
private readonly accessibility: AccessibilitySettings;
|
||||||
private readonly telemetrySettings: TelemetrySettings;
|
private readonly telemetrySettings: TelemetrySettings;
|
||||||
|
private readonly gitCoAuthor: GitCoAuthorSettings;
|
||||||
private readonly usageStatisticsEnabled: boolean;
|
private readonly usageStatisticsEnabled: boolean;
|
||||||
private geminiClient!: GeminiClient;
|
private geminiClient!: GeminiClient;
|
||||||
private readonly fileFiltering: {
|
private readonly fileFiltering: {
|
||||||
@@ -304,6 +312,11 @@ export class Config {
|
|||||||
logPrompts: params.telemetry?.logPrompts ?? true,
|
logPrompts: params.telemetry?.logPrompts ?? true,
|
||||||
outfile: params.telemetry?.outfile,
|
outfile: params.telemetry?.outfile,
|
||||||
};
|
};
|
||||||
|
this.gitCoAuthor = {
|
||||||
|
enabled: params.gitCoAuthor?.enabled ?? true,
|
||||||
|
name: params.gitCoAuthor?.name ?? 'Qwen-Coder',
|
||||||
|
email: params.gitCoAuthor?.email ?? 'qwen-coder@alibabacloud.com',
|
||||||
|
};
|
||||||
this.usageStatisticsEnabled = params.usageStatisticsEnabled ?? true;
|
this.usageStatisticsEnabled = params.usageStatisticsEnabled ?? true;
|
||||||
|
|
||||||
this.fileFiltering = {
|
this.fileFiltering = {
|
||||||
@@ -571,6 +584,10 @@ export class Config {
|
|||||||
return this.telemetrySettings.outfile;
|
return this.telemetrySettings.outfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGitCoAuthor(): GitCoAuthorSettings {
|
||||||
|
return this.gitCoAuthor;
|
||||||
|
}
|
||||||
|
|
||||||
getGeminiClient(): GeminiClient {
|
getGeminiClient(): GeminiClient {
|
||||||
return this.geminiClient;
|
return this.geminiClient;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ describe('ShellTool', () => {
|
|||||||
getSummarizeToolOutputConfig: vi.fn().mockReturnValue(undefined),
|
getSummarizeToolOutputConfig: vi.fn().mockReturnValue(undefined),
|
||||||
getWorkspaceContext: () => createMockWorkspaceContext('.'),
|
getWorkspaceContext: () => createMockWorkspaceContext('.'),
|
||||||
getGeminiClient: vi.fn(),
|
getGeminiClient: vi.fn(),
|
||||||
|
getGitCoAuthor: vi.fn().mockReturnValue({
|
||||||
|
enabled: true,
|
||||||
|
name: 'Qwen-Coder',
|
||||||
|
email: 'qwen-coder@alibabacloud.com',
|
||||||
|
}),
|
||||||
} as unknown as Config;
|
} as unknown as Config;
|
||||||
|
|
||||||
shellTool = new ShellTool(mockConfig);
|
shellTool = new ShellTool(mockConfig);
|
||||||
@@ -386,6 +391,123 @@ describe('ShellTool', () => {
|
|||||||
expect(confirmation).toBe(false);
|
expect(confirmation).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('addCoAuthorToGitCommit', () => {
|
||||||
|
it('should add co-author to git commit with double quotes', () => {
|
||||||
|
const command = 'git commit -m "Initial commit"';
|
||||||
|
// Use public test method
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe(
|
||||||
|
`git commit -m "Initial commit
|
||||||
|
|
||||||
|
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add co-author to git commit with single quotes', () => {
|
||||||
|
const command = "git commit -m 'Fix bug'";
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe(
|
||||||
|
`git commit -m 'Fix bug
|
||||||
|
|
||||||
|
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>'`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle git commit with additional flags', () => {
|
||||||
|
const command = 'git commit -a -m "Add feature"';
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe(
|
||||||
|
`git commit -a -m "Add feature
|
||||||
|
|
||||||
|
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify non-git commands', () => {
|
||||||
|
const command = 'npm install';
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe('npm install');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify git commands without -m flag', () => {
|
||||||
|
const command = 'git commit';
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe('git commit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle git commit with escaped quotes in message', () => {
|
||||||
|
const command = 'git commit -m "Fix \\"quoted\\" text"';
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe(
|
||||||
|
`git commit -m "Fix \\"quoted\\" text
|
||||||
|
|
||||||
|
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add co-author when disabled in config', () => {
|
||||||
|
// Mock config with disabled co-author
|
||||||
|
(mockConfig.getGitCoAuthor as Mock).mockReturnValue({
|
||||||
|
enabled: false,
|
||||||
|
name: 'Qwen-Coder',
|
||||||
|
email: 'qwen-coder@alibabacloud.com',
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = 'git commit -m "Initial commit"';
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe('git commit -m "Initial commit"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom name and email from config', () => {
|
||||||
|
// Mock config with custom co-author details
|
||||||
|
(mockConfig.getGitCoAuthor as Mock).mockReturnValue({
|
||||||
|
enabled: true,
|
||||||
|
name: 'Custom Bot',
|
||||||
|
email: 'custom@example.com',
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = 'git commit -m "Test commit"';
|
||||||
|
const result = (
|
||||||
|
shellTool as unknown as {
|
||||||
|
addCoAuthorToGitCommit: (command: string) => string;
|
||||||
|
}
|
||||||
|
).addCoAuthorToGitCommit(command);
|
||||||
|
expect(result).toBe(
|
||||||
|
`git commit -m "Test commit
|
||||||
|
|
||||||
|
Co-authored-by: Custom Bot <custom@example.com>"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateToolParams', () => {
|
describe('validateToolParams', () => {
|
||||||
|
|||||||
@@ -205,12 +205,15 @@ export class ShellTool extends BaseTool<ShellToolParams, ToolResult> {
|
|||||||
const tempFilePath = path.join(os.tmpdir(), tempFileName);
|
const tempFilePath = path.join(os.tmpdir(), tempFileName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Add co-author to git commit commands
|
||||||
|
const processedCommand = this.addCoAuthorToGitCommit(strippedCommand);
|
||||||
|
|
||||||
// pgrep is not available on Windows, so we can't get background PIDs
|
// pgrep is not available on Windows, so we can't get background PIDs
|
||||||
const commandToExecute = isWindows
|
const commandToExecute = isWindows
|
||||||
? strippedCommand
|
? processedCommand
|
||||||
: (() => {
|
: (() => {
|
||||||
// wrap command to append subprocess pids (via pgrep) to temporary file
|
// wrap command to append subprocess pids (via pgrep) to temporary file
|
||||||
let command = strippedCommand.trim();
|
let command = processedCommand.trim();
|
||||||
if (!command.endsWith('&')) command += ';';
|
if (!command.endsWith('&')) command += ';';
|
||||||
return `{ ${command} }; __code=$?; pgrep -g 0 >${tempFilePath} 2>&1; exit $__code;`;
|
return `{ ${command} }; __code=$?; pgrep -g 0 >${tempFilePath} 2>&1; exit $__code;`;
|
||||||
})();
|
})();
|
||||||
@@ -382,4 +385,40 @@ export class ShellTool extends BaseTool<ShellToolParams, ToolResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addCoAuthorToGitCommit(command: string): string {
|
||||||
|
// Check if co-author feature is enabled
|
||||||
|
const gitCoAuthorSettings = this.config.getGitCoAuthor();
|
||||||
|
if (!gitCoAuthorSettings.enabled) {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a git commit command
|
||||||
|
const gitCommitPattern = /^git\s+commit/;
|
||||||
|
if (!gitCommitPattern.test(command.trim())) {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the co-author line using configuration
|
||||||
|
const coAuthor = `
|
||||||
|
|
||||||
|
Co-authored-by: ${gitCoAuthorSettings.name} <${gitCoAuthorSettings.email}>`;
|
||||||
|
|
||||||
|
// Handle different git commit patterns
|
||||||
|
// Match -m "message" or -m 'message'
|
||||||
|
const messagePattern = /(-m\s+)(['"])((?:\\.|[^\\])*?)(\2)/;
|
||||||
|
const match = command.match(messagePattern);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const [fullMatch, prefix, quote, existingMessage, closingQuote] = match;
|
||||||
|
const newMessage = existingMessage + coAuthor;
|
||||||
|
const replacement = prefix + quote + newMessage + closingQuote;
|
||||||
|
|
||||||
|
return command.replace(fullMatch, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no -m flag found, the command might open an editor
|
||||||
|
// In this case, we can't easily modify it, so return as-is
|
||||||
|
return command;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user