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;
|
||||
}
|
||||
|
||||
export interface GitCoAuthorSettings {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export interface GeminiCLIExtension {
|
||||
name: string;
|
||||
version: string;
|
||||
@@ -164,6 +170,7 @@ export interface ConfigParameters {
|
||||
contextFileName?: string | string[];
|
||||
accessibility?: AccessibilitySettings;
|
||||
telemetry?: TelemetrySettings;
|
||||
gitCoAuthor?: GitCoAuthorSettings;
|
||||
usageStatisticsEnabled?: boolean;
|
||||
fileFiltering?: {
|
||||
respectGitIgnore?: boolean;
|
||||
@@ -227,6 +234,7 @@ export class Config {
|
||||
private readonly showMemoryUsage: boolean;
|
||||
private readonly accessibility: AccessibilitySettings;
|
||||
private readonly telemetrySettings: TelemetrySettings;
|
||||
private readonly gitCoAuthor: GitCoAuthorSettings;
|
||||
private readonly usageStatisticsEnabled: boolean;
|
||||
private geminiClient!: GeminiClient;
|
||||
private readonly fileFiltering: {
|
||||
@@ -304,6 +312,11 @@ export class Config {
|
||||
logPrompts: params.telemetry?.logPrompts ?? true,
|
||||
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.fileFiltering = {
|
||||
@@ -571,6 +584,10 @@ export class Config {
|
||||
return this.telemetrySettings.outfile;
|
||||
}
|
||||
|
||||
getGitCoAuthor(): GitCoAuthorSettings {
|
||||
return this.gitCoAuthor;
|
||||
}
|
||||
|
||||
getGeminiClient(): GeminiClient {
|
||||
return this.geminiClient;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@ describe('ShellTool', () => {
|
||||
getSummarizeToolOutputConfig: vi.fn().mockReturnValue(undefined),
|
||||
getWorkspaceContext: () => createMockWorkspaceContext('.'),
|
||||
getGeminiClient: vi.fn(),
|
||||
getGitCoAuthor: vi.fn().mockReturnValue({
|
||||
enabled: true,
|
||||
name: 'Qwen-Coder',
|
||||
email: 'qwen-coder@alibabacloud.com',
|
||||
}),
|
||||
} as unknown as Config;
|
||||
|
||||
shellTool = new ShellTool(mockConfig);
|
||||
@@ -386,6 +391,123 @@ describe('ShellTool', () => {
|
||||
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', () => {
|
||||
|
||||
@@ -205,12 +205,15 @@ export class ShellTool extends BaseTool<ShellToolParams, ToolResult> {
|
||||
const tempFilePath = path.join(os.tmpdir(), tempFileName);
|
||||
|
||||
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
|
||||
const commandToExecute = isWindows
|
||||
? strippedCommand
|
||||
? processedCommand
|
||||
: (() => {
|
||||
// wrap command to append subprocess pids (via pgrep) to temporary file
|
||||
let command = strippedCommand.trim();
|
||||
let command = processedCommand.trim();
|
||||
if (!command.endsWith('&')) command += ';';
|
||||
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