ccg-workflow 2.1.15 → 3.0.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.
Files changed (45) hide show
  1. package/README.md +120 -270
  2. package/README.zh-CN.md +119 -269
  3. package/dist/cli.mjs +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/shared/{ccg-workflow.C2MaYeHU.mjs → ccg-workflow.81OoN8XX.mjs} +629 -297
  6. package/package.json +6 -30
  7. package/templates/commands/go.md +199 -0
  8. package/templates/commands-legacy/team.md +475 -0
  9. package/templates/engine/model-router.md +117 -0
  10. package/templates/engine/phase-guide.md +95 -0
  11. package/templates/engine/strategies/debug-investigate.md +156 -0
  12. package/templates/engine/strategies/deep-research.md +141 -0
  13. package/templates/engine/strategies/direct-fix.md +108 -0
  14. package/templates/engine/strategies/full-collaborate.md +291 -0
  15. package/templates/engine/strategies/git-action.md +43 -0
  16. package/templates/engine/strategies/guided-develop.md +208 -0
  17. package/templates/engine/strategies/optimize-measure.md +103 -0
  18. package/templates/engine/strategies/quick-implement.md +96 -0
  19. package/templates/engine/strategies/refactor-safely.md +157 -0
  20. package/templates/engine/strategies/review-audit.md +116 -0
  21. package/templates/hooks/session-start.js +100 -0
  22. package/templates/hooks/skill-router.js +144 -0
  23. package/templates/hooks/subagent-context.js +118 -0
  24. package/templates/hooks/task-utils.js +113 -0
  25. package/templates/hooks/workflow-state.js +39 -0
  26. package/templates/spec/backend/index.md +31 -0
  27. package/templates/spec/frontend/index.md +31 -0
  28. package/templates/spec/guides/index.md +30 -0
  29. /package/templates/{commands → commands-legacy}/analyze.md +0 -0
  30. /package/templates/{commands → commands-legacy}/backend.md +0 -0
  31. /package/templates/{commands → commands-legacy}/codex-exec.md +0 -0
  32. /package/templates/{commands → commands-legacy}/debug.md +0 -0
  33. /package/templates/{commands → commands-legacy}/enhance.md +0 -0
  34. /package/templates/{commands → commands-legacy}/execute.md +0 -0
  35. /package/templates/{commands → commands-legacy}/feat.md +0 -0
  36. /package/templates/{commands → commands-legacy}/frontend.md +0 -0
  37. /package/templates/{commands → commands-legacy}/optimize.md +0 -0
  38. /package/templates/{commands → commands-legacy}/plan.md +0 -0
  39. /package/templates/{commands → commands-legacy}/review.md +0 -0
  40. /package/templates/{commands → commands-legacy}/team-exec.md +0 -0
  41. /package/templates/{commands → commands-legacy}/team-plan.md +0 -0
  42. /package/templates/{commands → commands-legacy}/team-research.md +0 -0
  43. /package/templates/{commands → commands-legacy}/team-review.md +0 -0
  44. /package/templates/{commands → commands-legacy}/test.md +0 -0
  45. /package/templates/{commands → commands-legacy}/workflow.md +0 -0
@@ -10,7 +10,7 @@ import fs from 'fs-extra';
10
10
  import { parse, stringify } from 'smol-toml';
11
11
  import i18next from 'i18next';
12
12
 
13
- const version = "2.1.15";
13
+ const version = "3.0.0";
14
14
 
15
15
  function cmd(id, order, category, name, nameEn, description, descriptionEn, cmdOverride) {
16
16
  return {
@@ -25,8 +25,25 @@ function cmd(id, order, category, name, nameEn, description, descriptionEn, cmdO
25
25
  descriptionEn
26
26
  };
27
27
  }
28
- const WORKFLOW_CONFIGS = [
29
- // ── Development ──────────────────────────────────────
28
+ const CORE_CONFIGS = [
29
+ // ── Smart Entry ──────────────────────────────────────
30
+ cmd("go", 0, "development", "CCG \u667A\u80FD\u5165\u53E3", "CCG Smart Entry", "\u63CF\u8FF0\u4F60\u8981\u505A\u4EC0\u4E48\uFF0CAI \u81EA\u52A8\u9009\u62E9\u6700\u4F73\u7B56\u7565\u6267\u884C", "Describe what you want, AI picks the best strategy"),
31
+ // ── Independent Tools ────────────────────────────────
32
+ cmd("init-project", 10, "init", "\u9879\u76EE\u521D\u59CB\u5316", "Project Init", "\u521D\u59CB\u5316\u9879\u76EE AI \u4E0A\u4E0B\u6587\uFF0C\u751F\u6210 CLAUDE.md", "Initialize project AI context, generate CLAUDE.md", "init"),
33
+ cmd("context", 11, "development", "\u9879\u76EE\u4E0A\u4E0B\u6587\u7BA1\u7406", "Project Context Manager", "\u521D\u59CB\u5316 .context \u76EE\u5F55\u3001\u8BB0\u5F55\u51B3\u7B56\u65E5\u5FD7\u3001\u538B\u7F29\u5F52\u6863\u3001\u67E5\u770B\u5386\u53F2", "Init .context dir, log decisions, compress, view history"),
34
+ // ── Git ──────────────────────────────────────────────
35
+ cmd("commit", 20, "git", "Git \u63D0\u4EA4", "Git Commit", "\u667A\u80FD\u751F\u6210 conventional commit \u4FE1\u606F", "Smart conventional commit message generation"),
36
+ cmd("rollback", 21, "git", "Git \u56DE\u6EDA", "Git Rollback", "\u4EA4\u4E92\u5F0F\u56DE\u6EDA\u5206\u652F\u5230\u5386\u53F2\u7248\u672C", "Interactive rollback to historical version"),
37
+ cmd("clean-branches", 22, "git", "Git \u6E05\u7406\u5206\u652F", "Git Clean Branches", "\u5B89\u5168\u6E05\u7406\u5DF2\u5408\u5E76\u6216\u8FC7\u671F\u5206\u652F", "Safely clean merged or stale branches"),
38
+ cmd("worktree", 23, "git", "Git Worktree", "Git Worktree", "\u7BA1\u7406 Git worktree", "Manage Git worktree"),
39
+ // ── Spec (OpenSpec / OPSX) ───────────────────────────
40
+ cmd("spec-init", 30, "spec", "OpenSpec \u521D\u59CB\u5316", "OpenSpec Init", "\u521D\u59CB\u5316 OpenSpec \u73AF\u5883 + \u9A8C\u8BC1\u591A\u6A21\u578B MCP \u5DE5\u5177", "Initialize OpenSpec environment with multi-model MCP validation"),
41
+ cmd("spec-research", 31, "spec", "\u9700\u6C42\u7814\u7A76", "Spec Research", "\u9700\u6C42 \u2192 \u7EA6\u675F\u96C6\uFF08\u5E76\u884C\u63A2\u7D22 + OpenSpec \u63D0\u6848\uFF09", "Transform requirements into constraint sets via parallel exploration"),
42
+ cmd("spec-plan", 32, "spec", "\u96F6\u51B3\u7B56\u89C4\u5212", "Spec Plan", "\u591A\u6A21\u578B\u5206\u6790 \u2192 \u6D88\u9664\u6B67\u4E49 \u2192 \u96F6\u51B3\u7B56\u53EF\u6267\u884C\u8BA1\u5212", "Refine proposals into zero-decision executable plans"),
43
+ cmd("spec-impl", 33, "spec", "\u89C4\u8303\u9A71\u52A8\u5B9E\u73B0", "Spec Implementation", "\u6309\u89C4\u8303\u6267\u884C + \u591A\u6A21\u578B\u534F\u4F5C + \u5F52\u6863", "Execute changes via multi-model collaboration with spec compliance"),
44
+ cmd("spec-review", 34, "spec", "\u5F52\u6863\u524D\u5BA1\u67E5", "Spec Review", "\u53CC\u6A21\u578B\u4EA4\u53C9\u5BA1\u67E5 \u2192 Critical \u5FC5\u987B\u4FEE\u590D \u2192 \u5141\u8BB8\u5F52\u6863", "Multi-model compliance review before archiving")
45
+ ];
46
+ const LEGACY_CONFIGS = [
30
47
  cmd("workflow", 1, "development", "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41", "Full Development Workflow", "\u5B8C\u65746\u9636\u6BB5\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08\u7814\u7A76\u2192\u6784\u601D\u2192\u8BA1\u5212\u2192\u6267\u884C\u2192\u4F18\u5316\u2192\u8BC4\u5BA1\uFF09", "Full 6-phase development workflow"),
31
48
  cmd("plan", 1.5, "development", "\u591A\u6A21\u578B\u534F\u4F5C\u89C4\u5212", "Multi-Model Planning", "\u4E0A\u4E0B\u6587\u68C0\u7D22 + \u53CC\u6A21\u578B\u5206\u6790 \u2192 \u751F\u6210 Step-by-step \u5B9E\u65BD\u8BA1\u5212", "Context retrieval + dual-model analysis \u2192 Step-by-step plan"),
32
49
  cmd("execute", 1.6, "development", "\u591A\u6A21\u578B\u534F\u4F5C\u6267\u884C", "Multi-Model Execution", "\u6839\u636E\u8BA1\u5212\u83B7\u53D6\u539F\u578B \u2192 Claude \u91CD\u6784\u5B9E\u65BD \u2192 \u591A\u6A21\u578B\u5BA1\u8BA1\u4EA4\u4ED8", "Get prototype from plan \u2192 Claude refactor \u2192 Multi-model audit"),
@@ -37,7 +54,6 @@ const WORKFLOW_CONFIGS = [
37
54
  cmd("team-review", 1.95, "development", "Agent Teams \u5BA1\u67E5", "Agent Teams Review", "\u53CC\u6A21\u578B\u4EA4\u53C9\u5BA1\u67E5\u5E76\u884C\u5B9E\u65BD\u4EA7\u51FA\uFF0C\u5206\u7EA7\u5904\u7406 Critical/Warning/Info", "Dual-model cross-review with severity classification"),
38
55
  cmd("frontend", 2, "development", "\u524D\u7AEF\u4E13\u9879", "Frontend Tasks", "\u524D\u7AEF\u4E13\u9879\u4EFB\u52A1\uFF08Gemini\u4E3B\u5BFC\uFF0C\u66F4\u5FEB\u66F4\u7CBE\u51C6\uFF09", "Frontend tasks (Gemini-led, faster)"),
39
56
  cmd("codex-exec", 2.5, "development", "Codex \u6267\u884C\u8BA1\u5212", "Codex Plan Executor", "\u8BFB\u53D6 /ccg:plan \u8BA1\u5212\u6587\u4EF6\uFF0CCodex \u5168\u6743\u6267\u884C + \u591A\u6A21\u578B\u5BA1\u6838", "Read plan file from /ccg:plan, Codex executes + multi-model review"),
40
- cmd("context", 2.6, "development", "\u9879\u76EE\u4E0A\u4E0B\u6587\u7BA1\u7406", "Project Context Manager", "\u521D\u59CB\u5316 .context \u76EE\u5F55\u3001\u8BB0\u5F55\u51B3\u7B56\u65E5\u5FD7\u3001\u538B\u7F29\u5F52\u6863\u3001\u67E5\u770B\u5386\u53F2", "Init .context dir, log decisions, compress, view history"),
41
57
  cmd("backend", 3, "development", "\u540E\u7AEF\u4E13\u9879", "Backend Tasks", "\u540E\u7AEF\u4E13\u9879\u4EFB\u52A1\uFF08Codex\u4E3B\u5BFC\uFF0C\u66F4\u5FEB\u66F4\u7CBE\u51C6\uFF09", "Backend tasks (Codex-led, faster)"),
42
58
  cmd("feat", 4, "development", "\u667A\u80FD\u529F\u80FD\u5F00\u53D1", "Smart Feature Development", "\u667A\u80FD\u529F\u80FD\u5F00\u53D1 - \u81EA\u52A8\u89C4\u5212\u3001\u8BBE\u8BA1\u3001\u5B9E\u65BD", "Smart feature development - auto plan, design, implement"),
43
59
  cmd("analyze", 5, "development", "\u6280\u672F\u5206\u6790", "Technical Analysis", "\u53CC\u6A21\u578B\u6280\u672F\u5206\u6790\uFF0C\u4EC5\u5206\u6790\u4E0D\u4FEE\u6539\u4EE3\u7801", "Dual-model technical analysis, analysis only"),
@@ -45,27 +61,21 @@ const WORKFLOW_CONFIGS = [
45
61
  cmd("optimize", 7, "development", "\u6027\u80FD\u4F18\u5316", "Performance Optimization", "\u591A\u6A21\u578B\u6027\u80FD\u4F18\u5316", "Multi-model performance optimization"),
46
62
  cmd("test", 8, "development", "\u6D4B\u8BD5\u751F\u6210", "Test Generation", "\u667A\u80FD\u8DEF\u7531\u6D4B\u8BD5\u751F\u6210", "Smart routing test generation"),
47
63
  cmd("review", 9, "development", "\u4EE3\u7801\u5BA1\u67E5", "Code Review", "\u53CC\u6A21\u578B\u4EE3\u7801\u5BA1\u67E5\uFF0C\u65E0\u53C2\u6570\u65F6\u81EA\u52A8\u5BA1\u67E5 git diff", "Dual-model code review, auto-review git diff when no args"),
48
- cmd("enhance", 9.5, "development", "Prompt \u589E\u5F3A", "Prompt Enhancement", "ace-tool Prompt \u589E\u5F3A\u5DE5\u5177", "ace-tool prompt enhancement"),
49
- // ── Init ─────────────────────────────────────────────
50
- cmd("init-project", 10, "init", "\u9879\u76EE\u521D\u59CB\u5316", "Project Init", "\u521D\u59CB\u5316\u9879\u76EE AI \u4E0A\u4E0B\u6587\uFF0C\u751F\u6210 CLAUDE.md", "Initialize project AI context, generate CLAUDE.md", "init"),
51
- // ── Git ──────────────────────────────────────────────
52
- cmd("commit", 20, "git", "Git \u63D0\u4EA4", "Git Commit", "\u667A\u80FD\u751F\u6210 conventional commit \u4FE1\u606F", "Smart conventional commit message generation"),
53
- cmd("rollback", 21, "git", "Git \u56DE\u6EDA", "Git Rollback", "\u4EA4\u4E92\u5F0F\u56DE\u6EDA\u5206\u652F\u5230\u5386\u53F2\u7248\u672C", "Interactive rollback to historical version"),
54
- cmd("clean-branches", 22, "git", "Git \u6E05\u7406\u5206\u652F", "Git Clean Branches", "\u5B89\u5168\u6E05\u7406\u5DF2\u5408\u5E76\u6216\u8FC7\u671F\u5206\u652F", "Safely clean merged or stale branches"),
55
- cmd("worktree", 23, "git", "Git Worktree", "Git Worktree", "\u7BA1\u7406 Git worktree", "Manage Git worktree"),
56
- // ── Spec (OpenSpec / OPSX) ───────────────────────────
57
- cmd("spec-init", 30, "spec", "OpenSpec \u521D\u59CB\u5316", "OpenSpec Init", "\u521D\u59CB\u5316 OpenSpec \u73AF\u5883 + \u9A8C\u8BC1\u591A\u6A21\u578B MCP \u5DE5\u5177", "Initialize OpenSpec environment with multi-model MCP validation"),
58
- cmd("spec-research", 31, "spec", "\u9700\u6C42\u7814\u7A76", "Spec Research", "\u9700\u6C42 \u2192 \u7EA6\u675F\u96C6\uFF08\u5E76\u884C\u63A2\u7D22 + OpenSpec \u63D0\u6848\uFF09", "Transform requirements into constraint sets via parallel exploration"),
59
- cmd("spec-plan", 32, "spec", "\u96F6\u51B3\u7B56\u89C4\u5212", "Spec Plan", "\u591A\u6A21\u578B\u5206\u6790 \u2192 \u6D88\u9664\u6B67\u4E49 \u2192 \u96F6\u51B3\u7B56\u53EF\u6267\u884C\u8BA1\u5212", "Refine proposals into zero-decision executable plans"),
60
- cmd("spec-impl", 33, "spec", "\u89C4\u8303\u9A71\u52A8\u5B9E\u73B0", "Spec Implementation", "\u6309\u89C4\u8303\u6267\u884C + \u591A\u6A21\u578B\u534F\u4F5C + \u5F52\u6863", "Execute changes via multi-model collaboration with spec compliance"),
61
- cmd("spec-review", 34, "spec", "\u5F52\u6863\u524D\u5BA1\u67E5", "Spec Review", "\u53CC\u6A21\u578B\u4EA4\u53C9\u5BA1\u67E5 \u2192 Critical \u5FC5\u987B\u4FEE\u590D \u2192 \u5141\u8BB8\u5F52\u6863", "Multi-model compliance review before archiving")
64
+ cmd("enhance", 9.5, "development", "Prompt \u589E\u5F3A", "Prompt Enhancement", "ace-tool Prompt \u589E\u5F3A\u5DE5\u5177", "ace-tool prompt enhancement")
62
65
  ];
66
+ const WORKFLOW_CONFIGS = [...CORE_CONFIGS, ...LEGACY_CONFIGS];
63
67
  function getWorkflowConfigs() {
64
68
  return WORKFLOW_CONFIGS.sort((a, b) => a.order - b.order);
65
69
  }
66
70
  function getWorkflowById(id) {
67
71
  return WORKFLOW_CONFIGS.find((w) => w.id === id);
68
72
  }
73
+ function getCoreCommandIds() {
74
+ return CORE_CONFIGS.map((w) => w.id);
75
+ }
76
+ function getLegacyCommandIds() {
77
+ return LEGACY_CONFIGS.map((w) => w.id);
78
+ }
69
79
  function getAllCommandIds() {
70
80
  return WORKFLOW_CONFIGS.map((w) => w.id);
71
81
  }
@@ -886,6 +896,7 @@ async function copyMdTemplates(ctx, srcDir, destDir, options = {}) {
886
896
  }
887
897
  async function installCommandFiles(ctx, workflowIds) {
888
898
  const commandsDir = join(ctx.installDir, "commands", "ccg");
899
+ const legacyIds = new Set(getLegacyCommandIds());
889
900
  for (const workflowId of workflowIds) {
890
901
  const workflow = getWorkflowById(workflowId);
891
902
  if (!workflow) {
@@ -893,7 +904,8 @@ async function installCommandFiles(ctx, workflowIds) {
893
904
  continue;
894
905
  }
895
906
  for (const cmd of workflow.commands) {
896
- const srcFile = join(ctx.templateDir, "commands", `${cmd}.md`);
907
+ const srcSubdir = legacyIds.has(workflowId) ? "commands-legacy" : "commands";
908
+ const srcFile = join(ctx.templateDir, srcSubdir, `${cmd}.md`);
897
909
  const destFile = join(commandsDir, `${cmd}.md`);
898
910
  try {
899
911
  if (await fs.pathExists(srcFile)) {
@@ -1198,6 +1210,88 @@ async function installBinaryFile(ctx) {
1198
1210
  ctx.result.errors.push(`Failed to install codeagent-wrapper (non-blocking): ${error}`);
1199
1211
  }
1200
1212
  }
1213
+ async function installEngineFiles(ctx) {
1214
+ const engineSrcDir = join(ctx.templateDir, "engine");
1215
+ if (!await fs.pathExists(engineSrcDir)) return;
1216
+ const engineDestDir = join(ctx.installDir, ".ccg", "engine");
1217
+ try {
1218
+ await copyMdTemplates(ctx, engineSrcDir, engineDestDir, { inject: true });
1219
+ const strategiesSrc = join(engineSrcDir, "strategies");
1220
+ const strategiesDest = join(engineDestDir, "strategies");
1221
+ if (await fs.pathExists(strategiesSrc)) {
1222
+ await copyMdTemplates(ctx, strategiesSrc, strategiesDest, { inject: true });
1223
+ }
1224
+ } catch (error) {
1225
+ ctx.result.errors.push(`Failed to install engine files: ${error}`);
1226
+ }
1227
+ }
1228
+ const HOOK_FILES = ["task-utils.js", "workflow-state.js", "session-start.js", "subagent-context.js", "skill-router.js"];
1229
+ async function installHookScripts(ctx) {
1230
+ const hooksSrcDir = join(ctx.templateDir, "hooks");
1231
+ if (!await fs.pathExists(hooksSrcDir)) return;
1232
+ const hooksDestDir = join(ctx.installDir, "hooks", "ccg");
1233
+ await fs.ensureDir(hooksDestDir);
1234
+ try {
1235
+ for (const file of HOOK_FILES) {
1236
+ const src = join(hooksSrcDir, file);
1237
+ const dest = join(hooksDestDir, file);
1238
+ if (await fs.pathExists(src)) {
1239
+ await fs.copy(src, dest, { overwrite: true });
1240
+ }
1241
+ }
1242
+ } catch (error) {
1243
+ ctx.result.errors.push(`Failed to install hook scripts: ${error}`);
1244
+ }
1245
+ }
1246
+ async function registerHooksInSettings(ctx) {
1247
+ const settingsPath = join(ctx.installDir, "settings.json");
1248
+ const hooksDir = join(ctx.installDir, "hooks", "ccg");
1249
+ try {
1250
+ let settings = {};
1251
+ if (await fs.pathExists(settingsPath)) {
1252
+ try {
1253
+ settings = JSON.parse(await fs.readFile(settingsPath, "utf-8"));
1254
+ } catch {
1255
+ settings = {};
1256
+ }
1257
+ }
1258
+ const hooks = settings.hooks || {};
1259
+ const ccgHookDefs = {
1260
+ UserPromptSubmit: {
1261
+ hooks: [
1262
+ { type: "command", command: `node ${join(hooksDir, "workflow-state.js")}`, timeout: 1e4 },
1263
+ { type: "command", command: `node ${join(hooksDir, "skill-router.js")}`, timeout: 5e3 }
1264
+ ]
1265
+ },
1266
+ SessionStart: {
1267
+ matcher: "startup|clear|compact",
1268
+ hooks: [{ type: "command", command: `node ${join(hooksDir, "session-start.js")}`, timeout: 15e3 }]
1269
+ },
1270
+ PreToolUse: {
1271
+ matcher: "Bash|Agent",
1272
+ hooks: [{ type: "command", command: `node ${join(hooksDir, "subagent-context.js")}`, timeout: 15e3 }]
1273
+ }
1274
+ };
1275
+ for (const [event, def] of Object.entries(ccgHookDefs)) {
1276
+ const eventHooks = hooks[event] || [];
1277
+ const ccgCommand = def.hooks[0].command;
1278
+ const existingIdx = eventHooks.findIndex((h) => {
1279
+ const hHooks = h.hooks || [];
1280
+ return hHooks.some((hh) => typeof hh.command === "string" && hh.command.includes("hooks/ccg/"));
1281
+ });
1282
+ if (existingIdx >= 0) {
1283
+ eventHooks[existingIdx] = def;
1284
+ } else {
1285
+ eventHooks.push(def);
1286
+ }
1287
+ hooks[event] = eventHooks;
1288
+ }
1289
+ settings.hooks = hooks;
1290
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
1291
+ } catch (error) {
1292
+ ctx.result.errors.push(`Failed to register hooks in settings.json: ${error}`);
1293
+ }
1294
+ }
1201
1295
  async function installWorkflows(workflowIds, installDir, force = false, config) {
1202
1296
  const ctx = {
1203
1297
  installDir,
@@ -1210,7 +1304,7 @@ async function installWorkflows(workflowIds, installDir, force = false, config)
1210
1304
  review: { models: ["codex", "gemini"] }
1211
1305
  },
1212
1306
  liteMode: config?.liteMode || false,
1213
- mcpProvider: config?.mcpProvider || "ace-tool",
1307
+ mcpProvider: config?.mcpProvider || "fast-context",
1214
1308
  skipImpeccable: config?.skipImpeccable || false
1215
1309
  },
1216
1310
  templateDir: join(PACKAGE_ROOT$1, "templates"),
@@ -1231,7 +1325,11 @@ async function installWorkflows(workflowIds, installDir, force = false, config)
1231
1325
  await fs.ensureDir(join(installDir, "commands", "ccg"));
1232
1326
  await fs.ensureDir(join(installDir, ".ccg"));
1233
1327
  await fs.ensureDir(join(installDir, ".ccg", "prompts"));
1328
+ await fs.ensureDir(join(installDir, ".ccg", "engine", "strategies"));
1234
1329
  await installCommandFiles(ctx, workflowIds);
1330
+ await installEngineFiles(ctx);
1331
+ await installHookScripts(ctx);
1332
+ await registerHooksInSettings(ctx);
1235
1333
  await installAgentFiles(ctx);
1236
1334
  await installPromptFiles(ctx);
1237
1335
  await installSkillFiles(ctx);
@@ -1327,6 +1425,8 @@ const installer = {
1327
1425
  collectInvocableSkills: collectInvocableSkills,
1328
1426
  collectSkills: collectSkills,
1329
1427
  getAllCommandIds: getAllCommandIds,
1428
+ getCoreCommandIds: getCoreCommandIds,
1429
+ getLegacyCommandIds: getLegacyCommandIds,
1330
1430
  getWorkflowById: getWorkflowById,
1331
1431
  getWorkflowConfigs: getWorkflowConfigs,
1332
1432
  injectConfigVariables: injectConfigVariables,
@@ -1795,15 +1895,31 @@ const zhCN = {
1795
1895
  collaboration: "\u534F\u4F5C\u6A21\u5F0F:",
1796
1896
  workflows: "\u5DE5\u4F5C\u6D41:",
1797
1897
  selected: "\u4E2A\u5DF2\u9009\u62E9",
1898
+ apiProvider: "API \u63D0\u4F9B\u65B9",
1798
1899
  modelRouting: "\u6A21\u578B\u8DEF\u7531",
1900
+ geminiModel: "Gemini \u578B\u53F7",
1799
1901
  commandCount: "\u547D\u4EE4\u6570\u91CF",
1800
1902
  mcpTool: "MCP \u5DE5\u5177",
1801
1903
  webUI: "Web UI",
1904
+ apiSelfManaged: "\u81EA\u884C\u7BA1\u7406\uFF08CCG \u4E0D\u4ECB\u5165\uFF09",
1802
1905
  pendingConfig: "\u5F85\u914D\u7F6E",
1803
1906
  skipped: "\u8DF3\u8FC7",
1804
1907
  enabled: "\u542F\u7528",
1805
1908
  disabled: "\u7981\u7528"
1806
1909
  },
1910
+ nav: {
1911
+ back: "\u8FD4\u56DE\u4E0A\u4E00\u6B65",
1912
+ cancel: "\u53D6\u6D88\u5B89\u88C5"
1913
+ },
1914
+ summaryMenu: {
1915
+ prompt: "\u786E\u8BA4\u914D\u7F6E\uFF1F",
1916
+ confirm: "\u786E\u8BA4\u5B89\u88C5",
1917
+ editApi: "\u6539 API \u914D\u7F6E",
1918
+ editModel: "\u6539\u6A21\u578B\u8DEF\u7531",
1919
+ editMcp: "\u6539 MCP \u5DE5\u5177",
1920
+ editPerf: "\u6539\u6027\u80FD\u6A21\u5F0F",
1921
+ cancel: "\u53D6\u6D88\u5B89\u88C5"
1922
+ },
1807
1923
  mcp: {
1808
1924
  title: "MCP \u4EE3\u7801\u68C0\u7D22\u5DE5\u5177\u914D\u7F6E",
1809
1925
  selectProvider: "\u9009\u62E9\u4EE3\u7801\u68C0\u7D22 MCP \u5DE5\u5177",
@@ -1851,7 +1967,9 @@ const zhCN = {
1851
1967
  cwConfiguring: "\u6B63\u5728\u914D\u7F6E ContextWeaver MCP...",
1852
1968
  cwFailed: "ContextWeaver MCP \u914D\u7F6E\u5931\u8D25",
1853
1969
  cwConfigFile: "\u914D\u7F6E\u6587\u4EF6",
1854
- cwIndexHint: "\u9996\u6B21\u4F7F\u7528\u9700\u8981\u7D22\u5F15\u4EE3\u7801\u5E93\uFF1A"
1970
+ cwIndexHint: "\u9996\u6B21\u4F7F\u7528\u9700\u8981\u7D22\u5F15\u4EE3\u7801\u5E93\uFF1A",
1971
+ gatePrompt: "\u914D\u7F6E MCP \u5DE5\u5177\uFF1F",
1972
+ gateContinue: "\u5F00\u59CB\u914D\u7F6E"
1855
1973
  },
1856
1974
  api: {
1857
1975
  title: "Claude Code API \u914D\u7F6E",
@@ -1861,6 +1979,8 @@ const zhCN = {
1861
1979
  thirdPartyOption: "\u7B2C\u4E09\u65B9 API \u4EE3\u7406\uFF08\u81EA\u5B9A\u4E49 URL + Key\uFF09",
1862
1980
  sponsor302AI: "302.AI\uFF08\u6309\u7528\u91CF\u4ED8\u8D39\u7684\u4F01\u4E1A\u7EA7 AI \u8D44\u6E90\u5E73\u53F0\uFF09",
1863
1981
  sponsor302AIGetKey: "\u83B7\u53D6 API Key",
1982
+ skipOption: "\u8DF3\u8FC7 \u2014 \u6211\u5DF2\u901A\u8FC7 cc-switch / \u5176\u4ED6\u5DE5\u5177\u81EA\u884C\u914D\u7F6E",
1983
+ skipNoticeTitle: "API \u914D\u7F6E\u5DF2\u8DF3\u8FC7\uFF0CCCG \u4E0D\u4F1A\u4FEE\u6539 settings.json \u4E2D\u7684 ANTHROPIC_*",
1864
1984
  urlPrompt: "API URL",
1865
1985
  urlRequired: "\u5FC5\u586B",
1866
1986
  keyPrompt: "API Key",
@@ -2271,15 +2391,31 @@ const en = {
2271
2391
  collaboration: "Collaboration:",
2272
2392
  workflows: "Workflows:",
2273
2393
  selected: "selected",
2394
+ apiProvider: "API Provider",
2274
2395
  modelRouting: "Model Routing",
2396
+ geminiModel: "Gemini Model",
2275
2397
  commandCount: "Commands",
2276
2398
  mcpTool: "MCP Tool",
2277
2399
  webUI: "Web UI",
2400
+ apiSelfManaged: "self-managed (CCG does not touch)",
2278
2401
  pendingConfig: "pending",
2279
2402
  skipped: "skipped",
2280
2403
  enabled: "Enabled",
2281
2404
  disabled: "Disabled"
2282
2405
  },
2406
+ nav: {
2407
+ back: "Back to previous step",
2408
+ cancel: "Cancel installation"
2409
+ },
2410
+ summaryMenu: {
2411
+ prompt: "Confirm configuration?",
2412
+ confirm: "Confirm and install",
2413
+ editApi: "Edit API configuration",
2414
+ editModel: "Edit model routing",
2415
+ editMcp: "Edit MCP tools",
2416
+ editPerf: "Edit performance mode",
2417
+ cancel: "Cancel installation"
2418
+ },
2283
2419
  mcp: {
2284
2420
  title: "MCP Code Retrieval Tool",
2285
2421
  selectProvider: "Select code retrieval MCP tool",
@@ -2327,7 +2463,9 @@ const en = {
2327
2463
  cwConfiguring: "Configuring ContextWeaver MCP...",
2328
2464
  cwFailed: "ContextWeaver MCP configuration failed",
2329
2465
  cwConfigFile: "Config file",
2330
- cwIndexHint: "First use requires indexing your codebase:"
2466
+ cwIndexHint: "First use requires indexing your codebase:",
2467
+ gatePrompt: "Configure MCP tools?",
2468
+ gateContinue: "Start configuration"
2331
2469
  },
2332
2470
  api: {
2333
2471
  title: "Claude Code API Configuration",
@@ -2337,6 +2475,8 @@ const en = {
2337
2475
  thirdPartyOption: "Third-party API proxy (custom URL + Key)",
2338
2476
  sponsor302AI: "302.AI (Pay-as-you-go Enterprise AI Resource Hub)",
2339
2477
  sponsor302AIGetKey: "Get API Key",
2478
+ skipOption: "Skip \u2014 I've already configured API via cc-switch / other tools",
2479
+ skipNoticeTitle: "API configuration skipped \u2014 CCG will not touch ANTHROPIC_* in settings.json",
2340
2480
  urlPrompt: "API URL",
2341
2481
  urlRequired: "Required",
2342
2482
  keyPrompt: "API Key",
@@ -2707,7 +2847,7 @@ function createDefaultConfig(options) {
2707
2847
  backup: join(CCG_DIR, "backup")
2708
2848
  },
2709
2849
  mcp: {
2710
- provider: options.mcpProvider || "ace-tool",
2850
+ provider: options.mcpProvider || "fast-context",
2711
2851
  setup_url: "https://augmentcode.com/"
2712
2852
  },
2713
2853
  performance: {
@@ -2903,6 +3043,22 @@ For example, when using the \`fastapi\` library to encapsulate an API endpoint,
2903
3043
  await fs.ensureDir(rulesDir);
2904
3044
  await fs.writeFile(rulePath, prompt, "utf-8");
2905
3045
  }
3046
+ const BACK_SENTINEL = "__ccg_back__";
3047
+ const CANCEL_SENTINEL = "__ccg_cancel__";
3048
+ function navSentinels(canGoBack) {
3049
+ const items = [new inquirer.Separator()];
3050
+ if (canGoBack) {
3051
+ items.push({
3052
+ name: `${ansis.cyan("\u2190")} ${i18n.t("init:nav.back")}`,
3053
+ value: BACK_SENTINEL
3054
+ });
3055
+ }
3056
+ items.push({
3057
+ name: `${ansis.red("\xD7")} ${i18n.t("init:nav.cancel")}`,
3058
+ value: CANCEL_SENTINEL
3059
+ });
3060
+ return items;
3061
+ }
2906
3062
  async function installGrokSearchMcp(keys) {
2907
3063
  const env = {};
2908
3064
  if (keys.tavilyKey)
@@ -2954,7 +3110,7 @@ async function init(options = {}) {
2954
3110
  let backendModels = ["codex"];
2955
3111
  let geminiModel = "gemini-3.1-pro-preview";
2956
3112
  const mode = "smart";
2957
- const selectedWorkflows = getAllCommandIds();
3113
+ let selectedWorkflows = getCoreCommandIds();
2958
3114
  if (options.skipPrompt) {
2959
3115
  const existingConfig = await readCcgConfig();
2960
3116
  if (existingConfig?.routing) {
@@ -2962,10 +3118,18 @@ async function init(options = {}) {
2962
3118
  backendModels = existingConfig.routing.backend?.models || ["codex"];
2963
3119
  geminiModel = existingConfig.routing.geminiModel || "gemini-3.1-pro-preview";
2964
3120
  }
3121
+ if (existingConfig?.workflows?.installed) {
3122
+ const hadLegacy = existingConfig.workflows.installed.some(
3123
+ (w) => ["workflow", "plan", "execute", "frontend", "backend", "feat", "debug", "team"].includes(w)
3124
+ );
3125
+ if (hadLegacy) {
3126
+ selectedWorkflows = getAllCommandIds();
3127
+ }
3128
+ }
2965
3129
  }
2966
3130
  let liteMode = false;
2967
3131
  let skipImpeccable = false;
2968
- let mcpProvider = "ace-tool";
3132
+ let mcpProvider = "fast-context";
2969
3133
  let aceToolBaseUrl = "";
2970
3134
  let aceToolToken = "";
2971
3135
  let contextWeaverApiKey = "";
@@ -2979,273 +3143,460 @@ async function init(options = {}) {
2979
3143
  let grokApiKey = "";
2980
3144
  let apiUrl = "";
2981
3145
  let apiKey = "";
3146
+ if (options.skipPrompt) {
3147
+ const existingConfig = await readCcgConfig();
3148
+ if (existingConfig?.performance?.liteMode !== void 0) {
3149
+ liteMode = existingConfig.performance.liteMode;
3150
+ }
3151
+ if (existingConfig?.performance?.skipImpeccable !== void 0) {
3152
+ skipImpeccable = existingConfig.performance.skipImpeccable;
3153
+ }
3154
+ if (options.skipMcp) {
3155
+ mcpProvider = existingConfig?.mcp?.provider || "skip";
3156
+ }
3157
+ }
2982
3158
  if (!options.skipPrompt) {
2983
- console.log();
2984
- console.log(ansis.cyan.bold(` \u{1F511} Step 1/4 \u2014 ${i18n.t("init:api.title")}`));
2985
- console.log();
2986
- const { apiProvider } = await inquirer.prompt([{
2987
- type: "list",
2988
- name: "apiProvider",
2989
- message: i18n.t("init:api.providerPrompt"),
2990
- choices: [
2991
- { name: `${ansis.green("\u25CF")} ${i18n.t("init:api.officialOption")}`, value: "official" },
2992
- { name: `${ansis.cyan("\u25CF")} ${i18n.t("init:api.thirdPartyOption")}`, value: "thirdparty" },
2993
- { name: `${ansis.yellow("\u2605")} ${i18n.t("init:api.sponsor302AI")} ${ansis.gray("\u2014 https://share.302.ai/oUDqQ6")}`, value: "302ai" }
2994
- ]
2995
- }]);
2996
- if (apiProvider === "302ai") {
2997
- apiUrl = "https://api.302.ai/cc";
3159
+ const existingConfig = await readCcgConfig();
3160
+ if (existingConfig?.routing) {
3161
+ const ef = existingConfig.routing.frontend?.primary;
3162
+ const eb = existingConfig.routing.backend?.primary;
3163
+ if (ef)
3164
+ frontendModels = [ef];
3165
+ if (eb)
3166
+ backendModels = [eb];
3167
+ if (existingConfig.routing.geminiModel)
3168
+ geminiModel = existingConfig.routing.geminiModel;
3169
+ }
3170
+ if (existingConfig?.performance?.liteMode !== void 0) {
3171
+ liteMode = existingConfig.performance.liteMode;
3172
+ }
3173
+ async function runApiStep(canGoBack) {
2998
3174
  console.log();
2999
- console.log(` ${ansis.yellow("\u2605")} ${i18n.t("init:api.sponsor302AIGetKey")}: ${ansis.cyan.underline("https://share.302.ai/oUDqQ6")}`);
3175
+ console.log(ansis.cyan.bold(` \u{1F511} Step 1/4 \u2014 ${i18n.t("init:api.title")}`));
3000
3176
  console.log();
3001
- const { key } = await inquirer.prompt([{
3002
- type: "password",
3003
- name: "key",
3004
- message: `302.AI API Key ${ansis.gray(`(${i18n.t("init:api.keyRequired")})`)}`,
3005
- mask: "*",
3006
- validate: (v) => v.trim() !== "" || i18n.t("init:api.enterKey")
3177
+ const { apiProvider } = await inquirer.prompt([{
3178
+ type: "list",
3179
+ name: "apiProvider",
3180
+ message: i18n.t("init:api.providerPrompt"),
3181
+ choices: [
3182
+ { name: `${ansis.green("\u25CF")} ${i18n.t("init:api.officialOption")}`, value: "official" },
3183
+ { name: `${ansis.cyan("\u25CF")} ${i18n.t("init:api.thirdPartyOption")}`, value: "thirdparty" },
3184
+ { name: `${ansis.yellow("\u2605")} ${i18n.t("init:api.sponsor302AI")} ${ansis.gray("\u2014 https://share.302.ai/oUDqQ6")}`, value: "302ai" },
3185
+ { name: `${ansis.gray("\u25CB")} ${i18n.t("init:api.skipOption")}`, value: "skip" },
3186
+ ...navSentinels(canGoBack)
3187
+ ]
3007
3188
  }]);
3008
- apiKey = key?.trim() || "";
3009
- } else if (apiProvider === "thirdparty") {
3010
- const apiAnswers = await inquirer.prompt([
3011
- {
3012
- type: "input",
3013
- name: "url",
3014
- message: `API URL ${ansis.gray(`(${i18n.t("init:api.urlRequired")})`)}`,
3015
- validate: (v) => v.trim() !== "" || i18n.t("init:api.enterUrl")
3016
- },
3017
- {
3189
+ if (apiProvider === BACK_SENTINEL)
3190
+ return "back";
3191
+ if (apiProvider === CANCEL_SENTINEL)
3192
+ return "cancel";
3193
+ apiUrl = "";
3194
+ apiKey = "";
3195
+ if (apiProvider === "302ai") {
3196
+ apiUrl = "https://api.302.ai/cc";
3197
+ console.log();
3198
+ console.log(` ${ansis.yellow("\u2605")} ${i18n.t("init:api.sponsor302AIGetKey")}: ${ansis.cyan.underline("https://share.302.ai/oUDqQ6")}`);
3199
+ console.log();
3200
+ const { key } = await inquirer.prompt([{
3018
3201
  type: "password",
3019
3202
  name: "key",
3020
- message: `API Key ${ansis.gray(`(${i18n.t("init:api.keyRequired")})`)}`,
3203
+ message: `302.AI API Key ${ansis.gray(`(${i18n.t("init:api.keyRequired")})`)}`,
3021
3204
  mask: "*",
3022
3205
  validate: (v) => v.trim() !== "" || i18n.t("init:api.enterKey")
3023
- }
3024
- ]);
3025
- apiUrl = apiAnswers.url?.trim() || "";
3026
- apiKey = apiAnswers.key?.trim() || "";
3206
+ }]);
3207
+ apiKey = key?.trim() || "";
3208
+ } else if (apiProvider === "thirdparty") {
3209
+ const apiAnswers = await inquirer.prompt([
3210
+ {
3211
+ type: "input",
3212
+ name: "url",
3213
+ message: `API URL ${ansis.gray(`(${i18n.t("init:api.urlRequired")})`)}`,
3214
+ validate: (v) => v.trim() !== "" || i18n.t("init:api.enterUrl")
3215
+ },
3216
+ {
3217
+ type: "password",
3218
+ name: "key",
3219
+ message: `API Key ${ansis.gray(`(${i18n.t("init:api.keyRequired")})`)}`,
3220
+ mask: "*",
3221
+ validate: (v) => v.trim() !== "" || i18n.t("init:api.enterKey")
3222
+ }
3223
+ ]);
3224
+ apiUrl = apiAnswers.url?.trim() || "";
3225
+ apiKey = apiAnswers.key?.trim() || "";
3226
+ } else if (apiProvider === "skip") {
3227
+ console.log();
3228
+ console.log(` ${ansis.gray("\u25CB")} ${i18n.t("init:api.skipNoticeTitle")}`);
3229
+ }
3230
+ return "next";
3027
3231
  }
3028
- }
3029
- if (!options.skipPrompt) {
3030
- const existingConfig = await readCcgConfig();
3031
- console.log();
3032
- console.log(ansis.cyan.bold(` \u{1F9E0} Step 2/4 \u2014 ${i18n.t("init:model.title")}`));
3033
- console.log();
3034
- const { selectedFrontend } = await inquirer.prompt([{
3035
- type: "list",
3036
- name: "selectedFrontend",
3037
- message: i18n.t("init:model.selectFrontend"),
3038
- choices: [
3039
- { name: `Gemini ${ansis.green(`(${i18n.t("init:model.recommended")})`)}`, value: "gemini" },
3040
- { name: "Codex", value: "codex" }
3041
- ],
3042
- default: existingConfig?.routing?.frontend?.primary || "gemini"
3043
- }]);
3044
- const { selectedBackend } = await inquirer.prompt([{
3045
- type: "list",
3046
- name: "selectedBackend",
3047
- message: i18n.t("init:model.selectBackend"),
3048
- choices: [
3049
- { name: "Gemini", value: "gemini" },
3050
- { name: `Codex ${ansis.green(`(${i18n.t("init:model.recommended")})`)}`, value: "codex" }
3051
- ],
3052
- default: existingConfig?.routing?.backend?.primary || "codex"
3053
- }]);
3054
- frontendModels = [selectedFrontend];
3055
- backendModels = [selectedBackend];
3056
- if (selectedFrontend === "gemini" || selectedBackend === "gemini") {
3057
- const { selectedGeminiModel } = await inquirer.prompt([{
3232
+ async function runModelStep(canGoBack) {
3233
+ console.log();
3234
+ console.log(ansis.cyan.bold(` \u{1F9E0} Step 2/4 \u2014 ${i18n.t("init:model.title")}`));
3235
+ console.log();
3236
+ const { selectedFrontend } = await inquirer.prompt([{
3237
+ type: "list",
3238
+ name: "selectedFrontend",
3239
+ message: i18n.t("init:model.selectFrontend"),
3240
+ choices: [
3241
+ { name: `Gemini ${ansis.green(`(${i18n.t("init:model.recommended")})`)}`, value: "gemini" },
3242
+ { name: "Codex", value: "codex" },
3243
+ ...navSentinels(canGoBack)
3244
+ ],
3245
+ default: frontendModels[0] || "gemini"
3246
+ }]);
3247
+ if (selectedFrontend === BACK_SENTINEL)
3248
+ return "back";
3249
+ if (selectedFrontend === CANCEL_SENTINEL)
3250
+ return "cancel";
3251
+ const { selectedBackend } = await inquirer.prompt([{
3058
3252
  type: "list",
3059
- name: "selectedGeminiModel",
3060
- message: i18n.t("init:model.selectGeminiModel"),
3253
+ name: "selectedBackend",
3254
+ message: i18n.t("init:model.selectBackend"),
3061
3255
  choices: [
3062
- { name: `gemini-3.1-pro-preview ${ansis.green(`(${i18n.t("init:model.recommended")})`)}`, value: "gemini-3.1-pro-preview" },
3063
- { name: "gemini-2.5-flash", value: "gemini-2.5-flash" },
3064
- { name: `${i18n.t("init:model.custom")}`, value: "custom" }
3256
+ { name: "Gemini", value: "gemini" },
3257
+ { name: `Codex ${ansis.green(`(${i18n.t("init:model.recommended")})`)}`, value: "codex" }
3065
3258
  ],
3066
- default: existingConfig?.routing?.geminiModel || "gemini-3.1-pro-preview"
3259
+ default: backendModels[0] || "codex"
3067
3260
  }]);
3068
- if (selectedGeminiModel === "custom") {
3069
- const { customModel } = await inquirer.prompt([{
3070
- type: "input",
3071
- name: "customModel",
3072
- message: i18n.t("init:model.enterCustomModel"),
3073
- validate: (v) => v.trim() !== "" || i18n.t("init:model.enterCustomModel")
3261
+ frontendModels = [selectedFrontend];
3262
+ backendModels = [selectedBackend];
3263
+ if (selectedFrontend === "gemini" || selectedBackend === "gemini") {
3264
+ const { selectedGeminiModel } = await inquirer.prompt([{
3265
+ type: "list",
3266
+ name: "selectedGeminiModel",
3267
+ message: i18n.t("init:model.selectGeminiModel"),
3268
+ choices: [
3269
+ { name: `gemini-3.1-pro-preview ${ansis.green(`(${i18n.t("init:model.recommended")})`)}`, value: "gemini-3.1-pro-preview" },
3270
+ { name: "gemini-2.5-flash", value: "gemini-2.5-flash" },
3271
+ { name: `${i18n.t("init:model.custom")}`, value: "custom" }
3272
+ ],
3273
+ default: geminiModel || "gemini-3.1-pro-preview"
3074
3274
  }]);
3075
- geminiModel = customModel.trim();
3076
- } else {
3077
- geminiModel = selectedGeminiModel;
3078
- }
3079
- }
3080
- }
3081
- if (options.skipMcp) {
3082
- const existingConfig = await readCcgConfig();
3083
- mcpProvider = existingConfig?.mcp?.provider || "skip";
3084
- } else if (!options.skipPrompt) {
3085
- console.log();
3086
- console.log(ansis.cyan.bold(` \u{1F527} Step 3/4 \u2014 ${i18n.t("init:mcp.title")}`));
3087
- console.log();
3088
- const { selectedTools } = await inquirer.prompt([{
3089
- type: "checkbox",
3090
- name: "selectedTools",
3091
- message: i18n.t("init:mcp.selectTools"),
3092
- choices: [
3093
- {
3094
- name: `ace-tool ${ansis.green(`(${i18n.t("common:info")})`)} ${ansis.gray("\u2014 search_context \u4EE3\u7801\u68C0\u7D22")}`,
3095
- value: "ace-tool",
3096
- checked: true
3097
- },
3098
- {
3099
- name: `fast-context ${ansis.gray("\u2014 AI \u9A71\u52A8\u8BED\u4E49\u641C\u7D22")}`,
3100
- value: "fast-context"
3101
- },
3102
- {
3103
- name: `context7 ${ansis.green("(free)")} ${ansis.gray("\u2014 \u5E93\u6587\u6863\u67E5\u8BE2")}`,
3104
- value: "context7",
3105
- checked: true
3106
- },
3107
- {
3108
- name: `grok-search ${ansis.gray("\u2014 \u8054\u7F51\u641C\u7D22 (\u9700 API Key)")}`,
3109
- value: "grok-search"
3110
- },
3111
- {
3112
- name: `contextweaver ${ansis.gray("\u2014 \u7845\u57FA\u6D41\u52A8\u5D4C\u5165\u68C0\u7D22 (\u9700 API Key)")}`,
3113
- value: "contextweaver"
3275
+ if (selectedGeminiModel === "custom") {
3276
+ const { customModel } = await inquirer.prompt([{
3277
+ type: "input",
3278
+ name: "customModel",
3279
+ message: i18n.t("init:model.enterCustomModel"),
3280
+ default: geminiModel || "",
3281
+ validate: (v) => v.trim() !== "" || i18n.t("init:model.enterCustomModel")
3282
+ }]);
3283
+ geminiModel = customModel.trim();
3284
+ } else {
3285
+ geminiModel = selectedGeminiModel;
3114
3286
  }
3115
- ]
3116
- }]);
3117
- const hasAceTool = selectedTools.includes("ace-tool");
3118
- const hasFastContext = selectedTools.includes("fast-context");
3119
- const hasContextWeaver = selectedTools.includes("contextweaver");
3120
- wantFastContext = hasFastContext;
3121
- wantGrokSearch = selectedTools.includes("grok-search");
3122
- if (hasAceTool) {
3123
- mcpProvider = "ace-tool";
3124
- } else if (hasFastContext) {
3125
- mcpProvider = "fast-context";
3126
- } else if (hasContextWeaver) {
3127
- mcpProvider = "contextweaver";
3128
- } else {
3129
- mcpProvider = "skip";
3287
+ }
3288
+ return "next";
3130
3289
  }
3131
- if (hasAceTool) {
3132
- console.log();
3133
- console.log(ansis.cyan.bold(` \u{1F527} ace-tool MCP`));
3290
+ async function runMcpStep(canGoBack) {
3291
+ if (options.skipMcp) {
3292
+ mcpProvider = existingConfig?.mcp?.provider || "skip";
3293
+ return "next";
3294
+ }
3134
3295
  console.log();
3135
- console.log(` ${ansis.gray("\u2022")} ${ansis.cyan(i18n.t("init:mcp.officialService"))}: ${ansis.underline("https://augmentcode.com/")}`);
3136
- console.log(` ${ansis.gray("\u2022")} ${ansis.cyan(i18n.t("init:mcp.proxyService"))} ${ansis.yellow(`(${i18n.t("init:mcp.noSignup")})`)}: ${ansis.underline("https://acemcp.heroman.wtf/")}`);
3296
+ console.log(ansis.cyan.bold(` \u{1F527} Step 3/4 \u2014 ${i18n.t("init:mcp.title")}`));
3137
3297
  console.log();
3138
- const aceAnswers = await inquirer.prompt([
3139
- {
3140
- type: "input",
3141
- name: "baseUrl",
3142
- message: `Base URL ${ansis.gray(`(${i18n.t("init:mcp.baseUrlHint")})`)}`,
3143
- default: ""
3144
- },
3145
- {
3298
+ const { gate } = await inquirer.prompt([{
3299
+ type: "list",
3300
+ name: "gate",
3301
+ message: i18n.t("init:mcp.gatePrompt"),
3302
+ choices: [
3303
+ { name: `${ansis.green("\u25CF")} ${i18n.t("init:mcp.gateContinue")}`, value: "continue" },
3304
+ ...navSentinels(canGoBack)
3305
+ ]
3306
+ }]);
3307
+ if (gate === BACK_SENTINEL)
3308
+ return "back";
3309
+ if (gate === CANCEL_SENTINEL)
3310
+ return "cancel";
3311
+ aceToolBaseUrl = "";
3312
+ aceToolToken = "";
3313
+ fastContextApiKey = "";
3314
+ fastContextIncludeSnippets = false;
3315
+ contextWeaverApiKey = "";
3316
+ wantFastContext = false;
3317
+ wantGrokSearch = false;
3318
+ tavilyKey = "";
3319
+ firecrawlKey = "";
3320
+ grokApiUrl = "";
3321
+ grokApiKey = "";
3322
+ const { selectedTools } = await inquirer.prompt([{
3323
+ type: "checkbox",
3324
+ name: "selectedTools",
3325
+ message: i18n.t("init:mcp.selectTools"),
3326
+ choices: [
3327
+ {
3328
+ name: `fast-context ${ansis.green(`(${i18n.t("common:recommended")})`)} ${ansis.gray("\u2014 AI \u9A71\u52A8\u8BED\u4E49\u641C\u7D22")}`,
3329
+ value: "fast-context",
3330
+ checked: true
3331
+ },
3332
+ {
3333
+ name: `ace-tool ${ansis.gray("\u2014 search_context \u4EE3\u7801\u68C0\u7D22")}`,
3334
+ value: "ace-tool"
3335
+ },
3336
+ {
3337
+ name: `context7 ${ansis.green("(free)")} ${ansis.gray("\u2014 \u5E93\u6587\u6863\u67E5\u8BE2")}`,
3338
+ value: "context7",
3339
+ checked: true
3340
+ },
3341
+ {
3342
+ name: `grok-search ${ansis.gray("\u2014 \u8054\u7F51\u641C\u7D22 (\u9700 API Key)")}`,
3343
+ value: "grok-search"
3344
+ },
3345
+ {
3346
+ name: `contextweaver ${ansis.gray("\u2014 \u7845\u57FA\u6D41\u52A8\u5D4C\u5165\u68C0\u7D22 (\u9700 API Key)")}`,
3347
+ value: "contextweaver"
3348
+ }
3349
+ ]
3350
+ }]);
3351
+ const hasAceTool = selectedTools.includes("ace-tool");
3352
+ const hasFastContext = selectedTools.includes("fast-context");
3353
+ const hasContextWeaver = selectedTools.includes("contextweaver");
3354
+ wantFastContext = hasFastContext;
3355
+ wantGrokSearch = selectedTools.includes("grok-search");
3356
+ if (hasAceTool) {
3357
+ mcpProvider = "ace-tool";
3358
+ } else if (hasFastContext) {
3359
+ mcpProvider = "fast-context";
3360
+ } else if (hasContextWeaver) {
3361
+ mcpProvider = "contextweaver";
3362
+ } else {
3363
+ mcpProvider = "skip";
3364
+ }
3365
+ if (hasAceTool) {
3366
+ console.log();
3367
+ console.log(ansis.cyan.bold(` \u{1F527} ace-tool MCP`));
3368
+ console.log();
3369
+ console.log(` ${ansis.gray("\u2022")} ${ansis.cyan(i18n.t("init:mcp.officialService"))}: ${ansis.underline("https://augmentcode.com/")}`);
3370
+ console.log(` ${ansis.gray("\u2022")} ${ansis.cyan(i18n.t("init:mcp.proxyService"))} ${ansis.yellow(`(${i18n.t("init:mcp.noSignup")})`)}: ${ansis.underline("https://acemcp.heroman.wtf/")}`);
3371
+ console.log();
3372
+ const aceAnswers = await inquirer.prompt([
3373
+ {
3374
+ type: "input",
3375
+ name: "baseUrl",
3376
+ message: `Base URL ${ansis.gray(`(${i18n.t("init:mcp.baseUrlHint")})`)}`,
3377
+ default: ""
3378
+ },
3379
+ {
3380
+ type: "password",
3381
+ name: "token",
3382
+ message: `Token ${ansis.gray(`(${i18n.t("init:mcp.tokenRequired")})`)}`,
3383
+ mask: "*",
3384
+ validate: (input) => input.trim() !== "" || i18n.t("init:mcp.enterToken")
3385
+ }
3386
+ ]);
3387
+ aceToolBaseUrl = aceAnswers.baseUrl || "";
3388
+ aceToolToken = aceAnswers.token || "";
3389
+ }
3390
+ if (hasFastContext) {
3391
+ console.log();
3392
+ console.log(ansis.cyan.bold(` \u{1F527} fast-context MCP`));
3393
+ console.log(ansis.gray(` Windsurf Fast Context \u2014 ${i18n.t("init:mcp.fcAutoExtract")}`));
3394
+ console.log();
3395
+ const fcAnswers = await inquirer.prompt([
3396
+ {
3397
+ type: "input",
3398
+ name: "apiKey",
3399
+ message: `WINDSURF_API_KEY ${ansis.gray(`(${i18n.t("init:mcp.fcLeaveEmpty")})`)}`,
3400
+ default: ""
3401
+ },
3402
+ {
3403
+ type: "list",
3404
+ name: "includeSnippets",
3405
+ message: i18n.t("init:mcp.fcSnippetMode"),
3406
+ choices: [
3407
+ { name: `${i18n.t("init:mcp.fcPathOnly")} ${ansis.gray(`(${i18n.t("init:mcp.fcSaveToken")})`)}`, value: false },
3408
+ { name: i18n.t("init:mcp.fcFullSnippet"), value: true }
3409
+ ]
3410
+ }
3411
+ ]);
3412
+ fastContextApiKey = fcAnswers.apiKey?.trim() || "";
3413
+ fastContextIncludeSnippets = fcAnswers.includeSnippets;
3414
+ }
3415
+ if (hasContextWeaver) {
3416
+ console.log();
3417
+ console.log(ansis.cyan.bold(` \u{1F527} ContextWeaver MCP`));
3418
+ console.log();
3419
+ console.log(` ${ansis.gray("1.")} ${i18n.t("init:mcp.siliconflowStep1", { url: ansis.underline("https://siliconflow.cn/") })}`);
3420
+ console.log(` ${ansis.gray("2.")} ${i18n.t("init:mcp.siliconflowStep2")}`);
3421
+ console.log(` ${ansis.gray("3.")} ${i18n.t("init:mcp.siliconflowStep3")}`);
3422
+ console.log();
3423
+ const cwAnswers = await inquirer.prompt([{
3146
3424
  type: "password",
3147
- name: "token",
3148
- message: `Token ${ansis.gray(`(${i18n.t("init:mcp.tokenRequired")})`)}`,
3149
- mask: "*",
3150
- validate: (input) => input.trim() !== "" || i18n.t("init:mcp.enterToken")
3151
- }
3152
- ]);
3153
- aceToolBaseUrl = aceAnswers.baseUrl || "";
3154
- aceToolToken = aceAnswers.token || "";
3155
- }
3156
- if (hasFastContext) {
3157
- console.log();
3158
- console.log(ansis.cyan.bold(` \u{1F527} fast-context MCP`));
3159
- console.log(ansis.gray(` Windsurf Fast Context \u2014 ${i18n.t("init:mcp.fcAutoExtract")}`));
3160
- console.log();
3161
- const fcAnswers = await inquirer.prompt([
3162
- {
3163
- type: "input",
3164
3425
  name: "apiKey",
3165
- message: `WINDSURF_API_KEY ${ansis.gray(`(${i18n.t("init:mcp.fcLeaveEmpty")})`)}`,
3166
- default: ""
3167
- },
3168
- {
3169
- type: "list",
3170
- name: "includeSnippets",
3171
- message: i18n.t("init:mcp.fcSnippetMode"),
3172
- choices: [
3173
- { name: `${i18n.t("init:mcp.fcPathOnly")} ${ansis.gray(`(${i18n.t("init:mcp.fcSaveToken")})`)}`, value: false },
3174
- { name: i18n.t("init:mcp.fcFullSnippet"), value: true }
3175
- ]
3176
- }
3177
- ]);
3178
- fastContextApiKey = fcAnswers.apiKey?.trim() || "";
3179
- fastContextIncludeSnippets = fcAnswers.includeSnippets;
3426
+ message: `SiliconFlow API Key ${ansis.gray("(sk-xxx)")}`,
3427
+ mask: "*",
3428
+ validate: (input) => input.trim() !== "" || i18n.t("init:mcp.enterApiKey")
3429
+ }]);
3430
+ contextWeaverApiKey = cwAnswers.apiKey || "";
3431
+ }
3432
+ if (wantGrokSearch) {
3433
+ console.log();
3434
+ console.log(ansis.cyan.bold(` \u{1F50D} grok-search MCP`));
3435
+ console.log();
3436
+ console.log(` Tavily: ${ansis.underline("https://www.tavily.com/")} ${ansis.gray(`(${i18n.t("init:grok.tavilyHint")})`)}`);
3437
+ console.log(` Firecrawl: ${ansis.underline("https://www.firecrawl.dev/")} ${ansis.gray(`(${i18n.t("init:grok.firecrawlHint")})`)}`);
3438
+ console.log(` Grok API: ${ansis.gray(i18n.t("init:grok.grokHint"))}`);
3439
+ console.log();
3440
+ const grokAnswers = await inquirer.prompt([
3441
+ { type: "input", name: "grokApiUrl", message: `GROK_API_URL ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, default: "" },
3442
+ { type: "password", name: "grokApiKey", message: `GROK_API_KEY ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, mask: "*" },
3443
+ { type: "password", name: "tavilyKey", message: `TAVILY_API_KEY ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, mask: "*" },
3444
+ { type: "password", name: "firecrawlKey", message: `FIRECRAWL_API_KEY ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, mask: "*" }
3445
+ ]);
3446
+ tavilyKey = grokAnswers.tavilyKey?.trim() || "";
3447
+ firecrawlKey = grokAnswers.firecrawlKey?.trim() || "";
3448
+ grokApiUrl = grokAnswers.grokApiUrl?.trim() || "";
3449
+ grokApiKey = grokAnswers.grokApiKey?.trim() || "";
3450
+ }
3451
+ return "next";
3180
3452
  }
3181
- if (hasContextWeaver) {
3453
+ async function runPerfStep(canGoBack) {
3182
3454
  console.log();
3183
- console.log(ansis.cyan.bold(` \u{1F527} ContextWeaver MCP`));
3455
+ console.log(ansis.cyan.bold(` \u26A1 Step 4/4 \u2014 ${i18n.t("init:perf.title")}`));
3184
3456
  console.log();
3185
- console.log(` ${ansis.gray("1.")} ${i18n.t("init:mcp.siliconflowStep1", { url: ansis.underline("https://siliconflow.cn/") })}`);
3186
- console.log(` ${ansis.gray("2.")} ${i18n.t("init:mcp.siliconflowStep2")}`);
3187
- console.log(` ${ansis.gray("3.")} ${i18n.t("init:mcp.siliconflowStep3")}`);
3188
- console.log();
3189
- const cwAnswers = await inquirer.prompt([{
3190
- type: "password",
3191
- name: "apiKey",
3192
- message: `SiliconFlow API Key ${ansis.gray("(sk-xxx)")}`,
3193
- mask: "*",
3194
- validate: (input) => input.trim() !== "" || i18n.t("init:mcp.enterApiKey")
3457
+ const { perfMode } = await inquirer.prompt([{
3458
+ type: "list",
3459
+ name: "perfMode",
3460
+ message: i18n.t("init:perf.selectMode"),
3461
+ choices: [
3462
+ { name: `${ansis.green("\u25CF")} ${i18n.t("init:perf.standardOption")}`, value: "standard" },
3463
+ { name: `${ansis.cyan("\u25CF")} ${i18n.t("init:perf.liteOption")}`, value: "lite" },
3464
+ ...navSentinels(canGoBack)
3465
+ ],
3466
+ default: liteMode ? "lite" : "standard"
3467
+ }]);
3468
+ if (perfMode === BACK_SENTINEL)
3469
+ return "back";
3470
+ if (perfMode === CANCEL_SENTINEL)
3471
+ return "cancel";
3472
+ liteMode = perfMode === "lite";
3473
+ const { versionMode } = await inquirer.prompt([{
3474
+ type: "list",
3475
+ name: "versionMode",
3476
+ message: "\u5B89\u88C5\u6A21\u5F0F",
3477
+ choices: [
3478
+ { name: `${ansis.green("v3 \u65B0\u7248")} \u2014 /ccg:go \u667A\u80FD\u5165\u53E3 + Hook \u5F15\u64CE + 12 \u6838\u5FC3\u547D\u4EE4\uFF08\u63A8\u8350\uFF09`, value: "v3" },
3479
+ { name: `${ansis.gray("\u65E7\u7248\u517C\u5BB9")} \u2014 \u65B0\u7248\u5168\u90E8 + 18 \u4E2A\u65E7\u7248\u547D\u4EE4\uFF08workflow/debug/team \u7B49\uFF09`, value: "legacy" }
3480
+ ],
3481
+ default: "v3"
3195
3482
  }]);
3196
- contextWeaverApiKey = cwAnswers.apiKey || "";
3483
+ if (versionMode === "legacy") {
3484
+ selectedWorkflows = getAllCommandIds();
3485
+ const { includeImpeccable } = await inquirer.prompt([{
3486
+ type: "confirm",
3487
+ name: "includeImpeccable",
3488
+ message: i18n.t("init:commands.includeImpeccable"),
3489
+ default: !skipImpeccable
3490
+ }]);
3491
+ skipImpeccable = !includeImpeccable;
3492
+ } else {
3493
+ selectedWorkflows = getCoreCommandIds();
3494
+ skipImpeccable = true;
3495
+ }
3496
+ return "next";
3197
3497
  }
3198
- if (wantGrokSearch) {
3498
+ const runSummaryStep = async (workflowsCount) => {
3199
3499
  console.log();
3200
- console.log(ansis.cyan.bold(` \u{1F50D} grok-search MCP`));
3500
+ console.log(ansis.yellow("\u2501".repeat(50)));
3501
+ console.log(ansis.bold(` ${i18n.t("init:summary.title")}`));
3201
3502
  console.log();
3202
- console.log(` Tavily: ${ansis.underline("https://www.tavily.com/")} ${ansis.gray(`(${i18n.t("init:grok.tavilyHint")})`)}`);
3203
- console.log(` Firecrawl: ${ansis.underline("https://www.firecrawl.dev/")} ${ansis.gray(`(${i18n.t("init:grok.firecrawlHint")})`)}`);
3204
- console.log(` Grok API: ${ansis.gray(i18n.t("init:grok.grokHint"))}`);
3503
+ const fmName = frontendModels[0].charAt(0).toUpperCase() + frontendModels[0].slice(1);
3504
+ const bmName = backendModels[0].charAt(0).toUpperCase() + backendModels[0].slice(1);
3505
+ const apiLabel = (() => {
3506
+ if (apiUrl && apiKey)
3507
+ return `${ansis.green("\u25CF")} ${apiUrl} ${ansis.gray("+ ***")}`;
3508
+ if (apiUrl)
3509
+ return `${ansis.green("\u25CF")} ${apiUrl}`;
3510
+ return `${ansis.gray("\u25CB")} ${i18n.t("init:summary.apiSelfManaged")}`;
3511
+ })();
3512
+ console.log(` ${ansis.cyan(i18n.t("init:summary.apiProvider"))} ${apiLabel}`);
3513
+ console.log(` ${ansis.cyan(i18n.t("init:summary.modelRouting"))} ${ansis.green(fmName)} (Frontend) + ${ansis.blue(bmName)} (Backend)`);
3514
+ if (frontendModels[0] === "gemini" || backendModels[0] === "gemini") {
3515
+ console.log(` ${ansis.cyan(i18n.t("init:summary.geminiModel"))} ${ansis.gray(geminiModel)}`);
3516
+ }
3517
+ console.log(` ${ansis.cyan(i18n.t("init:summary.commandCount"))} ${ansis.yellow(workflowsCount.toString())}`);
3518
+ const mcpSummary = (() => {
3519
+ if (mcpProvider === "fast-context")
3520
+ return ansis.green("fast-context");
3521
+ if (mcpProvider === "ace-tool" || mcpProvider === "ace-tool-rs")
3522
+ return aceToolToken ? ansis.green(mcpProvider) : ansis.yellow(`${mcpProvider} (${i18n.t("init:summary.pendingConfig")})`);
3523
+ if (mcpProvider === "contextweaver")
3524
+ return contextWeaverApiKey ? ansis.green("contextweaver") : ansis.yellow(`contextweaver (${i18n.t("init:summary.pendingConfig")})`);
3525
+ return ansis.gray(i18n.t("init:summary.skipped"));
3526
+ })();
3527
+ console.log(` ${ansis.cyan(i18n.t("init:summary.mcpTool"))} ${mcpSummary}`);
3528
+ console.log(` ${ansis.cyan(i18n.t("init:summary.webUI"))} ${liteMode ? ansis.gray(i18n.t("init:summary.disabled")) : ansis.green(i18n.t("init:summary.enabled"))}`);
3529
+ if (wantGrokSearch) {
3530
+ console.log(` ${ansis.cyan("grok-search")} ${tavilyKey ? ansis.green("\u2713") : ansis.yellow(`(${i18n.t("init:summary.pendingConfig")})`)}`);
3531
+ }
3532
+ console.log(ansis.yellow("\u2501".repeat(50)));
3205
3533
  console.log();
3206
- const grokAnswers = await inquirer.prompt([
3207
- { type: "input", name: "grokApiUrl", message: `GROK_API_URL ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, default: "" },
3208
- { type: "password", name: "grokApiKey", message: `GROK_API_KEY ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, mask: "*" },
3209
- { type: "password", name: "tavilyKey", message: `TAVILY_API_KEY ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, mask: "*" },
3210
- { type: "password", name: "firecrawlKey", message: `FIRECRAWL_API_KEY ${ansis.gray(`(${i18n.t("init:grok.optional")})`)}`, mask: "*" }
3211
- ]);
3212
- tavilyKey = grokAnswers.tavilyKey?.trim() || "";
3213
- firecrawlKey = grokAnswers.firecrawlKey?.trim() || "";
3214
- grokApiUrl = grokAnswers.grokApiUrl?.trim() || "";
3215
- grokApiKey = grokAnswers.grokApiKey?.trim() || "";
3216
- }
3217
- }
3218
- if (!options.skipPrompt) {
3219
- const existingConfig = await readCcgConfig();
3220
- const currentLiteMode = existingConfig?.performance?.liteMode || false;
3221
- console.log();
3222
- console.log(ansis.cyan.bold(` \u26A1 Step 4/4 \u2014 ${i18n.t("init:perf.title")}`));
3223
- console.log();
3224
- const { perfMode } = await inquirer.prompt([{
3225
- type: "list",
3226
- name: "perfMode",
3227
- message: i18n.t("init:perf.selectMode"),
3228
- choices: [
3229
- { name: `${ansis.green("\u25CF")} ${i18n.t("init:perf.standardOption")}`, value: "standard" },
3230
- { name: `${ansis.cyan("\u25CF")} ${i18n.t("init:perf.liteOption")}`, value: "lite" }
3231
- ],
3232
- default: currentLiteMode ? "lite" : "standard"
3233
- }]);
3234
- liteMode = perfMode === "lite";
3235
- const { includeImpeccable } = await inquirer.prompt([{
3236
- type: "confirm",
3237
- name: "includeImpeccable",
3238
- message: i18n.t("init:commands.includeImpeccable"),
3239
- default: false
3240
- }]);
3241
- skipImpeccable = !includeImpeccable;
3242
- } else {
3243
- const existingConfig = await readCcgConfig();
3244
- if (existingConfig?.performance?.liteMode !== void 0) {
3245
- liteMode = existingConfig.performance.liteMode;
3246
- }
3247
- if (existingConfig?.performance?.skipImpeccable !== void 0) {
3248
- skipImpeccable = existingConfig.performance.skipImpeccable;
3534
+ const { action } = await inquirer.prompt([{
3535
+ type: "list",
3536
+ name: "action",
3537
+ message: i18n.t("init:summaryMenu.prompt"),
3538
+ choices: [
3539
+ { name: `${ansis.green("\u2713")} ${i18n.t("init:summaryMenu.confirm")}`, value: "confirm" },
3540
+ new inquirer.Separator(),
3541
+ { name: `${ansis.cyan("\u270E")} ${i18n.t("init:summaryMenu.editApi")}`, value: "api" },
3542
+ { name: `${ansis.cyan("\u270E")} ${i18n.t("init:summaryMenu.editModel")}`, value: "model" },
3543
+ { name: `${ansis.cyan("\u270E")} ${i18n.t("init:summaryMenu.editMcp")}`, value: "mcp" },
3544
+ { name: `${ansis.cyan("\u270E")} ${i18n.t("init:summaryMenu.editPerf")}`, value: "perf" },
3545
+ new inquirer.Separator(),
3546
+ { name: `${ansis.red("\xD7")} ${i18n.t("init:summaryMenu.cancel")}`, value: "cancel" }
3547
+ ],
3548
+ default: "confirm"
3549
+ }]);
3550
+ return action;
3551
+ };
3552
+ const stepOrder = ["api", "model", "mcp", "perf"];
3553
+ let stepIdx = 0;
3554
+ let jumpingToSummary = false;
3555
+ while (true) {
3556
+ if (stepIdx < stepOrder.length) {
3557
+ const stepId = stepOrder[stepIdx];
3558
+ const canGoBack = stepIdx > 0;
3559
+ let result;
3560
+ switch (stepId) {
3561
+ case "api":
3562
+ result = await runApiStep(canGoBack);
3563
+ break;
3564
+ case "model":
3565
+ result = await runModelStep(canGoBack);
3566
+ break;
3567
+ case "mcp":
3568
+ result = await runMcpStep(canGoBack);
3569
+ break;
3570
+ case "perf":
3571
+ result = await runPerfStep(canGoBack);
3572
+ break;
3573
+ }
3574
+ if (result === "cancel") {
3575
+ console.log(ansis.yellow(i18n.t("init:installCancelled")));
3576
+ return;
3577
+ }
3578
+ if (result === "back") {
3579
+ stepIdx = Math.max(0, stepIdx - 1);
3580
+ continue;
3581
+ }
3582
+ if (jumpingToSummary) {
3583
+ jumpingToSummary = false;
3584
+ stepIdx = stepOrder.length;
3585
+ } else {
3586
+ stepIdx++;
3587
+ }
3588
+ } else {
3589
+ const summaryAction = await runSummaryStep(selectedWorkflows.length);
3590
+ if (summaryAction === "confirm") {
3591
+ break;
3592
+ }
3593
+ if (summaryAction === "cancel") {
3594
+ console.log(ansis.yellow(i18n.t("init:installCancelled")));
3595
+ return;
3596
+ }
3597
+ jumpingToSummary = true;
3598
+ stepIdx = stepOrder.indexOf(summaryAction);
3599
+ }
3249
3600
  }
3250
3601
  }
3251
3602
  const routing = {
@@ -3266,38 +3617,17 @@ async function init(options = {}) {
3266
3617
  mode,
3267
3618
  geminiModel
3268
3619
  };
3269
- console.log();
3270
- console.log(ansis.yellow("\u2501".repeat(50)));
3271
- console.log(ansis.bold(` ${i18n.t("init:summary.title")}`));
3272
- console.log();
3273
- const fmName = frontendModels[0].charAt(0).toUpperCase() + frontendModels[0].slice(1);
3274
- const bmName = backendModels[0].charAt(0).toUpperCase() + backendModels[0].slice(1);
3275
- console.log(` ${ansis.cyan(i18n.t("init:summary.modelRouting"))} ${ansis.green(fmName)} (Frontend) + ${ansis.blue(bmName)} (Backend)`);
3276
- console.log(` ${ansis.cyan(i18n.t("init:summary.commandCount"))} ${ansis.yellow(selectedWorkflows.length.toString())}`);
3277
- const mcpSummary = (() => {
3278
- if (mcpProvider === "fast-context") return ansis.green("fast-context");
3279
- if (mcpProvider === "ace-tool" || mcpProvider === "ace-tool-rs") return aceToolToken ? ansis.green(mcpProvider) : ansis.yellow(`${mcpProvider} (${i18n.t("init:summary.pendingConfig")})`);
3280
- if (mcpProvider === "contextweaver") return contextWeaverApiKey ? ansis.green("contextweaver") : ansis.yellow(`contextweaver (${i18n.t("init:summary.pendingConfig")})`);
3281
- return ansis.gray(i18n.t("init:summary.skipped"));
3282
- })();
3283
- console.log(` ${ansis.cyan(i18n.t("init:summary.mcpTool"))} ${mcpSummary}`);
3284
- console.log(` ${ansis.cyan(i18n.t("init:summary.webUI"))} ${liteMode ? ansis.gray(i18n.t("init:summary.disabled")) : ansis.green(i18n.t("init:summary.enabled"))}`);
3285
- if (wantGrokSearch) {
3286
- console.log(` ${ansis.cyan("grok-search")} ${tavilyKey ? ansis.green("\u2713") : ansis.yellow(`(${i18n.t("init:summary.pendingConfig")})`)}`);
3287
- }
3288
- console.log(ansis.yellow("\u2501".repeat(50)));
3289
- console.log();
3290
- if (!options.skipPrompt && !options.force) {
3291
- const { confirmed } = await inquirer.prompt([{
3292
- type: "confirm",
3293
- name: "confirmed",
3294
- message: i18n.t("init:confirmInstall"),
3295
- default: true
3296
- }]);
3297
- if (!confirmed) {
3298
- console.log(ansis.yellow(i18n.t("init:installCancelled")));
3299
- return;
3300
- }
3620
+ if (options.skipPrompt || options.force) {
3621
+ console.log();
3622
+ console.log(ansis.yellow("\u2501".repeat(50)));
3623
+ console.log(ansis.bold(` ${i18n.t("init:summary.title")}`));
3624
+ console.log();
3625
+ const fmName = frontendModels[0].charAt(0).toUpperCase() + frontendModels[0].slice(1);
3626
+ const bmName = backendModels[0].charAt(0).toUpperCase() + backendModels[0].slice(1);
3627
+ console.log(` ${ansis.cyan(i18n.t("init:summary.modelRouting"))} ${ansis.green(fmName)} (Frontend) + ${ansis.blue(bmName)} (Backend)`);
3628
+ console.log(` ${ansis.cyan(i18n.t("init:summary.commandCount"))} ${ansis.yellow(selectedWorkflows.length.toString())}`);
3629
+ console.log(ansis.yellow("\u2501".repeat(50)));
3630
+ console.log();
3301
3631
  }
3302
3632
  const spinner = ora(i18n.t("init:installing")).start();
3303
3633
  try {
@@ -3831,7 +4161,9 @@ async function performUpdate(fromVersion, toVersion, isNewVersion) {
3831
4161
  const backupTargets = [
3832
4162
  join(installDir, "commands", "ccg"),
3833
4163
  join(installDir, "agents", "ccg"),
3834
- join(installDir, "skills", "ccg")
4164
+ join(installDir, "skills", "ccg"),
4165
+ join(installDir, "hooks", "ccg"),
4166
+ join(installDir, ".ccg", "engine")
3835
4167
  ];
3836
4168
  spinner = ora(i18n.t("update:removingOld")).start();
3837
4169
  const backedUp = [];