@gajae-code/coding-agent 0.6.4 → 0.7.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 (231) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/types/async/job-manager.d.ts +3 -1
  3. package/dist/types/cli/daemon-cli.d.ts +25 -0
  4. package/dist/types/cli/migrate-cli.d.ts +20 -0
  5. package/dist/types/cli/notify-cli.d.ts +23 -0
  6. package/dist/types/cli/setup-cli.d.ts +20 -1
  7. package/dist/types/commands/daemon.d.ts +41 -0
  8. package/dist/types/commands/migrate.d.ts +33 -0
  9. package/dist/types/commands/notify.d.ts +41 -0
  10. package/dist/types/config/keybindings.d.ts +4 -0
  11. package/dist/types/config/model-profile-activation.d.ts +12 -0
  12. package/dist/types/config/model-profiles.d.ts +2 -1
  13. package/dist/types/config/model-registry.d.ts +3 -3
  14. package/dist/types/config/models-config-schema.d.ts +5 -0
  15. package/dist/types/config/settings-schema.d.ts +38 -0
  16. package/dist/types/coordinator/contract.d.ts +1 -1
  17. package/dist/types/daemon/builtin.d.ts +20 -0
  18. package/dist/types/daemon/control-types.d.ts +57 -0
  19. package/dist/types/daemon/runtime.d.ts +25 -0
  20. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  21. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +2 -0
  22. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -2
  23. package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
  24. package/dist/types/gjc-runtime/session-layout.d.ts +59 -0
  25. package/dist/types/gjc-runtime/session-resolution.d.ts +47 -0
  26. package/dist/types/gjc-runtime/state-graph.d.ts +1 -1
  27. package/dist/types/gjc-runtime/state-runtime.d.ts +5 -4
  28. package/dist/types/gjc-runtime/state-schema.d.ts +2 -0
  29. package/dist/types/gjc-runtime/state-writer.d.ts +38 -7
  30. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +15 -0
  31. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +21 -4
  32. package/dist/types/gjc-runtime/workflow-command-ref.d.ts +1 -1
  33. package/dist/types/gjc-runtime/workflow-manifest.d.ts +1 -1
  34. package/dist/types/harness-control-plane/storage.d.ts +2 -1
  35. package/dist/types/hooks/skill-state.d.ts +12 -4
  36. package/dist/types/migrate/action-planner.d.ts +11 -0
  37. package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
  38. package/dist/types/migrate/adapters/codex.d.ts +5 -0
  39. package/dist/types/migrate/adapters/index.d.ts +45 -0
  40. package/dist/types/migrate/adapters/opencode.d.ts +2 -0
  41. package/dist/types/migrate/executor.d.ts +2 -0
  42. package/dist/types/migrate/mcp-mapper.d.ts +20 -0
  43. package/dist/types/migrate/report.d.ts +18 -0
  44. package/dist/types/migrate/skill-normalizer.d.ts +27 -0
  45. package/dist/types/migrate/types.d.ts +126 -0
  46. package/dist/types/modes/components/custom-editor.d.ts +1 -1
  47. package/dist/types/modes/components/oauth-selector.d.ts +2 -0
  48. package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
  49. package/dist/types/modes/interactive-mode.d.ts +1 -1
  50. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
  51. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
  52. package/dist/types/modes/types.d.ts +7 -1
  53. package/dist/types/notifications/config-commands.d.ts +26 -0
  54. package/dist/types/notifications/config.d.ts +61 -0
  55. package/dist/types/notifications/helpers.d.ts +55 -0
  56. package/dist/types/notifications/html-format.d.ts +62 -0
  57. package/dist/types/notifications/index.d.ts +28 -0
  58. package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
  59. package/dist/types/notifications/telegram-cli.d.ts +19 -0
  60. package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
  61. package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
  62. package/dist/types/notifications/telegram-daemon.d.ts +276 -0
  63. package/dist/types/notifications/telegram-reference.d.ts +111 -0
  64. package/dist/types/notifications/threaded-inbound.d.ts +58 -0
  65. package/dist/types/notifications/threaded-render.d.ts +66 -0
  66. package/dist/types/notifications/topic-registry.d.ts +67 -0
  67. package/dist/types/research-plan/index.d.ts +1 -0
  68. package/dist/types/research-plan/ledger.d.ts +33 -0
  69. package/dist/types/rlm/artifacts.d.ts +1 -1
  70. package/dist/types/rlm/index.d.ts +12 -0
  71. package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
  72. package/dist/types/session/agent-session.d.ts +39 -2
  73. package/dist/types/session/auth-storage.d.ts +1 -1
  74. package/dist/types/setup/credential-auto-import.d.ts +63 -0
  75. package/dist/types/setup/credential-import.d.ts +3 -0
  76. package/dist/types/setup/host-plugin-setup.d.ts +39 -0
  77. package/dist/types/skill-state/active-state.d.ts +6 -11
  78. package/dist/types/skill-state/canonical-skills.d.ts +3 -0
  79. package/dist/types/skill-state/workflow-hud.d.ts +2 -0
  80. package/dist/types/task/spawn-gate.d.ts +1 -10
  81. package/dist/types/tools/ask-answer-registry.d.ts +13 -0
  82. package/dist/types/tools/index.d.ts +18 -0
  83. package/dist/types/tools/subagent.d.ts +3 -0
  84. package/package.json +7 -7
  85. package/scripts/build-binary.ts +3 -0
  86. package/src/async/job-manager.ts +5 -1
  87. package/src/cli/daemon-cli.ts +122 -0
  88. package/src/cli/migrate-cli.ts +106 -0
  89. package/src/cli/notify-cli.ts +274 -0
  90. package/src/cli/setup-cli.ts +173 -84
  91. package/src/cli.ts +3 -0
  92. package/src/commands/daemon.ts +47 -0
  93. package/src/commands/deep-interview.ts +2 -2
  94. package/src/commands/migrate.ts +46 -0
  95. package/src/commands/notify.ts +61 -0
  96. package/src/commands/setup.ts +11 -1
  97. package/src/commands/state.ts +2 -1
  98. package/src/commands/team.ts +7 -3
  99. package/src/config/model-profile-activation.ts +74 -5
  100. package/src/config/model-profiles.ts +7 -4
  101. package/src/config/model-registry.ts +6 -3
  102. package/src/config/models-config-schema.ts +1 -1
  103. package/src/config/settings-schema.ts +29 -0
  104. package/src/coordinator/contract.ts +3 -0
  105. package/src/coordinator-mcp/policy.ts +10 -2
  106. package/src/coordinator-mcp/server.ts +270 -1
  107. package/src/daemon/builtin.ts +46 -0
  108. package/src/daemon/control-types.ts +65 -0
  109. package/src/daemon/runtime.ts +51 -0
  110. package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +0 -1
  111. package/src/defaults/gjc/skills/deep-interview/SKILL.md +28 -24
  112. package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
  113. package/src/defaults/gjc/skills/team/SKILL.md +51 -47
  114. package/src/defaults/gjc/skills/ultragoal/SKILL.md +33 -13
  115. package/src/extensibility/custom-commands/loader.ts +0 -7
  116. package/src/extensibility/extensions/runner.ts +4 -0
  117. package/src/extensibility/extensions/types.ts +8 -0
  118. package/src/extensibility/gjc-plugins/injection.ts +23 -4
  119. package/src/extensibility/gjc-plugins/state.ts +16 -1
  120. package/src/gjc-runtime/deep-interview-recorder.ts +51 -18
  121. package/src/gjc-runtime/deep-interview-runtime.ts +49 -23
  122. package/src/gjc-runtime/goal-mode-request.ts +26 -11
  123. package/src/gjc-runtime/launch-tmux.ts +6 -1
  124. package/src/gjc-runtime/ralplan-runtime.ts +79 -50
  125. package/src/gjc-runtime/session-layout.ts +180 -0
  126. package/src/gjc-runtime/session-resolution.ts +217 -0
  127. package/src/gjc-runtime/state-graph.ts +1 -2
  128. package/src/gjc-runtime/state-migrations.ts +1 -0
  129. package/src/gjc-runtime/state-runtime.ts +247 -124
  130. package/src/gjc-runtime/state-schema.ts +2 -0
  131. package/src/gjc-runtime/state-writer.ts +289 -41
  132. package/src/gjc-runtime/team-runtime.ts +43 -19
  133. package/src/gjc-runtime/tmux-sessions.ts +7 -1
  134. package/src/gjc-runtime/ultragoal-guard.ts +102 -4
  135. package/src/gjc-runtime/ultragoal-runtime.ts +226 -60
  136. package/src/gjc-runtime/workflow-command-ref.ts +1 -2
  137. package/src/gjc-runtime/workflow-manifest.generated.json +27 -2
  138. package/src/gjc-runtime/workflow-manifest.ts +12 -3
  139. package/src/goals/tools/goal-tool.ts +11 -2
  140. package/src/harness-control-plane/storage.ts +14 -4
  141. package/src/hooks/native-skill-hook.ts +38 -12
  142. package/src/hooks/skill-state.ts +178 -83
  143. package/src/internal-urls/docs-index.generated.ts +9 -6
  144. package/src/main.ts +30 -0
  145. package/src/migrate/action-planner.ts +318 -0
  146. package/src/migrate/adapters/claude-code.ts +39 -0
  147. package/src/migrate/adapters/codex.ts +70 -0
  148. package/src/migrate/adapters/index.ts +277 -0
  149. package/src/migrate/adapters/opencode.ts +52 -0
  150. package/src/migrate/executor.ts +81 -0
  151. package/src/migrate/mcp-mapper.ts +152 -0
  152. package/src/migrate/report.ts +104 -0
  153. package/src/migrate/skill-normalizer.ts +80 -0
  154. package/src/migrate/types.ts +163 -0
  155. package/src/modes/acp/acp-event-mapper.ts +1 -0
  156. package/src/modes/bridge/bridge-mode.ts +2 -2
  157. package/src/modes/components/custom-editor.ts +30 -20
  158. package/src/modes/components/hook-editor.ts +7 -2
  159. package/src/modes/components/oauth-selector.ts +19 -0
  160. package/src/modes/controllers/event-controller.ts +20 -0
  161. package/src/modes/controllers/selector-controller.ts +80 -17
  162. package/src/modes/interactive-mode.ts +6 -2
  163. package/src/modes/rpc/rpc-mode.ts +2 -2
  164. package/src/modes/runtime-init.ts +1 -0
  165. package/src/modes/shared/agent-wire/event-contract.ts +1 -0
  166. package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
  167. package/src/modes/shared/agent-wire/event-observation.ts +16 -0
  168. package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
  169. package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
  170. package/src/modes/types.ts +7 -1
  171. package/src/modes/utils/ui-helpers.ts +23 -0
  172. package/src/notifications/config-commands.ts +50 -0
  173. package/src/notifications/config.ts +107 -0
  174. package/src/notifications/helpers.ts +135 -0
  175. package/src/notifications/html-format.ts +389 -0
  176. package/src/notifications/index.ts +663 -0
  177. package/src/notifications/rate-limit-pool.ts +179 -0
  178. package/src/notifications/telegram-cli.ts +194 -0
  179. package/src/notifications/telegram-daemon-cli.ts +74 -0
  180. package/src/notifications/telegram-daemon-control.ts +370 -0
  181. package/src/notifications/telegram-daemon.ts +1370 -0
  182. package/src/notifications/telegram-reference.ts +335 -0
  183. package/src/notifications/threaded-inbound.ts +80 -0
  184. package/src/notifications/threaded-render.ts +155 -0
  185. package/src/notifications/topic-registry.ts +133 -0
  186. package/src/prompts/agents/init.md +1 -1
  187. package/src/prompts/system/plan-mode-active.md +1 -1
  188. package/src/prompts/tools/ast-grep.md +1 -1
  189. package/src/prompts/tools/search.md +1 -1
  190. package/src/prompts/tools/task.md +1 -2
  191. package/src/research-plan/index.ts +1 -0
  192. package/src/research-plan/ledger.ts +177 -0
  193. package/src/rlm/artifacts.ts +12 -3
  194. package/src/rlm/index.ts +26 -0
  195. package/src/runtime-mcp/config-writer.ts +46 -0
  196. package/src/sdk.ts +16 -0
  197. package/src/session/agent-session.ts +128 -24
  198. package/src/session/auth-storage.ts +3 -0
  199. package/src/session/session-dump-format.ts +43 -2
  200. package/src/session/session-manager.ts +39 -5
  201. package/src/setup/credential-auto-import.ts +258 -0
  202. package/src/setup/credential-import.ts +17 -0
  203. package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
  204. package/src/setup/hermes-setup.ts +1 -1
  205. package/src/setup/host-plugin-setup.ts +142 -0
  206. package/src/skill-state/active-state.ts +72 -108
  207. package/src/skill-state/canonical-skills.ts +4 -0
  208. package/src/skill-state/deep-interview-mutation-guard.ts +28 -109
  209. package/src/skill-state/workflow-hud.ts +4 -2
  210. package/src/skill-state/workflow-state-contract.ts +3 -3
  211. package/src/slash-commands/builtin-registry.ts +4 -1
  212. package/src/task/agents.ts +1 -22
  213. package/src/task/executor.ts +5 -1
  214. package/src/task/index.ts +1 -41
  215. package/src/task/spawn-gate.ts +1 -38
  216. package/src/task/types.ts +1 -1
  217. package/src/tools/ask-answer-registry.ts +25 -0
  218. package/src/tools/ask.ts +108 -16
  219. package/src/tools/computer.ts +58 -4
  220. package/src/tools/image-gen.ts +5 -8
  221. package/src/tools/index.ts +19 -0
  222. package/src/tools/inspect-image.ts +16 -11
  223. package/src/tools/subagent-render.ts +7 -0
  224. package/src/tools/subagent.ts +38 -7
  225. package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
  226. package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
  227. package/src/prompts/agents/explore.md +0 -58
  228. package/src/prompts/agents/plan.md +0 -49
  229. package/src/prompts/agents/reviewer.md +0 -141
  230. package/src/prompts/agents/task.md +0 -16
  231. package/src/prompts/review-request.md +0 -70
@@ -71,7 +71,7 @@ import { type AgentRegistry } from "../registry/agent-registry";
71
71
  import { type DiscoverableMCPSearchIndex, type DiscoverableMCPTool } from "../runtime-mcp/discoverable-tool-metadata";
72
72
  import { type SecretObfuscator } from "../secrets/obfuscator";
73
73
  import { type DiscoverableTool, type DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
74
- import type { ToolSession } from "../tools";
74
+ import type { AskAnswerSource, ToolSession } from "../tools";
75
75
  import type { CheckpointState } from "../tools/checkpoint";
76
76
  import { type TodoItem, type TodoPhase } from "../tools/todo-write";
77
77
  import type { ClientBridge } from "./client-bridge";
@@ -128,6 +128,9 @@ export type AgentSessionEvent = AgentEvent | {
128
128
  } | {
129
129
  type: "irc_message";
130
130
  message: CustomMessage;
131
+ } | {
132
+ type: "subagent_steer_message";
133
+ message: CustomMessage;
131
134
  } | {
132
135
  type: "notice";
133
136
  level: "info" | "warning" | "error";
@@ -558,6 +561,7 @@ export declare class AgentSession {
558
561
  getGoalModeState(): GoalModeState | undefined;
559
562
  setGoalModeState(state: GoalModeState | undefined): void;
560
563
  getWorkflowGateEmitter(): WorkflowGateEmitter | undefined;
564
+ getAskAnswerSource(): AskAnswerSource | undefined;
561
565
  setWorkflowGateEmitter(emitter: WorkflowGateEmitter | undefined): void;
562
566
  get goalRuntime(): GoalRuntime;
563
567
  markPlanReferenceSent(): void;
@@ -713,12 +717,38 @@ export declare class AgentSession {
713
717
  }): Promise<void>;
714
718
  setActiveModelProfile(name: string | undefined): void;
715
719
  getActiveModelProfile(): string | undefined;
720
+ /**
721
+ * The model selector ("provider/id") that resume restores as the session
722
+ * default — the latest session-log `model_change` with role="default".
723
+ * Model-profile activation snapshots this before mutating the session so a
724
+ * failed-activation rollback can restore the pre-activation resume default
725
+ * instead of promoting a transient runtime model to the resume default.
726
+ */
727
+ getSessionDefaultModelSelector(): string | undefined;
728
+ /**
729
+ * Re-assert the session resume default ("provider/id") in the session log
730
+ * WITHOUT touching the live runtime model. Appends a `model_change` with
731
+ * role="default"; never writes to global settings (apply-for-this-session
732
+ * semantics). Used by model-profile activation rollback to neutralize the
733
+ * profile main model the failed activation already recorded as the default.
734
+ */
735
+ recordResumeDefaultModel(selector: string): void;
716
736
  /**
717
737
  * Set model temporarily (for this session only).
718
738
  * Validates API key, saves to session log but NOT to settings.
739
+ *
740
+ * The change is recorded in the session log as `role: "temporary"` by
741
+ * default, which means it is NOT restored as the session default on resume —
742
+ * transient retry/fallback/context-promotion/plan switches must not clobber
743
+ * the user's explicit pick (issue #849). Model-profile activation passes
744
+ * `persistAsSessionDefault: true` so the profile's main model becomes the
745
+ * session default and survives resume, while still not being written to
746
+ * global settings (new sessions keep the global default).
719
747
  * @throws Error if no API key available for the model
720
748
  */
721
- setModelTemporary(model: Model, thinkingLevel?: ThinkingLevel): Promise<void>;
749
+ setModelTemporary(model: Model, thinkingLevel?: ThinkingLevel, options?: {
750
+ persistAsSessionDefault?: boolean;
751
+ }): Promise<void>;
722
752
  /**
723
753
  * Cycle to next/previous model.
724
754
  * Uses scoped models (from --models flag) if available, otherwise all available models.
@@ -946,6 +976,13 @@ export declare class AgentSession {
946
976
  * Does not persist the record to history. Public so other sessions can forward.
947
977
  */
948
978
  emitIrcRelayObservation(record: CustomMessage): void;
979
+ emitSubagentSteerObservation(args: {
980
+ from: string;
981
+ to: string;
982
+ body: string;
983
+ timestamp?: number;
984
+ }): void;
985
+ emitSubagentSteerRelayObservation(record: CustomMessage): void;
949
986
  /**
950
987
  * Run a single ephemeral side-channel turn against this session's current
951
988
  * model + system prompt + history. No tools are used; the side request
@@ -2,5 +2,5 @@
2
2
  * Re-exports from @gajae-code/ai.
3
3
  * All credential storage types and the AuthStorage class now live in the ai package.
4
4
  */
5
- export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialStore, AuthStorageData, AuthStorageOptions, OAuthCredential, SerializedAuthStorage, StoredAuthCredential, } from "@gajae-code/ai";
5
+ export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialIfAbsentReason, AuthCredentialIfAbsentResult, AuthCredentialIfAbsentSnapshotResult, AuthCredentialStore, AuthStorageData, AuthStorageOptions, OAuthCredential, SerializedAuthStorage, StoredAuthCredential, } from "@gajae-code/ai";
6
6
  export { AuthBrokerClient, AuthStorage, REMOTE_REFRESH_SENTINEL, RemoteAuthCredentialStore, SqliteAuthCredentialStore, } from "@gajae-code/ai";
@@ -0,0 +1,63 @@
1
+ import type { AuthCredentialIfAbsentReason, AuthCredentialIfAbsentSnapshotResult, AuthStorage } from "@gajae-code/ai";
2
+ import type { ModelRegistry } from "../config/model-registry";
3
+ import { type CredentialDiscoveryResult, type CredentialOrigin, type DiscoveryOptions, type ImportableCredential } from "./credential-import";
4
+ export declare const CREDENTIAL_AUTO_IMPORT_ROTATION_WARNING = "Refreshing in gjc may log out the Claude/Codex CLI because OAuth refresh tokens can rotate.";
5
+ export type CredentialAutoImportSourceLabel = "claude-code-file" | "claude-code-keychain" | "codex-file";
6
+ export type CredentialAutoImportTrigger = "startup" | "bare-login" | "setup-cli";
7
+ export declare function getCredentialAutoImportStatePath(agentDir?: string): string;
8
+ export declare function readCredentialImportMarker(agentDir?: string): Promise<string | undefined>;
9
+ export declare function writeCredentialImportMarker(version: string, agentDir?: string): Promise<boolean>;
10
+ export declare enum CredentialAutoImportFailureClass {
11
+ DiscoveryUnavailable = "discovery-unavailable",
12
+ SourceUnreadable = "source-unreadable",
13
+ SourceMalformed = "source-malformed",
14
+ KeychainDenied = "keychain-denied",
15
+ WriteInvalid = "write-invalid",
16
+ WriteConflict = "write-conflict",
17
+ BrokerUnavailable = "broker-unavailable",
18
+ BrokerUnsupported = "broker-unsupported",
19
+ Unknown = "unknown"
20
+ }
21
+ export interface CredentialAutoImportSkipped {
22
+ credential: ImportableCredential;
23
+ reason: AuthCredentialIfAbsentReason;
24
+ entries: AuthCredentialIfAbsentSnapshotResult["entries"];
25
+ }
26
+ export interface CredentialAutoImportFailure {
27
+ credential?: ImportableCredential;
28
+ origin?: CredentialOrigin;
29
+ source?: string;
30
+ failureClass: CredentialAutoImportFailureClass;
31
+ }
32
+ export interface CredentialAutoImportResult {
33
+ imported: ImportableCredential[];
34
+ skipped: CredentialAutoImportSkipped[];
35
+ failures: CredentialAutoImportFailure[];
36
+ discovered: boolean;
37
+ discovery?: CredentialDiscoveryResult;
38
+ globalDiscoveryFailure?: CredentialAutoImportFailure;
39
+ }
40
+ export type CredentialAutoImportAuthStorage = Pick<AuthStorage, "importCredentialIfAbsent">;
41
+ export interface CredentialAutoImportOptions {
42
+ authStorage: CredentialAutoImportAuthStorage;
43
+ discover?: (options?: DiscoveryOptions) => Promise<CredentialDiscoveryResult>;
44
+ discoveryOptions?: DiscoveryOptions;
45
+ trigger: CredentialAutoImportTrigger;
46
+ sourceLabel?: CredentialAutoImportSourceLabel;
47
+ }
48
+ export declare function runExternalCredentialAutoImport({ authStorage, discover, discoveryOptions }: CredentialAutoImportOptions): Promise<CredentialAutoImportResult>;
49
+ export declare function buildCredentialAutoImportNotice(result: Pick<CredentialAutoImportResult, "imported">): string | undefined;
50
+ export declare function formatCredentialAutoImportResult(result: CredentialAutoImportResult): string[];
51
+ export interface CredentialImportMarkerStore {
52
+ read: () => Promise<string | undefined> | string | undefined;
53
+ write: (version: string) => Promise<boolean> | boolean;
54
+ }
55
+ export interface StartupCredentialAutoImportOptions {
56
+ authStorage: CredentialAutoImportOptions["authStorage"];
57
+ modelRegistry: Pick<ModelRegistry, "refresh">;
58
+ discover?: CredentialAutoImportOptions["discover"];
59
+ version?: string;
60
+ agentDir?: string;
61
+ markerStore?: CredentialImportMarkerStore;
62
+ }
63
+ export declare function runStartupCredentialAutoImportIfNeeded({ authStorage: activeAuthStorage, modelRegistry: activeModelRegistry, discover, version, agentDir, markerStore }: StartupCredentialAutoImportOptions): Promise<string | undefined>;
@@ -3,6 +3,7 @@ import type { AuthCredential } from "@gajae-code/ai";
3
3
  export type ExternalProvider = "anthropic" | "openai-codex";
4
4
  /** Where a discovered credential came from. */
5
5
  export type CredentialOrigin = "claude-code-file" | "claude-code-keychain" | "codex-file";
6
+ export declare const AUTO_IMPORT_OAUTH_PROVIDER_ORIGINS: Record<ExternalProvider, ReadonlySet<CredentialOrigin>>;
6
7
  /** Human labels for providers, used in redacted summaries. */
7
8
  export declare const EXTERNAL_PROVIDER_LABELS: Record<ExternalProvider, string>;
8
9
  /** A credential that can be safely imported into gjc's store. */
@@ -72,6 +73,8 @@ export declare function discoverExternalCredentials(options?: DiscoveryOptions):
72
73
  export declare function formatCredentialSummary(credential: ImportableCredential): string;
73
74
  /** Redacted summary lines for an entire discovery result. Never includes secrets. */
74
75
  export declare function formatDiscoverySummary(result: CredentialDiscoveryResult): string[];
76
+ export declare function isAutoImportOAuthCredential(credential: ImportableCredential): boolean;
77
+ export declare function filterAutoImportOAuthCredentials(credentials: readonly ImportableCredential[]): ImportableCredential[];
75
78
  /**
76
79
  * Persist discovered credentials via `upsert`. Each credential is imported
77
80
  * independently; a failure on one is recorded without aborting the rest.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Host plugin setup for `gjc setup claude` and `gjc setup codex`.
3
+ *
4
+ * Renders install guidance and a fail-closed coordinator MCP config preview for
5
+ * the canonical generated plugin bundle under `plugins/`. This is intentionally
6
+ * render-only and fail-closed: the workdir allowlist is scoped to the project
7
+ * root and no mutation class is enabled until the user opts in.
8
+ */
9
+ export type HostPluginKind = "claude" | "codex";
10
+ export interface HostPluginSetupFlags {
11
+ json?: boolean;
12
+ check?: boolean;
13
+ root?: string[];
14
+ repo?: string;
15
+ }
16
+ export interface HostPluginSetupResult {
17
+ ok: true;
18
+ host: HostPluginKind;
19
+ mode: "render";
20
+ gated: boolean;
21
+ pluginPath: string;
22
+ manifestPath: string;
23
+ marketplacePath: string;
24
+ installGuidance: string[];
25
+ coordinatorConfigPreview: {
26
+ command: string;
27
+ args: string[];
28
+ env: Record<string, string>;
29
+ };
30
+ mutationPolicy: string;
31
+ notes: string[];
32
+ check?: {
33
+ ok: boolean;
34
+ checked: string[];
35
+ missing: string[];
36
+ };
37
+ }
38
+ export declare function buildHostPluginSetup(host: HostPluginKind, flags?: HostPluginSetupFlags): HostPluginSetupResult;
39
+ export declare function formatHostPluginSetup(result: HostPluginSetupResult): string;
@@ -1,7 +1,7 @@
1
+ import { CANONICAL_GJC_WORKFLOW_SKILLS, type CanonicalGjcWorkflowSkill } from "./canonical-skills";
1
2
  import type { WorkflowStateReceipt } from "./workflow-state-contract";
2
3
  export declare const SKILL_ACTIVE_STATE_FILE = "skill-active-state.json";
3
- export declare const CANONICAL_GJC_WORKFLOW_SKILLS: readonly ["deep-interview", "ralplan", "ultragoal", "team"];
4
- export type CanonicalGjcWorkflowSkill = (typeof CANONICAL_GJC_WORKFLOW_SKILLS)[number];
4
+ export { CANONICAL_GJC_WORKFLOW_SKILLS, type CanonicalGjcWorkflowSkill };
5
5
  export type WorkflowHudSeverity = "info" | "warning" | "blocked" | "error" | "success";
6
6
  export interface WorkflowHudChip {
7
7
  label: string;
@@ -44,6 +44,7 @@ export interface SkillActiveEntry {
44
44
  handoff_to?: string;
45
45
  handoff_at?: string;
46
46
  active_subskills?: ActiveSubskillEntry[];
47
+ source_state_revision?: number;
47
48
  }
48
49
  export interface SkillActiveState {
49
50
  version?: number;
@@ -65,7 +66,7 @@ export interface SkillActiveState {
65
66
  }
66
67
  export interface SkillActiveStatePaths {
67
68
  rootPath: string;
68
- sessionPath?: string;
69
+ sessionPath: string;
69
70
  }
70
71
  export interface SyncSkillActiveStateOptions {
71
72
  cwd: string;
@@ -83,6 +84,7 @@ export interface SyncSkillActiveStateOptions {
83
84
  handoff_to?: string;
84
85
  handoff_at?: string;
85
86
  active_subskills?: ActiveSubskillEntry[];
87
+ sourceRevision?: number;
86
88
  }
87
89
  export declare function normalizeWorkflowHudSummary(raw: unknown): WorkflowHudSummary | undefined;
88
90
  export declare function isCanonicalGjcWorkflowSkill(skill: string): skill is CanonicalGjcWorkflowSkill;
@@ -102,13 +104,6 @@ export interface ApplyHandoffOptions {
102
104
  strict?: boolean;
103
105
  }
104
106
  /**
105
- * Atomically apply a workflow-skill handoff to both the session-scoped and
106
- * root `skill-active-state.json` files in a single write per file.
107
- *
108
- * Write order: **session first, root last**. The session file is the
109
- * source of truth for HUD; the root aggregate must never lead the session
110
- * during a handoff window. Each file is rewritten once with caller demoted
111
- * to `active:false` (preserving `handoff_to`/`handoff_at` lineage) and
112
- * callee promoted to `active:true` (with `handoff_from`/`handoff_at`).
107
+ * Atomically apply a workflow-skill handoff to the session-scoped active state.
113
108
  */
114
109
  export declare function applyHandoffToActiveState(options: ApplyHandoffOptions): Promise<void>;
@@ -0,0 +1,3 @@
1
+ /** Native-free canonical GJC workflow skill identifiers. */
2
+ export declare const CANONICAL_GJC_WORKFLOW_SKILLS: readonly ["deep-interview", "ralplan", "ultragoal", "team"];
3
+ export type CanonicalGjcWorkflowSkill = (typeof CANONICAL_GJC_WORKFLOW_SKILLS)[number];
@@ -39,6 +39,8 @@ interface UltragoalHudState extends WorkflowGateHudState {
39
39
  event?: string;
40
40
  goalId?: string;
41
41
  timestamp?: string;
42
+ kind?: string;
43
+ evidence?: string;
42
44
  };
43
45
  updatedAt?: string;
44
46
  }
@@ -1,6 +1,6 @@
1
1
  /** The hard, locked batch threshold enforced by the runtime gate. */
2
2
  export declare const DEFAULT_SPAWN_THRESHOLD = 4;
3
- /** The justification a large batch or reviewer-spawned explorer must supply to pass the hard gate. */
3
+ /** The justification a large batch must supply to pass the hard gate. */
4
4
  export interface SpawnPlanReceipt {
5
5
  whyParallel: string;
6
6
  whyNotLocal: string;
@@ -14,14 +14,6 @@ export interface SpawnGateRequest {
14
14
  /** The spawn-plan receipt, when provided. */
15
15
  plan?: SpawnPlanReceipt;
16
16
  }
17
- export interface ReviewerExploreGateRequest {
18
- /** Agent type/name doing the spawning, when known. */
19
- spawningAgentType?: string | null;
20
- /** Target agent type/name requested by the task call. */
21
- targetAgent: string;
22
- /** The spawn-plan receipt, when provided. */
23
- plan?: SpawnPlanReceipt;
24
- }
25
17
  export type SpawnGateOutcome = "allowed" | "rejected";
26
18
  export interface SpawnGateDecision {
27
19
  outcome: SpawnGateOutcome;
@@ -35,4 +27,3 @@ export interface SpawnGateDecision {
35
27
  export declare function findMissingPlanFields(plan: SpawnPlanReceipt | undefined): string[];
36
28
  export declare function decide(childCount: number, threshold: number, plan: SpawnPlanReceipt | undefined): SpawnGateDecision;
37
29
  export declare function evaluateSpawnGate(request: SpawnGateRequest): SpawnGateDecision;
38
- export declare function evaluateReviewerExploreGate(request: ReviewerExploreGateRequest): SpawnGateDecision;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Process-wide registry mapping a session id to its active {@link AskAnswerSource}.
3
+ *
4
+ * Decouples the `ask` tool (which reads the source via `AgentSession`) from the
5
+ * notifications extension (which registers one), without threading a new method
6
+ * through the extension/runner/controller wiring. A session has at most one
7
+ * source; registering returns a disposer.
8
+ */
9
+ import type { AskAnswerSource } from "./index";
10
+ /** Register `source` for `sessionId`. Returns a disposer that clears it. */
11
+ export declare function registerAskAnswerSource(sessionId: string, source: AskAnswerSource): () => void;
12
+ /** The answer source for `sessionId`, if one is registered. */
13
+ export declare function getAskAnswerSource(sessionId: string): AskAnswerSource | undefined;
@@ -68,6 +68,17 @@ export type ContextFileEntry = {
68
68
  };
69
69
  export type { DiscoverableMCPTool } from "../runtime-mcp/discoverable-tool-metadata";
70
70
  export type { DiscoverableTool, DiscoverableToolSearchIndex, DiscoverableToolSearchResult, DiscoverableToolSource, } from "../tool-discovery/tool-index";
71
+ /**
72
+ * Source of remote answers for interactive asks (e.g. a Telegram reply routed
73
+ * through the notifications SDK). Lets a pending ask resolve without RPC mode.
74
+ */
75
+ export interface AskAnswerSource {
76
+ /**
77
+ * Race a remote answer against the local UI for one question. Resolves with the
78
+ * chosen option label or free-text answer, or `undefined` to defer to local UI.
79
+ */
80
+ awaitAnswer(question: string, options: string[], signal?: AbortSignal): Promise<string | undefined>;
81
+ }
71
82
  /** Session context for tool factories */
72
83
  export interface ToolSession {
73
84
  /** Current working directory */
@@ -167,6 +178,13 @@ export interface ToolSession {
167
178
  getGoalModeState?: () => GoalModeState | undefined;
168
179
  /** Unattended workflow-gate emitter (present only when unattended mode is negotiated). */
169
180
  getWorkflowGateEmitter?: () => WorkflowGateEmitter | undefined;
181
+ /**
182
+ * Optional remote answer source for interactive asks. When present, the ask
183
+ * tool races the local UI selection against a remote answer (e.g. a Telegram
184
+ * reply via the notifications SDK) so asks can be answered without RPC mode.
185
+ * No-op when undefined: the ask path behaves exactly as before.
186
+ */
187
+ getAskAnswerSource?: () => AskAnswerSource | undefined;
170
188
  /** Optional per-session restriction for goal tool operations. */
171
189
  goalToolAllowedOps?: readonly ("create" | "get" | "complete" | "resume" | "drop" | "pause")[];
172
190
  /** Goal runtime for the active agent session. */
@@ -42,6 +42,9 @@ export interface SubagentSnapshot {
42
42
  outputRef?: string;
43
43
  truncated?: boolean;
44
44
  guidance?: string;
45
+ steerMessage?: string;
46
+ steerState?: "queued" | "resume_queued" | "resume_started";
47
+ steerPauseRequested?: boolean;
45
48
  /** Live streaming progress for the awaited subagent (await panel only; UI detail). */
46
49
  progress?: AgentProgress;
47
50
  /** True when a live in-session progress producer exists for this subagent. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.6.4",
4
+ "version": "0.7.0",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gaebal-gajae.dev",
7
7
  "author": "Yeachan-Heo",
@@ -51,12 +51,12 @@
51
51
  "@agentclientprotocol/sdk": "0.21.0",
52
52
  "@babel/parser": "^7.29.3",
53
53
  "@mozilla/readability": "^0.6.0",
54
- "@gajae-code/stats": "0.6.4",
55
- "@gajae-code/agent-core": "0.6.4",
56
- "@gajae-code/ai": "0.6.4",
57
- "@gajae-code/natives": "0.6.4",
58
- "@gajae-code/tui": "0.6.4",
59
- "@gajae-code/utils": "0.6.4",
54
+ "@gajae-code/stats": "0.7.0",
55
+ "@gajae-code/agent-core": "0.7.0",
56
+ "@gajae-code/ai": "0.7.0",
57
+ "@gajae-code/natives": "0.7.0",
58
+ "@gajae-code/tui": "0.7.0",
59
+ "@gajae-code/utils": "0.7.0",
60
60
  "@puppeteer/browsers": "^2.13.0",
61
61
  "@types/turndown": "5.0.6",
62
62
  "@xterm/headless": "^6.0.0",
@@ -66,6 +66,9 @@ async function main(): Promise<void> {
66
66
  "../stats/src/sync-worker.ts",
67
67
  "./src/tools/browser/tab-worker-entry.ts",
68
68
  "./src/eval/js/worker-entry.ts",
69
+ // Hidden notify daemon CLI (loaded via dynamic import from notify-cli); list it
70
+ // explicitly so Bun standalone keeps `gjc notify daemon-internal` usable.
71
+ "./src/notifications/telegram-daemon-cli.ts",
69
72
  "--outfile",
70
73
  "dist/gjc",
71
74
  ],
@@ -84,7 +84,11 @@ export interface SubagentLiveHandle {
84
84
  /** Request a cooperative safe-boundary pause (never aborts the in-flight tool). */
85
85
  requestPause(): void;
86
86
  /** Inject a steering message into the live session. */
87
- injectMessage(content: string, deliverAs: "steer" | "followUp" | "nextTurn"): Promise<void>;
87
+ injectMessage(
88
+ content: string,
89
+ deliverAs: "steer" | "followUp" | "nextTurn",
90
+ opts?: { fromAgentId?: string },
91
+ ): Promise<void>;
88
92
  }
89
93
 
90
94
  /**
@@ -0,0 +1,122 @@
1
+ /**
2
+ * `gjc daemon` command handler.
3
+ *
4
+ * Generic over the static built-in daemon controller map: lists/inspects
5
+ * daemons and drives cooperative stop/reload. Telegram is the only kind today.
6
+ */
7
+
8
+ import { Settings } from "../config/settings";
9
+ import { selectDaemonControllers } from "../daemon/builtin";
10
+ import type {
11
+ BuiltInDaemonController,
12
+ DaemonKind,
13
+ DaemonOperationOptions,
14
+ DaemonOperationResult,
15
+ DaemonStatus,
16
+ } from "../daemon/control-types";
17
+
18
+ export type DaemonCliAction = "list" | "status" | "stop" | "reload";
19
+
20
+ export interface DaemonCommandArgs {
21
+ action: DaemonCliAction;
22
+ kinds: DaemonKind[];
23
+ all: boolean;
24
+ json: boolean;
25
+ force: boolean;
26
+ gracefulTimeoutMs?: number;
27
+ killTimeoutMs?: number;
28
+ spawnIfStopped?: boolean;
29
+ }
30
+
31
+ export interface DaemonCommandDeps {
32
+ settings?: Settings;
33
+ controllers?: BuiltInDaemonController[];
34
+ }
35
+
36
+ const KNOWN_ACTIONS: DaemonCliAction[] = ["list", "status", "stop", "reload"];
37
+ const KNOWN_KINDS: DaemonKind[] = ["telegram"];
38
+
39
+ export function parseDaemonArgs(argv: string[]): DaemonCommandArgs | undefined {
40
+ if (argv.length === 0 || argv[0] !== "daemon") return undefined;
41
+ const rest = argv.slice(1);
42
+ const action = (KNOWN_ACTIONS as string[]).includes(rest[0] ?? "") ? (rest[0] as DaemonCliAction) : "status";
43
+ const positional = (KNOWN_ACTIONS as string[]).includes(rest[0] ?? "") ? rest.slice(1) : rest;
44
+ const kinds: DaemonKind[] = [];
45
+ let all = false;
46
+ let json = false;
47
+ let force = false;
48
+ let gracefulTimeoutMs: number | undefined;
49
+ let killTimeoutMs: number | undefined;
50
+ let spawnIfStopped: boolean | undefined;
51
+ for (let i = 0; i < positional.length; i++) {
52
+ const arg = positional[i];
53
+ if (arg === "--all") all = true;
54
+ else if (arg === "--json") json = true;
55
+ else if (arg === "--force") force = true;
56
+ else if (arg === "--spawn-if-stopped") spawnIfStopped = true;
57
+ else if (arg === "--graceful-timeout-ms") gracefulTimeoutMs = Number.parseInt(positional[++i], 10);
58
+ else if (arg === "--kill-timeout-ms") killTimeoutMs = Number.parseInt(positional[++i], 10);
59
+ else if (!arg.startsWith("--") && (KNOWN_KINDS as string[]).includes(arg)) kinds.push(arg as DaemonKind);
60
+ }
61
+ return { action, kinds, all, json, force, gracefulTimeoutMs, killTimeoutMs, spawnIfStopped };
62
+ }
63
+
64
+ function formatStatus(status: DaemonStatus): string {
65
+ const parts = [
66
+ `${status.kind}: ${status.health}`,
67
+ status.configured ? undefined : "(not configured)",
68
+ status.pid !== undefined ? `pid=${status.pid}` : undefined,
69
+ status.ownerId ? `owner=${status.ownerId}` : undefined,
70
+ status.rootCount !== undefined ? `roots=${status.rootCount}` : undefined,
71
+ `mode=${status.runtime.mode}`,
72
+ ].filter(Boolean);
73
+ let line = parts.join(" ");
74
+ if (status.runtime.warning) line += `\n warning: ${status.runtime.warning}`;
75
+ return line;
76
+ }
77
+
78
+ function formatResult(result: DaemonOperationResult): string {
79
+ const head = `${result.kind} ${result.action}: ${result.ok ? "ok" : "failed"} — ${result.message}`;
80
+ const warnings = result.warnings.map(w => `\n warning: ${w}`).join("");
81
+ return head + warnings;
82
+ }
83
+
84
+ export async function runDaemonCommand(cmd: DaemonCommandArgs, deps: DaemonCommandDeps = {}): Promise<void> {
85
+ const unknownKinds = cmd.kinds.filter(kind => !(KNOWN_KINDS as string[]).includes(kind));
86
+ if (unknownKinds.length > 0) {
87
+ process.stderr.write(
88
+ `Unknown daemon kind(s): ${unknownKinds.join(", ")}. Known kinds: ${KNOWN_KINDS.join(", ")}.\n`,
89
+ );
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+ const settings = deps.settings ?? (await Settings.init());
94
+ const controllers = deps.controllers ?? selectDaemonControllers(settings, cmd.kinds, cmd.all);
95
+
96
+ if (cmd.action === "list" || cmd.action === "status") {
97
+ const statuses = await Promise.all(controllers.map(c => c.status()));
98
+ if (cmd.json) {
99
+ process.stdout.write(`${JSON.stringify(statuses, null, 2)}\n`);
100
+ } else {
101
+ process.stdout.write(`${statuses.map(formatStatus).join("\n")}\n`);
102
+ }
103
+ return;
104
+ }
105
+
106
+ const opts: DaemonOperationOptions = {
107
+ gracefulTimeoutMs: cmd.gracefulTimeoutMs,
108
+ killTimeoutMs: cmd.killTimeoutMs,
109
+ force: cmd.force,
110
+ spawnIfStopped: cmd.spawnIfStopped,
111
+ };
112
+ const results: DaemonOperationResult[] = [];
113
+ for (const controller of controllers) {
114
+ results.push(cmd.action === "reload" ? await controller.reload(opts) : await controller.stop(opts));
115
+ }
116
+ if (cmd.json) {
117
+ process.stdout.write(`${JSON.stringify(results, null, 2)}\n`);
118
+ } else {
119
+ process.stdout.write(`${results.map(formatResult).join("\n")}\n`);
120
+ }
121
+ if (results.some(r => !r.ok)) process.exitCode = 1;
122
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * `gjc migrate` — import MCP servers and skills from other coding agents.
3
+ */
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+ import { getAgentDir, getMCPConfigPath, getProjectAgentDir, getProjectDir } from "@gajae-code/utils";
7
+ import { planMigration } from "../migrate/action-planner";
8
+ import { getAdapter } from "../migrate/adapters/index";
9
+ import { executeActions } from "../migrate/executor";
10
+ import { buildReport, renderHuman } from "../migrate/report";
11
+ import {
12
+ type AdapterResult,
13
+ CANONICAL_SOURCE_ORDER,
14
+ MIGRATE_SOURCES,
15
+ type MigrateDestinations,
16
+ type MigrateReport,
17
+ type MigrateSource,
18
+ } from "../migrate/types";
19
+
20
+ export interface MigrateCommandArgs {
21
+ from: string[];
22
+ project: boolean;
23
+ force: boolean;
24
+ dryRun: boolean;
25
+ json: boolean;
26
+ /** Test seam: override home dir for source discovery. */
27
+ homeDir?: string;
28
+ /** Test seam: override cwd for project-scope destinations. */
29
+ cwd?: string;
30
+ }
31
+
32
+ export class MigrateArgsError extends Error {}
33
+
34
+ /** Expand `all`/repeated `--from`, validate, and return sources in canonical order. */
35
+ export function resolveSources(from: string[]): MigrateSource[] {
36
+ if (from.length === 0) {
37
+ throw new MigrateArgsError("No source selected. Use --from <claude-code|codex|opencode|all> (repeatable).");
38
+ }
39
+ const selected = new Set<MigrateSource>();
40
+ for (const raw of from) {
41
+ const value = raw.trim().toLowerCase();
42
+ if (value === "all") {
43
+ for (const s of MIGRATE_SOURCES) selected.add(s);
44
+ continue;
45
+ }
46
+ if (!(MIGRATE_SOURCES as readonly string[]).includes(value)) {
47
+ throw new MigrateArgsError(`Unknown source "${raw}". Valid: ${MIGRATE_SOURCES.join(", ")}, all.`);
48
+ }
49
+ selected.add(value as MigrateSource);
50
+ }
51
+ return CANONICAL_SOURCE_ORDER.filter(s => selected.has(s));
52
+ }
53
+
54
+ function resolveDestinations(project: boolean, cwd: string): MigrateDestinations {
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 };
58
+ }
59
+
60
+ /** Run the migration and return the report (does not set process.exitCode). */
61
+ export async function runMigrate(args: MigrateCommandArgs): Promise<MigrateReport> {
62
+ const sources = resolveSources(args.from);
63
+ const cwd = args.cwd ?? getProjectDir();
64
+ const homeDir = args.homeDir ?? os.homedir();
65
+ const destinations = resolveDestinations(args.project, cwd);
66
+
67
+ const results: AdapterResult[] = [];
68
+ for (const source of sources) {
69
+ results.push(await getAdapter(source).collect({ homeDir }));
70
+ }
71
+
72
+ const { actions, warnings } = await planMigration({ results, destinations, force: args.force });
73
+ const finalActions = args.dryRun ? actions : await executeActions(actions);
74
+
75
+ return buildReport({
76
+ actions: finalActions,
77
+ warnings,
78
+ sources,
79
+ destinations,
80
+ dryRun: args.dryRun,
81
+ project: args.project,
82
+ force: args.force,
83
+ });
84
+ }
85
+
86
+ /** CLI entry: run, render, and set the process exit code. */
87
+ export async function runMigrateCommand(args: MigrateCommandArgs): Promise<void> {
88
+ let report: MigrateReport;
89
+ try {
90
+ report = await runMigrate(args);
91
+ } catch (error) {
92
+ if (error instanceof MigrateArgsError) {
93
+ process.stderr.write(`${error.message}\n`);
94
+ process.exitCode = 2;
95
+ return;
96
+ }
97
+ throw error;
98
+ }
99
+
100
+ if (args.json) {
101
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
102
+ } else {
103
+ process.stdout.write(`${renderHuman(report)}\n`);
104
+ }
105
+ process.exitCode = report.ok ? 0 : 1;
106
+ }