ccjk 16.0.7 → 16.3.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 (69) hide show
  1. package/README.md +72 -316
  2. package/dist/chunks/claude-code-config-manager.mjs +28 -7
  3. package/dist/chunks/claude-code-incremental-manager.mjs +2 -2
  4. package/dist/chunks/codex-config-switch.mjs +3 -3
  5. package/dist/chunks/codex-presets.mjs +98 -0
  6. package/dist/chunks/codex-provider-manager.mjs +4 -4
  7. package/dist/chunks/codex-runtime.mjs +246 -0
  8. package/dist/chunks/codex-uninstaller.mjs +2 -2
  9. package/dist/chunks/commands.mjs +1 -1
  10. package/dist/chunks/features.mjs +12 -12
  11. package/dist/chunks/simple-config.mjs +485 -130
  12. package/dist/cli.mjs +1730 -760
  13. package/dist/i18n/locales/en/clean.json +24 -0
  14. package/dist/i18n/locales/en/codex.json +24 -1
  15. package/dist/i18n/locales/en/configuration.json +20 -12
  16. package/dist/i18n/locales/en/hub.json +30 -0
  17. package/dist/i18n/locales/en/menu.json +30 -0
  18. package/dist/i18n/locales/en/workflow.json +13 -1
  19. package/dist/i18n/locales/zh-CN/clean.json +24 -0
  20. package/dist/i18n/locales/zh-CN/codex.json +24 -1
  21. package/dist/i18n/locales/zh-CN/configuration.json +20 -12
  22. package/dist/i18n/locales/zh-CN/hub.json +30 -0
  23. package/dist/i18n/locales/zh-CN/menu.json +30 -0
  24. package/dist/i18n/locales/zh-CN/workflow.json +13 -1
  25. package/dist/index.d.mts +3 -1
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.mjs +1 -1
  28. package/dist/shared/{ccjk.BSLlI-JL.mjs → ccjk.TC1_-qhV.mjs} +1 -1
  29. package/package.json +1 -1
  30. package/templates/common/output-styles/en/agents-md-baseline.md +28 -0
  31. package/templates/common/output-styles/en/plan-first.md +30 -0
  32. package/templates/common/output-styles/en/surgical-diff.md +27 -0
  33. package/templates/common/output-styles/en/verify-and-ship.md +31 -0
  34. package/templates/common/output-styles/zh-CN/agents-md-baseline.md +28 -0
  35. package/templates/common/output-styles/zh-CN/plan-first.md +30 -0
  36. package/templates/common/output-styles/zh-CN/surgical-diff.md +27 -0
  37. package/templates/common/output-styles/zh-CN/verify-and-ship.md +31 -0
  38. package/templates/common/workflow/bmad/en/bmad-init.md +275 -0
  39. package/templates/common/workflow/bmad/zh-CN/bmad-init.md +275 -0
  40. package/templates/common/workflow/codeReview/en/code-review.md +34 -0
  41. package/templates/common/workflow/codeReview/zh-CN/code-review.md +34 -0
  42. package/templates/common/workflow/continuousDelivery/en/continuous-delivery.md +628 -0
  43. package/templates/common/workflow/continuousDelivery/zh-CN/continuous-delivery.md +628 -0
  44. package/templates/common/workflow/essential/en/agents/ccjk-config-agent.md +187 -0
  45. package/templates/common/workflow/essential/en/agents/ccjk-mcp-agent.md +191 -0
  46. package/templates/common/workflow/essential/en/agents/ccjk-skill-agent.md +249 -0
  47. package/templates/common/workflow/essential/en/agents/ccjk-workflow-agent.md +277 -0
  48. package/templates/common/workflow/essential/en/agents/get-current-datetime.md +29 -0
  49. package/templates/common/workflow/essential/en/agents/init-architect.md +115 -0
  50. package/templates/common/workflow/essential/en/agents/planner.md +116 -0
  51. package/templates/common/workflow/essential/en/agents/teamagent.md +102 -0
  52. package/templates/common/workflow/essential/en/agents/ui-ux-designer.md +91 -0
  53. package/templates/common/workflow/essential/en/feat.md +92 -0
  54. package/templates/common/workflow/essential/en/init-project.md +53 -0
  55. package/templates/common/workflow/essential/zh-CN/agents/get-current-datetime.md +29 -0
  56. package/templates/common/workflow/essential/zh-CN/agents/init-architect.md +115 -0
  57. package/templates/common/workflow/essential/zh-CN/agents/planner.md +116 -0
  58. package/templates/common/workflow/essential/zh-CN/agents/teamagent.md +102 -0
  59. package/templates/common/workflow/essential/zh-CN/agents/ui-ux-designer.md +91 -0
  60. package/templates/common/workflow/essential/zh-CN/feat.md +315 -0
  61. package/templates/common/workflow/essential/zh-CN/init-project.md +53 -0
  62. package/templates/common/workflow/interview/en/interview.md +67 -0
  63. package/templates/common/workflow/interview/zh-CN/interview.md +67 -0
  64. package/templates/common/workflow/linearMethod/en/linear-method.md +651 -0
  65. package/templates/common/workflow/linearMethod/zh-CN/linear-method.md +750 -0
  66. package/templates/common/workflow/refactoringMaster/en/refactoring-master.md +516 -0
  67. package/templates/common/workflow/refactoringMaster/zh-CN/refactoring-master.md +810 -0
  68. package/templates/common/workflow/specFirstTDD/en/spec-first-tdd.md +364 -0
  69. package/templates/common/workflow/specFirstTDD/zh-CN/spec-first-tdd.md +364 -0
package/dist/cli.mjs CHANGED
@@ -1,20 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import cac from 'cac';
3
3
  import ansis from 'ansis';
4
- import { aq as ensureI18nInitialized, as as i18n, aZ as readCcrConfig, aU as isCcrInstalled, aV as installCcr, a_ as configureCcrFeature, av as promptBoolean, a$ as handleExitPromptError, b0 as handleGeneralError, b1 as checkAndUpdateTools, b2 as runCodexUpdate, b3 as resolveCodeType$1, aB as readJsonConfig, b4 as writeJsonConfig, c as CCJK_CONFIG_FILE, aO as readCcjkConfig, D as DEFAULT_CODE_TOOL_TYPE, a2 as isCodeToolType, at as addNumbersToChoices, b5 as displayBanner, aK as updateCcjkConfig, b6 as version, b7 as resolveAiOutputLanguage, aQ as isClaudeFamilyCodeTool, b8 as updatePromptOnly, b9 as selectAndInstallWorkflows, ba as checkClaudeCodeVersionAndPrompt, af as resolveCodeToolType$1, v as activeSettingsFile, C as CCJK_CONFIG_DIR, aL as changeLanguage, u as SUPPORTED_LANGS, L as LANG_LABELS, bb as STARTUP_CODE_TOOL_CHOICES, bc as displayBannerWithInfo, q as CODE_TOOL_BANNERS, _ as init, bd as runCodexUninstall, be as manageCodexMcp, bf as configureCodexApi, bg as runCodexWorkflowImportWithLanguageSelection, bh as runCodexFullInit, bi as switchCodexProvider, bj as listCodexProviders, aH as readCodexConfig, bk as switchToOfficialLogin, bl as switchToProvider, bm as readCcjkConfigAsync, bn as initI18n, bo as selectScriptLanguage } from './chunks/simple-config.mjs';
4
+ import { as as ensureI18nInitialized, au as i18n, b2 as readCcrConfig, aZ as isCcrInstalled, a_ as installCcr, b3 as configureCcrFeature, ax as promptBoolean, b4 as handleExitPromptError, b5 as handleGeneralError, o as CODEX_DIR, d as CLAUDE_DIR, i as CLAVUE_DIR, al as settingsFileForTool, b6 as MCP_SERVICE_CONFIGS, av as addNumbersToChoices, aT as readCcjkConfig, aM as backupCodexComplete, b7 as getBackupMessage, b8 as deleteAllCodexMcpServices, aL as readCodexConfig, ad as readMcpConfig, az as exists, b9 as readDir, ba as isDirectory, aB as readFile, p as CODEX_PROMPTS_DIR, bb as deleteCodexMcpService, ar as writeMcpConfig, bc as listCodexMcpServiceIds, l as CODEX_AGENTS_FILE, ag as resolveCodeToolType$1, w as activeSettingsFile, aF as readJsonConfig, bd as displayBanner, be as runCodexUpdate, aP as updateCcjkConfig, bf as version, bg as resolveAiOutputLanguage, aV as isClaudeFamilyCodeTool, bh as updatePromptOnly, bi as selectAndInstallWorkflows, bj as checkClaudeCodeVersionAndPrompt, D as DEFAULT_CODE_TOOL_TYPE, a3 as isCodeToolType, bk as runCodexWorkflowImportWithLanguageSelection, bl as runCodexFullInit, $ as init, bm as manageCodexMcp, bn as configureCodexApi, bo as checkAndUpdateTools, bp as resolveCodeType$1, bq as writeJsonConfig, c as CCJK_CONFIG_FILE, C as CCJK_CONFIG_DIR, aQ as changeLanguage, v as SUPPORTED_LANGS, L as LANG_LABELS, br as STARTUP_CODE_TOOL_CHOICES, bs as displayBannerWithInfo, r as CODE_TOOL_BANNERS, bt as runCodexUninstall, bu as switchCodexProvider, bv as listCodexProviders, bw as switchToOfficialLogin, bx as switchToProvider, by as listProviderProfiles, bz as getActiveProviderProfileId, bA as getProviderProfile, bB as readCredentials, bC as readCcjkConfigAsync, bD as initI18n, bE as selectScriptLanguage } from './chunks/simple-config.mjs';
5
5
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
6
6
  import { homedir } from 'node:os';
7
7
  import inquirer from 'inquirer';
8
8
  import { join } from 'pathe';
9
9
  import { runCcrStop, runCcrStart, runCcrRestart, runCcrStatus, runCcrUi } from './chunks/commands.mjs';
10
- import { changeScriptLanguageFeature, configureEnvPermissionFeature, configureAiMemoryFeature, configureDefaultModelFeature, configureMcpFeature, configureApiFeature, configureCodexAiMemoryFeature, configureCodexDefaultModelFeature } from './chunks/features.mjs';
11
- import process$1 from 'node:process';
12
- import { pathExists } from 'fs-extra';
13
- import { exec, x } from 'tinyexec';
10
+ import { detectCodexRuntimes, scanCodexConfigDoctorFindings, printCodexReloadHint } from './chunks/codex-runtime.mjs';
14
11
  import { m as moveToTrash } from './shared/ccjk.DGjQxTq_.mjs';
15
- import { g as getClaudeFamilyRuntimeLabel } from './shared/ccjk.BSLlI-JL.mjs';
12
+ import { g as getClaudeFamilyRuntimeLabel } from './shared/ccjk.TC1_-qhV.mjs';
16
13
  import { execSync, spawnSync } from 'node:child_process';
17
14
  import { ClaudeCodeConfigManager } from './chunks/claude-code-config-manager.mjs';
15
+ import { configureCodexAiMemoryFeature, configureCodexDefaultModelFeature, configureEnvPermissionFeature, configureAiMemoryFeature, configureDefaultModelFeature, configureMcpFeature, configureApiFeature, changeScriptLanguageFeature } from './chunks/features.mjs';
16
+ import process$1 from 'node:process';
17
+ import { pathExists } from 'fs-extra';
18
+ import { exec, x } from 'tinyexec';
18
19
  import 'node:util';
19
20
  import 'dayjs';
20
21
  import 'node:url';
@@ -148,6 +149,1334 @@ ${ansis.dim("\u2500".repeat(50))}
148
149
  }
149
150
  }
150
151
 
152
+ const GROK_DIR = join(homedir(), ".grok");
153
+ const AGENTS_SKILLS_DIR = join(homedir(), ".agents", "skills");
154
+ const CLAUDE_FAMILY_CAPABILITIES = {
155
+ fullInit: true,
156
+ updateWorkflow: true,
157
+ configureApi: true,
158
+ configureMcp: true,
159
+ configureModel: true,
160
+ configureAiMemory: true,
161
+ configureEnvPermission: true,
162
+ cleanEnvironment: true,
163
+ uninstall: true,
164
+ checkUpdates: true,
165
+ setupPresets: false
166
+ };
167
+ const CLAUDE_FAMILY_DOCTOR = [
168
+ "mcp-unmanaged",
169
+ "mcp-broken",
170
+ "mcp-bloat",
171
+ "skill-missing-file",
172
+ "skill-empty"
173
+ ];
174
+ const CLAUDE_FAMILY_OPERATOR$1 = [
175
+ "init-full",
176
+ "update-workflow",
177
+ "configure-api",
178
+ "configure-mcp",
179
+ "configure-model",
180
+ "configure-ai-memory",
181
+ "configure-env-permission",
182
+ "clean-environment"
183
+ ];
184
+ const TOOL_MATRIX = {
185
+ clavue: {
186
+ id: "clavue",
187
+ displayName: "Clavue",
188
+ configRoot: CLAVUE_DIR,
189
+ configFile: settingsFileForTool("clavue"),
190
+ skillsDirs: [join(CLAVUE_DIR, "skills"), AGENTS_SKILLS_DIR],
191
+ mcpFormat: "settings-json",
192
+ profileStorage: "settings-json",
193
+ roles: ["both"],
194
+ capabilities: { ...CLAUDE_FAMILY_CAPABILITIES },
195
+ doctorChecks: [...CLAUDE_FAMILY_DOCTOR],
196
+ operatorActions: [...CLAUDE_FAMILY_OPERATOR$1]
197
+ },
198
+ "claude-code": {
199
+ id: "claude-code",
200
+ displayName: "Claude Code",
201
+ configRoot: CLAUDE_DIR,
202
+ configFile: settingsFileForTool("claude-code"),
203
+ skillsDirs: [join(CLAUDE_DIR, "skills"), AGENTS_SKILLS_DIR],
204
+ mcpFormat: "settings-json",
205
+ profileStorage: "settings-json",
206
+ roles: ["both"],
207
+ capabilities: { ...CLAUDE_FAMILY_CAPABILITIES },
208
+ doctorChecks: [...CLAUDE_FAMILY_DOCTOR],
209
+ operatorActions: [...CLAUDE_FAMILY_OPERATOR$1]
210
+ },
211
+ codex: {
212
+ id: "codex",
213
+ displayName: "Codex (CLI / \u684C\u9762 / IDE)",
214
+ configRoot: CODEX_DIR,
215
+ configFile: settingsFileForTool("codex"),
216
+ skillsDirs: [join(CODEX_DIR, "skills")],
217
+ mcpFormat: "config-toml",
218
+ profileStorage: "config-toml",
219
+ roles: ["both"],
220
+ capabilities: {
221
+ fullInit: true,
222
+ updateWorkflow: true,
223
+ configureApi: true,
224
+ configureMcp: true,
225
+ configureModel: true,
226
+ configureAiMemory: true,
227
+ configureEnvPermission: false,
228
+ cleanEnvironment: true,
229
+ uninstall: true,
230
+ checkUpdates: true,
231
+ setupPresets: true
232
+ },
233
+ doctorChecks: [
234
+ "runtime-status",
235
+ "config-feature-risk",
236
+ "mcp-unmanaged",
237
+ "mcp-broken",
238
+ "mcp-orphan-toml",
239
+ "mcp-bloat",
240
+ "skill-missing-file",
241
+ "skill-empty",
242
+ "prompt-stale",
243
+ "agents-file-stale"
244
+ ],
245
+ operatorActions: [
246
+ "init-preset-clean",
247
+ "init-preset-optimized",
248
+ "init-preset-custom",
249
+ "update-workflow",
250
+ "configure-api",
251
+ "configure-mcp",
252
+ "configure-model",
253
+ "configure-ai-memory",
254
+ "clean-environment"
255
+ ]
256
+ },
257
+ grok: {
258
+ id: "grok",
259
+ displayName: "Grok CLI",
260
+ configRoot: GROK_DIR,
261
+ configFile: join(GROK_DIR, "config.toml"),
262
+ skillsDirs: [
263
+ join(GROK_DIR, "skills"),
264
+ join(GROK_DIR, "bundled", "skills")
265
+ ],
266
+ mcpFormat: "none",
267
+ profileStorage: "ccjk-profile",
268
+ roles: ["both"],
269
+ capabilities: {
270
+ fullInit: true,
271
+ updateWorkflow: false,
272
+ configureApi: true,
273
+ configureMcp: false,
274
+ configureModel: false,
275
+ configureAiMemory: false,
276
+ configureEnvPermission: false,
277
+ cleanEnvironment: true,
278
+ uninstall: false,
279
+ checkUpdates: false,
280
+ setupPresets: false
281
+ },
282
+ doctorChecks: [
283
+ "skill-purge-all",
284
+ "grok-auth-status"
285
+ ],
286
+ operatorActions: [
287
+ "save-ccjk-profile",
288
+ "launch-with-profile",
289
+ "clean-environment"
290
+ ]
291
+ }
292
+ };
293
+
294
+ const MANAGED_MCP_IDS = new Set(MCP_SERVICE_CONFIGS.map((service) => service.id.toLowerCase()));
295
+ function skillDirsForTool(tool) {
296
+ if (tool === "agents")
297
+ return TOOL_MATRIX.clavue.skillsDirs.filter((dir) => dir.includes(".agents"));
298
+ if (tool === "grok")
299
+ return TOOL_MATRIX.grok.skillsDirs;
300
+ if (tool === "codex")
301
+ return TOOL_MATRIX.codex.skillsDirs;
302
+ if (tool === "claude")
303
+ return TOOL_MATRIX["claude-code"].skillsDirs.filter((dir) => !dir.includes(".agents"));
304
+ return [];
305
+ }
306
+ function scanSkillDirectory(baseDir, tool) {
307
+ const results = [];
308
+ if (!exists(baseDir))
309
+ return results;
310
+ for (const entry of readDir(baseDir)) {
311
+ const fullPath = join(baseDir, entry);
312
+ if (!isDirectory(fullPath))
313
+ continue;
314
+ const skillFile = join(fullPath, "SKILL.md");
315
+ if (!exists(skillFile)) {
316
+ results.push({
317
+ id: `skill:${tool}:${entry}:missing-skill-md`,
318
+ category: "skill",
319
+ tool,
320
+ label: entry,
321
+ path: fullPath,
322
+ reason: i18n.t("clean:reason.skillMissingFile"),
323
+ checked: true
324
+ });
325
+ continue;
326
+ }
327
+ const content = readFile(skillFile).trim();
328
+ if (content.length < 40) {
329
+ results.push({
330
+ id: `skill:${tool}:${entry}:empty-skill`,
331
+ category: "skill",
332
+ tool,
333
+ label: entry,
334
+ path: fullPath,
335
+ reason: i18n.t("clean:reason.skillEmpty"),
336
+ checked: true
337
+ });
338
+ }
339
+ }
340
+ return results;
341
+ }
342
+ function scanCodexMcpCandidates() {
343
+ const config = readCodexConfig();
344
+ if (!config?.mcpServices?.length)
345
+ return [];
346
+ const seen = /* @__PURE__ */ new Set();
347
+ const results = [];
348
+ for (const service of config.mcpServices) {
349
+ const id = service.id.toLowerCase();
350
+ if (seen.has(id)) {
351
+ results.push({
352
+ id: `mcp:codex:${id}:duplicate`,
353
+ category: "mcp",
354
+ tool: "codex",
355
+ label: service.id,
356
+ configKey: service.id,
357
+ reason: i18n.t("clean:reason.mcpDuplicate"),
358
+ checked: true
359
+ });
360
+ }
361
+ seen.add(id);
362
+ const hasCommand = Boolean(service.command?.trim());
363
+ const hasUrl = Boolean(service.url?.trim());
364
+ if (!hasCommand && !hasUrl) {
365
+ results.push({
366
+ id: `mcp:codex:${id}:broken`,
367
+ category: "mcp",
368
+ tool: "codex",
369
+ label: service.id,
370
+ configKey: service.id,
371
+ reason: i18n.t("clean:reason.mcpBroken"),
372
+ checked: true
373
+ });
374
+ continue;
375
+ }
376
+ if (!MANAGED_MCP_IDS.has(id)) {
377
+ results.push({
378
+ id: `mcp:codex:${id}:unmanaged`,
379
+ category: "mcp",
380
+ tool: "codex",
381
+ label: service.id,
382
+ configKey: service.id,
383
+ reason: i18n.t("clean:reason.mcpUnmanaged"),
384
+ checked: true
385
+ });
386
+ }
387
+ }
388
+ if (config.mcpServices.length > 3) {
389
+ for (const service of config.mcpServices) {
390
+ const id = service.id.toLowerCase();
391
+ if (results.some((item) => item.configKey === service.id))
392
+ continue;
393
+ results.push({
394
+ id: `mcp:codex:${id}:bloat`,
395
+ category: "mcp",
396
+ tool: "codex",
397
+ label: service.id,
398
+ configKey: service.id,
399
+ reason: i18n.t("clean:reason.mcpBloat", { count: config.mcpServices.length }),
400
+ checked: false
401
+ });
402
+ }
403
+ }
404
+ return results;
405
+ }
406
+ function scanClaudeMcpCandidates() {
407
+ const config = readMcpConfig("claude-code");
408
+ if (!config?.mcpServers)
409
+ return [];
410
+ const results = [];
411
+ const entries = Object.entries(config.mcpServers);
412
+ if (entries.length > 3) {
413
+ for (const [name] of entries) {
414
+ results.push({
415
+ id: `mcp:claude:${name}:bloat`,
416
+ category: "mcp",
417
+ tool: "claude",
418
+ label: name,
419
+ configKey: name,
420
+ reason: i18n.t("clean:reason.mcpBloat", { count: entries.length }),
421
+ checked: false
422
+ });
423
+ }
424
+ }
425
+ for (const [name, server] of entries) {
426
+ const hasCommand = Boolean(server.command?.trim());
427
+ const hasUrl = Boolean(server.url?.trim());
428
+ if (!hasCommand && !hasUrl) {
429
+ results.push({
430
+ id: `mcp:claude:${name}:broken`,
431
+ category: "mcp",
432
+ tool: "claude",
433
+ label: name,
434
+ configKey: name,
435
+ reason: i18n.t("clean:reason.mcpBroken"),
436
+ checked: true
437
+ });
438
+ continue;
439
+ }
440
+ if (!MANAGED_MCP_IDS.has(name.toLowerCase())) {
441
+ const already = results.some((item) => item.configKey === name);
442
+ if (!already) {
443
+ results.push({
444
+ id: `mcp:claude:${name}:unmanaged`,
445
+ category: "mcp",
446
+ tool: "claude",
447
+ label: name,
448
+ configKey: name,
449
+ reason: i18n.t("clean:reason.mcpUnmanaged"),
450
+ checked: true
451
+ });
452
+ }
453
+ }
454
+ }
455
+ return results;
456
+ }
457
+ function scanCodexPromptCandidates() {
458
+ if (!exists(CODEX_PROMPTS_DIR))
459
+ return [];
460
+ return readDir(CODEX_PROMPTS_DIR).filter((file) => file.endsWith(".md")).map((file) => {
461
+ const fullPath = join(CODEX_PROMPTS_DIR, file);
462
+ return {
463
+ id: `prompt:codex:${file}`,
464
+ category: "prompt",
465
+ tool: "codex",
466
+ label: file,
467
+ path: fullPath,
468
+ reason: i18n.t("clean:reason.promptInstalled"),
469
+ checked: false
470
+ };
471
+ });
472
+ }
473
+ function scanAllSkillDirectories(baseDir, tool) {
474
+ if (!exists(baseDir))
475
+ return [];
476
+ const results = [];
477
+ for (const entry of readDir(baseDir)) {
478
+ if (entry === "." || entry === "..")
479
+ continue;
480
+ const fullPath = join(baseDir, entry);
481
+ if (!isDirectory(fullPath))
482
+ continue;
483
+ results.push({
484
+ id: `skill:${tool}:${entry}:all`,
485
+ category: "skill",
486
+ tool,
487
+ label: entry,
488
+ path: fullPath,
489
+ reason: i18n.t("clean:reason.skillPurgeAll"),
490
+ checked: true
491
+ });
492
+ }
493
+ return results;
494
+ }
495
+ function scanAgentsFileCandidates() {
496
+ const files = [
497
+ CODEX_AGENTS_FILE,
498
+ join(TOOL_MATRIX.codex.configRoot, "AGENTS.override.md")
499
+ ];
500
+ const results = [];
501
+ for (const filePath of files) {
502
+ if (!exists(filePath))
503
+ continue;
504
+ const name = filePath.split("/").pop() || filePath;
505
+ results.push({
506
+ id: `agents-file:codex:${name}`,
507
+ category: "agents-file",
508
+ tool: "codex",
509
+ label: name,
510
+ path: filePath,
511
+ reason: i18n.t("clean:reason.agentsFileStale"),
512
+ checked: true
513
+ });
514
+ }
515
+ return results;
516
+ }
517
+ function scanAllCodexMcpCandidates() {
518
+ const parsedIds = new Set(
519
+ (readCodexConfig()?.mcpServices || []).map((service) => service.id.toLowerCase())
520
+ );
521
+ const rawIds = listCodexMcpServiceIds();
522
+ const ids = /* @__PURE__ */ new Set();
523
+ for (const id of rawIds)
524
+ ids.add(id);
525
+ for (const id of parsedIds)
526
+ ids.add(id);
527
+ return Array.from(ids).map((id) => ({
528
+ id: `mcp:codex:${id.toLowerCase()}:all`,
529
+ category: "mcp",
530
+ tool: "codex",
531
+ label: id,
532
+ configKey: id,
533
+ reason: parsedIds.has(id.toLowerCase()) ? i18n.t("clean:reason.mcpPurgeAll") : i18n.t("clean:reason.mcpOrphan"),
534
+ checked: true
535
+ }));
536
+ }
537
+ function scanAllClaudeMcpCandidates() {
538
+ const config = readMcpConfig("claude-code");
539
+ if (!config?.mcpServers)
540
+ return [];
541
+ return Object.keys(config.mcpServers).map((name) => ({
542
+ id: `mcp:claude:${name}:all`,
543
+ category: "mcp",
544
+ tool: "claude",
545
+ label: name,
546
+ configKey: name,
547
+ reason: i18n.t("clean:reason.mcpPurgeAll"),
548
+ checked: true
549
+ }));
550
+ }
551
+ function scanPurgeAllEnvironment() {
552
+ ensureI18nInitialized();
553
+ const candidates = [
554
+ ...scanAllCodexMcpCandidates(),
555
+ ...scanAllClaudeMcpCandidates(),
556
+ ...scanAllSkillDirectories(skillDirsForTool("codex")[0], "codex"),
557
+ ...skillDirsForTool("claude").flatMap((dir) => scanAllSkillDirectories(dir, "claude")),
558
+ ...skillDirsForTool("agents").flatMap((dir) => scanAllSkillDirectories(dir, "agents")),
559
+ ...skillDirsForTool("grok").flatMap((dir) => scanAllSkillDirectories(dir, "grok")),
560
+ ...scanCodexPromptCandidates().map((item) => ({ ...item, checked: true, reason: i18n.t("clean:reason.promptPurgeAll") })),
561
+ ...scanAgentsFileCandidates()
562
+ ];
563
+ const unique = /* @__PURE__ */ new Map();
564
+ for (const item of candidates)
565
+ unique.set(item.id, item);
566
+ return { candidates: Array.from(unique.values()) };
567
+ }
568
+ const SCAN_SECTION_KEYS = {
569
+ mcp: "hub:doctor.scanSection.mcp",
570
+ skill: "hub:doctor.scanSection.skill",
571
+ prompt: "hub:doctor.scanSection.prompt",
572
+ "agents-file": "hub:doctor.scanSection.agents",
573
+ config: "hub:doctor.scanSection.config"
574
+ };
575
+ function scanCodexConfigCandidates() {
576
+ const findings = scanCodexConfigDoctorFindings();
577
+ return findings.filter((finding) => finding.severity === "warn").map((finding) => {
578
+ const label = finding.messageParams ? i18n.t(finding.messageKey, finding.messageParams) : i18n.t(finding.messageKey);
579
+ return {
580
+ id: `config:codex:${finding.id}`,
581
+ category: "config",
582
+ tool: "codex",
583
+ label,
584
+ reason: i18n.t("clean:reason.configDoctor"),
585
+ checked: true
586
+ };
587
+ });
588
+ }
589
+ function printCodexRuntimeStatus() {
590
+ ensureI18nInitialized();
591
+ const summary = detectCodexRuntimes();
592
+ console.log(ansis.white(` ${i18n.t("hub:doctor.scanSection.runtime")}`));
593
+ console.log(ansis.gray(` ${i18n.t("codex:runtime.sharedConfig", { path: summary.configHome })}`));
594
+ if (summary.configHomeFromEnv)
595
+ console.log(ansis.gray(` ${i18n.t("codex:runtime.codexHomeEnv")}`));
596
+ for (const runtime of summary.runtimes) {
597
+ const label = i18n.t(`codex:runtime.kind.${runtime.kind}`);
598
+ if (!runtime.detected) {
599
+ console.log(ansis.dim(` ${label}: ${i18n.t("codex:runtime.notDetected")}`));
600
+ continue;
601
+ }
602
+ const pathText = runtime.path ? ansis.gray(` (${runtime.path})`) : "";
603
+ const weak = runtime.weakSignal ? ansis.yellow(` \u2014 ${i18n.t("codex:runtime.weakDesktopSignal")}`) : "";
604
+ console.log(` ${label}: ${ansis.green(i18n.t("codex:runtime.detected"))}${pathText}${weak}`);
605
+ }
606
+ console.log("");
607
+ }
608
+ function printEnvironmentScanReport() {
609
+ ensureI18nInitialized();
610
+ const smart = scanEnvironmentIssues(false).candidates;
611
+ const all = scanEnvironmentIssues(true).candidates;
612
+ const configFindings = scanCodexConfigCandidates();
613
+ console.log(ansis.cyan(`
614
+ ${i18n.t("hub:doctor.scanReport")}
615
+ `));
616
+ printCodexRuntimeStatus();
617
+ console.log(ansis.gray(` ${i18n.t("hub:doctor.scanHint", { smart: smart.length, all: all.length })}
618
+ `));
619
+ if (configFindings.length > 0) {
620
+ console.log(ansis.white(` ${i18n.t("hub:doctor.scanSection.config")} (${configFindings.length})`));
621
+ for (const item of configFindings)
622
+ console.log(` ${ansis.yellow("[codex]")} ${item.label}`);
623
+ console.log("");
624
+ }
625
+ if (smart.length === 0 && configFindings.length === 0) {
626
+ console.log(ansis.green(` ${i18n.t("clean:nothingFound")}
627
+ `));
628
+ return false;
629
+ }
630
+ if (smart.length === 0) {
631
+ console.log(ansis.green(` ${i18n.t("clean:nothingFound")}
632
+ `));
633
+ return configFindings.length > 0;
634
+ }
635
+ const grouped = /* @__PURE__ */ new Map();
636
+ for (const item of smart) {
637
+ const list = grouped.get(item.category) || [];
638
+ list.push(item);
639
+ grouped.set(item.category, list);
640
+ }
641
+ for (const [category, items] of grouped) {
642
+ console.log(ansis.white(` ${i18n.t(SCAN_SECTION_KEYS[category])} (${items.length})`));
643
+ for (const item of items) {
644
+ console.log(
645
+ ` ${ansis.cyan(`[${item.tool}]`)} ${item.label} ${ansis.gray(`\u2014 ${item.reason}`)}`
646
+ );
647
+ }
648
+ console.log("");
649
+ }
650
+ return true;
651
+ }
652
+ function scanEnvironmentIssues(purgeAll = false) {
653
+ ensureI18nInitialized();
654
+ if (purgeAll)
655
+ return scanPurgeAllEnvironment();
656
+ const candidates = [
657
+ ...scanCodexMcpCandidates(),
658
+ ...scanClaudeMcpCandidates(),
659
+ ...skillDirsForTool("agents").flatMap((dir) => scanSkillDirectory(dir, "agents")),
660
+ ...skillDirsForTool("claude").flatMap((dir) => scanSkillDirectory(dir, "claude")),
661
+ ...skillDirsForTool("codex").flatMap((dir) => scanSkillDirectory(dir, "codex")),
662
+ ...scanCodexPromptCandidates()
663
+ ];
664
+ const unique = /* @__PURE__ */ new Map();
665
+ for (const item of candidates)
666
+ unique.set(item.id, item);
667
+ return { candidates: Array.from(unique.values()) };
668
+ }
669
+ async function removeCodexMcp(serviceId) {
670
+ deleteCodexMcpService(serviceId);
671
+ }
672
+ async function removeClaudeMcp(serviceId) {
673
+ const config = readMcpConfig("claude-code");
674
+ if (!config?.mcpServers?.[serviceId])
675
+ return;
676
+ const next = { ...config, mcpServers: { ...config.mcpServers } };
677
+ delete next.mcpServers[serviceId];
678
+ writeMcpConfig(next, "claude-code");
679
+ }
680
+ async function executeCleanup(selected, options = {}) {
681
+ const removed = [];
682
+ const errors = [];
683
+ const backupPath = backupCodexComplete();
684
+ if (backupPath)
685
+ console.log(ansis.gray(getBackupMessage(backupPath)));
686
+ const codexMcpItems = selected.filter((item) => item.category === "mcp" && item.tool === "codex");
687
+ if (options.purgeAllMcp && codexMcpItems.length > 0) {
688
+ try {
689
+ deleteAllCodexMcpServices();
690
+ for (const item of codexMcpItems)
691
+ removed.push(`codex MCP: ${item.label}`);
692
+ } catch (error) {
693
+ errors.push(`codex MCP: ${error instanceof Error ? error.message : String(error)}`);
694
+ }
695
+ }
696
+ for (const item of selected) {
697
+ if (options.purgeAllMcp && item.category === "mcp" && item.tool === "codex")
698
+ continue;
699
+ try {
700
+ if (item.category === "mcp" && item.configKey) {
701
+ if (item.tool === "codex")
702
+ await removeCodexMcp(item.configKey);
703
+ else if (item.tool === "claude")
704
+ await removeClaudeMcp(item.configKey);
705
+ removed.push(`${item.tool} MCP: ${item.label}`);
706
+ } else if ((item.category === "skill" || item.category === "prompt" || item.category === "agents-file") && item.path) {
707
+ const result = await moveToTrash(item.path);
708
+ if (result[0]?.success)
709
+ removed.push(`${item.category === "agents-file" ? "AGENTS" : item.category}: ${item.label}`);
710
+ else
711
+ errors.push(result[0]?.error || item.path);
712
+ }
713
+ } catch (error) {
714
+ errors.push(`${item.label}: ${error instanceof Error ? error.message : String(error)}`);
715
+ }
716
+ }
717
+ return { removed, errors };
718
+ }
719
+ function resolveCodeTool() {
720
+ const config = readCcjkConfig();
721
+ if (config?.codeToolType === "claude-code" || config?.codeToolType === "codex")
722
+ return config.codeToolType;
723
+ return "codex";
724
+ }
725
+ async function runEnvironmentCleanup(options = {}) {
726
+ ensureI18nInitialized();
727
+ void resolveCodeTool();
728
+ void options.codeToolType;
729
+ if (options.purgeAll && !options.skipPrompt) {
730
+ console.log(ansis.yellow(`
731
+ ${i18n.t("clean:purgeAllWarning")}
732
+ `));
733
+ }
734
+ const { candidates } = scanEnvironmentIssues(options.purgeAll);
735
+ if (candidates.length === 0) {
736
+ console.log(ansis.green(i18n.t("clean:nothingFound")));
737
+ return { removed: [], errors: [] };
738
+ }
739
+ let selected = candidates;
740
+ if (options.aggressive)
741
+ selected = candidates;
742
+ if (!options.skipPrompt) {
743
+ console.log(ansis.cyan(`
744
+ ${i18n.t("clean:scanSummary", { count: candidates.length })}
745
+ `));
746
+ const { picked } = await inquirer.prompt({
747
+ type: "checkbox",
748
+ name: "picked",
749
+ message: `${i18n.t("clean:selectItems")}${i18n.t("common:multiSelectHint")}`,
750
+ choices: addNumbersToChoices(candidates.map((item) => ({
751
+ name: `${ansis.cyan(`[${item.tool}]`)} ${item.label} - ${ansis.gray(item.reason)}`,
752
+ value: item.id,
753
+ checked: options.purgeAll || options.aggressive ? true : item.checked
754
+ })))
755
+ });
756
+ if (!picked?.length) {
757
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
758
+ return { removed: [], errors: [] };
759
+ }
760
+ selected = candidates.filter((item) => picked.includes(item.id));
761
+ const { confirmed } = await inquirer.prompt({
762
+ type: "confirm",
763
+ name: "confirmed",
764
+ message: i18n.t("clean:confirmCleanup", { count: selected.length }),
765
+ default: false
766
+ });
767
+ if (!confirmed) {
768
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
769
+ return { removed: [], errors: [] };
770
+ }
771
+ } else if (options.purgeAll || options.aggressive) {
772
+ selected = candidates;
773
+ } else {
774
+ selected = candidates.filter((item) => item.checked);
775
+ }
776
+ const result = await executeCleanup(selected, { purgeAllMcp: options.purgeAll });
777
+ if (result.removed.length > 0) {
778
+ console.log(ansis.green(`
779
+ ${i18n.t("clean:cleanupDone", { count: result.removed.length })}`));
780
+ for (const line of result.removed)
781
+ console.log(ansis.gray(` \u2714 ${line}`));
782
+ }
783
+ if (result.errors.length > 0) {
784
+ console.log(ansis.red(`
785
+ ${i18n.t("clean:cleanupErrors", { count: result.errors.length })}`));
786
+ for (const line of result.errors)
787
+ console.log(ansis.red(` \u2717 ${line}`));
788
+ }
789
+ return result;
790
+ }
791
+
792
+ async function cleanCommand(options = {}) {
793
+ ensureI18nInitialized();
794
+ const codeToolType = options.codeType ? resolveCodeToolType$1(options.codeType) : void 0;
795
+ await runEnvironmentCleanup({
796
+ codeToolType,
797
+ skipPrompt: options.skipPrompt,
798
+ aggressive: options.aggressive,
799
+ purgeAll: options.purgeAll
800
+ });
801
+ }
802
+
803
+ const TIMEOUT_MS = 1e4;
804
+ const EXIT_CODE = {
805
+ ok: 0,
806
+ "auth-failed": 1,
807
+ unreachable: 2,
808
+ unexpected: 3
809
+ };
810
+ async function probeCommand(opts = {}) {
811
+ const creds = readActiveApiCreds();
812
+ if (!creds.baseUrl) {
813
+ throw new Error("\u672A\u627E\u5230 API \u914D\u7F6E\uFF0C\u8BF7\u5148\u8FD0\u884C ccjk init \u6216\u83DC\u5355 3 \u914D\u7F6E API");
814
+ }
815
+ const result = await runProbe(creds.baseUrl, creds.apiKey, creds.authToken);
816
+ if (opts.json) {
817
+ console.log(JSON.stringify(result, null, 2));
818
+ } else {
819
+ const color = result.status === "ok" ? ansis.green : ansis.red;
820
+ console.log(`
821
+ \u63A2\u6D3B ${creds.baseUrl}`);
822
+ console.log(color(` \u72B6\u6001: ${result.status}`));
823
+ if (result.httpStatus) console.log(ansis.dim(` HTTP: ${result.httpStatus}`));
824
+ if (result.latencyMs) console.log(ansis.dim(` \u5EF6\u8FDF: ${result.latencyMs}ms`));
825
+ if (result.error) console.log(ansis.dim(` \u9519\u8BEF: ${result.error}`));
826
+ console.log();
827
+ }
828
+ process.exitCode = EXIT_CODE[result.status];
829
+ }
830
+ function readActiveApiCreds() {
831
+ const tool = readCcjkConfig()?.codeToolType ?? "clavue";
832
+ const file = activeSettingsFile(tool === "codex" || tool === "grok" ? "claude-code" : tool);
833
+ const settings = readJsonConfig(file) || {};
834
+ const env = settings.env || {};
835
+ return {
836
+ baseUrl: env.ANTHROPIC_BASE_URL || "",
837
+ apiKey: env.ANTHROPIC_API_KEY || "",
838
+ authToken: env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY || ""
839
+ };
840
+ }
841
+ async function runProbe(baseUrl, apiKey, authToken) {
842
+ const url = `${baseUrl.replace(/\/$/, "")}/v1/messages`;
843
+ const headers = {
844
+ "content-type": "application/json",
845
+ "anthropic-version": "2023-06-01"
846
+ };
847
+ if (authToken) headers.authorization = `Bearer ${authToken}`;
848
+ else if (apiKey) headers["x-api-key"] = apiKey;
849
+ const body = JSON.stringify({
850
+ model: "claude-3-5-haiku-20241022",
851
+ max_tokens: 1,
852
+ messages: [{ role: "user", content: "ping" }]
853
+ });
854
+ const t0 = Date.now();
855
+ try {
856
+ const res = await fetch(url, { method: "POST", headers, body, signal: AbortSignal.timeout(TIMEOUT_MS) });
857
+ const latencyMs = Date.now() - t0;
858
+ if (res.status === 401 || res.status === 403) {
859
+ return { baseUrl, status: "auth-failed", httpStatus: res.status, latencyMs };
860
+ }
861
+ if (res.ok) return { baseUrl, status: "ok", httpStatus: res.status, latencyMs };
862
+ return { baseUrl, status: "unexpected", httpStatus: res.status, latencyMs };
863
+ } catch (e) {
864
+ return { baseUrl, status: "unreachable", error: e.message, latencyMs: Date.now() - t0 };
865
+ }
866
+ }
867
+
868
+ async function runDoctorHub(tool) {
869
+ ensureI18nInitialized();
870
+ const supportsProbe = tool !== "grok";
871
+ let exitHub = false;
872
+ while (!exitHub) {
873
+ console.log(ansis.cyan(`
874
+ ${i18n.t("hub:doctor.title")}`));
875
+ console.log(ansis.gray(` ${i18n.t("hub:doctor.subtitle")}`));
876
+ console.log(ansis.dim(` ${TOOL_MATRIX[tool].displayName}
877
+ `));
878
+ if (tool === "codex")
879
+ printCodexRuntimeStatus();
880
+ const choices = addNumbersToChoices([
881
+ {
882
+ name: `${i18n.t("hub:doctor.scanReport")} \u2014 ${ansis.gray(i18n.t("hub:doctor.scanReportDesc"))}`,
883
+ value: "scan",
884
+ short: i18n.t("hub:doctor.scanReport")
885
+ },
886
+ ...supportsProbe ? [{
887
+ name: i18n.t("hub:doctor.probeApi"),
888
+ value: "probe",
889
+ short: i18n.t("hub:doctor.probeApi")
890
+ }] : [],
891
+ {
892
+ name: `${i18n.t("hub:doctor.smartClean")} \u2014 ${ansis.gray(i18n.t("hub:doctor.smartCleanDesc"))}`,
893
+ value: "smart",
894
+ short: i18n.t("hub:doctor.smartClean")
895
+ },
896
+ {
897
+ name: `${i18n.t("hub:doctor.purgeAll")} \u2014 ${ansis.gray(i18n.t("hub:doctor.purgeAllDesc"))}`,
898
+ value: "purge",
899
+ short: i18n.t("hub:doctor.purgeAll")
900
+ },
901
+ {
902
+ name: i18n.t("common:back"),
903
+ value: "back",
904
+ short: i18n.t("common:back")
905
+ }
906
+ ]);
907
+ const { action } = await inquirer.prompt({
908
+ type: "list",
909
+ name: "action",
910
+ message: i18n.t("menu:selectFunction"),
911
+ choices
912
+ });
913
+ switch (action) {
914
+ case "scan":
915
+ printEnvironmentScanReport();
916
+ break;
917
+ case "probe":
918
+ await probeCommand();
919
+ break;
920
+ case "smart":
921
+ await cleanCommand({ codeType: tool });
922
+ break;
923
+ case "purge":
924
+ await cleanCommand({ codeType: tool, purgeAll: true });
925
+ break;
926
+ case "back":
927
+ exitHub = true;
928
+ break;
929
+ }
930
+ }
931
+ }
932
+
933
+ function resolveCodeToolType(optionValue, savedValue) {
934
+ if (optionValue !== void 0) {
935
+ const resolved = resolveCodeToolType$1(optionValue);
936
+ if (resolved !== DEFAULT_CODE_TOOL_TYPE || optionValue === DEFAULT_CODE_TOOL_TYPE) {
937
+ return resolved;
938
+ }
939
+ }
940
+ if (savedValue && isCodeToolType(savedValue)) {
941
+ return savedValue;
942
+ }
943
+ return DEFAULT_CODE_TOOL_TYPE;
944
+ }
945
+ async function update(options = {}) {
946
+ try {
947
+ if (!options.skipBanner) {
948
+ displayBanner(i18n.t("cli:banner.updateSubtitle"));
949
+ }
950
+ const ccjkConfig = readCcjkConfig();
951
+ const codeToolType = resolveCodeToolType(options.codeType, ccjkConfig?.codeToolType);
952
+ options.codeType = codeToolType;
953
+ if (codeToolType === "codex") {
954
+ await runCodexUpdate();
955
+ const newPreferredLang = options.configLang || ccjkConfig?.preferredLang;
956
+ if (newPreferredLang) {
957
+ updateCcjkConfig({
958
+ version,
959
+ preferredLang: newPreferredLang,
960
+ codeToolType
961
+ });
962
+ } else {
963
+ updateCcjkConfig({
964
+ version,
965
+ codeToolType
966
+ });
967
+ }
968
+ return;
969
+ }
970
+ const { resolveTemplateLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bN; });
971
+ const configLang = await resolveTemplateLanguage(
972
+ options.configLang,
973
+ // Command line option
974
+ ccjkConfig,
975
+ options.skipPrompt
976
+ // Non-interactive mode flag
977
+ );
978
+ const aiOutputLang = await resolveAiOutputLanguage(i18n.language, options.aiOutputLang, ccjkConfig, options.skipPrompt);
979
+ const runtimeLabel = isClaudeFamilyCodeTool(codeToolType) ? getClaudeFamilyRuntimeLabel(codeToolType) : "Claude Code";
980
+ console.log(ansis.cyan(`
981
+ ${i18n.t("configuration:updatingPrompts", { runtime: runtimeLabel })}
982
+ `));
983
+ await updatePromptOnly(aiOutputLang);
984
+ await selectAndInstallWorkflows(configLang, void 0, { codeToolType: isClaudeFamilyCodeTool(codeToolType) ? codeToolType : "claude-code" });
985
+ if (codeToolType === "claude-code") {
986
+ await checkClaudeCodeVersionAndPrompt(false);
987
+ }
988
+ updateCcjkConfig({
989
+ version,
990
+ templateLang: configLang,
991
+ // 保存模板语言选择
992
+ aiOutputLang,
993
+ codeToolType
994
+ });
995
+ } catch (error) {
996
+ if (!handleExitPromptError(error)) {
997
+ handleGeneralError(error);
998
+ }
999
+ }
1000
+ }
1001
+
1002
+ const LEGACY_PROFILES_DIR = join(homedir(), ".ccjk", "profiles");
1003
+ function fromClaudeProfile(profile) {
1004
+ return {
1005
+ name: profile.name,
1006
+ baseUrl: profile.baseUrl,
1007
+ apiKey: profile.apiKey,
1008
+ model: profile.primaryModel
1009
+ };
1010
+ }
1011
+ function readLegacyJsonProfile(name) {
1012
+ const profilePath = join(LEGACY_PROFILES_DIR, `${name}.json`);
1013
+ if (!existsSync(profilePath)) {
1014
+ return null;
1015
+ }
1016
+ try {
1017
+ const data = JSON.parse(readFileSync(profilePath, "utf-8"));
1018
+ return data.name ? data : { ...data, name };
1019
+ } catch {
1020
+ return null;
1021
+ }
1022
+ }
1023
+ function readCcjkProfileByName(name) {
1024
+ const byName = ClaudeCodeConfigManager.getProfileByName(name);
1025
+ if (byName) {
1026
+ return fromClaudeProfile(byName);
1027
+ }
1028
+ const byId = ClaudeCodeConfigManager.getProfileById(name);
1029
+ if (byId) {
1030
+ return fromClaudeProfile(byId);
1031
+ }
1032
+ return readLegacyJsonProfile(name);
1033
+ }
1034
+ function listCcjkProfileNames() {
1035
+ return ClaudeCodeConfigManager.listProfiles().map((profile) => profile.name);
1036
+ }
1037
+
1038
+ const GROK_AUTH_FILE = join(homedir(), ".grok", "auth.json");
1039
+ function isGrokInstalled() {
1040
+ try {
1041
+ execSync("command -v grok", { stdio: "ignore" });
1042
+ return true;
1043
+ } catch {
1044
+ return false;
1045
+ }
1046
+ }
1047
+ function isGrokLoggedIn() {
1048
+ if (!existsSync(GROK_AUTH_FILE)) return false;
1049
+ try {
1050
+ const data = JSON.parse(readFileSync(GROK_AUTH_FILE, "utf-8"));
1051
+ return Object.keys(data).some((k) => k.includes("auth.x.ai"));
1052
+ } catch {
1053
+ return false;
1054
+ }
1055
+ }
1056
+ function getGrokQuickStatus() {
1057
+ if (!isGrokInstalled()) {
1058
+ return {
1059
+ installed: false,
1060
+ loggedIn: false,
1061
+ hint: "\u672A\u5B89\u88C5\uFF1Acurl -fsSL https://x.ai/cli/install.sh | bash"
1062
+ };
1063
+ }
1064
+ const loggedIn = isGrokLoggedIn();
1065
+ return {
1066
+ installed: true,
1067
+ loggedIn,
1068
+ hint: loggedIn ? "\u5DF2\u767B\u5F55\uFF08OAuth\uFF0C~/.grok/auth.json\uFF09" : "\u672A\u767B\u5F55\uFF0C\u8FD0\u884C `grok login`"
1069
+ };
1070
+ }
1071
+ function buildGrokHints() {
1072
+ const s = getGrokQuickStatus();
1073
+ const lines = [];
1074
+ if (!s.installed) {
1075
+ lines.push(ansis.dim(" \u5B89\u88C5: ") + ansis.cyan("curl -fsSL https://x.ai/cli/install.sh | bash"));
1076
+ return lines;
1077
+ }
1078
+ if (!s.loggedIn) {
1079
+ lines.push(ansis.yellow(" \u767B\u5F55: ") + ansis.cyan("grok login"));
1080
+ }
1081
+ lines.push(ansis.dim(" \u542F\u52A8: ") + ansis.cyan("grok"));
1082
+ lines.push(ansis.dim(" \u6A21\u578B: ") + ansis.cyan("grok models"));
1083
+ return lines;
1084
+ }
1085
+
1086
+ async function grokCommand(profileName) {
1087
+ if (profileName) {
1088
+ await launchWithProfile$1(profileName);
1089
+ return;
1090
+ }
1091
+ const status = getGrokQuickStatus();
1092
+ console.log(ansis.bold("\nGrok CLI"));
1093
+ console.log(` \u5B89\u88C5: ${status.installed ? ansis.green("\u662F") : ansis.gray("\u5426")} \u767B\u5F55: ${status.loggedIn ? ansis.green("\u662F") : ansis.yellow("\u5426")}`);
1094
+ for (const line of buildGrokHints()) console.log(line);
1095
+ const names = listCcjkProfileNames();
1096
+ if (names.length > 0) {
1097
+ console.log(ansis.dim(`
1098
+ \u53EF\u7528 profile: ${names.join(", ")}`));
1099
+ }
1100
+ console.log(ansis.dim(" ccjk grok <profile> \u2014 \u7528 ~/.ccjk/config.toml \u4E2D\u7684 profile \u542F\u52A8\n"));
1101
+ }
1102
+ async function launchWithProfile$1(name) {
1103
+ const profile = readCcjkProfileByName(name);
1104
+ if (!profile) {
1105
+ console.log(ansis.red(`Profile "${name}" \u4E0D\u5B58\u5728`));
1106
+ console.log(ansis.dim(" \u5148\u8FD0\u884C ccjk init -T grok \u6216\u83DC\u5355\u914D\u7F6E API\uFF08profile \u4FDD\u5B58\u5728 ~/.ccjk/config.toml\uFF09\n"));
1107
+ return;
1108
+ }
1109
+ const env = { ...process.env };
1110
+ if (profile.baseUrl) {
1111
+ env.OPENAI_BASE_URL = profile.baseUrl;
1112
+ env.ANTHROPIC_BASE_URL = profile.baseUrl;
1113
+ }
1114
+ if (profile.apiKey) {
1115
+ env.OPENAI_API_KEY = profile.apiKey;
1116
+ env.ANTHROPIC_API_KEY = profile.apiKey;
1117
+ env.ANTHROPIC_AUTH_TOKEN = profile.apiKey;
1118
+ }
1119
+ console.log(ansis.cyan(`
1120
+ \u4F7F\u7528 profile "${name}" \u542F\u52A8 grok...
1121
+ `));
1122
+ const res = spawnSync("grok", [], { stdio: "inherit", env });
1123
+ if (res.error) console.log(ansis.red(`\u542F\u52A8\u5931\u8D25: ${res.error.message}`));
1124
+ }
1125
+
1126
+ function line(key, labelKey, descKey) {
1127
+ return { key, labelKey, descKey };
1128
+ }
1129
+ const CROSS_TOOL_LINES = [
1130
+ line("g", "menu:menuOptions.grokCrossTool", "menu:menuDescriptions.grokCrossTool"),
1131
+ line("x", "menu:menuOptions.reasonixCrossTool", "menu:menuDescriptions.reasonixCrossTool")
1132
+ ];
1133
+ const DOCTOR_HUB = line("h", "menu:menuOptions.doctorHub", "menu:menuDescriptions.doctorHub");
1134
+ const OPERATOR_HUB = line("w", "menu:menuOptions.operatorHub", "menu:menuDescriptions.operatorHub");
1135
+ const CLAUDE_FAMILY_OPERATOR = [
1136
+ line("1", "menu:menuOptions.fullInit", "menu:menuDescriptions.fullInit"),
1137
+ line("2", "menu:menuOptions.importWorkflow", "menu:menuDescriptions.importWorkflow"),
1138
+ line("3", "menu:menuOptions.configureApiOrCcr", "menu:menuDescriptions.configureApiOrCcr"),
1139
+ line("4", "menu:menuOptions.configureMcp", "menu:menuDescriptions.configureMcp"),
1140
+ line("5", "menu:menuOptions.configureModel", "menu:menuDescriptions.configureModel"),
1141
+ line("6", "menu:menuOptions.configureAiMemory", "menu:menuDescriptions.configureAiMemory"),
1142
+ line("7", "menu:menuOptions.configureEnvPermission", "menu:menuDescriptions.configureEnvPermission")
1143
+ ];
1144
+ const TOOL_MENU_LAYOUTS = {
1145
+ clavue: {
1146
+ toolLabelKey: "menu:toolHeaders.clavue",
1147
+ operatorLines: [
1148
+ line("1", "menu:menuOptions.clavueFullInit", "menu:menuDescriptions.clavueFullInit"),
1149
+ line("2", "menu:menuOptions.clavueUpdateWorkflow", "menu:menuDescriptions.clavueUpdateWorkflow"),
1150
+ line("3", "menu:menuOptions.clavueConfigureApi", "menu:menuDescriptions.clavueConfigureApi"),
1151
+ line("4", "menu:menuOptions.clavueConfigureMcp", "menu:menuDescriptions.clavueConfigureMcp"),
1152
+ line("5", "menu:menuOptions.clavueConfigureModel", "menu:menuDescriptions.clavueConfigureModel"),
1153
+ line("6", "menu:menuOptions.clavueConfigureAiMemory", "menu:menuDescriptions.clavueConfigureAiMemory"),
1154
+ line("7", "menu:menuOptions.clavueConfigureEnvPermission", "menu:menuDescriptions.clavueConfigureEnvPermission")
1155
+ ],
1156
+ doctorLines: [DOCTOR_HUB],
1157
+ ccjkUninstallKey: "menu:menuOptions.clavueUninstall",
1158
+ ccjkUninstallDescKey: "menu:menuDescriptions.clavueUninstall",
1159
+ ccjkUpdateKey: "menu:menuOptions.clavueCheckUpdates",
1160
+ ccjkUpdateDescKey: "menu:menuDescriptions.clavueCheckUpdates"
1161
+ },
1162
+ "claude-code": {
1163
+ toolLabelKey: "menu:toolHeaders.claudeCode",
1164
+ operatorLines: CLAUDE_FAMILY_OPERATOR,
1165
+ doctorLines: [DOCTOR_HUB],
1166
+ ccjkUninstallKey: "menu:menuOptions.uninstall",
1167
+ ccjkUninstallDescKey: "menu:menuDescriptions.uninstall",
1168
+ ccjkUpdateKey: "menu:menuOptions.checkUpdates",
1169
+ ccjkUpdateDescKey: "menu:menuDescriptions.checkUpdates"
1170
+ },
1171
+ codex: {
1172
+ toolLabelKey: "menu:toolHeaders.codex",
1173
+ operatorLines: [
1174
+ line("1", "menu:menuOptions.codexFullInit", "menu:menuDescriptions.codexFullInitWithPreset"),
1175
+ line("2", "menu:menuOptions.codexImportWorkflow", "menu:menuDescriptions.codexImportWorkflow"),
1176
+ line("3", "menu:menuOptions.codexConfigureApi", "menu:menuDescriptions.codexConfigureApi"),
1177
+ line("4", "menu:menuOptions.codexConfigureMcp", "menu:menuDescriptions.codexConfigureMcp"),
1178
+ line("5", "menu:menuOptions.codexConfigureModel", "menu:menuDescriptions.codexConfigureModel"),
1179
+ line("6", "menu:menuOptions.codexConfigureAiMemory", "menu:menuDescriptions.codexConfigureAiMemory")
1180
+ ],
1181
+ doctorLines: [DOCTOR_HUB],
1182
+ ccjkUninstallKey: "menu:menuOptions.codexUninstall",
1183
+ ccjkUninstallDescKey: "menu:menuDescriptions.codexUninstall",
1184
+ ccjkUpdateKey: "menu:menuOptions.codexCheckUpdates",
1185
+ ccjkUpdateDescKey: "menu:menuDescriptions.codexCheckUpdates"
1186
+ },
1187
+ grok: {
1188
+ toolLabelKey: "menu:toolHeaders.grok",
1189
+ operatorLines: [
1190
+ line("1", "menu:menuOptions.grokInit", "menu:menuDescriptions.grokInit"),
1191
+ line("2", "menu:menuOptions.grokStatus", "menu:menuDescriptions.grokStatus")
1192
+ ],
1193
+ doctorLines: [DOCTOR_HUB],
1194
+ ccjkUninstallKey: "menu:menuOptions.uninstall",
1195
+ ccjkUninstallDescKey: "menu:menuDescriptions.uninstall",
1196
+ ccjkUpdateKey: "menu:menuOptions.checkUpdates",
1197
+ ccjkUpdateDescKey: "menu:menuDescriptions.checkUpdates"
1198
+ }
1199
+ };
1200
+ function shortenHomePath(path) {
1201
+ const home = homedir();
1202
+ if (path.startsWith(home))
1203
+ return `~${path.slice(home.length)}`;
1204
+ return path;
1205
+ }
1206
+ function formatChoiceLabel(item) {
1207
+ return `${item.key.toUpperCase()}. ${i18n.t(item.labelKey)} ${ansis.gray(`\u2014 ${i18n.t(item.descKey)}`)}`;
1208
+ }
1209
+ function sectionSeparator(titleKey) {
1210
+ return {
1211
+ name: ansis.dim(`\u2500\u2500 ${i18n.t(titleKey)} \u2500\u2500`),
1212
+ value: `__section_${titleKey}`,
1213
+ disabled: true
1214
+ };
1215
+ }
1216
+ function buildMenuListChoices(tool) {
1217
+ const layout = TOOL_MENU_LAYOUTS[tool];
1218
+ const choices = [
1219
+ sectionSeparator("menu:menuSections.operator"),
1220
+ {
1221
+ name: formatChoiceLabel(OPERATOR_HUB),
1222
+ value: OPERATOR_HUB.key
1223
+ },
1224
+ ...layout.operatorLines.map((item) => ({
1225
+ name: formatChoiceLabel(item),
1226
+ value: item.key
1227
+ })),
1228
+ sectionSeparator("menu:menuSections.doctor"),
1229
+ ...layout.doctorLines.map((item) => ({
1230
+ name: formatChoiceLabel(item),
1231
+ value: item.key
1232
+ })),
1233
+ sectionSeparator("menu:menuSections.crossTool"),
1234
+ ...CROSS_TOOL_LINES.map((item) => ({
1235
+ name: formatChoiceLabel(item),
1236
+ value: item.key
1237
+ })),
1238
+ sectionSeparator("menu:menuSections.ccjk"),
1239
+ {
1240
+ name: formatChoiceLabel(line("0", "menu:menuOptions.changeLanguage", "menu:menuDescriptions.changeLanguage")),
1241
+ value: "0"
1242
+ },
1243
+ {
1244
+ name: formatChoiceLabel(line("s", "menu:menuOptions.switchCodeTool", "menu:menuDescriptions.switchCodeTool")),
1245
+ value: "s"
1246
+ },
1247
+ {
1248
+ name: formatChoiceLabel(line("-", layout.ccjkUninstallKey, layout.ccjkUninstallDescKey)),
1249
+ value: "-"
1250
+ },
1251
+ {
1252
+ name: formatChoiceLabel(line("+", layout.ccjkUpdateKey, layout.ccjkUpdateDescKey)),
1253
+ value: "+"
1254
+ },
1255
+ {
1256
+ name: `${ansis.red("Q.")} ${ansis.red(i18n.t("menu:menuOptions.exit"))}`,
1257
+ value: "q"
1258
+ }
1259
+ ];
1260
+ return choices;
1261
+ }
1262
+ function printToolMenuHeader(tool) {
1263
+ const entry = TOOL_MATRIX[tool];
1264
+ const layout = TOOL_MENU_LAYOUTS[tool];
1265
+ const configRoot = shortenHomePath(entry.configRoot);
1266
+ const roles = entry.roles.includes("both") ? i18n.t("menu:roleBadge.both") : entry.roles.includes("doctor") ? i18n.t("menu:roleBadge.doctor") : i18n.t("menu:roleBadge.operator");
1267
+ console.log(ansis.dim(`
1268
+ ${i18n.t("menu:tagline")}`));
1269
+ console.log(
1270
+ ansis.white(
1271
+ ` ${i18n.t(layout.toolLabelKey)} ${ansis.dim("|")} ${ansis.gray(i18n.t("menu:configRoot", { path: configRoot }))} ${ansis.dim("|")} ${roles}`
1272
+ )
1273
+ );
1274
+ console.log("");
1275
+ }
1276
+ function getToolMenuLayout(tool) {
1277
+ return TOOL_MENU_LAYOUTS[tool];
1278
+ }
1279
+
1280
+ async function finishCodexOperatorAction() {
1281
+ printCodexReloadHint();
1282
+ }
1283
+ async function promptTuneSingle(tool) {
1284
+ const layout = getToolMenuLayout(tool);
1285
+ const tuneLines = layout.operatorLines.filter((line) => !["1", "2"].includes(line.key));
1286
+ const { picked } = await inquirer.prompt({
1287
+ type: "list",
1288
+ name: "picked",
1289
+ message: i18n.t("hub:operator.pickTune"),
1290
+ choices: addNumbersToChoices(tuneLines.map((line) => ({
1291
+ name: `${i18n.t(line.labelKey)} \u2014 ${ansis.gray(i18n.t(line.descKey))}`,
1292
+ value: line.key,
1293
+ short: i18n.t(line.labelKey)
1294
+ })))
1295
+ });
1296
+ if (tool === "codex") {
1297
+ switch (picked) {
1298
+ case "3":
1299
+ await configureCodexApi();
1300
+ break;
1301
+ case "4":
1302
+ await manageCodexMcp();
1303
+ break;
1304
+ case "5":
1305
+ await configureCodexDefaultModelFeature();
1306
+ break;
1307
+ case "6":
1308
+ await configureCodexAiMemoryFeature();
1309
+ break;
1310
+ }
1311
+ await finishCodexOperatorAction();
1312
+ return;
1313
+ }
1314
+ if (tool === "clavue" || tool === "claude-code") {
1315
+ switch (picked) {
1316
+ case "3":
1317
+ await configureApiFeature();
1318
+ break;
1319
+ case "4":
1320
+ await configureMcpFeature();
1321
+ break;
1322
+ case "5":
1323
+ await configureDefaultModelFeature();
1324
+ break;
1325
+ case "6":
1326
+ await configureAiMemoryFeature();
1327
+ break;
1328
+ case "7":
1329
+ await configureEnvPermissionFeature();
1330
+ break;
1331
+ }
1332
+ }
1333
+ }
1334
+ async function runCodexFreshStart() {
1335
+ const { confirmed } = await inquirer.prompt({
1336
+ type: "confirm",
1337
+ name: "confirmed",
1338
+ message: i18n.t("hub:operator.freshStartConfirm"),
1339
+ default: false
1340
+ });
1341
+ if (!confirmed) {
1342
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
1343
+ return;
1344
+ }
1345
+ await cleanCommand({ codeType: "codex", purgeAll: true, skipPrompt: true });
1346
+ const { applyCodexSetupPreset, getCodexPresetChoices } = await import('./chunks/codex-presets.mjs');
1347
+ const { preset } = await inquirer.prompt({
1348
+ type: "list",
1349
+ name: "preset",
1350
+ message: i18n.t("codex:preset.selectPrompt"),
1351
+ choices: addNumbersToChoices(
1352
+ getCodexPresetChoices().filter((choice) => choice.id !== "custom").map((choice) => ({
1353
+ name: `${choice.name} \u2014 ${ansis.gray(choice.description)}`,
1354
+ value: choice.id,
1355
+ short: choice.name
1356
+ }))
1357
+ ),
1358
+ default: "optimized"
1359
+ });
1360
+ const presetOptions = applyCodexSetupPreset(preset, { setupPreset: preset, skipPrompt: true });
1361
+ await runCodexFullInit(presetOptions);
1362
+ await finishCodexOperatorAction();
1363
+ }
1364
+ async function runCodexOperatorHub() {
1365
+ const { action } = await inquirer.prompt({
1366
+ type: "list",
1367
+ name: "action",
1368
+ message: i18n.t("menu:selectFunction"),
1369
+ choices: addNumbersToChoices([
1370
+ {
1371
+ name: `${i18n.t("hub:operator.fullInit")} \u2014 ${ansis.gray(i18n.t("menu:menuDescriptions.codexFullInitWithPreset"))}`,
1372
+ value: "init"
1373
+ },
1374
+ {
1375
+ name: i18n.t("hub:operator.updateWorkflow"),
1376
+ value: "workflow"
1377
+ },
1378
+ {
1379
+ name: `${i18n.t("hub:operator.tuneSingle")} \u2014 ${ansis.gray(i18n.t("hub:operator.tuneSingleDesc"))}`,
1380
+ value: "tune"
1381
+ },
1382
+ {
1383
+ name: `${i18n.t("hub:operator.freshStart")} \u2014 ${ansis.gray(i18n.t("hub:operator.freshStartDesc"))}`,
1384
+ value: "fresh"
1385
+ },
1386
+ { name: i18n.t("common:back"), value: "back" }
1387
+ ])
1388
+ });
1389
+ switch (action) {
1390
+ case "init":
1391
+ await runCodexFullInit();
1392
+ await finishCodexOperatorAction();
1393
+ break;
1394
+ case "workflow":
1395
+ await runCodexWorkflowImportWithLanguageSelection();
1396
+ await finishCodexOperatorAction();
1397
+ break;
1398
+ case "tune":
1399
+ await promptTuneSingle("codex");
1400
+ break;
1401
+ case "fresh":
1402
+ await runCodexFreshStart();
1403
+ break;
1404
+ }
1405
+ }
1406
+ async function runClaudeFamilyOperatorHub(tool) {
1407
+ const initDesc = tool === "clavue" ? i18n.t("menu:menuDescriptions.clavueFullInit") : i18n.t("menu:menuDescriptions.fullInit");
1408
+ const { action } = await inquirer.prompt({
1409
+ type: "list",
1410
+ name: "action",
1411
+ message: i18n.t("menu:selectFunction"),
1412
+ choices: addNumbersToChoices([
1413
+ {
1414
+ name: `${i18n.t("hub:operator.fullInit")} \u2014 ${ansis.gray(initDesc)}`,
1415
+ value: "init"
1416
+ },
1417
+ {
1418
+ name: i18n.t("hub:operator.updateWorkflow"),
1419
+ value: "workflow"
1420
+ },
1421
+ {
1422
+ name: `${i18n.t("hub:operator.tuneSingle")} \u2014 ${ansis.gray(i18n.t("hub:operator.tuneSingleDesc"))}`,
1423
+ value: "tune"
1424
+ },
1425
+ { name: i18n.t("common:back"), value: "back" }
1426
+ ])
1427
+ });
1428
+ switch (action) {
1429
+ case "init":
1430
+ await init({ skipBanner: true, codeType: tool });
1431
+ break;
1432
+ case "workflow":
1433
+ await update({ skipBanner: true, codeType: tool });
1434
+ break;
1435
+ case "tune":
1436
+ await promptTuneSingle(tool);
1437
+ break;
1438
+ }
1439
+ }
1440
+ async function runGrokOperatorHub() {
1441
+ const { action } = await inquirer.prompt({
1442
+ type: "list",
1443
+ name: "action",
1444
+ message: i18n.t("menu:selectFunction"),
1445
+ choices: addNumbersToChoices([
1446
+ {
1447
+ name: `${i18n.t("hub:operator.grokProfile")} \u2014 ${ansis.gray(i18n.t("menu:menuDescriptions.grokInit"))}`,
1448
+ value: "profile"
1449
+ },
1450
+ {
1451
+ name: `${i18n.t("hub:operator.grokLaunch")} \u2014 ${ansis.gray(i18n.t("menu:menuDescriptions.grokStatus"))}`,
1452
+ value: "launch"
1453
+ },
1454
+ { name: i18n.t("common:back"), value: "back" }
1455
+ ])
1456
+ });
1457
+ switch (action) {
1458
+ case "profile":
1459
+ await init({ skipBanner: true, codeType: "grok" });
1460
+ break;
1461
+ case "launch":
1462
+ await grokCommand();
1463
+ break;
1464
+ }
1465
+ }
1466
+ async function runOperatorHub(tool) {
1467
+ ensureI18nInitialized();
1468
+ console.log(ansis.cyan(`
1469
+ ${i18n.t("hub:operator.title")}`));
1470
+ console.log(ansis.gray(` ${i18n.t("hub:operator.subtitle")}`));
1471
+ console.log(ansis.dim(` ${TOOL_MATRIX[tool].displayName}
1472
+ `));
1473
+ if (tool === "codex")
1474
+ return runCodexOperatorHub();
1475
+ if (tool === "grok")
1476
+ return runGrokOperatorHub();
1477
+ return runClaudeFamilyOperatorHub(tool);
1478
+ }
1479
+
151
1480
  class ToolUpdateScheduler {
152
1481
  /**
153
1482
  * Update tools based on code type
@@ -491,7 +1820,7 @@ class CcjkUninstaller {
491
1820
  result.removed.push(".claude.json (includes MCP configuration)");
492
1821
  }
493
1822
  try {
494
- const { uninstallCodeTool } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bz; });
1823
+ const { uninstallCodeTool } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bR; });
495
1824
  const success = await uninstallCodeTool("claude-code");
496
1825
  if (success) {
497
1826
  result.removed.push("@anthropic-ai/claude-code");
@@ -727,7 +2056,7 @@ async function uninstall(options = {}) {
727
2056
  }
728
2057
  const uninstaller = new CcjkUninstaller(options.lang || "en");
729
2058
  if (codeType === "codex") {
730
- const { runCodexUninstall } = await import('./chunks/simple-config.mjs').then(function (n) { return n.by; });
2059
+ const { runCodexUninstall } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bQ; });
731
2060
  await runCodexUninstall();
732
2061
  return;
733
2062
  }
@@ -901,256 +2230,63 @@ function displayUninstallResult(mode, results) {
901
2230
  result.removed.forEach((item) => {
902
2231
  console.log(ansis.gray(` \u2022 ${item}`));
903
2232
  });
904
- }
905
- if (result.removedConfigs && result.removedConfigs.length > 0) {
906
- console.log(ansis.green(`\u2714 ${i18n.t("uninstall:removedConfigs")}:`));
907
- result.removedConfigs.forEach((item) => {
908
- console.log(ansis.gray(` \u2022 ${item}`));
909
- });
910
- }
911
- if (result.errors && result.errors.length > 0) {
912
- totalErrors += result.errors.length;
913
- console.log(ansis.red(`\u2716 ${i18n.t("uninstall:errors")}:`));
914
- result.errors.forEach((error) => {
915
- console.log(ansis.red(` \u2022 ${error}`));
916
- });
917
- }
918
- if (result.warnings && result.warnings.length > 0) {
919
- totalWarnings += result.warnings.length;
920
- console.log(ansis.yellow(`\u26A0 ${i18n.t("uninstall:warnings")}:`));
921
- result.warnings.forEach((warning) => {
922
- console.log(ansis.yellow(` \u2022 ${warning}`));
923
- });
924
- }
925
- });
926
- const totalRemovedFiles = results.reduce((count, result) => count + (result.removed?.length || 0), 0);
927
- const totalRemovedConfigs = results.reduce((count, result) => count + (result.removedConfigs?.length || 0), 0);
928
- console.log("");
929
- console.log(ansis.cyan("\u2500".repeat(50)));
930
- if (mode === "complete") {
931
- if (totalErrors === 0) {
932
- console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:completeSuccess")}`));
933
- } else {
934
- console.log(ansis.yellow.bold(`\u26A0 ${i18n.t("uninstall:completePartialSuccess")}`));
935
- }
936
- } else {
937
- if (totalRemovedFiles > 0 && totalRemovedConfigs > 0) {
938
- console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessBoth", {
939
- fileCount: totalRemovedFiles,
940
- configCount: totalRemovedConfigs
941
- })}`));
942
- } else if (totalRemovedFiles > 0) {
943
- console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessFiles", {
944
- count: totalRemovedFiles
945
- })}`));
946
- } else if (totalRemovedConfigs > 0) {
947
- console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessConfigs", {
948
- count: totalRemovedConfigs
949
- })}`));
950
- } else {
951
- console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccess", { count: totalSuccess })}`));
952
- }
953
- if (totalErrors > 0) {
954
- console.log(ansis.red(`\u2716 ${i18n.t("uninstall:errorsCount", { count: totalErrors })}`));
955
- }
956
- if (totalWarnings > 0) {
957
- console.log(ansis.yellow(`\u26A0 ${i18n.t("uninstall:warningsCount", { count: totalWarnings })}`));
958
- }
959
- }
960
- console.log("");
961
- }
962
-
963
- function resolveCodeToolType(optionValue, savedValue) {
964
- if (optionValue !== void 0) {
965
- const resolved = resolveCodeToolType$1(optionValue);
966
- if (resolved !== DEFAULT_CODE_TOOL_TYPE || optionValue === DEFAULT_CODE_TOOL_TYPE) {
967
- return resolved;
968
- }
969
- }
970
- if (savedValue && isCodeToolType(savedValue)) {
971
- return savedValue;
972
- }
973
- return DEFAULT_CODE_TOOL_TYPE;
974
- }
975
- async function update(options = {}) {
976
- try {
977
- if (!options.skipBanner) {
978
- displayBanner(i18n.t("cli:banner.updateSubtitle"));
979
- }
980
- const ccjkConfig = readCcjkConfig();
981
- const codeToolType = resolveCodeToolType(options.codeType, ccjkConfig?.codeToolType);
982
- options.codeType = codeToolType;
983
- if (codeToolType === "codex") {
984
- await runCodexUpdate();
985
- const newPreferredLang = options.configLang || ccjkConfig?.preferredLang;
986
- if (newPreferredLang) {
987
- updateCcjkConfig({
988
- version,
989
- preferredLang: newPreferredLang,
990
- codeToolType
991
- });
992
- } else {
993
- updateCcjkConfig({
994
- version,
995
- codeToolType
996
- });
997
- }
998
- return;
999
- }
1000
- const { resolveTemplateLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bv; });
1001
- const configLang = await resolveTemplateLanguage(
1002
- options.configLang,
1003
- // Command line option
1004
- ccjkConfig,
1005
- options.skipPrompt
1006
- // Non-interactive mode flag
1007
- );
1008
- const aiOutputLang = await resolveAiOutputLanguage(i18n.language, options.aiOutputLang, ccjkConfig, options.skipPrompt);
1009
- const runtimeLabel = isClaudeFamilyCodeTool(codeToolType) ? getClaudeFamilyRuntimeLabel(codeToolType) : "Claude Code";
1010
- console.log(ansis.cyan(`
1011
- ${i18n.t("configuration:updatingPrompts", { runtime: runtimeLabel })}
1012
- `));
1013
- await updatePromptOnly(aiOutputLang);
1014
- await selectAndInstallWorkflows(configLang, void 0, { codeToolType: isClaudeFamilyCodeTool(codeToolType) ? codeToolType : "claude-code" });
1015
- if (codeToolType === "claude-code") {
1016
- await checkClaudeCodeVersionAndPrompt(false);
1017
- }
1018
- updateCcjkConfig({
1019
- version,
1020
- templateLang: configLang,
1021
- // 保存模板语言选择
1022
- aiOutputLang,
1023
- codeToolType
1024
- });
1025
- } catch (error) {
1026
- if (!handleExitPromptError(error)) {
1027
- handleGeneralError(error);
1028
- }
1029
- }
1030
- }
1031
-
1032
- const LEGACY_PROFILES_DIR = join(homedir(), ".ccjk", "profiles");
1033
- function fromClaudeProfile(profile) {
1034
- return {
1035
- name: profile.name,
1036
- baseUrl: profile.baseUrl,
1037
- apiKey: profile.apiKey,
1038
- model: profile.primaryModel
1039
- };
1040
- }
1041
- function readLegacyJsonProfile(name) {
1042
- const profilePath = join(LEGACY_PROFILES_DIR, `${name}.json`);
1043
- if (!existsSync(profilePath)) {
1044
- return null;
1045
- }
1046
- try {
1047
- const data = JSON.parse(readFileSync(profilePath, "utf-8"));
1048
- return data.name ? data : { ...data, name };
1049
- } catch {
1050
- return null;
1051
- }
1052
- }
1053
- function readCcjkProfileByName(name) {
1054
- const byName = ClaudeCodeConfigManager.getProfileByName(name);
1055
- if (byName) {
1056
- return fromClaudeProfile(byName);
1057
- }
1058
- const byId = ClaudeCodeConfigManager.getProfileById(name);
1059
- if (byId) {
1060
- return fromClaudeProfile(byId);
1061
- }
1062
- return readLegacyJsonProfile(name);
1063
- }
1064
- function listCcjkProfileNames() {
1065
- return ClaudeCodeConfigManager.listProfiles().map((profile) => profile.name);
1066
- }
1067
-
1068
- const GROK_AUTH_FILE = join(homedir(), ".grok", "auth.json");
1069
- function isGrokInstalled() {
1070
- try {
1071
- execSync("command -v grok", { stdio: "ignore" });
1072
- return true;
1073
- } catch {
1074
- return false;
1075
- }
1076
- }
1077
- function isGrokLoggedIn() {
1078
- if (!existsSync(GROK_AUTH_FILE)) return false;
1079
- try {
1080
- const data = JSON.parse(readFileSync(GROK_AUTH_FILE, "utf-8"));
1081
- return Object.keys(data).some((k) => k.includes("auth.x.ai"));
1082
- } catch {
1083
- return false;
1084
- }
1085
- }
1086
- function getGrokQuickStatus() {
1087
- if (!isGrokInstalled()) {
1088
- return {
1089
- installed: false,
1090
- loggedIn: false,
1091
- hint: "\u672A\u5B89\u88C5\uFF1Acurl -fsSL https://x.ai/cli/install.sh | bash"
1092
- };
1093
- }
1094
- const loggedIn = isGrokLoggedIn();
1095
- return {
1096
- installed: true,
1097
- loggedIn,
1098
- hint: loggedIn ? "\u5DF2\u767B\u5F55\uFF08OAuth\uFF0C~/.grok/auth.json\uFF09" : "\u672A\u767B\u5F55\uFF0C\u8FD0\u884C `grok login`"
1099
- };
1100
- }
1101
- function buildGrokHints() {
1102
- const s = getGrokQuickStatus();
1103
- const lines = [];
1104
- if (!s.installed) {
1105
- lines.push(ansis.dim(" \u5B89\u88C5: ") + ansis.cyan("curl -fsSL https://x.ai/cli/install.sh | bash"));
1106
- return lines;
1107
- }
1108
- if (!s.loggedIn) {
1109
- lines.push(ansis.yellow(" \u767B\u5F55: ") + ansis.cyan("grok login"));
1110
- }
1111
- lines.push(ansis.dim(" \u542F\u52A8: ") + ansis.cyan("grok"));
1112
- lines.push(ansis.dim(" \u6A21\u578B: ") + ansis.cyan("grok models"));
1113
- return lines;
1114
- }
1115
-
1116
- async function grokCommand(profileName) {
1117
- if (profileName) {
1118
- await launchWithProfile$1(profileName);
1119
- return;
1120
- }
1121
- const status = getGrokQuickStatus();
1122
- console.log(ansis.bold("\nGrok CLI"));
1123
- console.log(` \u5B89\u88C5: ${status.installed ? ansis.green("\u662F") : ansis.gray("\u5426")} \u767B\u5F55: ${status.loggedIn ? ansis.green("\u662F") : ansis.yellow("\u5426")}`);
1124
- for (const line of buildGrokHints()) console.log(line);
1125
- const names = listCcjkProfileNames();
1126
- if (names.length > 0) {
1127
- console.log(ansis.dim(`
1128
- \u53EF\u7528 profile: ${names.join(", ")}`));
1129
- }
1130
- console.log(ansis.dim(" ccjk grok <profile> \u2014 \u7528 ~/.ccjk/config.toml \u4E2D\u7684 profile \u542F\u52A8\n"));
1131
- }
1132
- async function launchWithProfile$1(name) {
1133
- const profile = readCcjkProfileByName(name);
1134
- if (!profile) {
1135
- console.log(ansis.red(`Profile "${name}" \u4E0D\u5B58\u5728`));
1136
- console.log(ansis.dim(" \u5148\u8FD0\u884C ccjk init -T grok \u6216\u83DC\u5355\u914D\u7F6E API\uFF08profile \u4FDD\u5B58\u5728 ~/.ccjk/config.toml\uFF09\n"));
1137
- return;
1138
- }
1139
- const env = { ...process.env };
1140
- if (profile.baseUrl) {
1141
- env.OPENAI_BASE_URL = profile.baseUrl;
1142
- env.ANTHROPIC_BASE_URL = profile.baseUrl;
1143
- }
1144
- if (profile.apiKey) {
1145
- env.OPENAI_API_KEY = profile.apiKey;
1146
- env.ANTHROPIC_API_KEY = profile.apiKey;
1147
- env.ANTHROPIC_AUTH_TOKEN = profile.apiKey;
2233
+ }
2234
+ if (result.removedConfigs && result.removedConfigs.length > 0) {
2235
+ console.log(ansis.green(`\u2714 ${i18n.t("uninstall:removedConfigs")}:`));
2236
+ result.removedConfigs.forEach((item) => {
2237
+ console.log(ansis.gray(` \u2022 ${item}`));
2238
+ });
2239
+ }
2240
+ if (result.errors && result.errors.length > 0) {
2241
+ totalErrors += result.errors.length;
2242
+ console.log(ansis.red(`\u2716 ${i18n.t("uninstall:errors")}:`));
2243
+ result.errors.forEach((error) => {
2244
+ console.log(ansis.red(` \u2022 ${error}`));
2245
+ });
2246
+ }
2247
+ if (result.warnings && result.warnings.length > 0) {
2248
+ totalWarnings += result.warnings.length;
2249
+ console.log(ansis.yellow(`\u26A0 ${i18n.t("uninstall:warnings")}:`));
2250
+ result.warnings.forEach((warning) => {
2251
+ console.log(ansis.yellow(` \u2022 ${warning}`));
2252
+ });
2253
+ }
2254
+ });
2255
+ const totalRemovedFiles = results.reduce((count, result) => count + (result.removed?.length || 0), 0);
2256
+ const totalRemovedConfigs = results.reduce((count, result) => count + (result.removedConfigs?.length || 0), 0);
2257
+ console.log("");
2258
+ console.log(ansis.cyan("\u2500".repeat(50)));
2259
+ if (mode === "complete") {
2260
+ if (totalErrors === 0) {
2261
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:completeSuccess")}`));
2262
+ } else {
2263
+ console.log(ansis.yellow.bold(`\u26A0 ${i18n.t("uninstall:completePartialSuccess")}`));
2264
+ }
2265
+ } else {
2266
+ if (totalRemovedFiles > 0 && totalRemovedConfigs > 0) {
2267
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessBoth", {
2268
+ fileCount: totalRemovedFiles,
2269
+ configCount: totalRemovedConfigs
2270
+ })}`));
2271
+ } else if (totalRemovedFiles > 0) {
2272
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessFiles", {
2273
+ count: totalRemovedFiles
2274
+ })}`));
2275
+ } else if (totalRemovedConfigs > 0) {
2276
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessConfigs", {
2277
+ count: totalRemovedConfigs
2278
+ })}`));
2279
+ } else {
2280
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccess", { count: totalSuccess })}`));
2281
+ }
2282
+ if (totalErrors > 0) {
2283
+ console.log(ansis.red(`\u2716 ${i18n.t("uninstall:errorsCount", { count: totalErrors })}`));
2284
+ }
2285
+ if (totalWarnings > 0) {
2286
+ console.log(ansis.yellow(`\u26A0 ${i18n.t("uninstall:warningsCount", { count: totalWarnings })}`));
2287
+ }
1148
2288
  }
1149
- console.log(ansis.cyan(`
1150
- \u4F7F\u7528 profile "${name}" \u542F\u52A8 grok...
1151
- `));
1152
- const res = spawnSync("grok", [], { stdio: "inherit", env });
1153
- if (res.error) console.log(ansis.red(`\u542F\u52A8\u5931\u8D25: ${res.error.message}`));
2289
+ console.log("");
1154
2290
  }
1155
2291
 
1156
2292
  const REASONIX_GLOBAL_CONFIG = join(homedir(), ".config", "reasonix", "config.toml");
@@ -1400,71 +2536,6 @@ function buildRunArgs(model, prompt) {
1400
2536
  return args;
1401
2537
  }
1402
2538
 
1403
- const TIMEOUT_MS = 1e4;
1404
- const EXIT_CODE = {
1405
- ok: 0,
1406
- "auth-failed": 1,
1407
- unreachable: 2,
1408
- unexpected: 3
1409
- };
1410
- async function probeCommand(opts = {}) {
1411
- const creds = readActiveApiCreds();
1412
- if (!creds.baseUrl) {
1413
- throw new Error("\u672A\u627E\u5230 API \u914D\u7F6E\uFF0C\u8BF7\u5148\u8FD0\u884C ccjk init \u6216\u83DC\u5355 3 \u914D\u7F6E API");
1414
- }
1415
- const result = await runProbe(creds.baseUrl, creds.apiKey, creds.authToken);
1416
- if (opts.json) {
1417
- console.log(JSON.stringify(result, null, 2));
1418
- } else {
1419
- const color = result.status === "ok" ? ansis.green : ansis.red;
1420
- console.log(`
1421
- \u63A2\u6D3B ${creds.baseUrl}`);
1422
- console.log(color(` \u72B6\u6001: ${result.status}`));
1423
- if (result.httpStatus) console.log(ansis.dim(` HTTP: ${result.httpStatus}`));
1424
- if (result.latencyMs) console.log(ansis.dim(` \u5EF6\u8FDF: ${result.latencyMs}ms`));
1425
- if (result.error) console.log(ansis.dim(` \u9519\u8BEF: ${result.error}`));
1426
- console.log();
1427
- }
1428
- process.exitCode = EXIT_CODE[result.status];
1429
- }
1430
- function readActiveApiCreds() {
1431
- const tool = readCcjkConfig()?.codeToolType ?? "clavue";
1432
- const file = activeSettingsFile(tool === "codex" || tool === "grok" ? "claude-code" : tool);
1433
- const settings = readJsonConfig(file) || {};
1434
- const env = settings.env || {};
1435
- return {
1436
- baseUrl: env.ANTHROPIC_BASE_URL || "",
1437
- apiKey: env.ANTHROPIC_API_KEY || "",
1438
- authToken: env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY || ""
1439
- };
1440
- }
1441
- async function runProbe(baseUrl, apiKey, authToken) {
1442
- const url = `${baseUrl.replace(/\/$/, "")}/v1/messages`;
1443
- const headers = {
1444
- "content-type": "application/json",
1445
- "anthropic-version": "2023-06-01"
1446
- };
1447
- if (authToken) headers.authorization = `Bearer ${authToken}`;
1448
- else if (apiKey) headers["x-api-key"] = apiKey;
1449
- const body = JSON.stringify({
1450
- model: "claude-3-5-haiku-20241022",
1451
- max_tokens: 1,
1452
- messages: [{ role: "user", content: "ping" }]
1453
- });
1454
- const t0 = Date.now();
1455
- try {
1456
- const res = await fetch(url, { method: "POST", headers, body, signal: AbortSignal.timeout(TIMEOUT_MS) });
1457
- const latencyMs = Date.now() - t0;
1458
- if (res.status === 401 || res.status === 403) {
1459
- return { baseUrl, status: "auth-failed", httpStatus: res.status, latencyMs };
1460
- }
1461
- if (res.ok) return { baseUrl, status: "ok", httpStatus: res.status, latencyMs };
1462
- return { baseUrl, status: "unexpected", httpStatus: res.status, latencyMs };
1463
- } catch (e) {
1464
- return { baseUrl, status: "unreachable", error: e.message, latencyMs: Date.now() - t0 };
1465
- }
1466
- }
1467
-
1468
2539
  const ONBOARDING_STATE_FILE = join(CCJK_CONFIG_DIR, "onboarding.json");
1469
2540
  const LANGUAGE_SELECTION_MESSAGES = {
1470
2541
  selectLanguage: "Select CCJK display language / \u9009\u62E9 CCJK \u663E\u793A\u8BED\u8A00"
@@ -1645,404 +2716,129 @@ function printSeparator() {
1645
2716
  ${ansis.dim("\u2500".repeat(50))}
1646
2717
  `);
1647
2718
  }
1648
- function getCodeToolLabel(codeTool) {
1649
- return CODE_TOOL_LABELS[codeTool] || codeTool;
1650
- }
1651
- async function promptCodeToolSelection(current) {
1652
- const choices = addNumbersToChoices(Object.entries(CODE_TOOL_LABELS).map(([value, label]) => ({
1653
- name: label,
1654
- value,
1655
- short: label
1656
- })));
1657
- const { tool } = await inquirer.prompt({
1658
- type: "list",
1659
- name: "tool",
1660
- message: i18n.t("menu:switchCodeToolPrompt"),
1661
- default: current,
1662
- choices
1663
- });
1664
- if (!tool) {
1665
- console.log(ansis.yellow(i18n.t("common:cancelled")));
1666
- return null;
1667
- }
1668
- return tool;
1669
- }
1670
- async function handleCodeToolSwitch(current) {
1671
- const newTool = await promptCodeToolSelection(current);
1672
- if (!newTool || newTool === current) {
1673
- return false;
1674
- }
1675
- updateCcjkConfig({ codeToolType: newTool });
1676
- console.log(ansis.green(`\u2714 ${i18n.t("menu:codeToolSwitched", { tool: getCodeToolLabel(newTool) })}`));
1677
- return true;
1678
- }
1679
- function printOtherToolsSection() {
1680
- console.log(` --------- ${i18n.t("menu:menuSections.otherTools")} ----------`);
1681
- console.log(
1682
- ` ${ansis.cyan("P.")} Probe API ${ansis.gray("- HTTP \u63A2\u6D3B\u5F53\u524D\u914D\u7F6E")}`
1683
- );
1684
- console.log(
1685
- ` ${ansis.cyan("G.")} Grok CLI ${ansis.gray("- \u72B6\u6001 / \u542F\u52A8\u6307\u5F15")}`
1686
- );
1687
- console.log(
1688
- ` ${ansis.cyan("X.")} Reasonix ${ansis.gray("- DeepSeek agent \u72B6\u6001 / \u542F\u52A8\u6307\u5F15")}`
1689
- );
1690
- console.log("");
1691
- }
1692
- function printCcjkSection(options) {
1693
- console.log(" ------------ CCJK ------------");
1694
- console.log(
1695
- ` ${ansis.cyan("0.")} ${i18n.t("menu:menuOptions.changeLanguage")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.changeLanguage")}`)}`
1696
- );
1697
- console.log(
1698
- ` ${ansis.cyan("S.")} ${i18n.t("menu:menuOptions.switchCodeTool")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.switchCodeTool")}`)}`
1699
- );
1700
- console.log(
1701
- ` ${ansis.cyan("-.")} ${options.uninstallOption} ${ansis.gray(`- ${options.uninstallDescription}`)}`
1702
- );
1703
- console.log(
1704
- ` ${ansis.cyan("+.")} ${options.updateOption} ${ansis.gray(`- ${options.updateDescription}`)}`
1705
- );
1706
- console.log(` ${ansis.red("Q.")} ${ansis.red(i18n.t("menu:menuOptions.exit"))}`);
1707
- console.log("");
1708
- }
1709
- async function showClavueMenu() {
1710
- console.log(ansis.cyan(i18n.t("menu:selectFunction")));
1711
- console.log(" -------- Clavue --------");
1712
- console.log(
1713
- ` ${ansis.cyan("1.")} ${i18n.t("menu:menuOptions.clavueFullInit")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueFullInit")}`)}`
1714
- );
1715
- console.log(
1716
- ` ${ansis.cyan("2.")} ${i18n.t("menu:menuOptions.clavueUpdateWorkflow")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueUpdateWorkflow")}`)}`
1717
- );
1718
- console.log(
1719
- ` ${ansis.cyan("3.")} ${i18n.t("menu:menuOptions.clavueConfigureApi")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueConfigureApi")}`)}`
1720
- );
1721
- console.log(
1722
- ` ${ansis.cyan("4.")} ${i18n.t("menu:menuOptions.clavueConfigureMcp")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueConfigureMcp")}`)}`
1723
- );
1724
- console.log(
1725
- ` ${ansis.cyan("5.")} ${i18n.t("menu:menuOptions.clavueConfigureModel")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueConfigureModel")}`)}`
1726
- );
1727
- console.log(
1728
- ` ${ansis.cyan("6.")} ${i18n.t("menu:menuOptions.clavueConfigureAiMemory")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueConfigureAiMemory")}`)}`
1729
- );
1730
- console.log(
1731
- ` ${ansis.cyan("7.")} ${i18n.t("menu:menuOptions.clavueConfigureEnvPermission")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueConfigureEnvPermission")}`)}`
1732
- );
1733
- console.log(
1734
- ` ${ansis.cyan("8.")} ${i18n.t("menu:menuOptions.clavueStatus")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.clavueStatus")}`)}`
1735
- );
1736
- console.log("");
1737
- printOtherToolsSection();
1738
- printCcjkSection({
1739
- uninstallOption: i18n.t("menu:menuOptions.clavueUninstall"),
1740
- uninstallDescription: i18n.t("menu:menuDescriptions.clavueUninstall"),
1741
- updateOption: i18n.t("menu:menuOptions.clavueCheckUpdates"),
1742
- updateDescription: i18n.t("menu:menuDescriptions.clavueCheckUpdates")
1743
- });
1744
- const { choice } = await inquirer.prompt({
1745
- type: "input",
1746
- name: "choice",
1747
- message: i18n.t("common:enterChoice"),
1748
- validate: (value) => {
1749
- const valid = ["1", "2", "3", "4", "5", "6", "7", "8", "p", "P", "g", "G", "x", "X", "0", "-", "+", "s", "S", "q", "Q"];
1750
- return valid.includes(value) || i18n.t("common:invalidChoice");
1751
- }
1752
- });
1753
- if (!choice) {
1754
- console.log(ansis.yellow(i18n.t("common:cancelled")));
1755
- return "exit";
1756
- }
1757
- const normalized = choice.toLowerCase();
1758
- switch (normalized) {
1759
- case "1":
1760
- await init({ skipBanner: true, codeType: "clavue" });
1761
- break;
1762
- case "2":
1763
- await update({ skipBanner: true, codeType: "clavue" });
1764
- break;
1765
- case "3":
1766
- await configureApiFeature();
1767
- break;
1768
- case "4":
1769
- await configureMcpFeature();
1770
- break;
1771
- case "5":
1772
- await configureDefaultModelFeature();
1773
- break;
1774
- case "6":
1775
- await configureAiMemoryFeature();
1776
- break;
1777
- case "7":
1778
- await configureEnvPermissionFeature();
1779
- break;
1780
- case "8":
1781
- case "p":
1782
- await probeCommand();
1783
- printSeparator();
1784
- return void 0;
1785
- case "g":
1786
- await grokCommand();
1787
- printSeparator();
1788
- return void 0;
1789
- case "x":
1790
- await reasonixCommand();
1791
- printSeparator();
1792
- return void 0;
1793
- case "0": {
1794
- const currentLang = i18n.language;
1795
- await changeScriptLanguageFeature(currentLang);
1796
- printSeparator();
1797
- return void 0;
1798
- }
1799
- case "-":
1800
- await uninstall();
1801
- printSeparator();
1802
- return void 0;
1803
- case "+":
1804
- await checkUpdates();
1805
- printSeparator();
1806
- return void 0;
1807
- case "s": {
1808
- const switched = await handleCodeToolSwitch("clavue");
1809
- if (switched) {
1810
- return "switch";
1811
- }
1812
- printSeparator();
1813
- return void 0;
1814
- }
1815
- case "q":
1816
- console.log(ansis.cyan(i18n.t("common:goodbye")));
1817
- return "exit";
1818
- default:
1819
- return void 0;
1820
- }
1821
- printSeparator();
1822
- const shouldContinue = await promptBoolean({
1823
- message: i18n.t("common:returnToMenu"),
1824
- defaultValue: true
1825
- });
1826
- if (!shouldContinue) {
1827
- console.log(ansis.cyan(i18n.t("common:goodbye")));
1828
- return "exit";
1829
- }
1830
- return void 0;
2719
+ function getCodeToolLabel(codeTool) {
2720
+ return CODE_TOOL_LABELS[codeTool] || codeTool;
1831
2721
  }
1832
- async function showClaudeCodeMenu() {
1833
- console.log(ansis.cyan(i18n.t("menu:selectFunction")));
1834
- console.log(" -------- Claude Code --------");
1835
- console.log(
1836
- ` ${ansis.cyan("1.")} ${i18n.t("menu:menuOptions.fullInit")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.fullInit")}`)}`
1837
- );
1838
- console.log(
1839
- ` ${ansis.cyan("2.")} ${i18n.t("menu:menuOptions.importWorkflow")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.importWorkflow")}`)}`
1840
- );
1841
- console.log(
1842
- ` ${ansis.cyan("3.")} ${i18n.t("menu:menuOptions.configureApiOrCcr")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.configureApiOrCcr")}`)}`
1843
- );
1844
- console.log(
1845
- ` ${ansis.cyan("4.")} ${i18n.t("menu:menuOptions.configureMcp")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.configureMcp")}`)}`
1846
- );
1847
- console.log(
1848
- ` ${ansis.cyan("5.")} ${i18n.t("menu:menuOptions.configureModel")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.configureModel")}`)}`
1849
- );
1850
- console.log(
1851
- ` ${ansis.cyan("6.")} ${i18n.t("menu:menuOptions.configureAiMemory")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.configureAiMemory")}`)}`
1852
- );
1853
- console.log(
1854
- ` ${ansis.cyan("7.")} ${i18n.t("menu:menuOptions.configureEnvPermission")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.configureEnvPermission")}`)}`
1855
- );
1856
- console.log("");
1857
- printOtherToolsSection();
1858
- printCcjkSection({
1859
- uninstallOption: i18n.t("menu:menuOptions.uninstall"),
1860
- uninstallDescription: i18n.t("menu:menuDescriptions.uninstall"),
1861
- updateOption: i18n.t("menu:menuOptions.checkUpdates"),
1862
- updateDescription: i18n.t("menu:menuDescriptions.checkUpdates")
1863
- });
1864
- const { choice } = await inquirer.prompt({
1865
- type: "input",
1866
- name: "choice",
1867
- message: i18n.t("common:enterChoice"),
1868
- validate: (value) => {
1869
- const valid = ["1", "2", "3", "4", "5", "6", "7", "p", "P", "g", "G", "x", "X", "0", "-", "+", "s", "S", "q", "Q"];
1870
- return valid.includes(value) || i18n.t("common:invalidChoice");
1871
- }
2722
+ async function promptCodeToolSelection(current) {
2723
+ const { addNumbersToChoices } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bM; });
2724
+ const choices = addNumbersToChoices(Object.entries(CODE_TOOL_LABELS).map(([value, label]) => ({
2725
+ name: label,
2726
+ value,
2727
+ short: label
2728
+ })));
2729
+ const { tool } = await inquirer.prompt({
2730
+ type: "list",
2731
+ name: "tool",
2732
+ message: i18n.t("menu:switchCodeToolPrompt"),
2733
+ default: current,
2734
+ choices
1872
2735
  });
1873
- if (!choice) {
2736
+ if (!tool) {
1874
2737
  console.log(ansis.yellow(i18n.t("common:cancelled")));
1875
- return "exit";
2738
+ return null;
2739
+ }
2740
+ return tool;
2741
+ }
2742
+ async function handleCodeToolSwitch(current) {
2743
+ const newTool = await promptCodeToolSelection(current);
2744
+ if (!newTool || newTool === current) {
2745
+ return false;
1876
2746
  }
1877
- const normalized = choice.toLowerCase();
1878
- switch (normalized) {
2747
+ updateCcjkConfig({ codeToolType: newTool });
2748
+ console.log(ansis.green(`\u2714 ${i18n.t("menu:codeToolSwitched", { tool: getCodeToolLabel(newTool) })}`));
2749
+ return true;
2750
+ }
2751
+ async function handleClaudeFamilyChoice(tool, choice) {
2752
+ switch (choice) {
1879
2753
  case "1":
1880
- await init({ skipBanner: true });
1881
- break;
2754
+ await init({ skipBanner: true, codeType: tool });
2755
+ return "handled";
1882
2756
  case "2":
1883
- await update({ skipBanner: true });
1884
- break;
2757
+ await update({ skipBanner: true, codeType: tool });
2758
+ return "handled";
1885
2759
  case "3":
1886
2760
  await configureApiFeature();
1887
- break;
2761
+ return "handled";
1888
2762
  case "4":
1889
2763
  await configureMcpFeature();
1890
- break;
2764
+ return "handled";
1891
2765
  case "5":
1892
2766
  await configureDefaultModelFeature();
1893
- break;
2767
+ return "handled";
1894
2768
  case "6":
1895
2769
  await configureAiMemoryFeature();
1896
- break;
2770
+ return "handled";
1897
2771
  case "7":
1898
2772
  await configureEnvPermissionFeature();
1899
- break;
1900
- case "p":
1901
- await probeCommand();
1902
- printSeparator();
1903
- return void 0;
1904
- case "g":
1905
- await grokCommand();
1906
- printSeparator();
1907
- return void 0;
1908
- case "x":
1909
- await reasonixCommand();
1910
- printSeparator();
1911
- return void 0;
1912
- case "0": {
1913
- const currentLang = i18n.language;
1914
- await changeScriptLanguageFeature(currentLang);
1915
- printSeparator();
1916
- return void 0;
1917
- }
1918
- case "-":
1919
- await uninstall();
1920
- printSeparator();
1921
- return void 0;
1922
- case "+":
1923
- await checkUpdates();
1924
- printSeparator();
1925
- return void 0;
1926
- case "s": {
1927
- const switched = await handleCodeToolSwitch("claude-code");
1928
- if (switched) {
1929
- return "switch";
1930
- }
1931
- printSeparator();
1932
- return void 0;
1933
- }
1934
- case "q":
1935
- console.log(ansis.cyan(i18n.t("common:goodbye")));
1936
- return "exit";
2773
+ return "handled";
1937
2774
  default:
1938
2775
  return void 0;
1939
2776
  }
1940
- printSeparator();
1941
- const shouldContinue = await promptBoolean({
1942
- message: i18n.t("common:returnToMenu"),
1943
- defaultValue: true
1944
- });
1945
- if (!shouldContinue) {
1946
- console.log(ansis.cyan(i18n.t("common:goodbye")));
1947
- return "exit";
1948
- }
1949
- return void 0;
1950
2777
  }
1951
- async function showCodexMenu() {
1952
- console.log(ansis.cyan(i18n.t("menu:selectFunction")));
1953
- console.log(" -------- Codex --------");
1954
- console.log(
1955
- ` ${ansis.cyan("1.")} ${i18n.t("menu:menuOptions.codexFullInit")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.codexFullInit")}`)}`
1956
- );
1957
- console.log(
1958
- ` ${ansis.cyan("2.")} ${i18n.t("menu:menuOptions.codexImportWorkflow")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.codexImportWorkflow")}`)}`
1959
- );
1960
- console.log(
1961
- ` ${ansis.cyan("3.")} ${i18n.t("menu:menuOptions.codexConfigureApi")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.codexConfigureApi")}`)}`
1962
- );
1963
- console.log(
1964
- ` ${ansis.cyan("4.")} ${i18n.t("menu:menuOptions.codexConfigureMcp")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.codexConfigureMcp")}`)}`
1965
- );
1966
- console.log(
1967
- ` ${ansis.cyan("5.")} ${i18n.t("menu:menuOptions.codexConfigureModel")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.codexConfigureModel")}`)}`
1968
- );
1969
- console.log(
1970
- ` ${ansis.cyan("6.")} ${i18n.t("menu:menuOptions.codexConfigureAiMemory")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.codexConfigureAiMemory")}`)}`
1971
- );
1972
- console.log("");
1973
- printOtherToolsSection();
1974
- printCcjkSection({
1975
- uninstallOption: i18n.t("menu:menuOptions.codexUninstall"),
1976
- uninstallDescription: i18n.t("menu:menuDescriptions.codexUninstall"),
1977
- updateOption: i18n.t("menu:menuOptions.codexCheckUpdates"),
1978
- updateDescription: i18n.t("menu:menuDescriptions.codexCheckUpdates")
1979
- });
1980
- const { choice } = await inquirer.prompt({
1981
- type: "input",
1982
- name: "choice",
1983
- message: i18n.t("common:enterChoice"),
1984
- validate: (value) => {
1985
- const valid = ["1", "2", "3", "4", "5", "6", "p", "P", "g", "G", "x", "X", "0", "-", "+", "s", "S", "q", "Q"];
1986
- return valid.includes(value) || i18n.t("common:invalidChoice");
1987
- }
1988
- });
1989
- if (!choice) {
1990
- console.log(ansis.yellow(i18n.t("common:cancelled")));
1991
- return "exit";
1992
- }
1993
- const normalized = choice.toLowerCase();
1994
- switch (normalized) {
2778
+ async function handleCodexChoice(choice) {
2779
+ switch (choice) {
1995
2780
  case "1":
1996
2781
  await runCodexFullInit();
1997
- break;
2782
+ return "handled";
1998
2783
  case "2":
1999
2784
  await runCodexWorkflowImportWithLanguageSelection();
2000
- break;
2785
+ return "handled";
2001
2786
  case "3":
2002
2787
  await configureCodexApi();
2003
- break;
2788
+ return "handled";
2004
2789
  case "4":
2005
2790
  await manageCodexMcp();
2006
- break;
2791
+ return "handled";
2007
2792
  case "5":
2008
2793
  await configureCodexDefaultModelFeature();
2009
- break;
2794
+ return "handled";
2010
2795
  case "6":
2011
2796
  await configureCodexAiMemoryFeature();
2012
- break;
2013
- case "p":
2014
- await probeCommand();
2015
- printSeparator();
2797
+ return "handled";
2798
+ default:
2016
2799
  return void 0;
2017
- case "g":
2800
+ }
2801
+ }
2802
+ async function handleGrokChoice(choice) {
2803
+ switch (choice) {
2804
+ case "1":
2805
+ await init({ skipBanner: true, codeType: "grok" });
2806
+ return "handled";
2807
+ case "2":
2018
2808
  await grokCommand();
2019
- printSeparator();
2809
+ return "handled";
2810
+ default:
2811
+ return void 0;
2812
+ }
2813
+ }
2814
+ async function handleHubChoice(tool, choice) {
2815
+ switch (choice) {
2816
+ case "h":
2817
+ await runDoctorHub(tool);
2818
+ return "continue";
2819
+ case "w":
2820
+ await runOperatorHub(tool);
2821
+ return "handled";
2822
+ default:
2020
2823
  return void 0;
2824
+ }
2825
+ }
2826
+ async function handleSharedChoice(tool, choice) {
2827
+ switch (choice) {
2828
+ case "g":
2829
+ await grokCommand();
2830
+ return "continue";
2021
2831
  case "x":
2022
2832
  await reasonixCommand();
2023
- printSeparator();
2024
- return void 0;
2833
+ return "continue";
2025
2834
  case "0": {
2026
2835
  const currentLang = i18n.language;
2027
2836
  await changeScriptLanguageFeature(currentLang);
2028
- printSeparator();
2029
- return void 0;
2837
+ return "continue";
2030
2838
  }
2031
- case "-":
2032
- await runCodexUninstall();
2033
- printSeparator();
2034
- return void 0;
2035
- case "+":
2036
- await runCodexUpdate();
2037
- printSeparator();
2038
- return void 0;
2039
2839
  case "s": {
2040
- const switched = await handleCodeToolSwitch("codex");
2041
- if (switched) {
2042
- return "switch";
2043
- }
2044
- printSeparator();
2045
- return void 0;
2840
+ const switched = await handleCodeToolSwitch(tool);
2841
+ return switched ? "switch" : "continue";
2046
2842
  }
2047
2843
  case "q":
2048
2844
  console.log(ansis.cyan(i18n.t("common:goodbye")));
@@ -2050,94 +2846,69 @@ async function showCodexMenu() {
2050
2846
  default:
2051
2847
  return void 0;
2052
2848
  }
2053
- printSeparator();
2054
- const shouldContinue = await promptBoolean({
2055
- message: i18n.t("common:returnToMenu"),
2056
- defaultValue: true
2057
- });
2058
- if (!shouldContinue) {
2059
- console.log(ansis.cyan(i18n.t("common:goodbye")));
2060
- return "exit";
2849
+ }
2850
+ async function handleToolSpecificMaintenance(tool, choice) {
2851
+ if (choice === "-") {
2852
+ if (tool === "codex") {
2853
+ await runCodexUninstall();
2854
+ } else {
2855
+ await uninstall();
2856
+ }
2857
+ return "continue";
2858
+ }
2859
+ if (choice === "+") {
2860
+ if (tool === "codex") {
2861
+ await runCodexUpdate();
2862
+ } else {
2863
+ await checkUpdates();
2864
+ }
2865
+ return "continue";
2061
2866
  }
2062
2867
  return void 0;
2063
2868
  }
2064
- async function showGrokMenu() {
2065
- console.log(ansis.cyan(i18n.t("menu:selectFunction")));
2066
- console.log(" -------- Grok CLI --------");
2067
- console.log(
2068
- ` ${ansis.cyan("1.")} ${i18n.t("menu:menuOptions.grokInit")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.grokInit")}`)}`
2069
- );
2070
- console.log(
2071
- ` ${ansis.cyan("2.")} ${i18n.t("menu:menuOptions.grokStatus")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.grokStatus")}`)}`
2072
- );
2073
- console.log("");
2074
- printOtherToolsSection();
2075
- printCcjkSection({
2076
- uninstallOption: i18n.t("menu:menuOptions.uninstall"),
2077
- uninstallDescription: i18n.t("menu:menuDescriptions.uninstall"),
2078
- updateOption: i18n.t("menu:menuOptions.checkUpdates"),
2079
- updateDescription: i18n.t("menu:menuDescriptions.checkUpdates")
2080
- });
2869
+ async function dispatchMenuChoice(tool, rawChoice) {
2870
+ const choice = rawChoice.toLowerCase();
2871
+ const hubResult = ["h", "w"].includes(choice) ? await handleHubChoice(tool, choice) : void 0;
2872
+ if (hubResult)
2873
+ return hubResult;
2874
+ const sharedResult = await handleSharedChoice(tool, choice);
2875
+ if (sharedResult)
2876
+ return sharedResult;
2877
+ const maintenanceResult = await handleToolSpecificMaintenance(tool, choice);
2878
+ if (maintenanceResult)
2879
+ return maintenanceResult;
2880
+ const layout = getToolMenuLayout(tool);
2881
+ if (!layout.operatorLines.some((item) => item.key === choice))
2882
+ return void 0;
2883
+ if (tool === "clavue" || tool === "claude-code")
2884
+ return await handleClaudeFamilyChoice(tool, choice);
2885
+ if (tool === "codex")
2886
+ return await handleCodexChoice(choice);
2887
+ if (tool === "grok")
2888
+ return await handleGrokChoice(choice);
2889
+ return void 0;
2890
+ }
2891
+ async function showToolMenu(tool) {
2892
+ printToolMenuHeader(tool);
2081
2893
  const { choice } = await inquirer.prompt({
2082
- type: "input",
2894
+ type: "list",
2083
2895
  name: "choice",
2084
- message: i18n.t("common:enterChoice"),
2085
- validate: (value) => {
2086
- const valid = ["1", "2", "p", "P", "g", "G", "x", "X", "0", "-", "+", "s", "S", "q", "Q"];
2087
- return valid.includes(value) || i18n.t("common:invalidChoice");
2088
- }
2896
+ message: i18n.t("menu:selectFunction"),
2897
+ choices: buildMenuListChoices(tool),
2898
+ pageSize: 20
2089
2899
  });
2090
2900
  if (!choice) {
2091
2901
  console.log(ansis.yellow(i18n.t("common:cancelled")));
2092
2902
  return "exit";
2093
2903
  }
2094
- const normalized = choice.toLowerCase();
2095
- switch (normalized) {
2096
- case "1":
2097
- await init({ skipBanner: true, codeType: "grok" });
2098
- break;
2099
- case "2":
2100
- await grokCommand();
2101
- break;
2102
- case "p":
2103
- await probeCommand();
2104
- printSeparator();
2105
- return void 0;
2106
- case "g":
2107
- await grokCommand();
2108
- printSeparator();
2109
- return void 0;
2110
- case "x":
2111
- await reasonixCommand();
2112
- printSeparator();
2113
- return void 0;
2114
- case "0": {
2115
- const currentLang = i18n.language;
2116
- await changeScriptLanguageFeature(currentLang);
2117
- printSeparator();
2118
- return void 0;
2119
- }
2120
- case "-":
2121
- await uninstall();
2122
- printSeparator();
2123
- return void 0;
2124
- case "+":
2125
- await checkUpdates();
2126
- printSeparator();
2127
- return void 0;
2128
- case "s": {
2129
- const switched = await handleCodeToolSwitch("grok");
2130
- if (switched) {
2131
- return "switch";
2132
- }
2133
- printSeparator();
2134
- return void 0;
2135
- }
2136
- case "q":
2137
- console.log(ansis.cyan(i18n.t("common:goodbye")));
2138
- return "exit";
2139
- default:
2140
- return void 0;
2904
+ const result = await dispatchMenuChoice(tool, choice);
2905
+ if (result === "exit")
2906
+ return "exit";
2907
+ if (result === "switch")
2908
+ return "switch";
2909
+ if (result === "continue") {
2910
+ printSeparator();
2911
+ return void 0;
2141
2912
  }
2142
2913
  printSeparator();
2143
2914
  const shouldContinue = await promptBoolean({
@@ -2172,7 +2943,7 @@ async function showMainMenu(options = {}) {
2172
2943
  while (!exitMenu) {
2173
2944
  const codeTool = getCurrentCodeTool();
2174
2945
  displayBannerWithInfo(CODE_TOOL_BANNERS[codeTool] || "CCJK");
2175
- const result = codeTool === "clavue" ? await showClavueMenu() : codeTool === "codex" ? await showCodexMenu() : codeTool === "grok" ? await showGrokMenu() : await showClaudeCodeMenu();
2946
+ const result = await showToolMenu(codeTool);
2176
2947
  if (result === "exit") {
2177
2948
  exitMenu = true;
2178
2949
  } else if (result === "switch") {
@@ -2281,7 +3052,7 @@ async function listCodexProvidersWithDisplay() {
2281
3052
  console.log(ansis.cyan(i18n.t("codex:currentProvider", { provider: currentProvider })));
2282
3053
  console.log();
2283
3054
  }
2284
- const { listCodexProfileFiles, codexProfileLaunchHint } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bw; });
3055
+ const { listCodexProfileFiles, codexProfileLaunchHint } = await import('./chunks/simple-config.mjs').then(function (n) { return n.bO; });
2285
3056
  const profileFiles = listCodexProfileFiles();
2286
3057
  if (profileFiles.length > 0) {
2287
3058
  console.log(ansis.bold("Codex 0.135+ profiles (.config.toml):"));
@@ -2514,6 +3285,194 @@ async function benchCommand(opts = {}) {
2514
3285
  await probeCommand({ json: opts.json });
2515
3286
  }
2516
3287
 
3288
+ async function providerCommand(options) {
3289
+ const { action, profileId, json } = options;
3290
+ const subAction = action || "list";
3291
+ if (subAction === "list") {
3292
+ await listAction(json);
3293
+ } else if (subAction === "current") {
3294
+ await currentAction(json);
3295
+ } else if (subAction === "validate") {
3296
+ await validateAction(profileId, json);
3297
+ } else {
3298
+ await validateAction(subAction, json);
3299
+ }
3300
+ }
3301
+ async function listAction(json) {
3302
+ const profiles = listProviderProfiles();
3303
+ const activeId = getActiveProviderProfileId();
3304
+ if (json) {
3305
+ console.log(JSON.stringify({ profiles, activeId }, null, 2));
3306
+ return;
3307
+ }
3308
+ if (profiles.length === 0) {
3309
+ console.log(ansis.dim("\n No provider profiles found.\n"));
3310
+ return;
3311
+ }
3312
+ console.log(ansis.bold("\nProvider Profiles (Clavue 9.x)"));
3313
+ console.log(ansis.dim("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
3314
+ for (const profile of profiles) {
3315
+ const isActive = profile.id === activeId;
3316
+ const prefix = isActive ? ansis.green("\u25B6") : " ";
3317
+ const activeLabel = isActive ? ansis.green(" (active)") : "";
3318
+ const modeLabel = profile.modelMode ? ansis.dim(` ${profile.modelMode}`) : "";
3319
+ console.log(`${prefix} ${ansis.cyan(profile.id)}${activeLabel}${modeLabel}`);
3320
+ console.log(` URL: ${ansis.dim(profile.baseUrl)}`);
3321
+ console.log(` Auth: ${ansis.dim(profile.authType)}`);
3322
+ const mr = profile.modelRouting;
3323
+ const parts = [];
3324
+ if (mr.primaryModel) parts.push(`Primary: ${mr.primaryModel}`);
3325
+ if (mr.smallFastModel) parts.push(`Haiku: ${mr.smallFastModel}`);
3326
+ if (mr.subagentModel) parts.push(`Sonnet: ${mr.subagentModel}`);
3327
+ if (mr.planModel) parts.push(`Opus: ${mr.planModel}`);
3328
+ console.log(` ${ansis.dim(parts.join(" | "))}`);
3329
+ console.log();
3330
+ }
3331
+ }
3332
+ async function currentAction(json) {
3333
+ const activeId = getActiveProviderProfileId();
3334
+ if (!activeId) {
3335
+ if (json) {
3336
+ console.log(JSON.stringify({ error: "No active profile" }));
3337
+ } else {
3338
+ console.log(ansis.yellow("\n No active provider profile.\n"));
3339
+ }
3340
+ return;
3341
+ }
3342
+ const profile = getProviderProfile(activeId);
3343
+ if (!profile) {
3344
+ if (json) {
3345
+ console.log(JSON.stringify({ error: `Active profile "${activeId}" not found` }));
3346
+ } else {
3347
+ console.log(ansis.red(`
3348
+ Active profile "${activeId}" not found.
3349
+ `));
3350
+ }
3351
+ return;
3352
+ }
3353
+ if (json) {
3354
+ console.log(JSON.stringify({ active: profile }, null, 2));
3355
+ return;
3356
+ }
3357
+ renderProfileDetail(profile);
3358
+ }
3359
+ function renderProfileDetail(profile) {
3360
+ console.log(ansis.bold(`
3361
+ Active Profile: ${ansis.cyan(profile.name)}`));
3362
+ console.log(` ID: ${ansis.dim(profile.id)}`);
3363
+ if (profile.modelMode) {
3364
+ console.log(` Mode: ${ansis.dim(profile.modelMode)}`);
3365
+ }
3366
+ console.log(` URL: ${ansis.dim(profile.baseUrl)}`);
3367
+ console.log(` Auth: ${ansis.dim(profile.authType)}`);
3368
+ console.log(" Model Routing:");
3369
+ const mr = profile.modelRouting;
3370
+ console.log(` Primary: ${ansis.dim(mr.primaryModel || "-")}`);
3371
+ console.log(` Haiku: ${ansis.dim(mr.smallFastModel || "-")}`);
3372
+ console.log(` Sonnet: ${ansis.dim(mr.subagentModel || "-")}`);
3373
+ console.log(` Opus/Plan: ${ansis.dim(mr.planModel || "-")}`);
3374
+ if (mr.subagentModel) {
3375
+ console.log(` Subagent: ${ansis.dim(mr.subagentModel)}`);
3376
+ }
3377
+ if (mr.generalModel) {
3378
+ console.log(` General: ${ansis.dim(mr.generalModel)}`);
3379
+ }
3380
+ const created = new Date(profile.createdAt).toISOString().slice(0, 10);
3381
+ const updated = new Date(profile.updatedAt).toISOString().slice(0, 10);
3382
+ console.log(` Created: ${ansis.dim(created)}`);
3383
+ console.log(` Updated: ${ansis.dim(updated)}`);
3384
+ console.log();
3385
+ }
3386
+ const PROBE_TIMEOUT_MS = 1e4;
3387
+ async function validateAction(profileId, json) {
3388
+ const targetId = profileId || getActiveProviderProfileId();
3389
+ if (!targetId) {
3390
+ if (json) {
3391
+ console.log(JSON.stringify({ error: "No profile specified and no active profile" }));
3392
+ } else {
3393
+ console.log(ansis.yellow("\n Please specify a profile ID or set an active profile.\n"));
3394
+ }
3395
+ return;
3396
+ }
3397
+ const profile = getProviderProfile(targetId);
3398
+ if (!profile) {
3399
+ if (json) {
3400
+ console.log(JSON.stringify({ error: `Profile "${targetId}" not found` }));
3401
+ } else {
3402
+ console.log(ansis.red(`
3403
+ Profile "${targetId}" not found.
3404
+ `));
3405
+ }
3406
+ return;
3407
+ }
3408
+ if (json) {
3409
+ const result = await buildValidationResultJson(profile);
3410
+ console.log(JSON.stringify(result, null, 2));
3411
+ return;
3412
+ }
3413
+ console.log(ansis.bold(`
3414
+ Validating ${ansis.cyan(targetId)}...`));
3415
+ let probeOk = false;
3416
+ let probeDisplay = "";
3417
+ try {
3418
+ const probeUrl = profile.baseUrl.replace(/\/$/, "");
3419
+ const res = await fetch(probeUrl, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
3420
+ probeOk = res.ok;
3421
+ probeDisplay = res.ok ? ansis.green(`OK (${res.status})`) : ansis.red(`${res.status}`);
3422
+ } catch (e) {
3423
+ probeDisplay = ansis.red(`Unreachable: ${e.message}`);
3424
+ }
3425
+ console.log(` Probe ${ansis.dim(profile.baseUrl)}... ${probeDisplay}`);
3426
+ console.log(` Auth type: ${ansis.dim(profile.authType)}`);
3427
+ const mr = profile.modelRouting;
3428
+ console.log(` Primary model: ${renderSlot(mr.primaryModel)}`);
3429
+ console.log(` Haiku model: ${renderSlot(mr.smallFastModel)}`);
3430
+ console.log(` Sonnet model: ${renderSlot(mr.subagentModel)}`);
3431
+ console.log(` Opus model: ${renderSlot(mr.planModel)}`);
3432
+ const credentials = readCredentials();
3433
+ const hasCredential = !!credentials.providerProfiles[profile.id];
3434
+ console.log(` Credential: ${hasCredential ? ansis.green("stored \u2713") : ansis.red("missing")}`);
3435
+ const issues = [];
3436
+ if (!probeOk) issues.push("base URL unreachable");
3437
+ if (!mr.primaryModel) issues.push("primary model unset");
3438
+ if (!hasCredential) issues.push("credential missing");
3439
+ const statusLabel = issues.length === 0 ? ansis.green("Healthy") : ansis.yellow(`Issues: ${issues.join(", ")}`);
3440
+ console.log(`Status: ${statusLabel}`);
3441
+ console.log();
3442
+ }
3443
+ function renderSlot(value) {
3444
+ if (value) return ansis.green(`${value} (set)`);
3445
+ return ansis.yellow("(unset)");
3446
+ }
3447
+ async function buildValidationResultJson(profile) {
3448
+ let probe = { ok: false };
3449
+ try {
3450
+ const probeUrl = profile.baseUrl.replace(/\/$/, "");
3451
+ const res = await fetch(probeUrl, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
3452
+ probe = { ok: res.ok, status: res.status };
3453
+ } catch (e) {
3454
+ probe = { ok: false, error: e.message };
3455
+ }
3456
+ const credentials = readCredentials();
3457
+ const hasCredential = !!credentials.providerProfiles[profile.id];
3458
+ const mr = profile.modelRouting;
3459
+ const probeOk = probe.ok;
3460
+ return {
3461
+ profile: profile.id,
3462
+ baseUrl: profile.baseUrl,
3463
+ probe,
3464
+ authType: profile.authType,
3465
+ modelRouting: {
3466
+ primaryModel: { value: mr.primaryModel || null, set: !!mr.primaryModel },
3467
+ haikuModel: { value: mr.smallFastModel || null, set: !!mr.smallFastModel },
3468
+ sonnetModel: { value: mr.subagentModel || null, set: !!mr.subagentModel },
3469
+ opusModel: { value: mr.planModel || null, set: !!mr.planModel }
3470
+ },
3471
+ credentialStored: hasCredential,
3472
+ healthy: probeOk && !!mr.primaryModel && hasCredential
3473
+ };
3474
+ }
3475
+
2517
3476
  async function resolveAndSwitchLanguage(lang, options, skipPrompt = false) {
2518
3477
  const ccjkConfig = await readCcjkConfigAsync();
2519
3478
  const targetLang = options?.allLang || lang || options?.lang || ccjkConfig?.preferredLang || (skipPrompt ? "en" : await selectScriptLanguage());
@@ -2648,7 +3607,7 @@ async function setupCommands(cli) {
2648
3607
  cli.command("", "Show interactive menu (default)").option("--lang, -l <lang>", "CCJK display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").option("--force, -f", "Force overwrite existing configuration").option("--code-type, -T <codeType>", "Select code tool type (clavue, claude-code, codex, grok, cv, cc, cx, gk)").action(await withLanguageResolution(async (options) => {
2649
3608
  await showMainMenu({ codeType: options.codeType });
2650
3609
  }, true));
2651
- cli.command("init", "Initialize Claude Code configuration").alias("i").option("--lang, -l <lang>", "CCJK display language (zh-CN, en)").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").option("--ai-output-lang, -a <lang>", "AI output language").option("--force, -f", "Force overwrite existing configuration").option("--skip-prompt, -s", "Skip all interactive prompts (non-interactive mode)").option("--config-action, -r <action>", `Config handling (new/backup/merge/docs-only/skip), ${i18n.t("cli:help.defaults.prefix")} backup`).option("--api-type, -t <type>", "API type (auth_token/api_key/ccr_proxy/skip)").option("--api-key, -k <key>", "API key (used for both API key and auth token types)").option("--api-url, -u <url>", "Custom API URL").option("--api-model, -M <model>", "Primary API model (e.g., claude-sonnet-4-5)").option("--api-haiku-model, -H <model>", "Default Haiku model (e.g., claude-haiku-4-5)").option("--api-sonnet-model, -S <model>", "Default Sonnet model (e.g., claude-sonnet-4-5)").option("--api-opus-model, -O <model>", "Default Opus model (e.g., claude-opus-4-5)").option("--provider, -p <provider>", "API provider preset (302ai, glm, minimax, kimi, custom)").option("--mcp-services, -m <services>", `Comma-separated MCP services to install (context7,mcp-deepwiki,Playwright,exa), "skip" to skip all, "all" for all non-key services, ${i18n.t("cli:help.defaults.prefix")} all`).option("--workflows, -w <workflows>", `Comma-separated workflows to install (sixStepsWorkflow,featPlanUx,gitWorkflow,bmadWorkflow), "skip" to skip all, "all" for all workflows, ${i18n.t("cli:help.defaults.prefix")} all`).option("--output-styles, -o <styles>", `Comma-separated output styles (linus-mode,uncle-bob-mode,dhh-mode,carmack-mode,jobs-mode,evan-you-mode,default,explanatory,learning), "skip" to skip all, "all" for zcf curated styles, ${i18n.t("cli:help.defaults.prefix")} none`).option("--default-output-style, -d <style>", `Default output style (or "none" to keep original behavior), ${i18n.t("cli:help.defaults.prefix")} none`).option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--code-type, -T <codeType>", "Select code tool type (clavue, claude-code, codex, grok, cv, cc, cx, gk)").option("--install-cometix-line, -x <value>", `Install CCometixLine statusline tool (true/false), ${i18n.t("cli:help.defaults.prefix")} true`).option("--api-configs <configs>", "API configurations as JSON string for multiple profiles").option("--api-configs-file <file>", "Path to JSON file containing API configurations").action(await withLanguageResolution(async (options) => {
3610
+ cli.command("init", "Initialize Claude Code configuration").alias("i").option("--lang, -l <lang>", "CCJK display language (zh-CN, en)").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").option("--ai-output-lang, -a <lang>", "AI output language").option("--force, -f", "Force overwrite existing configuration").option("--skip-prompt, -s", "Skip all interactive prompts (non-interactive mode)").option("--config-action, -r <action>", `Config handling (new/backup/merge/docs-only/skip), ${i18n.t("cli:help.defaults.prefix")} backup`).option("--api-type, -t <type>", "API type (auth_token/api_key/ccr_proxy/skip)").option("--api-key, -k <key>", "API key (used for both API key and auth token types)").option("--api-url, -u <url>", "Custom API URL").option("--api-model, -M <model>", "Primary API model (e.g., claude-sonnet-4-5)").option("--api-haiku-model, -H <model>", "Default Haiku model (e.g., claude-haiku-4-5)").option("--api-sonnet-model, -S <model>", "Default Sonnet model (e.g., claude-sonnet-4-5)").option("--api-opus-model, -O <model>", "Default Opus model (e.g., claude-opus-4-5)").option("--provider, -p <provider>", "API provider preset (302ai, glm, minimax, kimi, custom)").option("--mcp-services, -m <services>", `Comma-separated MCP services to install (context7,mcp-deepwiki,Playwright,exa), "skip" to skip all, "all" for all non-key services, ${i18n.t("cli:help.defaults.prefix")} all`).option("--workflows, -w <workflows>", `Comma-separated workflows to install (sixStepsWorkflow,featPlanUx,gitWorkflow,bmadWorkflow), "skip" to skip all, "all" for all workflows, ${i18n.t("cli:help.defaults.prefix")} all`).option("--output-styles, -o <styles>", `Comma-separated output styles (agents-md-baseline,plan-first,verify-and-ship,surgical-diff,engineer-professional,default,explanatory,learning), "skip" to skip all, "all" for practical engineering styles, ${i18n.t("cli:help.defaults.prefix")} none`).option("--default-output-style, -d <style>", `Default output style (or "none" to keep original behavior), ${i18n.t("cli:help.defaults.prefix")} none`).option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--code-type, -T <codeType>", "Select code tool type (clavue, claude-code, codex, grok, cv, cc, cx, gk)").option("--install-cometix-line, -x <value>", `Install CCometixLine statusline tool (true/false), ${i18n.t("cli:help.defaults.prefix")} true`).option("--api-configs <configs>", "API configurations as JSON string for multiple profiles").option("--api-configs-file <file>", "Path to JSON file containing API configurations").action(await withLanguageResolution(async (options) => {
2652
3611
  await init(options);
2653
3612
  }));
2654
3613
  cli.command("update", "Update Claude Code prompts only").alias("u").option("--lang, -l <lang>", "CCJK display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").action(await withLanguageResolution(async (options) => {
@@ -2667,6 +3626,14 @@ async function setupCommands(cli) {
2667
3626
  list: options.list
2668
3627
  });
2669
3628
  }));
3629
+ cli.command("clean", "Clean dirty MCP servers, skills, and redundant prompts").option("--code-type, -T <codeType>", "Code tool context (clavue, claude-code, codex, grok)").option("-s, --skip-prompt", "Non-interactive: clean only high-confidence items").option("--aggressive", "Select all detected issues in non-interactive mode").option("--purge-all", "Remove ALL MCP, skills, prompts, and ~/.codex/AGENTS.md").action(await withLanguageResolution(async (options) => {
3630
+ await cleanCommand({
3631
+ codeType: options.codeType,
3632
+ skipPrompt: options.skipPrompt,
3633
+ aggressive: options.aggressive,
3634
+ purgeAll: options.purgeAll
3635
+ });
3636
+ }));
2670
3637
  cli.command("uninstall", "Remove CCJK configurations and tools").option("--lang, -l <lang>", "CCJK display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--code-type, -T <codeType>", "Select code tool type (clavue, claude-code, codex, grok, cv, cc, cx, gk)").option("--mode, -m <mode>", "Uninstall mode (complete/custom/interactive), default: interactive").option("--items, -i <items>", "Comma-separated items for custom uninstall mode").action(await withLanguageResolution(async (options) => {
2671
3638
  await uninstall(options);
2672
3639
  }));
@@ -2682,9 +3649,12 @@ async function setupCommands(cli) {
2682
3649
  }));
2683
3650
  cli.command("probe", "HTTP probe current API configuration").option("--json", "JSON output").action(await withLanguageResolution(async (options) => {
2684
3651
  await probeCommand({ json: options.json });
2685
- }));
3652
+ }, true));
2686
3653
  cli.command("bench", "Lightweight latency benchmark (probe)").option("--json", "JSON output").action(await withLanguageResolution(async (options) => {
2687
3654
  await benchCommand({ json: options.json });
3655
+ }, true));
3656
+ cli.command("provider [action] [profileId]", "Manage Clavue provider profiles").option("--json", "JSON output").action(await withLanguageResolution(async (action, profileId, options) => {
3657
+ await providerCommand({ action, profileId, json: options.json });
2688
3658
  }));
2689
3659
  cli.command("check-updates", "Check and update Claude Code and CCR to latest versions").alias("check").option("--lang, -l <lang>", "CCJK display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--code-type, -T <codeType>", "Select code tool type (clavue, claude-code, codex, grok, cv, cc, cx, gk)").option("--skip-prompt, -s", "Skip all interactive prompts (non-interactive mode)").action(await withLanguageResolution(async (options) => {
2690
3660
  await checkUpdates(options);