ccg-ros2-workflow 1.4.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +128 -241
- package/bin/ccg.mjs +2 -0
- package/bin/codeagent-wrapper-darwin-amd64 +0 -0
- package/bin/codeagent-wrapper-darwin-arm64 +0 -0
- package/bin/codeagent-wrapper-linux-amd64 +0 -0
- package/bin/codeagent-wrapper-linux-arm64 +0 -0
- package/bin/codeagent-wrapper-windows-amd64.exe +0 -0
- package/bin/codeagent-wrapper-windows-arm64.exe +0 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +173 -0
- package/dist/index.d.mts +229 -0
- package/dist/index.d.ts +229 -0
- package/dist/index.mjs +12 -0
- package/dist/shared/ccg-ros2-workflow.CpLJvcLP.mjs +2274 -0
- package/package.json +85 -22
- package/templates/commands/agents/planner.md +345 -0
- package/templates/commands/agents/system-integrator.md +397 -0
- package/{src/commands/ccg → templates/commands}/analyze.md +17 -17
- package/{src/commands/ccg → templates/commands}/backend.md +25 -25
- package/{src/commands/ccg → templates/commands}/debug.md +12 -12
- package/{src/commands/ccg → templates/commands}/execute.md +24 -23
- package/{src/commands/ccg → templates/commands}/feat.md +21 -21
- package/{src/commands/ccg → templates/commands}/frontend.md +26 -26
- package/{src/commands/ccg → templates/commands}/optimize.md +24 -24
- package/{src/commands/ccg → templates/commands}/plan.md +20 -19
- package/{src/commands/ccg → templates/commands}/review.md +9 -9
- package/templates/commands/spec-impl.md +123 -0
- package/templates/commands/spec-init.md +91 -0
- package/templates/commands/spec-plan.md +109 -0
- package/templates/commands/spec-research.md +104 -0
- package/templates/commands/spec-review.md +120 -0
- package/{src/commands/ccg → templates/commands}/test.md +23 -23
- package/templates/commands/workflow.md +193 -0
- package/{src/commands/ccg → templates/commands}/worktree.md +8 -8
- package/templates/prompts/claude/analyzer.md +59 -0
- package/templates/prompts/claude/architect.md +54 -0
- package/templates/prompts/claude/debugger.md +71 -0
- package/templates/prompts/claude/optimizer.md +73 -0
- package/templates/prompts/claude/reviewer.md +63 -0
- package/templates/prompts/claude/tester.md +69 -0
- package/templates/prompts/codex/analyzer.md +50 -0
- package/templates/prompts/codex/architect.md +46 -0
- package/templates/prompts/codex/debugger.md +66 -0
- package/templates/prompts/codex/optimizer.md +74 -0
- package/templates/prompts/codex/reviewer.md +66 -0
- package/templates/prompts/codex/tester.md +55 -0
- package/templates/prompts/gemini/analyzer.md +53 -0
- package/templates/prompts/gemini/architect.md +47 -0
- package/templates/prompts/gemini/debugger.md +70 -0
- package/templates/prompts/gemini/frontend.md +56 -0
- package/templates/prompts/gemini/optimizer.md +77 -0
- package/templates/prompts/gemini/reviewer.md +73 -0
- package/templates/prompts/gemini/tester.md +61 -0
- package/bin/cli.js +0 -903
- package/src/agents/ccg/planner.md +0 -358
- package/src/agents/ccg/system-integrator.md +0 -627
- package/src/codeagent-wrapper.sh +0 -86
- package/src/commands/ccg/workflow.md +0 -212
- package/src/config.toml +0 -36
- package/src/prompts/claude/analyzer.md +0 -25
- package/src/prompts/claude/architect.md +0 -25
- package/src/prompts/claude/debugger.md +0 -24
- package/src/prompts/claude/optimizer.md +0 -25
- package/src/prompts/claude/reviewer.md +0 -26
- package/src/prompts/claude/tester.md +0 -24
- package/src/prompts/codex/analyzer.md +0 -32
- package/src/prompts/codex/architect.md +0 -42
- package/src/prompts/codex/debugger.md +0 -24
- package/src/prompts/codex/optimizer.md +0 -25
- package/src/prompts/codex/reviewer.md +0 -32
- package/src/prompts/codex/tester.md +0 -24
- package/src/prompts/gemini/analyzer.md +0 -32
- package/src/prompts/gemini/architect.md +0 -34
- package/src/prompts/gemini/debugger.md +0 -24
- package/src/prompts/gemini/frontend.md +0 -25
- package/src/prompts/gemini/optimizer.md +0 -25
- package/src/prompts/gemini/reviewer.md +0 -32
- package/src/prompts/gemini/tester.md +0 -24
- /package/{src/agents/ccg → templates/commands/agents}/get-current-datetime.md +0 -0
- /package/{src/agents/ccg → templates/commands/agents}/init-architect.md +0 -0
- /package/{src/commands/ccg → templates/commands}/clean-branches.md +0 -0
- /package/{src/commands/ccg → templates/commands}/commit.md +0 -0
- /package/{src/commands/ccg → templates/commands}/enhance.md +0 -0
- /package/{src/commands/ccg → templates/commands}/init.md +0 -0
- /package/{src/commands/ccg → templates/commands}/rollback.md +0 -0
package/bin/cli.js
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { execSync } = require('child_process');
|
|
6
|
-
|
|
7
|
-
// 延迟加载依赖
|
|
8
|
-
let inquirer, chalk, ora;
|
|
9
|
-
|
|
10
|
-
async function loadDependencies() {
|
|
11
|
-
try {
|
|
12
|
-
inquirer = require('inquirer');
|
|
13
|
-
chalk = require('chalk');
|
|
14
|
-
ora = require('ora');
|
|
15
|
-
} catch {
|
|
16
|
-
console.log('正在安装依赖...');
|
|
17
|
-
execSync('npm install inquirer@8.2.6 chalk@4.1.2 ora@5.4.1', {
|
|
18
|
-
stdio: 'inherit',
|
|
19
|
-
cwd: path.join(__dirname, '..')
|
|
20
|
-
});
|
|
21
|
-
inquirer = require('inquirer');
|
|
22
|
-
chalk = require('chalk');
|
|
23
|
-
ora = require('ora');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 路径常量
|
|
28
|
-
const CLAUDE_DIR = path.join(process.env.HOME, '.claude');
|
|
29
|
-
const CCG_DIR = path.join(CLAUDE_DIR, '.ccg');
|
|
30
|
-
const COMMANDS_DIR = path.join(CLAUDE_DIR, 'commands', 'ccg');
|
|
31
|
-
const AGENTS_DIR = path.join(CLAUDE_DIR, 'agents', 'ccg');
|
|
32
|
-
const BIN_DIR = path.join(CLAUDE_DIR, 'bin');
|
|
33
|
-
const CLAUDE_JSON_PATH = path.join(process.env.HOME, '.claude.json');
|
|
34
|
-
const SRC_DIR = path.join(__dirname, '..', 'src');
|
|
35
|
-
const PACKAGE_NAME = 'ccg-ros2-workflow';
|
|
36
|
-
|
|
37
|
-
// 版本信息
|
|
38
|
-
const PACKAGE_JSON = require('../package.json');
|
|
39
|
-
const VERSION = PACKAGE_JSON.version;
|
|
40
|
-
|
|
41
|
-
// 所有命令列表
|
|
42
|
-
const ALL_COMMANDS = [
|
|
43
|
-
'workflow', 'plan', 'execute', 'feat',
|
|
44
|
-
'frontend', 'backend',
|
|
45
|
-
'analyze', 'review', 'test', 'debug', 'optimize',
|
|
46
|
-
'init', 'enhance',
|
|
47
|
-
'commit', 'rollback', 'clean-branches', 'worktree'
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
// ==================== 工具函数 ====================
|
|
51
|
-
|
|
52
|
-
function copyRecursive(src, dest) {
|
|
53
|
-
if (!fs.existsSync(src)) return 0;
|
|
54
|
-
let count = 0;
|
|
55
|
-
|
|
56
|
-
if (fs.statSync(src).isDirectory()) {
|
|
57
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
58
|
-
fs.readdirSync(src).forEach(child => {
|
|
59
|
-
count += copyRecursive(path.join(src, child), path.join(dest, child));
|
|
60
|
-
});
|
|
61
|
-
} else {
|
|
62
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
63
|
-
fs.copyFileSync(src, dest);
|
|
64
|
-
count = 1;
|
|
65
|
-
}
|
|
66
|
-
return count;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function replaceInFile(filePath, replacements) {
|
|
70
|
-
if (!fs.existsSync(filePath)) return;
|
|
71
|
-
let content = fs.readFileSync(filePath, 'utf8');
|
|
72
|
-
for (const [search, replace] of Object.entries(replacements)) {
|
|
73
|
-
content = content.replace(new RegExp(search, 'g'), replace);
|
|
74
|
-
}
|
|
75
|
-
fs.writeFileSync(filePath, content);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function isInstalled() {
|
|
79
|
-
return fs.existsSync(path.join(CCG_DIR, 'config.toml')) ||
|
|
80
|
-
fs.existsSync(COMMANDS_DIR);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function getInstalledVersion() {
|
|
84
|
-
const configPath = path.join(CCG_DIR, 'config.toml');
|
|
85
|
-
if (fs.existsSync(configPath)) {
|
|
86
|
-
const content = fs.readFileSync(configPath, 'utf8');
|
|
87
|
-
const match = content.match(/version\s*=\s*"([^"]+)"/);
|
|
88
|
-
if (match) return match[1];
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function countFiles(dir, ext = '.md') {
|
|
94
|
-
if (!fs.existsSync(dir)) return 0;
|
|
95
|
-
return fs.readdirSync(dir).filter(f => f.endsWith(ext)).length;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function countFilesRecursive(dir, ext = '.md') {
|
|
99
|
-
if (!fs.existsSync(dir)) return 0;
|
|
100
|
-
let count = 0;
|
|
101
|
-
const items = fs.readdirSync(dir);
|
|
102
|
-
for (const item of items) {
|
|
103
|
-
const fullPath = path.join(dir, item);
|
|
104
|
-
if (fs.statSync(fullPath).isDirectory()) {
|
|
105
|
-
count += countFilesRecursive(fullPath, ext);
|
|
106
|
-
} else if (item.endsWith(ext)) {
|
|
107
|
-
count++;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return count;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ==================== 主菜单 ====================
|
|
114
|
-
|
|
115
|
-
async function showMainMenu() {
|
|
116
|
-
while (true) {
|
|
117
|
-
console.log();
|
|
118
|
-
console.log(chalk.cyan.bold(' CCG-ROS2 - Claude + Codex + Gemini'));
|
|
119
|
-
console.log(chalk.gray(' ROS2 多模型协作开发系统'));
|
|
120
|
-
console.log();
|
|
121
|
-
|
|
122
|
-
const { action } = await inquirer.prompt([{
|
|
123
|
-
type: 'list',
|
|
124
|
-
name: 'action',
|
|
125
|
-
message: 'CCG 主菜单',
|
|
126
|
-
choices: [
|
|
127
|
-
{ name: `${chalk.green('➜')} 初始化 CCG 配置`, value: 'init' },
|
|
128
|
-
{ name: `${chalk.blue('➜')} 更新工作流`, value: 'update' },
|
|
129
|
-
{ name: `${chalk.cyan('⚙')} 配置 MCP`, value: 'config-mcp' },
|
|
130
|
-
{ name: `${chalk.yellow('?')} 帮助`, value: 'help' },
|
|
131
|
-
new inquirer.Separator(),
|
|
132
|
-
{ name: `${chalk.magenta('➜')} 卸载工作流`, value: 'uninstall' },
|
|
133
|
-
{ name: `${chalk.red('✕')} 退出`, value: 'exit' },
|
|
134
|
-
],
|
|
135
|
-
}]);
|
|
136
|
-
|
|
137
|
-
switch (action) {
|
|
138
|
-
case 'init':
|
|
139
|
-
await init();
|
|
140
|
-
break;
|
|
141
|
-
case 'update':
|
|
142
|
-
await update();
|
|
143
|
-
break;
|
|
144
|
-
case 'config-mcp':
|
|
145
|
-
await configMcp();
|
|
146
|
-
break;
|
|
147
|
-
case 'help':
|
|
148
|
-
showHelp();
|
|
149
|
-
break;
|
|
150
|
-
case 'uninstall':
|
|
151
|
-
await uninstall();
|
|
152
|
-
break;
|
|
153
|
-
case 'exit':
|
|
154
|
-
console.log(chalk.gray('Goodbye!'));
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// 操作完成后暂停
|
|
159
|
-
console.log();
|
|
160
|
-
await inquirer.prompt([{
|
|
161
|
-
type: 'input',
|
|
162
|
-
name: 'continue',
|
|
163
|
-
message: chalk.gray('按 Enter 返回主菜单...'),
|
|
164
|
-
}]);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// ==================== 初始化/安装 ====================
|
|
169
|
-
|
|
170
|
-
async function init() {
|
|
171
|
-
console.log();
|
|
172
|
-
console.log(chalk.cyan.bold(' CCG - Claude + Codex + Gemini'));
|
|
173
|
-
console.log(chalk.gray(' ROS2 多模型协作开发工作流'));
|
|
174
|
-
console.log();
|
|
175
|
-
|
|
176
|
-
// 固定配置
|
|
177
|
-
const frontendModel = 'gemini'; // 上层应用:Launch、Python、配置
|
|
178
|
-
const backendModel = 'codex'; // 底层控制:C++、硬件、实时
|
|
179
|
-
|
|
180
|
-
// MCP 配置变量
|
|
181
|
-
let mcpProvider = 'skip';
|
|
182
|
-
let aceToolBaseUrl = '';
|
|
183
|
-
let aceToolToken = '';
|
|
184
|
-
|
|
185
|
-
// MCP 工具选择
|
|
186
|
-
console.log();
|
|
187
|
-
console.log(chalk.cyan.bold(' 🔧 MCP 工具配置'));
|
|
188
|
-
console.log();
|
|
189
|
-
|
|
190
|
-
const { selectedMcp } = await inquirer.prompt([{
|
|
191
|
-
type: 'list',
|
|
192
|
-
name: 'selectedMcp',
|
|
193
|
-
message: '选择 MCP 工具',
|
|
194
|
-
choices: [
|
|
195
|
-
{
|
|
196
|
-
name: `ace-tool-rs ${chalk.green('(推荐)')} ${chalk.gray('(Rust 实现) - 更轻量、更快速')}`,
|
|
197
|
-
value: 'ace-tool-rs',
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
name: `ace-tool ${chalk.gray('(Node.js 实现) - 含 Prompt 增强 + 代码检索')}`,
|
|
201
|
-
value: 'ace-tool',
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
name: `跳过 ${chalk.gray('- 稍后手动配置')}`,
|
|
205
|
-
value: 'skip',
|
|
206
|
-
},
|
|
207
|
-
],
|
|
208
|
-
default: 'ace-tool-rs',
|
|
209
|
-
}]);
|
|
210
|
-
|
|
211
|
-
mcpProvider = selectedMcp;
|
|
212
|
-
|
|
213
|
-
// 配置 ace-tool
|
|
214
|
-
if (selectedMcp === 'ace-tool' || selectedMcp === 'ace-tool-rs') {
|
|
215
|
-
const toolName = selectedMcp === 'ace-tool-rs' ? 'ace-tool-rs' : 'ace-tool';
|
|
216
|
-
const toolDesc = selectedMcp === 'ace-tool-rs'
|
|
217
|
-
? 'Rust 实现的 ace-tool,更轻量、更快速'
|
|
218
|
-
: 'Node.js 实现,含 Prompt 增强 + 代码检索';
|
|
219
|
-
|
|
220
|
-
console.log();
|
|
221
|
-
console.log(chalk.cyan.bold(` 🔧 ${toolName} MCP 配置`));
|
|
222
|
-
console.log(chalk.gray(` ${toolDesc}`));
|
|
223
|
-
console.log();
|
|
224
|
-
|
|
225
|
-
const { skipToken } = await inquirer.prompt([{
|
|
226
|
-
type: 'confirm',
|
|
227
|
-
name: 'skipToken',
|
|
228
|
-
message: '是否跳过 Token 配置?(可稍后运行 npx ccg-ros2-workflow 配置)',
|
|
229
|
-
default: false,
|
|
230
|
-
}]);
|
|
231
|
-
|
|
232
|
-
if (!skipToken) {
|
|
233
|
-
console.log();
|
|
234
|
-
console.log(chalk.cyan(' 📖 获取 ace-tool 访问方式:'));
|
|
235
|
-
console.log();
|
|
236
|
-
console.log(` ${chalk.gray('•')} ${chalk.cyan('官方服务')}: ${chalk.underline('https://augmentcode.com/')}`);
|
|
237
|
-
console.log(` ${chalk.gray('注册账号后获取 Token')}`);
|
|
238
|
-
console.log();
|
|
239
|
-
console.log(` ${chalk.gray('•')} ${chalk.cyan('中转服务')} ${chalk.yellow('(无需注册)')}: ${chalk.underline('https://linux.do/t/topic/1291730')}`);
|
|
240
|
-
console.log(` ${chalk.gray('linux.do 社区提供的免费中转服务')}`);
|
|
241
|
-
console.log();
|
|
242
|
-
|
|
243
|
-
const aceAnswers = await inquirer.prompt([
|
|
244
|
-
{
|
|
245
|
-
type: 'input',
|
|
246
|
-
name: 'baseUrl',
|
|
247
|
-
message: `Base URL ${chalk.gray('(使用中转服务时必填,官方服务留空)')}`,
|
|
248
|
-
default: '',
|
|
249
|
-
},
|
|
250
|
-
{
|
|
251
|
-
type: 'password',
|
|
252
|
-
name: 'token',
|
|
253
|
-
message: `Token ${chalk.gray('(必填)')}`,
|
|
254
|
-
mask: '*',
|
|
255
|
-
validate: input => input.trim() !== '' || '请输入 Token',
|
|
256
|
-
},
|
|
257
|
-
]);
|
|
258
|
-
aceToolBaseUrl = aceAnswers.baseUrl || '';
|
|
259
|
-
aceToolToken = aceAnswers.token || '';
|
|
260
|
-
} else {
|
|
261
|
-
console.log();
|
|
262
|
-
console.log(chalk.yellow(' ℹ️ 已跳过 Token 配置'));
|
|
263
|
-
console.log(chalk.gray(` • ace-tool MCP 将不会自动安装`));
|
|
264
|
-
console.log(chalk.gray(` • 可稍后运行 ${chalk.cyan('npx ccg-ros2-workflow')} 配置 Token`));
|
|
265
|
-
console.log();
|
|
266
|
-
}
|
|
267
|
-
} else {
|
|
268
|
-
console.log();
|
|
269
|
-
console.log(chalk.yellow(' ℹ️ 已跳过 MCP 配置'));
|
|
270
|
-
console.log(chalk.gray(' • 可稍后手动配置任何 MCP 服务'));
|
|
271
|
-
console.log();
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// 显示配置摘要
|
|
275
|
-
console.log();
|
|
276
|
-
console.log(chalk.yellow('━'.repeat(50)));
|
|
277
|
-
console.log(chalk.bold(' 配置摘要:'));
|
|
278
|
-
console.log();
|
|
279
|
-
console.log(` ${chalk.cyan('模型路由')} ${chalk.blue('Codex')} (底层控制) + ${chalk.green('Gemini')} (上层应用)`);
|
|
280
|
-
console.log(` ${chalk.cyan('命令数量')} ${chalk.yellow(ALL_COMMANDS.length.toString())} 个`);
|
|
281
|
-
|
|
282
|
-
const mcpDisplay = (mcpProvider === 'ace-tool' || mcpProvider === 'ace-tool-rs')
|
|
283
|
-
? (aceToolToken ? chalk.green(mcpProvider) : chalk.yellow(`${mcpProvider} (待配置)`))
|
|
284
|
-
: chalk.gray('跳过');
|
|
285
|
-
console.log(` ${chalk.cyan('MCP 工具')} ${mcpDisplay}`);
|
|
286
|
-
console.log(` ${chalk.cyan('目标平台')} ${chalk.white('ROS2 Humble 物理机器人')}`);
|
|
287
|
-
console.log(chalk.yellow('━'.repeat(50)));
|
|
288
|
-
console.log();
|
|
289
|
-
|
|
290
|
-
// 确认安装
|
|
291
|
-
const { confirmed } = await inquirer.prompt([{
|
|
292
|
-
type: 'confirm',
|
|
293
|
-
name: 'confirmed',
|
|
294
|
-
message: '确认安装以上配置?',
|
|
295
|
-
default: true,
|
|
296
|
-
}]);
|
|
297
|
-
|
|
298
|
-
if (!confirmed) {
|
|
299
|
-
console.log(chalk.yellow('安装已取消'));
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// 开始安装
|
|
304
|
-
const spinner = ora('正在安装...').start();
|
|
305
|
-
|
|
306
|
-
try {
|
|
307
|
-
// 创建目录
|
|
308
|
-
spinner.text = '创建目录结构...';
|
|
309
|
-
[CCG_DIR, COMMANDS_DIR, AGENTS_DIR, BIN_DIR].forEach(dir => {
|
|
310
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// 复制命令文件
|
|
314
|
-
spinner.text = '安装命令文件...';
|
|
315
|
-
copyRecursive(path.join(SRC_DIR, 'commands', 'ccg'), COMMANDS_DIR);
|
|
316
|
-
const commandCount = countFiles(COMMANDS_DIR);
|
|
317
|
-
|
|
318
|
-
// 复制提示词文件
|
|
319
|
-
spinner.text = '安装提示词文件...';
|
|
320
|
-
copyRecursive(path.join(SRC_DIR, 'prompts'), path.join(CCG_DIR, 'prompts'));
|
|
321
|
-
const promptCount = countFilesRecursive(path.join(CCG_DIR, 'prompts'));
|
|
322
|
-
|
|
323
|
-
// 安装 agents
|
|
324
|
-
spinner.text = '安装 agents...';
|
|
325
|
-
copyRecursive(path.join(SRC_DIR, 'agents', 'ccg'), AGENTS_DIR);
|
|
326
|
-
const agentCount = countFiles(AGENTS_DIR);
|
|
327
|
-
|
|
328
|
-
// 复制配置
|
|
329
|
-
spinner.text = '写入配置...';
|
|
330
|
-
const configContent = `version = "${VERSION}"
|
|
331
|
-
language = "zh-CN"
|
|
332
|
-
target = "ros2-humble"
|
|
333
|
-
|
|
334
|
-
[routing]
|
|
335
|
-
mode = "smart"
|
|
336
|
-
frontend = "gemini"
|
|
337
|
-
backend = "codex"
|
|
338
|
-
`;
|
|
339
|
-
fs.writeFileSync(path.join(CCG_DIR, 'config.toml'), configContent);
|
|
340
|
-
|
|
341
|
-
// 安装 codeagent-wrapper
|
|
342
|
-
spinner.text = '安装 codeagent-wrapper...';
|
|
343
|
-
const wrapperSrc = path.join(SRC_DIR, 'codeagent-wrapper.sh');
|
|
344
|
-
const wrapperDest = path.join(BIN_DIR, 'codeagent-wrapper');
|
|
345
|
-
if (fs.existsSync(wrapperSrc)) {
|
|
346
|
-
fs.copyFileSync(wrapperSrc, wrapperDest);
|
|
347
|
-
fs.chmodSync(wrapperDest, '755');
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// 处理路径变量
|
|
351
|
-
spinner.text = '处理路径变量...';
|
|
352
|
-
const homeDir = process.env.HOME;
|
|
353
|
-
const filesToUpdate = [
|
|
354
|
-
path.join(CCG_DIR, 'config.toml'),
|
|
355
|
-
...fs.readdirSync(COMMANDS_DIR)
|
|
356
|
-
.filter(f => f.endsWith('.md'))
|
|
357
|
-
.map(f => path.join(COMMANDS_DIR, f))
|
|
358
|
-
];
|
|
359
|
-
filesToUpdate.forEach(file => {
|
|
360
|
-
replaceInFile(file, { '\\$HOME': homeDir });
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// 安装 ace-tool MCP(如果配置了 token)
|
|
364
|
-
if ((mcpProvider === 'ace-tool' || mcpProvider === 'ace-tool-rs') && aceToolToken) {
|
|
365
|
-
spinner.text = `配置 ${mcpProvider} MCP...`;
|
|
366
|
-
const aceResult = await installAceTool(mcpProvider, aceToolBaseUrl, aceToolToken);
|
|
367
|
-
|
|
368
|
-
if (aceResult.success) {
|
|
369
|
-
spinner.succeed(chalk.green('安装成功!'));
|
|
370
|
-
console.log();
|
|
371
|
-
console.log(` ${chalk.green('✓')} ${mcpProvider} MCP ${chalk.gray(`→ ${aceResult.configPath}`)}`);
|
|
372
|
-
} else {
|
|
373
|
-
spinner.warn(chalk.yellow(`${mcpProvider} 配置失败`));
|
|
374
|
-
console.log(chalk.gray(` ${aceResult.message}`));
|
|
375
|
-
}
|
|
376
|
-
} else if ((mcpProvider === 'ace-tool' || mcpProvider === 'ace-tool-rs') && !aceToolToken) {
|
|
377
|
-
spinner.succeed(chalk.green('安装成功!'));
|
|
378
|
-
console.log();
|
|
379
|
-
console.log(` ${chalk.yellow('⚠')} ${mcpProvider} MCP 未安装 ${chalk.gray('(Token 未提供)')}`);
|
|
380
|
-
console.log(` ${chalk.gray('→')} 稍后运行 ${chalk.cyan('npx ccg-ros2-workflow')} 完成配置`);
|
|
381
|
-
} else {
|
|
382
|
-
spinner.succeed(chalk.green('安装成功!'));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// 显示已安装的命令
|
|
386
|
-
console.log();
|
|
387
|
-
console.log(chalk.cyan(' 已安装命令:'));
|
|
388
|
-
const installedCommands = fs.readdirSync(COMMANDS_DIR)
|
|
389
|
-
.filter(f => f.endsWith('.md'))
|
|
390
|
-
.map(f => f.replace('.md', ''));
|
|
391
|
-
installedCommands.forEach(cmd => {
|
|
392
|
-
console.log(` ${chalk.green('✓')} /ccg:${cmd}`);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
// 显示已安装的提示词
|
|
396
|
-
const promptsDir = path.join(CCG_DIR, 'prompts');
|
|
397
|
-
if (fs.existsSync(promptsDir)) {
|
|
398
|
-
console.log();
|
|
399
|
-
console.log(chalk.cyan(' 已安装提示词:'));
|
|
400
|
-
const modelDirs = ['codex', 'gemini', 'claude'];
|
|
401
|
-
modelDirs.forEach(model => {
|
|
402
|
-
const modelDir = path.join(promptsDir, model);
|
|
403
|
-
if (fs.existsSync(modelDir)) {
|
|
404
|
-
const roles = fs.readdirSync(modelDir)
|
|
405
|
-
.filter(f => f.endsWith('.md'))
|
|
406
|
-
.map(f => f.replace('.md', ''));
|
|
407
|
-
if (roles.length > 0) {
|
|
408
|
-
console.log(` ${chalk.green('✓')} ${model}: ${roles.join(', ')}`);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// 显示已安装的二进制文件
|
|
415
|
-
if (fs.existsSync(wrapperDest)) {
|
|
416
|
-
console.log();
|
|
417
|
-
console.log(chalk.cyan(' 已安装二进制文件:'));
|
|
418
|
-
console.log(` ${chalk.green('✓')} codeagent-wrapper ${chalk.gray(`→ ${BIN_DIR}`)}`);
|
|
419
|
-
|
|
420
|
-
// 配置 PATH
|
|
421
|
-
const shell = process.env.SHELL || '';
|
|
422
|
-
const isZsh = shell.includes('zsh');
|
|
423
|
-
const shellRc = isZsh ? path.join(homeDir, '.zshrc') : path.join(homeDir, '.bashrc');
|
|
424
|
-
const shellRcDisplay = isZsh ? '~/.zshrc' : '~/.bashrc';
|
|
425
|
-
const exportCommand = `export PATH="${BIN_DIR}:$PATH"`;
|
|
426
|
-
|
|
427
|
-
try {
|
|
428
|
-
let rcContent = fs.existsSync(shellRc) ? fs.readFileSync(shellRc, 'utf8') : '';
|
|
429
|
-
|
|
430
|
-
if (rcContent.includes(BIN_DIR) || rcContent.includes('/.claude/bin')) {
|
|
431
|
-
console.log(` ${chalk.green('✓')} PATH ${chalk.gray(`→ ${shellRcDisplay} (已配置)`)}`);
|
|
432
|
-
} else {
|
|
433
|
-
const configLine = `\n# CCG-ROS2 multi-model collaboration system\n${exportCommand}\n`;
|
|
434
|
-
fs.appendFileSync(shellRc, configLine, 'utf8');
|
|
435
|
-
console.log(` ${chalk.green('✓')} PATH ${chalk.gray(`→ ${shellRcDisplay}`)}`);
|
|
436
|
-
}
|
|
437
|
-
} catch {
|
|
438
|
-
console.log(` ${chalk.yellow('⚠')} PATH ${chalk.gray('→ 请手动添加到 shell 配置:')}`);
|
|
439
|
-
console.log(` ${chalk.cyan(exportCommand)}`);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// 显示 MCP 资源(如果用户跳过了安装)
|
|
444
|
-
if (mcpProvider === 'skip' || ((mcpProvider === 'ace-tool' || mcpProvider === 'ace-tool-rs') && !aceToolToken)) {
|
|
445
|
-
console.log();
|
|
446
|
-
console.log(chalk.cyan.bold(' 📖 MCP 服务选项'));
|
|
447
|
-
console.log();
|
|
448
|
-
console.log(chalk.gray(' 如需使用代码检索和 Prompt 增强功能,可选择以下 MCP 服务:'));
|
|
449
|
-
console.log();
|
|
450
|
-
console.log(` ${chalk.green('1.')} ${chalk.cyan('ace-tool')} ${chalk.gray('(推荐)')}: ${chalk.underline('https://augmentcode.com/')}`);
|
|
451
|
-
console.log(` ${chalk.gray('一键安装,含 Prompt 增强 + 代码检索')}`);
|
|
452
|
-
console.log();
|
|
453
|
-
console.log(` ${chalk.green('2.')} ${chalk.cyan('ace-tool 中转服务')} ${chalk.yellow('(无需注册)')}: ${chalk.underline('https://linux.do/t/topic/1291730')}`);
|
|
454
|
-
console.log(` ${chalk.gray('linux.do 社区提供的免费中转服务')}`);
|
|
455
|
-
console.log();
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
console.log();
|
|
459
|
-
|
|
460
|
-
} catch (error) {
|
|
461
|
-
spinner.fail(chalk.red('安装失败'));
|
|
462
|
-
console.error(error);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
// ==================== 安装 ace-tool ====================
|
|
468
|
-
|
|
469
|
-
async function installAceTool(provider, baseUrl, token) {
|
|
470
|
-
try {
|
|
471
|
-
// 读取或创建配置
|
|
472
|
-
let config = {};
|
|
473
|
-
if (fs.existsSync(CLAUDE_JSON_PATH)) {
|
|
474
|
-
try {
|
|
475
|
-
config = JSON.parse(fs.readFileSync(CLAUDE_JSON_PATH, 'utf8'));
|
|
476
|
-
} catch {
|
|
477
|
-
config = {};
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (!config.mcpServers) {
|
|
482
|
-
config.mcpServers = {};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// 构建 args
|
|
486
|
-
const args = provider === 'ace-tool'
|
|
487
|
-
? ['-y', 'ace-tool@latest']
|
|
488
|
-
: ['ace-tool-rs'];
|
|
489
|
-
|
|
490
|
-
if (baseUrl) {
|
|
491
|
-
args.push('--base-url', baseUrl);
|
|
492
|
-
}
|
|
493
|
-
if (token) {
|
|
494
|
-
args.push('--token', token);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// 创建 MCP 配置
|
|
498
|
-
const mcpConfig = {
|
|
499
|
-
type: 'stdio',
|
|
500
|
-
command: 'npx',
|
|
501
|
-
args: args,
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
if (provider === 'ace-tool-rs') {
|
|
505
|
-
mcpConfig.env = { RUST_LOG: 'info' };
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
config.mcpServers['ace-tool'] = mcpConfig;
|
|
509
|
-
|
|
510
|
-
// 写入配置
|
|
511
|
-
fs.writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(config, null, 2));
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
success: true,
|
|
515
|
-
message: `${provider} MCP 配置成功`,
|
|
516
|
-
configPath: '~/.claude.json',
|
|
517
|
-
};
|
|
518
|
-
} catch (error) {
|
|
519
|
-
return {
|
|
520
|
-
success: false,
|
|
521
|
-
message: `配置失败: ${error.message}`,
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// ==================== 更新 ====================
|
|
527
|
-
|
|
528
|
-
async function update() {
|
|
529
|
-
console.log();
|
|
530
|
-
console.log(chalk.blue.bold(' 🔄 更新工作流'));
|
|
531
|
-
console.log();
|
|
532
|
-
|
|
533
|
-
if (!isInstalled()) {
|
|
534
|
-
console.log(chalk.yellow('未检测到安装,请先初始化'));
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
const installedVersion = getInstalledVersion() || '未知';
|
|
539
|
-
console.log(chalk.gray(` 当前版本: v${installedVersion}`));
|
|
540
|
-
console.log(chalk.gray(` 包版本: v${VERSION}`));
|
|
541
|
-
|
|
542
|
-
// 检查 npm 最新版本
|
|
543
|
-
let latestVersion = null;
|
|
544
|
-
try {
|
|
545
|
-
const spinner = ora('检查最新版本...').start();
|
|
546
|
-
latestVersion = execSync(`npm view ${PACKAGE_NAME} version`, { encoding: 'utf8', timeout: 10000 }).trim();
|
|
547
|
-
spinner.stop();
|
|
548
|
-
console.log(chalk.gray(` 最新版本: v${latestVersion}`));
|
|
549
|
-
} catch {
|
|
550
|
-
console.log(chalk.gray(' 最新版本: 无法获取'));
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
if (latestVersion && latestVersion !== VERSION) {
|
|
554
|
-
console.log();
|
|
555
|
-
console.log(chalk.yellow(` 发现新版本 v${latestVersion}`));
|
|
556
|
-
console.log(chalk.gray(' 运行以下命令更新:'));
|
|
557
|
-
console.log(chalk.cyan(` npx ${PACKAGE_NAME}@latest`));
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (installedVersion === VERSION) {
|
|
562
|
-
console.log();
|
|
563
|
-
console.log(chalk.green(' ✓ 已是最新版本'));
|
|
564
|
-
|
|
565
|
-
const { reinstall } = await inquirer.prompt([{
|
|
566
|
-
type: 'confirm',
|
|
567
|
-
name: 'reinstall',
|
|
568
|
-
message: '是否重新安装工作流文件?',
|
|
569
|
-
default: false,
|
|
570
|
-
}]);
|
|
571
|
-
|
|
572
|
-
if (reinstall) {
|
|
573
|
-
await init();
|
|
574
|
-
}
|
|
575
|
-
} else {
|
|
576
|
-
console.log();
|
|
577
|
-
console.log(chalk.blue(' → 更新工作流文件...'));
|
|
578
|
-
await init();
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// ==================== 配置 MCP ====================
|
|
583
|
-
|
|
584
|
-
async function configMcp() {
|
|
585
|
-
console.log();
|
|
586
|
-
console.log(chalk.cyan.bold(' 🔧 配置 ace-tool MCP'));
|
|
587
|
-
console.log();
|
|
588
|
-
|
|
589
|
-
const { selectedMcp } = await inquirer.prompt([{
|
|
590
|
-
type: 'list',
|
|
591
|
-
name: 'selectedMcp',
|
|
592
|
-
message: '选择 MCP 工具',
|
|
593
|
-
choices: [
|
|
594
|
-
{
|
|
595
|
-
name: `ace-tool-rs ${chalk.green('(推荐)')} ${chalk.gray('(Rust 实现)')}`,
|
|
596
|
-
value: 'ace-tool-rs',
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
name: `ace-tool ${chalk.gray('(Node.js 实现)')}`,
|
|
600
|
-
value: 'ace-tool',
|
|
601
|
-
},
|
|
602
|
-
{
|
|
603
|
-
name: `${chalk.red('卸载')} ace-tool MCP 配置`,
|
|
604
|
-
value: 'uninstall',
|
|
605
|
-
},
|
|
606
|
-
{
|
|
607
|
-
name: chalk.gray('返回'),
|
|
608
|
-
value: 'back',
|
|
609
|
-
},
|
|
610
|
-
],
|
|
611
|
-
}]);
|
|
612
|
-
|
|
613
|
-
if (selectedMcp === 'back') return;
|
|
614
|
-
|
|
615
|
-
if (selectedMcp === 'uninstall') {
|
|
616
|
-
await uninstallMcp();
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
const toolName = selectedMcp;
|
|
621
|
-
|
|
622
|
-
console.log();
|
|
623
|
-
console.log(chalk.cyan(' 📖 获取 ace-tool 访问方式:'));
|
|
624
|
-
console.log();
|
|
625
|
-
console.log(` ${chalk.gray('•')} ${chalk.cyan('官方服务')}: ${chalk.underline('https://augmentcode.com/')}`);
|
|
626
|
-
console.log(` ${chalk.gray('注册账号后获取 Token')}`);
|
|
627
|
-
console.log();
|
|
628
|
-
console.log(` ${chalk.gray('•')} ${chalk.cyan('中转服务')} ${chalk.yellow('(无需注册)')}: ${chalk.underline('https://linux.do/t/topic/1291730')}`);
|
|
629
|
-
console.log(` ${chalk.gray('linux.do 社区提供的免费中转服务')}`);
|
|
630
|
-
console.log();
|
|
631
|
-
|
|
632
|
-
const { baseUrl, token } = await inquirer.prompt([
|
|
633
|
-
{
|
|
634
|
-
type: 'input',
|
|
635
|
-
name: 'baseUrl',
|
|
636
|
-
message: `Base URL ${chalk.gray('(使用中转服务时必填,官方服务留空)')}`,
|
|
637
|
-
default: '',
|
|
638
|
-
},
|
|
639
|
-
{
|
|
640
|
-
type: 'password',
|
|
641
|
-
name: 'token',
|
|
642
|
-
message: `Token ${chalk.gray('(必填)')}`,
|
|
643
|
-
mask: '*',
|
|
644
|
-
validate: input => input.trim() !== '' || '请输入 Token',
|
|
645
|
-
},
|
|
646
|
-
]);
|
|
647
|
-
|
|
648
|
-
const spinner = ora(`配置 ${toolName} MCP...`).start();
|
|
649
|
-
const result = await installAceTool(toolName, baseUrl, token);
|
|
650
|
-
|
|
651
|
-
if (result.success) {
|
|
652
|
-
spinner.succeed(chalk.green(`${toolName} MCP 配置成功`));
|
|
653
|
-
console.log();
|
|
654
|
-
console.log(` ${chalk.green('✓')} 配置文件 ${chalk.gray(`→ ${result.configPath}`)}`);
|
|
655
|
-
console.log();
|
|
656
|
-
console.log(chalk.yellow(' ⚠️ 请重启 Claude Code 使配置生效'));
|
|
657
|
-
} else {
|
|
658
|
-
spinner.fail(chalk.red(result.message));
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
async function uninstallMcp() {
|
|
663
|
-
if (!fs.existsSync(CLAUDE_JSON_PATH)) {
|
|
664
|
-
console.log(chalk.yellow('未找到配置文件'));
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
const { confirm } = await inquirer.prompt([{
|
|
669
|
-
type: 'confirm',
|
|
670
|
-
name: 'confirm',
|
|
671
|
-
message: chalk.red('确定要移除 ace-tool MCP 配置吗?'),
|
|
672
|
-
default: false,
|
|
673
|
-
}]);
|
|
674
|
-
|
|
675
|
-
if (!confirm) {
|
|
676
|
-
console.log(chalk.gray('取消卸载'));
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
try {
|
|
681
|
-
let config = JSON.parse(fs.readFileSync(CLAUDE_JSON_PATH, 'utf8'));
|
|
682
|
-
|
|
683
|
-
if (!config.mcpServers || !config.mcpServers['ace-tool']) {
|
|
684
|
-
console.log(chalk.yellow('配置中没有 ace-tool'));
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
delete config.mcpServers['ace-tool'];
|
|
689
|
-
fs.writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(config, null, 2));
|
|
690
|
-
console.log(chalk.green('✅ 已从配置中移除 ace-tool'));
|
|
691
|
-
} catch (e) {
|
|
692
|
-
console.log(chalk.red('❌ 移除失败:'), e.message);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// ==================== 帮助 ====================
|
|
697
|
-
|
|
698
|
-
function showHelp() {
|
|
699
|
-
console.log();
|
|
700
|
-
console.log(chalk.cyan.bold(' 📖 CCG-ROS2 帮助'));
|
|
701
|
-
console.log();
|
|
702
|
-
|
|
703
|
-
// 开发工作流
|
|
704
|
-
console.log(chalk.yellow.bold(' 开发工作流:'));
|
|
705
|
-
console.log(` ${chalk.green('/ccg:workflow')} 完整7阶段开发工作流`);
|
|
706
|
-
console.log(` ${chalk.green('/ccg:plan')} 多模型协作规划`);
|
|
707
|
-
console.log(` ${chalk.green('/ccg:execute')} 多模型协作执行`);
|
|
708
|
-
console.log(` ${chalk.green('/ccg:frontend')} 上层应用开发 (Gemini 主导)`);
|
|
709
|
-
console.log(` ${chalk.green('/ccg:backend')} 底层控制开发 (Codex 主导)`);
|
|
710
|
-
console.log(` ${chalk.green('/ccg:feat')} 智能功能开发`);
|
|
711
|
-
console.log(` ${chalk.green('/ccg:analyze')} 多模型技术分析`);
|
|
712
|
-
console.log(` ${chalk.green('/ccg:debug')} 问题诊断 + 修复`);
|
|
713
|
-
console.log(` ${chalk.green('/ccg:optimize')} 性能优化`);
|
|
714
|
-
console.log(` ${chalk.green('/ccg:test')} 测试生成`);
|
|
715
|
-
console.log(` ${chalk.green('/ccg:review')} 代码审查`);
|
|
716
|
-
console.log();
|
|
717
|
-
|
|
718
|
-
// Git 工具
|
|
719
|
-
console.log(chalk.yellow.bold(' Git 工具:'));
|
|
720
|
-
console.log(` ${chalk.green('/ccg:commit')} 智能 Git 提交`);
|
|
721
|
-
console.log(` ${chalk.green('/ccg:rollback')} Git 回滚`);
|
|
722
|
-
console.log(` ${chalk.green('/ccg:clean-branches')} 清理已合并分支`);
|
|
723
|
-
console.log(` ${chalk.green('/ccg:worktree')} Git Worktree 管理`);
|
|
724
|
-
console.log();
|
|
725
|
-
|
|
726
|
-
// 项目管理
|
|
727
|
-
console.log(chalk.yellow.bold(' 项目管理:'));
|
|
728
|
-
console.log(` ${chalk.green('/ccg:init')} 初始化项目 CLAUDE.md`);
|
|
729
|
-
console.log(` ${chalk.green('/ccg:enhance')} Prompt 增强`);
|
|
730
|
-
console.log();
|
|
731
|
-
|
|
732
|
-
// 智能路由说明
|
|
733
|
-
console.log(chalk.yellow.bold(' 智能路由:'));
|
|
734
|
-
console.log(chalk.gray(' • 控制算法、C++、硬件驱动、实时性 → Codex'));
|
|
735
|
-
console.log(chalk.gray(' • Launch、Python、RViz、配置、诊断 → Gemini'));
|
|
736
|
-
console.log();
|
|
737
|
-
|
|
738
|
-
console.log(chalk.gray(' 更多信息: https://github.com/GuYu-001/ccg-ros2-workflow'));
|
|
739
|
-
console.log();
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// ==================== 卸载 ====================
|
|
743
|
-
|
|
744
|
-
async function uninstall() {
|
|
745
|
-
console.log();
|
|
746
|
-
|
|
747
|
-
// 检查是否全局安装
|
|
748
|
-
let isGlobalInstall = false;
|
|
749
|
-
try {
|
|
750
|
-
const result = execSync(`npm list -g ${PACKAGE_NAME} --depth=0`, { encoding: 'utf8', timeout: 5000 });
|
|
751
|
-
isGlobalInstall = result.includes(`${PACKAGE_NAME}@`);
|
|
752
|
-
} catch {
|
|
753
|
-
isGlobalInstall = false;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
if (isGlobalInstall) {
|
|
757
|
-
console.log(chalk.yellow('⚠️ 检测到你是通过 npm 全局安装的'));
|
|
758
|
-
console.log();
|
|
759
|
-
console.log('完整卸载需要两步:');
|
|
760
|
-
console.log(` ${chalk.cyan('1. 移除工作流文件')} (即将执行)`);
|
|
761
|
-
console.log(` ${chalk.cyan('2. 卸载 npm 全局包')} (需要手动执行)`);
|
|
762
|
-
console.log();
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// 确认卸载
|
|
766
|
-
const { confirm } = await inquirer.prompt([{
|
|
767
|
-
type: 'confirm',
|
|
768
|
-
name: 'confirm',
|
|
769
|
-
message: isGlobalInstall ? '继续卸载工作流文件?' : '确定要卸载工作流吗?',
|
|
770
|
-
default: false,
|
|
771
|
-
}]);
|
|
772
|
-
|
|
773
|
-
if (!confirm) {
|
|
774
|
-
console.log(chalk.gray('卸载已取消'));
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
// 询问是否移除 ace-tool
|
|
779
|
-
let removeAceTool = false;
|
|
780
|
-
if (fs.existsSync(CLAUDE_JSON_PATH)) {
|
|
781
|
-
try {
|
|
782
|
-
const config = JSON.parse(fs.readFileSync(CLAUDE_JSON_PATH, 'utf8'));
|
|
783
|
-
if (config.mcpServers && config.mcpServers['ace-tool']) {
|
|
784
|
-
const { remove } = await inquirer.prompt([{
|
|
785
|
-
type: 'confirm',
|
|
786
|
-
name: 'remove',
|
|
787
|
-
message: '同时移除 ace-tool MCP 配置?',
|
|
788
|
-
default: false,
|
|
789
|
-
}]);
|
|
790
|
-
removeAceTool = remove;
|
|
791
|
-
}
|
|
792
|
-
} catch {}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
console.log();
|
|
796
|
-
console.log(chalk.yellow('正在卸载...'));
|
|
797
|
-
|
|
798
|
-
const removedCommands = [];
|
|
799
|
-
const removedAgents = [];
|
|
800
|
-
|
|
801
|
-
// 删除命令目录
|
|
802
|
-
if (fs.existsSync(COMMANDS_DIR)) {
|
|
803
|
-
try {
|
|
804
|
-
const files = fs.readdirSync(COMMANDS_DIR);
|
|
805
|
-
files.filter(f => f.endsWith('.md')).forEach(f => {
|
|
806
|
-
removedCommands.push(f.replace('.md', ''));
|
|
807
|
-
});
|
|
808
|
-
fs.rmSync(COMMANDS_DIR, { recursive: true });
|
|
809
|
-
} catch (error) {
|
|
810
|
-
console.log(chalk.red(`删除命令目录失败: ${error.message}`));
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// 删除 agents 目录
|
|
815
|
-
if (fs.existsSync(AGENTS_DIR)) {
|
|
816
|
-
try {
|
|
817
|
-
const files = fs.readdirSync(AGENTS_DIR);
|
|
818
|
-
files.forEach(f => removedAgents.push(f.replace('.md', '')));
|
|
819
|
-
fs.rmSync(AGENTS_DIR, { recursive: true });
|
|
820
|
-
} catch (error) {
|
|
821
|
-
console.log(chalk.red(`删除 agents 目录失败: ${error.message}`));
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// 删除 .ccg 配置目录
|
|
826
|
-
if (fs.existsSync(CCG_DIR)) {
|
|
827
|
-
try {
|
|
828
|
-
fs.rmSync(CCG_DIR, { recursive: true });
|
|
829
|
-
} catch (error) {
|
|
830
|
-
console.log(chalk.red(`删除配置目录失败: ${error.message}`));
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// 删除 codeagent-wrapper
|
|
835
|
-
const wrapperPath = path.join(BIN_DIR, 'codeagent-wrapper');
|
|
836
|
-
let removedBin = false;
|
|
837
|
-
if (fs.existsSync(wrapperPath)) {
|
|
838
|
-
try {
|
|
839
|
-
fs.unlinkSync(wrapperPath);
|
|
840
|
-
removedBin = true;
|
|
841
|
-
} catch {}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
console.log(chalk.green('✅ 工作流文件已移除'));
|
|
845
|
-
|
|
846
|
-
if (removedCommands.length > 0) {
|
|
847
|
-
console.log();
|
|
848
|
-
console.log(chalk.cyan('已移除命令:'));
|
|
849
|
-
removedCommands.forEach(cmd => {
|
|
850
|
-
console.log(` ${chalk.gray('•')} /ccg:${cmd}`);
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
if (removedAgents.length > 0) {
|
|
855
|
-
console.log();
|
|
856
|
-
console.log(chalk.cyan('已移除 agents:'));
|
|
857
|
-
removedAgents.forEach(agent => {
|
|
858
|
-
console.log(` ${chalk.gray('•')} ${agent}`);
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
if (removedBin) {
|
|
863
|
-
console.log();
|
|
864
|
-
console.log(chalk.cyan('已移除二进制文件:'));
|
|
865
|
-
console.log(` ${chalk.gray('•')} codeagent-wrapper`);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
// 移除 ace-tool
|
|
869
|
-
if (removeAceTool) {
|
|
870
|
-
try {
|
|
871
|
-
let config = JSON.parse(fs.readFileSync(CLAUDE_JSON_PATH, 'utf8'));
|
|
872
|
-
delete config.mcpServers['ace-tool'];
|
|
873
|
-
fs.writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(config, null, 2));
|
|
874
|
-
console.log(chalk.green('✅ ace-tool MCP 配置已移除'));
|
|
875
|
-
} catch {}
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
// 全局安装提示
|
|
879
|
-
if (isGlobalInstall) {
|
|
880
|
-
console.log();
|
|
881
|
-
console.log(chalk.yellow.bold('🔸 最后一步:卸载 npm 全局包'));
|
|
882
|
-
console.log();
|
|
883
|
-
console.log('请在新的终端窗口中运行:');
|
|
884
|
-
console.log();
|
|
885
|
-
console.log(chalk.cyan.bold(` npm uninstall -g ${PACKAGE_NAME}`));
|
|
886
|
-
console.log();
|
|
887
|
-
console.log(chalk.gray('(完成后 ccg-ros2-workflow 命令将彻底移除)'));
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
console.log();
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
// ==================== 主程序 ====================
|
|
894
|
-
|
|
895
|
-
async function main() {
|
|
896
|
-
await loadDependencies();
|
|
897
|
-
await showMainMenu();
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
main().catch(err => {
|
|
901
|
-
console.error(chalk.red('错误:'), err.message);
|
|
902
|
-
process.exit(1);
|
|
903
|
-
});
|