@gajae-code/coding-agent 0.18.11 → 0.18.12

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 (270) hide show
  1. package/CHANGELOG.md +293 -0
  2. package/dist/types/async/job-manager.d.ts +7 -3
  3. package/dist/types/async/result-formatting.d.ts +2 -0
  4. package/dist/types/autoresearch/git.d.ts +1 -1
  5. package/dist/types/capability/fs.d.ts +2 -2
  6. package/dist/types/cli/accounts-cli.d.ts +2 -2
  7. package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
  8. package/dist/types/cli/install-activation.d.ts +10 -4
  9. package/dist/types/cli/public-command-errors.d.ts +1 -0
  10. package/dist/types/cli/update-cli.d.ts +14 -1
  11. package/dist/types/cli-ordinary.d.ts +2 -2
  12. package/dist/types/commands/accounts.d.ts +2 -1
  13. package/dist/types/commands/migrate.d.ts +1 -1
  14. package/dist/types/commands/sdk.d.ts +5 -0
  15. package/dist/types/commit/changelog/index.d.ts +1 -0
  16. package/dist/types/config/autorouting-tier-map.d.ts +30 -0
  17. package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
  18. package/dist/types/config/config-hot-reload.d.ts +39 -0
  19. package/dist/types/config/file-lock.d.ts +8 -1
  20. package/dist/types/config/model-discovery-manager.d.ts +2 -0
  21. package/dist/types/config/model-profile-activation.d.ts +62 -6
  22. package/dist/types/config/model-registry.d.ts +56 -5
  23. package/dist/types/config/model-resolver.d.ts +2 -0
  24. package/dist/types/config/models-config-schema.d.ts +10 -0
  25. package/dist/types/config/settings-schema.d.ts +46 -3
  26. package/dist/types/config/settings.d.ts +33 -1
  27. package/dist/types/dap/config.d.ts +8 -4
  28. package/dist/types/debug/system-info.d.ts +6 -1
  29. package/dist/types/discovery/mcp-compat.d.ts +3 -1
  30. package/dist/types/edit/modes/patch.d.ts +5 -0
  31. package/dist/types/edit/modes/replace.d.ts +3 -0
  32. package/dist/types/exec/bash-executor.d.ts +12 -0
  33. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
  34. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  35. package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
  36. package/dist/types/migrate/adapters/codex.d.ts +2 -1
  37. package/dist/types/migrate/adapters/index.d.ts +10 -1
  38. package/dist/types/migrate/executor.d.ts +3 -1
  39. package/dist/types/migrate/types.d.ts +15 -3
  40. package/dist/types/modes/components/settings-selector.d.ts +6 -0
  41. package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
  42. package/dist/types/modes/theme/theme.d.ts +1 -0
  43. package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
  44. package/dist/types/runtime-mcp/manager.d.ts +7 -0
  45. package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
  46. package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
  47. package/dist/types/sdk/broker/broker-exit.d.ts +2 -2
  48. package/dist/types/sdk/broker/broker.d.ts +7 -1
  49. package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
  50. package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
  51. package/dist/types/sdk/broker/ensure.d.ts +136 -7
  52. package/dist/types/sdk/broker/hop.d.ts +36 -0
  53. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
  54. package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
  55. package/dist/types/sdk/broker/session-index.d.ts +4 -1
  56. package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
  57. package/dist/types/sdk/bus/index.d.ts +23 -0
  58. package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
  59. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  60. package/dist/types/sdk/cli/session-cli.d.ts +0 -1
  61. package/dist/types/sdk/host/session-runtime.d.ts +32 -0
  62. package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
  63. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
  64. package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
  65. package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
  66. package/dist/types/sdk/session-list.d.ts +1 -1
  67. package/dist/types/session/agent-session.d.ts +58 -2
  68. package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
  69. package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
  70. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
  71. package/dist/types/session/messages.d.ts +5 -1
  72. package/dist/types/session/session-manager.d.ts +13 -3
  73. package/dist/types/session/startup-auth-config.d.ts +24 -2
  74. package/dist/types/session/streaming-output.d.ts +11 -0
  75. package/dist/types/session/terminal-abort.d.ts +3 -1
  76. package/dist/types/session-import/service.d.ts +1 -1
  77. package/dist/types/skill-state/workflow-mutation-guard.d.ts +7 -3
  78. package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
  79. package/dist/types/task/executor.d.ts +19 -2
  80. package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
  81. package/dist/types/tools/atomic-file-write.d.ts +9 -7
  82. package/dist/types/tools/bash-allowed-prefixes.d.ts +11 -0
  83. package/dist/types/tools/descriptors.d.ts +2 -0
  84. package/dist/types/tools/fetch.d.ts +1 -1
  85. package/dist/types/tools/index.d.ts +2 -0
  86. package/dist/types/tools/python.d.ts +1 -1
  87. package/dist/types/tools/report-issue.d.ts +41 -0
  88. package/dist/types/tools/subagent.d.ts +23 -0
  89. package/dist/types/web/scrapers/types.d.ts +8 -1
  90. package/dist/types/web/search/providers/insane.d.ts +7 -1
  91. package/dist/types/web/search/providers/searxng.d.ts +5 -0
  92. package/package.json +8 -8
  93. package/scripts/build-binary.ts +2 -1
  94. package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
  95. package/scripts/generate-docs-index.ts +8 -1
  96. package/scripts/generate-sdk-operation-inventory.ts +22 -4
  97. package/src/async/job-manager.ts +356 -56
  98. package/src/async/result-formatting.ts +40 -0
  99. package/src/autoresearch/git.ts +9 -2
  100. package/src/autoresearch/prompts/prompt-setup.md +1 -1
  101. package/src/capability/fs.ts +27 -19
  102. package/src/capability/index.ts +2 -2
  103. package/src/cli/accounts-cli.ts +176 -8
  104. package/src/cli/auth-broker-cli.ts +21 -11
  105. package/src/cli/doctor/install-repairs.ts +35 -5
  106. package/src/cli/fast-help.ts +1 -1
  107. package/src/cli/install-activation.ts +47 -4
  108. package/src/cli/migrate-cli.ts +7 -5
  109. package/src/cli/public-command-errors.ts +4 -0
  110. package/src/cli/root-flags.ts +2 -2
  111. package/src/cli/stats-cli.ts +9 -5
  112. package/src/cli/update-cli.ts +224 -7
  113. package/src/cli-ordinary.ts +7 -2
  114. package/src/commands/accounts.ts +10 -5
  115. package/src/commands/migrate.ts +3 -3
  116. package/src/commands/sdk.ts +199 -42
  117. package/src/commands/ultragoal.ts +16 -8
  118. package/src/commit/changelog/index.ts +39 -1
  119. package/src/commit/message.ts +10 -3
  120. package/src/config/autorouting-tier-map.ts +30 -0
  121. package/src/config/config-hot-reload-trust.ts +148 -0
  122. package/src/config/config-hot-reload.ts +734 -0
  123. package/src/config/file-lock.ts +216 -56
  124. package/src/config/model-bindings-applier.ts +85 -47
  125. package/src/config/model-discovery-manager.ts +10 -0
  126. package/src/config/model-profile-activation.ts +716 -113
  127. package/src/config/model-registry.ts +1334 -158
  128. package/src/config/model-resolver.ts +3 -1
  129. package/src/config/models-config-schema.ts +6 -0
  130. package/src/config/settings-schema.ts +60 -3
  131. package/src/config/settings.ts +458 -24
  132. package/src/coordinator-mcp/policy.ts +26 -1
  133. package/src/crash/sanitize.ts +4 -0
  134. package/src/customization/import.ts +3 -1
  135. package/src/dap/config.ts +31 -11
  136. package/src/dap/defaults.json +1 -1
  137. package/src/debug/crash-diagnostics.ts +2 -1
  138. package/src/debug/report-bundle.ts +19 -0
  139. package/src/debug/system-info.ts +8 -3
  140. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
  141. package/src/discovery/agents-md.ts +1 -1
  142. package/src/discovery/helpers.ts +1 -2
  143. package/src/discovery/mcp-compat.ts +120 -17
  144. package/src/edit/apply-patch/index.ts +12 -1
  145. package/src/edit/index.ts +13 -17
  146. package/src/edit/modes/patch.ts +187 -8
  147. package/src/edit/modes/replace.ts +136 -0
  148. package/src/eval/js/context-manager.ts +29 -3
  149. package/src/eval/js/worker-entry.ts +51 -18
  150. package/src/eval/py/executor.ts +14 -4
  151. package/src/eval/py/runner.py +144 -5
  152. package/src/exec/bash-executor.ts +81 -4
  153. package/src/exec/bash-shell-guardian.ts +4 -1
  154. package/src/exec/bash-shell-supervisor.ts +8 -4
  155. package/src/exec/isolated-shell.ts +5 -1
  156. package/src/export/html/index.ts +46 -6
  157. package/src/extensibility/custom-tools/wrapper.ts +3 -1
  158. package/src/extensibility/extensions/loader.ts +8 -1
  159. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
  160. package/src/gjc-runtime/gc-runtime.ts +33 -6
  161. package/src/gjc-runtime/launch-tmux.ts +7 -2
  162. package/src/gjc-runtime/launch-worktree.test.ts +148 -0
  163. package/src/gjc-runtime/launch-worktree.ts +101 -14
  164. package/src/gjc-runtime/managed-owner-admission.ts +46 -2
  165. package/src/gjc-runtime/session-resolution.ts +38 -6
  166. package/src/gjc-runtime/session-state-lock.ts +59 -14
  167. package/src/gjc-runtime/state-runtime.ts +23 -19
  168. package/src/hooks/native-skill-hook.ts +14 -11
  169. package/src/internal-urls/docs-index.generated.ts +14 -13
  170. package/src/internal-urls/local-protocol.ts +142 -8
  171. package/src/lsp/index.ts +29 -0
  172. package/src/lsp/workspace-path.ts +23 -0
  173. package/src/migrate/action-planner.ts +57 -0
  174. package/src/migrate/adapters/claude-code.ts +23 -4
  175. package/src/migrate/adapters/codex.ts +22 -4
  176. package/src/migrate/adapters/index.ts +22 -4
  177. package/src/migrate/adapters/opencode.ts +6 -1
  178. package/src/migrate/executor.ts +84 -7
  179. package/src/migrate/report.ts +19 -2
  180. package/src/migrate/types.ts +14 -4
  181. package/src/modes/DESIGN.md +51 -0
  182. package/src/modes/acp/permission-mode.ts +18 -1
  183. package/src/modes/components/assistant-message.ts +55 -1
  184. package/src/modes/components/settings-selector.ts +103 -37
  185. package/src/modes/controllers/command-controller.ts +38 -2
  186. package/src/modes/controllers/plan-mode-controller.ts +17 -7
  187. package/src/modes/controllers/selector-controller.ts +62 -6
  188. package/src/modes/interactive-mode.ts +68 -0
  189. package/src/modes/theme/theme.ts +15 -0
  190. package/src/prompts/tools/debug.md +1 -1
  191. package/src/prompts/tools/read.md +1 -1
  192. package/src/prompts/tools/report-issue.md +13 -0
  193. package/src/prompts/tools/task.md +1 -1
  194. package/src/runtime-credential-selector.ts +2 -2
  195. package/src/runtime-mcp/content-limits.ts +7 -0
  196. package/src/runtime-mcp/manager-pool.test.ts +5 -4
  197. package/src/runtime-mcp/manager.ts +32 -0
  198. package/src/runtime-mcp/oauth-flow.ts +32 -6
  199. package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
  200. package/src/runtime-mcp/render.ts +16 -1
  201. package/src/runtime-mcp/tool-bridge.ts +126 -17
  202. package/src/sdk/broker/broker-exit.ts +8 -5
  203. package/src/sdk/broker/broker.ts +342 -82
  204. package/src/sdk/broker/daemon-entry.ts +338 -116
  205. package/src/sdk/broker/doctor-restart.ts +2 -1
  206. package/src/sdk/broker/ensure.ts +1143 -265
  207. package/src/sdk/broker/hop.ts +274 -0
  208. package/src/sdk/broker/lifecycle-ledger.ts +35 -11
  209. package/src/sdk/broker/lock-host-identity.ts +29 -0
  210. package/src/sdk/broker/session-index.ts +62 -12
  211. package/src/sdk/bus/chat-command-policy.ts +16 -3
  212. package/src/sdk/bus/index.ts +52 -21
  213. package/src/sdk/bus/reconciliation-store.ts +201 -8
  214. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  215. package/src/sdk/cli/session-cli.ts +21 -6
  216. package/src/sdk/host/session-runtime.test.ts +118 -23
  217. package/src/sdk/host/session-runtime.ts +352 -47
  218. package/src/sdk/lifecycle/service.ts +1 -0
  219. package/src/sdk/prompt-deadline-flush.ts +78 -5
  220. package/src/sdk/prompt-deadline-manager.ts +68 -14
  221. package/src/sdk/protocol/adapter-validation.ts +53 -11
  222. package/src/sdk/protocol/operation-inventory.generated.json +101 -2
  223. package/src/sdk/reconciliation-extensions.ts +1 -0
  224. package/src/sdk/session-list.ts +1 -0
  225. package/src/sdk/session.ts +114 -61
  226. package/src/session/agent-session.ts +1043 -81
  227. package/src/session/internal/managed-session-scope.ts +126 -39
  228. package/src/session/internal/managed-session-storage.ts +14 -11
  229. package/src/session/internal/native-publish-outcome.ts +5 -1
  230. package/src/session/messages.ts +47 -6
  231. package/src/session/session-manager.ts +236 -14
  232. package/src/session/startup-auth-config.ts +116 -8
  233. package/src/session/streaming-output.ts +262 -4
  234. package/src/session/terminal-abort.ts +14 -2
  235. package/src/session-import/service.ts +1 -0
  236. package/src/skill-state/workflow-mutation-guard.ts +491 -32
  237. package/src/slash-commands/builtin-registry.ts +15 -3
  238. package/src/slash-commands/helpers/credential-switch.ts +10 -5
  239. package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
  240. package/src/task/executor.ts +103 -12
  241. package/src/task/index.ts +389 -175
  242. package/src/tools/ast-edit-write-path.ts +64 -0
  243. package/src/tools/ast-edit.ts +18 -1
  244. package/src/tools/atomic-file-write.ts +46 -15
  245. package/src/tools/auto-generated-guard.ts +3 -0
  246. package/src/tools/bash-allowed-prefixes.ts +153 -5
  247. package/src/tools/bash-interactive.ts +14 -2
  248. package/src/tools/bash.ts +14 -0
  249. package/src/tools/debug.ts +7 -6
  250. package/src/tools/descriptors.test.ts +44 -0
  251. package/src/tools/descriptors.ts +34 -2
  252. package/src/tools/fetch.ts +3 -2
  253. package/src/tools/index.ts +2 -0
  254. package/src/tools/output-meta.ts +23 -2
  255. package/src/tools/python.ts +1 -1
  256. package/src/tools/read.ts +17 -35
  257. package/src/tools/report-issue.ts +184 -0
  258. package/src/tools/subagent-render.ts +114 -12
  259. package/src/tools/subagent.ts +108 -1
  260. package/src/tools/todo-write.ts +41 -3
  261. package/src/tools/tool-catalog.generated.ts +31 -3
  262. package/src/tools/write.ts +35 -3
  263. package/src/utils/commit-message-generator.ts +9 -2
  264. package/src/utils/file-mentions.ts +29 -1
  265. package/src/utils/title-generator.ts +7 -2
  266. package/src/web/scrapers/index.ts +3 -1
  267. package/src/web/scrapers/types.ts +8 -0
  268. package/src/web/scrapers/youtube.ts +3 -2
  269. package/src/web/search/providers/insane.ts +91 -28
  270. package/src/web/search/providers/searxng.ts +62 -10
@@ -504,8 +504,12 @@ export interface ReconcileResult {
504
504
  readonly record?: ActivationRecordV1;
505
505
  readonly recordIdentity?: FileIdentity;
506
506
  readonly reason?: string;
507
- /** Present only for a pre-mutation Windows sharing violation on the destination open. */
507
+ /** Present only for a pre-mutation Windows exact-replacement failure. */
508
508
  readonly windowsErrorCode?: string;
509
+ readonly detachedPath?: string;
510
+ readonly retainedSuccessorPath?: string;
511
+ readonly retainedPlaceholderPath?: string;
512
+ readonly retainedUnknownPath?: string;
509
513
  }
510
514
 
511
515
  /**
@@ -519,9 +523,11 @@ export interface ReconcileResult {
519
523
  * records are re-verified and advanced to `verified` once the target
520
524
  * digest/identity matches the candidate. `verified` records are read-only
521
525
  * re-confirmations. Anything else (target/parent drift, missing staged
522
- * candidate, an already-occupied backup name, a pre-mutation Windows
523
- * sharing violation) is reported as `conflict` with the exact reason and
524
- * never silently promoted.
526
+ * candidate, an already-occupied backup name) is reported as `conflict` with
527
+ * the exact reason and never silently promoted. A pre-mutation Windows
528
+ * sharing violation becomes `pending_activation`; an access-denied result is
529
+ * also recorded as pending for safe reuse but remains a `conflict` so the
530
+ * caller reports the publication failure.
525
531
  */
526
532
  export async function reconcileActivationRecord(
527
533
  targetPath: string,
@@ -637,11 +643,48 @@ export async function reconcileActivationRecord(
637
643
  };
638
644
  }
639
645
  }
646
+ if (
647
+ record.targetIdentity &&
648
+ record.stagingIdentity &&
649
+ result.code === "access_denied" &&
650
+ (process.platform === "win32" || result.windowsErrorCode === "0xC0000022") &&
651
+ !result.detachedPath &&
652
+ !result.retainedSuccessorPath &&
653
+ !result.retainedUnknownPath &&
654
+ !result.retainedPlaceholderPath
655
+ ) {
656
+ const unchangedTarget = await snapshotRegularFile(targetPath);
657
+ const unchangedStaging = await snapshotRegularFile(record.stagingPath);
658
+ if (
659
+ unchangedTarget &&
660
+ unchangedStaging &&
661
+ sameFileIdentity(record.targetIdentity, unchangedTarget.identity) &&
662
+ sameFileIdentity(record.stagingIdentity, unchangedStaging.identity)
663
+ ) {
664
+ const pending: ActivationRecordV1 = { ...record, phase: "pending_activation" };
665
+ const pendingIdentity = await updateActivationRecordFile(pending, recordIdentity);
666
+ return {
667
+ status: "conflict",
668
+ record: pending,
669
+ recordIdentity: pendingIdentity,
670
+ reason: "access_denied",
671
+ ...(result.windowsErrorCode ? { windowsErrorCode: result.windowsErrorCode } : {}),
672
+ };
673
+ }
674
+ }
640
675
  return {
641
676
  status: "conflict",
642
677
  record,
643
678
  reason: result.code ?? "activation_promotion_failed",
644
679
  ...(result.windowsErrorCode ? { windowsErrorCode: result.windowsErrorCode } : {}),
680
+ ...(result.detachedPath !== undefined ? { detachedPath: result.detachedPath } : {}),
681
+ ...(result.retainedSuccessorPath !== undefined
682
+ ? { retainedSuccessorPath: result.retainedSuccessorPath }
683
+ : {}),
684
+ ...(result.retainedPlaceholderPath !== undefined
685
+ ? { retainedPlaceholderPath: result.retainedPlaceholderPath }
686
+ : {}),
687
+ ...(result.retainedUnknownPath !== undefined ? { retainedUnknownPath: result.retainedUnknownPath } : {}),
645
688
  };
646
689
  }
647
690
  const promoted = await snapshotRegularFile(targetPath);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `gjc migrate` — import MCP servers and skills from other coding agents.
2
+ * `gjc migrate` — import MCP servers, skills, and instruction files from other coding agents.
3
3
  */
4
4
  import * as os from "node:os";
5
5
  import * as path from "node:path";
@@ -53,8 +53,10 @@ export function resolveSources(from: string[]): MigrateSource[] {
53
53
 
54
54
  function resolveDestinations(project: boolean, cwd: string): MigrateDestinations {
55
55
  const scope = project ? "project" : "user";
56
- const skillsDir = project ? path.join(getProjectAgentDir(cwd), "skills") : path.join(getAgentDir(), "skills");
57
- return { mcpConfigPath: getMCPConfigPath(scope, cwd), skillsDir };
56
+ const agentDir = project ? getProjectAgentDir(cwd) : getAgentDir();
57
+ const skillsDir = path.join(agentDir, "skills");
58
+ const instructionsPath = path.join(agentDir, "AGENTS.md");
59
+ return { mcpConfigPath: getMCPConfigPath(scope, cwd), skillsDir, instructionsPath };
58
60
  }
59
61
 
60
62
  /** Run the migration and return the report (does not set process.exitCode). */
@@ -66,11 +68,11 @@ export async function runMigrate(args: MigrateCommandArgs): Promise<MigrateRepor
66
68
 
67
69
  const results: AdapterResult[] = [];
68
70
  for (const source of sources) {
69
- results.push(await getAdapter(source).collect({ homeDir }));
71
+ results.push(await getAdapter(source).collect({ homeDir, cwd, scope: args.project ? "project" : "user" }));
70
72
  }
71
73
 
72
74
  const { actions, warnings } = await planMigration({ results, destinations, force: args.force });
73
- const finalActions = args.dryRun ? actions : await executeActions(actions);
75
+ const finalActions = args.dryRun ? actions : await executeActions(actions, { project: args.project });
74
76
 
75
77
  return buildReport({
76
78
  actions: finalActions,
@@ -111,6 +111,10 @@ export const PUBLIC_COMMAND_DIAGNOSTICS = {
111
111
  // of them and never implies that another key would be safe.
112
112
  lifecycle_idempotency_conflict:
113
113
  "The broker reported an idempotency conflict. Reconcile existing lifecycle state before deciding whether another request is safe.",
114
+ // Refusals of the request payload itself: the broker's own text names the missing field
115
+ // (a required `sessionPath`, for one), and it is never rendered, so the class is named here.
116
+ lifecycle_input_refused:
117
+ "The broker refused the request as invalid input. Correct the request payload and reconcile existing lifecycle state before deciding whether another request is safe.",
114
118
  usage_transport_exclusive: "Specify exactly one of --stdio or --socket <path>.",
115
119
  usage_duplicate_option: "Each option on this command may occur at most once.",
116
120
  usage_unknown_argument: "An unrecognized argument was supplied; re-read the command help for its accepted grammar.",
@@ -19,11 +19,11 @@ export const ROOT_LAUNCH_FLAGS = {
19
19
  "api-key": Flags.string({ description: "API key (defaults to env vars)" }),
20
20
  credential: Flags.string({
21
21
  description:
22
- "Stored credential selector: email:<addr>, id:<n>, account:<id>, project:<id>, or provider/email:<addr>",
22
+ "Stored credential selector: email:<addr>, id:<n>, account:<id>, project:<id>, org:<id|name>, or provider/email:<addr>",
23
23
  }),
24
24
  "prefer-credential": Flags.string({
25
25
  description:
26
- "Preferred stored credential with quota fallback: email:<addr>, id:<n>, account:<id>, project:<id>, or provider/id:<n>",
26
+ "Preferred stored credential with quota fallback: email:<addr>, id:<n>, account:<id>, project:<id>, org:<id|name>, or provider/id:<n>",
27
27
  }),
28
28
  "system-prompt": Flags.string({ description: "System prompt (default: coding assistant prompt)" }),
29
29
  "append-system-prompt": Flags.string({ description: "Append text or file contents to the system prompt" }),
@@ -4,7 +4,7 @@
4
4
  * Handles `gjc stats` subcommand for viewing AI usage statistics.
5
5
  */
6
6
 
7
- import { APP_NAME, formatDuration, formatNumber, formatPercent } from "@gajae-code/utils";
7
+ import { APP_NAME, formatDuration, formatNumber, formatPercent, sanitizeDisplayLine } from "@gajae-code/utils";
8
8
  import chalk from "chalk";
9
9
  import { openPath } from "../utils/open";
10
10
 
@@ -168,7 +168,7 @@ async function printStatsSummary(): Promise<void> {
168
168
  console.log(chalk.bold("\nBy Model:"));
169
169
  for (const m of byModel.slice(0, 10)) {
170
170
  console.log(
171
- ` ${m.model}: ${formatNumber(m.totalRequests)} reqs, ${formatCost(m.totalCost)}, ${formatPercent(m.cacheRate)} cache`,
171
+ ` ${sanitizeDisplayLine(m.model)}: ${formatNumber(m.totalRequests)} reqs, ${formatCost(m.totalCost)}, ${formatPercent(m.cacheRate)} cache`,
172
172
  );
173
173
  }
174
174
  }
@@ -177,7 +177,7 @@ async function printStatsSummary(): Promise<void> {
177
177
  console.log(chalk.bold("\nBy Agent Role:"));
178
178
  for (const agent of byAgent) {
179
179
  console.log(
180
- ` ${agent.agent}: ${formatNumber(agent.totalRequests)} reqs, ${formatNumber(agent.totalInputTokens)} input / ${formatNumber(agent.totalOutputTokens)} output, ${formatPercent(agent.cacheRate)} cache, ${formatCost(agent.totalCost)}`,
180
+ ` ${sanitizeDisplayLine(agent.agent)}: ${formatNumber(agent.totalRequests)} reqs, ${formatNumber(agent.totalInputTokens)} input / ${formatNumber(agent.totalOutputTokens)} output, ${formatPercent(agent.cacheRate)} cache, ${formatCost(agent.totalCost)}`,
181
181
  );
182
182
  }
183
183
  }
@@ -185,7 +185,9 @@ async function printStatsSummary(): Promise<void> {
185
185
  if (byFolder.length > 0) {
186
186
  console.log(chalk.bold("\nBy Folder:"));
187
187
  for (const f of byFolder.slice(0, 10)) {
188
- console.log(` ${f.folder}: ${formatNumber(f.totalRequests)} reqs, ${formatCost(f.totalCost)}`);
188
+ console.log(
189
+ ` ${sanitizeDisplayLine(f.folder)}: ${formatNumber(f.totalRequests)} reqs, ${formatCost(f.totalCost)}`,
190
+ );
189
191
  }
190
192
  }
191
193
 
@@ -201,7 +203,9 @@ async function printStatsSummary(): Promise<void> {
201
203
  console.log(` Provider-side (prefix intact): ${formatNumber(misses.providerSideMisses)}`);
202
204
  console.log(` Model switch: ${formatNumber(misses.modelSwitchMisses)}`);
203
205
  for (const cause of misses.byCause) {
204
- const label = cause.divergedRole ? `${cause.change} (${cause.divergedRole})` : cause.change;
206
+ const label = sanitizeDisplayLine(
207
+ cause.divergedRole ? `${cause.change} (${cause.divergedRole})` : cause.change,
208
+ );
205
209
  console.log(` ${label}: ${formatNumber(cause.misses)}`);
206
210
  }
207
211
  }
@@ -11,7 +11,7 @@ import * as fs from "node:fs";
11
11
  import * as os from "node:os";
12
12
  import * as path from "node:path";
13
13
  import { exactReplaceRetained, exactUnlink } from "@gajae-code/natives";
14
- import { $which, APP_NAME, isCompiledBinary, isEnoent, redactCrashSecrets, VERSION } from "@gajae-code/utils";
14
+ import { $which, APP_NAME, isCompiledBinary, isEnoent, logger, redactCrashSecrets, VERSION } from "@gajae-code/utils";
15
15
  import { $ } from "bun";
16
16
  import chalk from "chalk";
17
17
  import { acquireFileLock } from "../config/file-lock";
@@ -38,6 +38,7 @@ import {
38
38
  createActivationRecordFile,
39
39
  type DirectoryIdentity,
40
40
  type FileIdentity,
41
+ type ReconcileResult,
41
42
  readActivationRecord,
42
43
  reconcileActivationRecord,
43
44
  sameDirectoryIdentity,
@@ -108,6 +109,60 @@ export interface BinaryReplacementOptions {
108
109
  verifyInstalledVersion: (expectedVersion: string) => Promise<InstalledVersionVerification>;
109
110
  }
110
111
 
112
+ function formatRetainedActivationPaths(
113
+ paths:
114
+ | Pick<
115
+ ReconcileResult,
116
+ "detachedPath" | "retainedSuccessorPath" | "retainedPlaceholderPath" | "retainedUnknownPath"
117
+ >
118
+ | undefined,
119
+ ): string {
120
+ if (!paths) return "";
121
+ const entries = [
122
+ paths.detachedPath !== undefined ? `detached=${paths.detachedPath}` : undefined,
123
+ paths.retainedSuccessorPath !== undefined ? `successor=${paths.retainedSuccessorPath}` : undefined,
124
+ paths.retainedPlaceholderPath !== undefined ? `placeholder=${paths.retainedPlaceholderPath}` : undefined,
125
+ paths.retainedUnknownPath !== undefined ? `unknown=${paths.retainedUnknownPath}` : undefined,
126
+ ].filter((entry): entry is string => entry !== undefined);
127
+ return entries.length > 0 ? `; retained paths: ${entries.join("|")}` : "";
128
+ }
129
+
130
+ export class InstallationAccessDeniedError extends Error {
131
+ readonly code = "access_denied";
132
+
133
+ constructor(
134
+ readonly targetPath: string,
135
+ readonly windowsErrorCode?: string,
136
+ readonly stagingCleanupWarning?: string,
137
+ readonly retainedPaths?: Pick<
138
+ ReconcileResult,
139
+ "detachedPath" | "retainedSuccessorPath" | "retainedPlaceholderPath" | "retainedUnknownPath"
140
+ >,
141
+ ) {
142
+ super(
143
+ `access_denied during exact replacement of ${targetPath}${windowsErrorCode ? ` (${windowsErrorCode})` : ""}${stagingCleanupWarning ? `; ${stagingCleanupWarning}` : ""}${formatRetainedActivationPaths(retainedPaths)}`,
144
+ );
145
+ this.name = "InstallationAccessDeniedError";
146
+ }
147
+ }
148
+
149
+ export class ActivationRetryCandidateRepointError extends Error {
150
+ readonly reasonCode: string;
151
+
152
+ constructor(
153
+ readonly candidatePath: string,
154
+ cause: unknown,
155
+ ) {
156
+ const reasonCode =
157
+ cause instanceof Error && /^[a-z][a-z0-9_]{0,63}$/.test(cause.message)
158
+ ? cause.message
159
+ : "activation_record_repoint_failed";
160
+ super(`${reasonCode}; retained retry candidate: ${candidatePath}`, { cause });
161
+ this.name = "ActivationRetryCandidateRepointError";
162
+ this.reasonCode = reasonCode;
163
+ }
164
+ }
165
+
111
166
  /**
112
167
  * Parse update subcommand arguments.
113
168
  * Returns undefined if not an update command.
@@ -782,6 +837,60 @@ export async function recoverWindowsUpdateJournal(journalPath: string): Promise<
782
837
  throw new Error("legacy_update_journal_requires_manual_review");
783
838
  }
784
839
 
840
+ async function removeExactStagingFile(filePath: string, identity: FileIdentity): Promise<void> {
841
+ const quarantineName = `.${path.basename(filePath)}.cleanup.${randomUUID()}`;
842
+ const removed = exactUnlink(filePath, toNativeIdentity(identity, quarantineName));
843
+ let pathStillExists = false;
844
+ let snapshotFailure: string | undefined;
845
+ try {
846
+ pathStillExists = (await snapshotRegularFile(filePath)) !== undefined;
847
+ } catch (error) {
848
+ snapshotFailure = error instanceof Error ? error.message : String(error);
849
+ }
850
+ const recoveryPaths = [
851
+ removed.detachedPath ? `detached=${removed.detachedPath}` : undefined,
852
+ removed.retainedPlaceholderPath ? `placeholder=${removed.retainedPlaceholderPath}` : undefined,
853
+ removed.retainedUnknownPath ? `unknown=${removed.retainedUnknownPath}` : undefined,
854
+ removed.retainedSuccessorPath ? `successor=${removed.retainedSuccessorPath}` : undefined,
855
+ ].filter((value): value is string => value !== undefined);
856
+ if (
857
+ (!removed.ok && removed.code !== "not_found") ||
858
+ pathStillExists ||
859
+ snapshotFailure !== undefined ||
860
+ recoveryPaths.length > 0
861
+ ) {
862
+ const reason =
863
+ removed.code ?? (pathStillExists ? "path_still_exists" : snapshotFailure ? "snapshot_failed" : "unverified");
864
+ const details = [
865
+ `path=${filePath}`,
866
+ ...recoveryPaths,
867
+ ...(snapshotFailure !== undefined ? [`snapshot=${snapshotFailure}`] : []),
868
+ ].join("|");
869
+ throw new Error(`activation_retry_staging_cleanup_failed:${reason}:${details}`);
870
+ }
871
+ }
872
+
873
+ async function tryRemoveExactStagingFile(filePath: string, identity: FileIdentity): Promise<string | undefined> {
874
+ try {
875
+ await removeExactStagingFile(filePath, identity);
876
+ return undefined;
877
+ } catch (error) {
878
+ return error instanceof Error ? error.message : String(error);
879
+ }
880
+ }
881
+
882
+ async function repointActivationRecordToRetryCandidate(
883
+ record: ActivationRecordV1,
884
+ recordIdentity: FileIdentity,
885
+ candidatePath: string,
886
+ ): Promise<FileIdentity> {
887
+ try {
888
+ return await updateActivationRecordFile(record, recordIdentity);
889
+ } catch (error) {
890
+ throw new ActivationRetryCandidateRepointError(candidatePath, error);
891
+ }
892
+ }
893
+
785
894
  /**
786
895
  * Atomically replace the installed binary and roll back if version verification fails.
787
896
  */
@@ -830,6 +939,7 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
830
939
  }
831
940
  let record: ActivationRecordV1;
832
941
  let recordIdentity: FileIdentity;
942
+ let redundantStage: { readonly path: string; readonly identity: FileIdentity } | undefined;
833
943
  if (existing.status === "valid" && existing.record.phase !== "verified" && existing.record.phase !== "rolled_back") {
834
944
  if (
835
945
  existing.record.candidate.digest !== (options.candidate?.sha256.toLowerCase() ?? staged?.identity.sha256) ||
@@ -839,8 +949,43 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
839
949
  existing.record.candidate.channel !== options.candidate.channel))
840
950
  )
841
951
  throw new Error("activation_transaction_requires_reconciliation");
842
- record = existing.record;
843
- recordIdentity = existing.identity;
952
+ const currentStagingPath = path.resolve(options.tempPath);
953
+ const recordedStagingPath = path.resolve(existing.record.stagingPath);
954
+ if (currentStagingPath !== recordedStagingPath) {
955
+ const retryCandidate = staged ?? (await snapshotRegularFile(currentStagingPath));
956
+ if (retryCandidate && retryCandidate.identity.sha256 !== existing.record.candidate.digest)
957
+ throw new Error("activation_retry_candidate_digest_mismatch");
958
+ const activationNeedsStagedCandidate =
959
+ existing.record.phase === "staged" || existing.record.phase === "pending_activation";
960
+ const recordedStage = activationNeedsStagedCandidate
961
+ ? await snapshotRegularFile(recordedStagingPath)
962
+ : undefined;
963
+ const recordedStageIsValid =
964
+ recordedStage &&
965
+ existing.record.stagingIdentity &&
966
+ sameFileIdentity(existing.record.stagingIdentity, recordedStage.identity) &&
967
+ recordedStage.identity.sha256 === existing.record.candidate.digest;
968
+ if (activationNeedsStagedCandidate && !recordedStageIsValid) {
969
+ if (!retryCandidate) throw new Error("activation_retry_candidate_missing");
970
+ record = {
971
+ ...existing.record,
972
+ stagingPath: currentStagingPath,
973
+ stagingIdentity: retryCandidate.identity,
974
+ };
975
+ recordIdentity = await repointActivationRecordToRetryCandidate(
976
+ record,
977
+ existing.identity,
978
+ currentStagingPath,
979
+ );
980
+ } else {
981
+ if (retryCandidate) redundantStage = { path: currentStagingPath, identity: retryCandidate.identity };
982
+ record = existing.record;
983
+ recordIdentity = existing.identity;
984
+ }
985
+ } else {
986
+ record = existing.record;
987
+ recordIdentity = existing.identity;
988
+ }
844
989
  } else {
845
990
  if (!staged) throw new Error("installation_staging_unavailable");
846
991
  record = {
@@ -873,26 +1018,97 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
873
1018
  allowPromotion: true,
874
1019
  expectedRecordIdentity: recordIdentity,
875
1020
  });
876
- if (promoted.status === "pending_activation") throw new Error("installation_pending_activation");
1021
+ if (promoted.reason === "candidate_changed" && redundantStage) {
1022
+ logger.warn("Activation staging path changed during reconciliation; leaving it untouched", {
1023
+ changedStagingPath: record.stagingPath,
1024
+ retryCandidatePath: redundantStage.path,
1025
+ });
1026
+ await repointActivationRecordToRetryCandidate(
1027
+ {
1028
+ ...record,
1029
+ phase: "pending_activation",
1030
+ stagingPath: redundantStage.path,
1031
+ stagingIdentity: redundantStage.identity,
1032
+ },
1033
+ recordIdentity,
1034
+ redundantStage.path,
1035
+ );
1036
+ throw new Error("installation_pending_activation");
1037
+ }
1038
+ if (promoted.status === "pending_activation") {
1039
+ if (promoted.reason === "candidate_missing" && redundantStage) {
1040
+ await repointActivationRecordToRetryCandidate(
1041
+ {
1042
+ ...record,
1043
+ phase: "pending_activation",
1044
+ stagingPath: redundantStage.path,
1045
+ stagingIdentity: redundantStage.identity,
1046
+ },
1047
+ recordIdentity,
1048
+ redundantStage.path,
1049
+ );
1050
+ } else {
1051
+ const stagingCleanupWarning = redundantStage
1052
+ ? await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity)
1053
+ : undefined;
1054
+ if (stagingCleanupWarning)
1055
+ logger.warn("Update staging cleanup failed while activation is pending", {
1056
+ warning: stagingCleanupWarning,
1057
+ });
1058
+ }
1059
+ throw new Error("installation_pending_activation");
1060
+ }
877
1061
  if (
878
1062
  (promoted.status !== "reconciled" &&
879
1063
  promoted.status !== "applied_unverified" &&
880
1064
  promoted.status !== "verified") ||
881
1065
  !promoted.record ||
882
1066
  !promoted.recordIdentity
883
- )
1067
+ ) {
1068
+ const stagingCleanupWarning = redundantStage
1069
+ ? await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity)
1070
+ : undefined;
1071
+ if (promoted.reason === "access_denied")
1072
+ throw new InstallationAccessDeniedError(targetPath, promoted.windowsErrorCode, stagingCleanupWarning, {
1073
+ detachedPath: promoted.detachedPath,
1074
+ retainedSuccessorPath: promoted.retainedSuccessorPath,
1075
+ retainedPlaceholderPath: promoted.retainedPlaceholderPath,
1076
+ retainedUnknownPath: promoted.retainedUnknownPath,
1077
+ });
1078
+ if (stagingCleanupWarning)
1079
+ logger.warn("Update staging cleanup failed after activation reconciliation failed", {
1080
+ reason: promoted.reason ?? promoted.status,
1081
+ warning: stagingCleanupWarning,
1082
+ });
884
1083
  throw new Error(promoted.reason ?? "installation_publication_unverified");
1084
+ }
885
1085
  record = promoted.record;
886
1086
  recordIdentity = promoted.recordIdentity;
1087
+ const cleanupWarnings: string[] = [];
887
1088
  try {
888
- const verification = await options.verifyInstalledVersion(options.expectedVersion);
1089
+ let verification = await options.verifyInstalledVersion(options.expectedVersion);
889
1090
  if (!verification.ok) throw new Error(formatVerificationFailure(verification, options.expectedVersion));
890
1091
  const after = await snapshotRegularFile(targetPath);
891
1092
  if (!after || !record.targetIdentity || !sameFileIdentity(record.targetIdentity, after.identity))
892
1093
  throw new Error("installation_postcheck_identity_changed");
1094
+ if (redundantStage) {
1095
+ const cleanupWarning = await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity);
1096
+ if (cleanupWarning) {
1097
+ cleanupWarnings.push(cleanupWarning);
1098
+ verification = { ...verification, cleanupWarning };
1099
+ }
1100
+ }
893
1101
  await updateActivationRecordFile({ ...record, phase: "verified" }, recordIdentity);
894
1102
  return verification;
895
1103
  } catch (primary) {
1104
+ const rollbackCleanupWarning = redundantStage
1105
+ ? await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity)
1106
+ : undefined;
1107
+ if (rollbackCleanupWarning) cleanupWarnings.push(rollbackCleanupWarning);
1108
+ if (cleanupWarnings.length > 0)
1109
+ logger.warn("Update staging cleanup failed before activation rollback", {
1110
+ warning: cleanupWarnings.join("\n"),
1111
+ });
896
1112
  try {
897
1113
  const after = await snapshotRegularFile(targetPath);
898
1114
  if (!after || !record.targetIdentity || !sameFileIdentity(record.targetIdentity, after.identity))
@@ -1286,7 +1502,8 @@ async function updateViaBinaryAt(
1286
1502
  beforeReplace: () => console.log(chalk.dim("Installing update...")),
1287
1503
  });
1288
1504
 
1289
- if (verification.cleanupWarning) console.warn(chalk.yellow(verification.cleanupWarning));
1505
+ if (verification.cleanupWarning)
1506
+ logger.warn("Update staging cleanup failed", { warning: verification.cleanupWarning });
1290
1507
  return verification;
1291
1508
  }
1292
1509
 
@@ -3,8 +3,8 @@
3
3
  * This module owns the effectful bootstrap (startup timing, stderr drainer,
4
4
  * bash-shell worker protocol) plus the unchanged ordinary routing order:
5
5
  * bash-shell workers -> darwin malloc re-exec -> internal smoke -> tmux owner
6
- * isolation -> managed-owner supervisor/admission/recovery -> private sdk
7
- * stderr drainer -> public family dispatch -> community app -> doctor worker /
6
+ * isolation -> managed-owner supervisor/admission/recovery -> broker hop ->
7
+ * private sdk stderr drainer -> public family dispatch -> community app -> doctor worker /
8
8
  * probe / cli -> full registry. `cli.ts` imports it lazily, only after argv has
9
9
  * been proven not to be a `sdk diagnostics` observation. */
10
10
 
@@ -16,6 +16,7 @@ import {
16
16
  BASH_SHELL_SUPERVISOR_ARG,
17
17
  BASH_SHELL_WORKER_ARG,
18
18
  } from "./exec/bash-shell-worker-protocol";
19
+ import { runBrokerHopFromArgv } from "./sdk/broker/hop";
19
20
  import { runSdkStderrDrainerFromArgv } from "./sdk/broker/stderr-drainer";
20
21
 
21
22
  export { commands };
@@ -128,6 +129,10 @@ export async function runOrdinaryCli(argv: string[]): Promise<void> {
128
129
  return;
129
130
  }
130
131
  }
132
+ if (argv.length >= 2 && argv[0] === "internal" && argv[1] === "broker-hop") {
133
+ await runBrokerHopFromArgv(argv.slice(2));
134
+ return;
135
+ }
131
136
  if (argv[0] === "sdk" && argv[1] === "stderr-drain-internal") {
132
137
  // Private lifecycle stderr drainer: must stay ahead of the public sdk family
133
138
  // dispatcher, which would otherwise reject the private argv as a usage error.
@@ -8,7 +8,7 @@ import {
8
8
  } from "../cli/accounts-cli";
9
9
 
10
10
  export default class Accounts extends Command {
11
- static description = "List, check, pin, and remove stored OAuth accounts";
11
+ static description = "List, check, pin, prefer, and remove stored OAuth accounts";
12
12
  static strict = true;
13
13
 
14
14
  static args = {
@@ -22,16 +22,18 @@ export default class Accounts extends Command {
22
22
  required: false,
23
23
  }),
24
24
  selector: Args.string({
25
- description: "Account selector (bare email, id:, email:, or account:)",
25
+ description:
26
+ "Account selector (bare email, id:, email:, or account:); prefer takes several in order, each optionally =<max used percent>",
26
27
  required: false,
28
+ multiple: true,
27
29
  }),
28
30
  };
29
31
 
30
32
  static flags = {
31
33
  json: Flags.boolean({ description: "Output safe machine-readable JSON" }),
32
34
  persistent: Flags.boolean({ description: "Required for persistent pin changes" }),
33
- clear: Flags.boolean({ description: "Clear the persistent pin" }),
34
- account: Flags.string({ description: "Logout one account by row id or email" }),
35
+ clear: Flags.boolean({ description: "Clear the persistent pin or preferred accounts" }),
36
+ account: Flags.string({ description: "Logout one account by row id, email, or org:<organization id or name>" }),
35
37
  all: Flags.boolean({ description: "Logout every OAuth account for the provider" }),
36
38
  };
37
39
 
@@ -42,7 +44,10 @@ export default class Accounts extends Command {
42
44
  "gjc accounts check anthropic --json",
43
45
  "gjc accounts pin anthropic me@example.com --persistent",
44
46
  "gjc accounts pin anthropic id:42 --persistent",
47
+ "gjc accounts pin anthropic 'org:Acme Team' --persistent",
45
48
  "gjc accounts pin anthropic --clear --persistent",
49
+ "gjc accounts prefer anthropic cheap@example.com shared@example.com=75",
50
+ "gjc accounts prefer anthropic --clear",
46
51
  "gjc accounts logout anthropic --account me@example.com",
47
52
  "gjc accounts logout anthropic --all",
48
53
  ];
@@ -57,7 +62,7 @@ export default class Accounts extends Command {
57
62
  const cmd: AccountsCommandArgs = {
58
63
  action,
59
64
  provider: args.provider,
60
- selector: args.selector,
65
+ selectors: Array.isArray(args.selector) ? args.selector : args.selector ? [args.selector] : undefined,
61
66
  flags: {
62
67
  json: flags.json,
63
68
  persistent: flags.persistent,
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Import MCP servers and skills from other coding agents into GJC.
2
+ * Import MCP servers, skills, and instructions from other coding agents into GJC.
3
3
  */
4
4
  import { Command, Flags } from "@gajae-code/utils/cli";
5
5
  import { type MigrateCommandArgs, runMigrateCommand } from "../cli/migrate-cli";
6
6
 
7
7
  export default class Migrate extends Command {
8
- static description = "Import MCP servers and skills from Claude Code, Codex, or OpenCode";
8
+ static description = "Import MCP servers and skills, plus Claude Code/Codex instructions";
9
9
 
10
10
  static examples = [
11
11
  "gjc migrate --from claude-code",
@@ -25,7 +25,7 @@ export default class Migrate extends Command {
25
25
  default: false,
26
26
  }),
27
27
  force: Flags.boolean({
28
- description: "Overwrite existing skills/MCP servers instead of skipping them",
28
+ description: "Overwrite existing skills, MCP servers, or instruction files instead of skipping them",
29
29
  default: false,
30
30
  }),
31
31
  "dry-run": Flags.boolean({ description: "Preview the migration without writing anything", default: false }),