@claudexor/cli 3.1.2 → 3.3.7-rc.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 (244) hide show
  1. package/dist/about-command.d.ts.map +1 -1
  2. package/dist/accounts-projection.d.ts +8 -1
  3. package/dist/accounts-projection.d.ts.map +1 -1
  4. package/dist/accounts-projection.js +33 -20
  5. package/dist/accounts-projection.js.map +1 -1
  6. package/dist/accounts-services.d.ts +208 -0
  7. package/dist/accounts-services.d.ts.map +1 -0
  8. package/dist/accounts-services.js +88 -0
  9. package/dist/accounts-services.js.map +1 -0
  10. package/dist/acp-surface-runner.d.ts +39 -0
  11. package/dist/acp-surface-runner.d.ts.map +1 -1
  12. package/dist/acp-surface-runner.js +104 -30
  13. package/dist/acp-surface-runner.js.map +1 -1
  14. package/dist/attachment-upload.d.ts +5 -0
  15. package/dist/attachment-upload.d.ts.map +1 -0
  16. package/dist/attachment-upload.js +64 -0
  17. package/dist/attachment-upload.js.map +1 -0
  18. package/dist/capabilities.d.ts.map +1 -1
  19. package/dist/capabilities.js +6 -1
  20. package/dist/capabilities.js.map +1 -1
  21. package/dist/choice-input.d.ts +7 -0
  22. package/dist/choice-input.d.ts.map +1 -0
  23. package/dist/choice-input.js +17 -0
  24. package/dist/choice-input.js.map +1 -0
  25. package/dist/claude-oauth-usage.d.ts.map +1 -1
  26. package/dist/claude-oauth-usage.js +13 -1
  27. package/dist/claude-oauth-usage.js.map +1 -1
  28. package/dist/claudexord-entry.js.map +1 -1
  29. package/dist/claudexord.d.ts +1 -13
  30. package/dist/claudexord.d.ts.map +1 -1
  31. package/dist/claudexord.js +57 -94
  32. package/dist/claudexord.js.map +1 -1
  33. package/dist/cli-error.d.ts.map +1 -1
  34. package/dist/cli-error.js +21 -11
  35. package/dist/cli-error.js.map +1 -1
  36. package/dist/cli.js +143 -163
  37. package/dist/cli.js.map +1 -1
  38. package/dist/codex-device-login.js.map +1 -1
  39. package/dist/codex-quota-source.js.map +1 -1
  40. package/dist/command-flags.d.ts +16 -1
  41. package/dist/command-flags.d.ts.map +1 -1
  42. package/dist/command-flags.js +41 -19
  43. package/dist/command-flags.js.map +1 -1
  44. package/dist/command-registry.d.ts +21 -8
  45. package/dist/command-registry.d.ts.map +1 -1
  46. package/dist/command-registry.js +59 -116
  47. package/dist/command-registry.js.map +1 -1
  48. package/dist/command-scope.d.ts +13 -0
  49. package/dist/command-scope.d.ts.map +1 -0
  50. package/dist/command-scope.js +61 -0
  51. package/dist/command-scope.js.map +1 -0
  52. package/dist/control-services.d.ts +1638 -1003
  53. package/dist/control-services.d.ts.map +1 -1
  54. package/dist/control-services.js +49 -67
  55. package/dist/control-services.js.map +1 -1
  56. package/dist/credential-commands.d.ts.map +1 -1
  57. package/dist/credential-commands.js +13 -3
  58. package/dist/credential-commands.js.map +1 -1
  59. package/dist/daemon-lifecycle.d.ts.map +1 -1
  60. package/dist/daemon-outcome.d.ts +3 -5
  61. package/dist/daemon-outcome.d.ts.map +1 -1
  62. package/dist/daemon-outcome.js +7 -19
  63. package/dist/daemon-outcome.js.map +1 -1
  64. package/dist/daemon-run.d.ts +21 -12
  65. package/dist/daemon-run.d.ts.map +1 -1
  66. package/dist/daemon-run.js +59 -40
  67. package/dist/daemon-run.js.map +1 -1
  68. package/dist/daemon-runtime-shutdown.d.ts +7 -0
  69. package/dist/daemon-runtime-shutdown.d.ts.map +1 -1
  70. package/dist/daemon-runtime-shutdown.js +29 -0
  71. package/dist/daemon-runtime-shutdown.js.map +1 -1
  72. package/dist/delegation-belt-descriptor.js.map +1 -1
  73. package/dist/delegation-output.d.ts +2 -0
  74. package/dist/delegation-output.d.ts.map +1 -1
  75. package/dist/delegation-output.js +3 -1
  76. package/dist/delegation-output.js.map +1 -1
  77. package/dist/harness-command.d.ts +13 -0
  78. package/dist/harness-command.d.ts.map +1 -0
  79. package/dist/harness-command.js +35 -0
  80. package/dist/harness-command.js.map +1 -0
  81. package/dist/harness-installer.d.ts +79 -0
  82. package/dist/harness-installer.d.ts.map +1 -0
  83. package/dist/harness-installer.js +256 -0
  84. package/dist/harness-installer.js.map +1 -0
  85. package/dist/interaction-prompt.d.ts +19 -0
  86. package/dist/interaction-prompt.d.ts.map +1 -0
  87. package/dist/interaction-prompt.js +114 -0
  88. package/dist/interaction-prompt.js.map +1 -0
  89. package/dist/live.d.ts +2 -6
  90. package/dist/live.d.ts.map +1 -1
  91. package/dist/live.js +88 -75
  92. package/dist/live.js.map +1 -1
  93. package/dist/mcp-daemon-unavailable.d.ts +6 -0
  94. package/dist/mcp-daemon-unavailable.d.ts.map +1 -0
  95. package/dist/mcp-daemon-unavailable.js +16 -0
  96. package/dist/mcp-daemon-unavailable.js.map +1 -0
  97. package/dist/mcp-run-projections.d.ts +30 -0
  98. package/dist/mcp-run-projections.d.ts.map +1 -1
  99. package/dist/mcp-run-projections.js +17 -17
  100. package/dist/mcp-run-projections.js.map +1 -1
  101. package/dist/mcp-runner.d.ts.map +1 -1
  102. package/dist/mcp-runner.js +81 -39
  103. package/dist/mcp-runner.js.map +1 -1
  104. package/dist/native-login.js.map +1 -1
  105. package/dist/ops-command-specs.d.ts +64 -0
  106. package/dist/ops-command-specs.d.ts.map +1 -0
  107. package/dist/ops-command-specs.js +146 -0
  108. package/dist/ops-command-specs.js.map +1 -0
  109. package/dist/ops-commands.d.ts.map +1 -1
  110. package/dist/ops-commands.js +23 -5
  111. package/dist/ops-commands.js.map +1 -1
  112. package/dist/orphan-reaper.js.map +1 -1
  113. package/dist/output-mode.d.ts +11 -0
  114. package/dist/output-mode.d.ts.map +1 -0
  115. package/dist/output-mode.js +28 -0
  116. package/dist/output-mode.js.map +1 -0
  117. package/dist/plan-question-loop.d.ts.map +1 -1
  118. package/dist/plan-question-loop.js +17 -13
  119. package/dist/plan-question-loop.js.map +1 -1
  120. package/dist/plugins.js +1 -1
  121. package/dist/plugins.js.map +1 -1
  122. package/dist/primary-output.d.ts +9 -4
  123. package/dist/primary-output.d.ts.map +1 -1
  124. package/dist/primary-output.js +50 -2
  125. package/dist/primary-output.js.map +1 -1
  126. package/dist/quota-subject-universe.d.ts +6 -0
  127. package/dist/quota-subject-universe.d.ts.map +1 -0
  128. package/dist/quota-subject-universe.js +31 -0
  129. package/dist/quota-subject-universe.js.map +1 -0
  130. package/dist/registry.js.map +1 -1
  131. package/dist/release-command.d.ts +1 -1
  132. package/dist/release-command.js +2 -2
  133. package/dist/remote-command-specs.d.ts +45 -0
  134. package/dist/remote-command-specs.d.ts.map +1 -0
  135. package/dist/remote-command-specs.js +29 -0
  136. package/dist/remote-command-specs.js.map +1 -0
  137. package/dist/remote-command.d.ts +11 -0
  138. package/dist/remote-command.d.ts.map +1 -0
  139. package/dist/remote-command.js +262 -0
  140. package/dist/remote-command.js.map +1 -0
  141. package/dist/remote-filesystem.d.ts +33 -0
  142. package/dist/remote-filesystem.d.ts.map +1 -0
  143. package/dist/remote-filesystem.js +280 -0
  144. package/dist/remote-filesystem.js.map +1 -0
  145. package/dist/repo-asset-authority.js.map +1 -1
  146. package/dist/request-preflight.d.ts +11 -2
  147. package/dist/request-preflight.d.ts.map +1 -1
  148. package/dist/request-preflight.js +36 -13
  149. package/dist/request-preflight.js.map +1 -1
  150. package/dist/retention-service.d.ts.map +1 -1
  151. package/dist/retention-service.js +6 -1
  152. package/dist/retention-service.js.map +1 -1
  153. package/dist/retry-command-specs.d.ts.map +1 -1
  154. package/dist/retry-command-specs.js +2 -0
  155. package/dist/retry-command-specs.js.map +1 -1
  156. package/dist/run-applicability.d.ts +74 -0
  157. package/dist/run-applicability.d.ts.map +1 -0
  158. package/dist/run-applicability.js +80 -0
  159. package/dist/run-applicability.js.map +1 -0
  160. package/dist/run-detail-projections.d.ts +23 -0
  161. package/dist/run-detail-projections.d.ts.map +1 -1
  162. package/dist/run-detail-projections.js +32 -1
  163. package/dist/run-detail-projections.js.map +1 -1
  164. package/dist/run-detail-response.d.ts +7 -0
  165. package/dist/run-detail-response.d.ts.map +1 -0
  166. package/dist/run-detail-response.js +23 -0
  167. package/dist/run-detail-response.js.map +1 -0
  168. package/dist/run-facts-projection.d.ts +22 -0
  169. package/dist/run-facts-projection.d.ts.map +1 -0
  170. package/dist/run-facts-projection.js +86 -0
  171. package/dist/run-facts-projection.js.map +1 -0
  172. package/dist/runtime-replacement-stop.d.ts +25 -0
  173. package/dist/runtime-replacement-stop.d.ts.map +1 -0
  174. package/dist/runtime-replacement-stop.js +145 -0
  175. package/dist/runtime-replacement-stop.js.map +1 -0
  176. package/dist/settings-command.d.ts +1 -0
  177. package/dist/settings-command.d.ts.map +1 -1
  178. package/dist/settings-command.js +27 -21
  179. package/dist/settings-command.js.map +1 -1
  180. package/dist/settings-service.d.ts +7 -7
  181. package/dist/settings-service.d.ts.map +1 -1
  182. package/dist/settings-service.js +3 -3
  183. package/dist/settings-service.js.map +1 -1
  184. package/dist/setup-client-pty.d.ts +14 -0
  185. package/dist/setup-client-pty.d.ts.map +1 -0
  186. package/dist/setup-client-pty.js +61 -0
  187. package/dist/setup-client-pty.js.map +1 -0
  188. package/dist/setup-device-code-disclosure.d.ts +15 -0
  189. package/dist/setup-device-code-disclosure.d.ts.map +1 -0
  190. package/dist/setup-device-code-disclosure.js +52 -0
  191. package/dist/setup-device-code-disclosure.js.map +1 -0
  192. package/dist/setup-job-reducer.d.ts.map +1 -1
  193. package/dist/setup-job-reducer.js +1 -0
  194. package/dist/setup-job-reducer.js.map +1 -1
  195. package/dist/setup-job-store.d.ts +275 -75
  196. package/dist/setup-job-store.d.ts.map +1 -1
  197. package/dist/setup-job-store.js.map +1 -1
  198. package/dist/setup-job-support.js.map +1 -1
  199. package/dist/setup-jobs.d.ts +517 -137
  200. package/dist/setup-jobs.d.ts.map +1 -1
  201. package/dist/setup-jobs.js +68 -118
  202. package/dist/setup-jobs.js.map +1 -1
  203. package/dist/setup-lifecycle-binding.d.ts +7 -0
  204. package/dist/setup-lifecycle-binding.d.ts.map +1 -1
  205. package/dist/setup-lifecycle-binding.js +6 -0
  206. package/dist/setup-lifecycle-binding.js.map +1 -1
  207. package/dist/setup-login-child-lifecycle.d.ts +13 -0
  208. package/dist/setup-login-child-lifecycle.d.ts.map +1 -0
  209. package/dist/setup-login-child-lifecycle.js +45 -0
  210. package/dist/setup-login-child-lifecycle.js.map +1 -0
  211. package/dist/setup-login-inline.d.ts +11 -12
  212. package/dist/setup-login-inline.d.ts.map +1 -1
  213. package/dist/setup-login-inline.js +26 -15
  214. package/dist/setup-login-inline.js.map +1 -1
  215. package/dist/setup-login-permit.d.ts +10 -0
  216. package/dist/setup-login-permit.d.ts.map +1 -0
  217. package/dist/setup-login-permit.js +52 -0
  218. package/dist/setup-login-permit.js.map +1 -0
  219. package/dist/setup-login-protocol.d.ts.map +1 -1
  220. package/dist/setup-login-protocol.js +9 -2
  221. package/dist/setup-login-protocol.js.map +1 -1
  222. package/dist/setup-login-runner.d.ts +1 -0
  223. package/dist/setup-login-runner.d.ts.map +1 -1
  224. package/dist/setup-login-runner.js +20 -31
  225. package/dist/setup-login-runner.js.map +1 -1
  226. package/dist/setup-supervisor.d.ts.map +1 -1
  227. package/dist/setup-supervisor.js.map +1 -1
  228. package/dist/terminal-run-output.d.ts +12 -0
  229. package/dist/terminal-run-output.d.ts.map +1 -0
  230. package/dist/terminal-run-output.js +33 -0
  231. package/dist/terminal-run-output.js.map +1 -0
  232. package/dist/thread-continuity-context.d.ts +36 -0
  233. package/dist/thread-continuity-context.d.ts.map +1 -0
  234. package/dist/thread-continuity-context.js +33 -0
  235. package/dist/thread-continuity-context.js.map +1 -0
  236. package/dist/thread-execution-workspace.d.ts +15 -0
  237. package/dist/thread-execution-workspace.d.ts.map +1 -1
  238. package/dist/thread-execution-workspace.js +39 -5
  239. package/dist/thread-execution-workspace.js.map +1 -1
  240. package/dist/thread-turn-services.d.ts +107 -0
  241. package/dist/thread-turn-services.d.ts.map +1 -0
  242. package/dist/thread-turn-services.js +28 -0
  243. package/dist/thread-turn-services.js.map +1 -0
  244. package/package.json +23 -23
package/dist/cli.js CHANGED
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import process from "node:process";
3
- import { createReadStream, existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
4
- import { createHash } from "node:crypto";
5
- import { Readable, Transform } from "node:stream";
3
+ import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
6
4
  import { dirname, join, relative, resolve, sep } from "node:path";
7
5
  import { ArtifactStore } from "@claudexor/artifact-store";
8
6
  import { CLAUDEXOR_VERSION, noProjectRepoRoot, readTextSafe, userConfigDir } from "@claudexor/util";
@@ -10,13 +8,15 @@ import { releaseCommand } from "./release-command.js";
10
8
  import { serveBeltBridge, serveMcpBridge } from "./bridge-serve.js";
11
9
  import { dispatchAcpCommand } from "./acp-auth-command.js";
12
10
  import { initProjectConfig } from "@claudexor/config";
13
- import { DecisionRecord, EFFORT_HINT_HELP, EffortHint, ExternalContextPolicy, ModeKind as ModeKindSchema, RoutingGoal, ControlThreadListResponse, RunTelemetry, StructuredOutputConformance, TaskContract, runOutcomeLabel, } from "@claudexor/schema";
11
+ import { DecisionRecord, EFFORT_HINT_HELP, EffortHint, ExternalContextPolicy, ModeKind as ModeKindSchema, RoutingGoal, ControlThreadListResponse, RunFailure, RunTelemetry, StructuredOutputConformance, TaskContract, runOutcomeLabel, } from "@claudexor/schema";
14
12
  import { flagBool, flagStr, flagStringList, flagValues, parseArgs, requiredStringFlagError, } from "./args.js";
15
13
  import { print, printJson, printJsonLine, printUsageError, statusGlyph } from "./cli-io.js";
16
14
  import { controlProblemError, minIntError, renderCliFailure, usageError } from "./cli-error.js";
15
+ import { cliOutputMode, outputModeIsMachine, outputModeIsStream, renderOutputFailure, renderOutputUsageFailure, } from "./output-mode.js";
17
16
  import { handleHelpRequest } from "./command-help.js";
18
17
  import { pickResumableThread } from "./thread-select.js";
19
- import { KNOWN_FLAGS, VALUE_FLAGS, commandFlagScopeError, helpJson, renderHelp, } from "./command-registry.js";
18
+ import { KNOWN_FLAGS, VALUE_FLAGS, helpJson, renderHelp } from "./command-registry.js";
19
+ import { commandFlagScopeError, commandPositionalError, runModeFlagScopeError, } from "./command-scope.js";
20
20
  import { buildAgentCapabilityCatalog } from "./capabilities.js";
21
21
  import { aboutJson, renderAbout } from "./about-command.js";
22
22
  import { dispatchOpsCommand } from "./ops-commands.js";
@@ -24,18 +24,22 @@ import { reviewCommand } from "./review-command.js";
24
24
  import { controlApiFetch, followRun } from "./live.js";
25
25
  import { retryCommand, runAgainCommand } from "./retry-command.js";
26
26
  import { assertCliRunParamsHaveNoInlineSecrets } from "./run-secret-scan.js";
27
- import { openLocalAttachment, resolveLocalAttachment, } from "./local-attachment.js";
28
- import { connectDaemonIfRunning, daemonOutcomeProblemFields, daemonOutcomeSummary, ensureDaemon, enqueueAndAwait, exitCodeForState, fetchApplyEligibility, fetchCouncil, fetchRunDetail, fetchRunOutcomeFacts, projectOutcomeBanner, projectRunOutcomeFacts, mergeDaemonRunOutcome, } from "./daemon-run.js";
29
- import { inspectDelegationLines, projectDelegation, terminalDelegationLines, terminalDetailFields, } from "./delegation-output.js";
27
+ import { resolveLocalAttachment } from "./local-attachment.js";
28
+ import { uploadLocalAttachment } from "./attachment-upload.js";
29
+ import { connectDaemonIfRunning, daemonOutcomeSummary, ensureDaemon, enqueueAndAwait, exitCodeForState, fetchApplyEligibility, fetchCouncil, fetchRunDetail, fetchRunOutcomeFacts, projectOutcomeBanner, projectRunOutcomeFacts, mergeDaemonRunOutcome, } from "./daemon-run.js";
30
+ import { inspectDelegationLines, projectDelegation, terminalDelegationLines, } from "./delegation-output.js";
31
+ import { readRunFactsArtifact } from "./run-facts-projection.js";
32
+ import { projectTerminalRunOutput, terminalRunRequiredActionLines } from "./terminal-run-output.js";
30
33
  import { runPlanQuestionLoop } from "./plan-question-loop.js";
31
34
  import { resolveDecisionBody } from "./decision.js";
32
35
  import { primaryOutputForCli } from "./primary-output.js";
33
36
  import { PLUGIN_TARGETS, PLUGIN_VERBS, formatPluginResult, pluginCommandErrorResult, runPluginCommand, } from "./plugins.js";
34
- import { buildRegistry } from "./registry.js";
35
37
  import { settingsCommand } from "./settings-command.js";
36
38
  import { quotaCommand } from "./quota-command.js";
37
39
  import { trustCommand } from "./trust-command.js";
38
40
  import { projectCommand } from "./project-command.js";
41
+ import { remoteCommand, setupCommand } from "./remote-command.js";
42
+ import { harnessCommand } from "./harness-command.js";
39
43
  import { runRepl } from "./repl.js";
40
44
  import { parseProtectedPathApprovalFlags, parseTestCommandFlags, parseReviewerEffortFlags, parseReviewerModelFlags, parseReviewerPanelFlags, } from "./run-options.js";
41
45
  const CLI_VERSION = CLAUDEXOR_VERSION;
@@ -160,55 +164,6 @@ function attachmentInputs(args) {
160
164
  const out = attachmentPaths(args).map(({ path, forceImage }) => resolveLocalAttachment(path, forceImage));
161
165
  return out.length > 0 ? out : undefined;
162
166
  }
163
- async function uploadLocalAttachment(addr, attachment) {
164
- const created = (await controlJson(addr, "/uploads", {
165
- method: "POST",
166
- headers: { "content-type": "application/json" },
167
- body: JSON.stringify({
168
- kind: attachment.kind,
169
- mime: attachment.mime,
170
- name: attachment.name,
171
- sizeBytes: attachment.sizeBytes,
172
- }),
173
- }));
174
- try {
175
- const source = createReadStream(attachment.path, {
176
- fd: openLocalAttachment(attachment),
177
- autoClose: true,
178
- });
179
- const hash = createHash("sha256");
180
- const hashingStream = new Transform({
181
- transform(chunk, _encoding, callback) {
182
- hash.update(chunk);
183
- callback(null, chunk);
184
- },
185
- });
186
- source.pipe(hashingStream);
187
- const response = await controlApiFetch(addr, `/uploads/${encodeURIComponent(created.uploadId)}/bytes`, {
188
- method: "PUT",
189
- headers: { "content-type": "application/octet-stream" },
190
- body: Readable.toWeb(hashingStream),
191
- duplex: "half",
192
- });
193
- if (!response.ok) {
194
- const detail = (await response.json().catch(() => ({})));
195
- throw new Error(String(detail["message"] ?? detail["error"] ?? `HTTP ${response.status}`));
196
- }
197
- const expectedSha256 = `sha256:${hash.digest("hex")}`;
198
- const resource = (await controlJson(addr, `/uploads/${encodeURIComponent(created.uploadId)}/finalize`, {
199
- method: "POST",
200
- headers: { "content-type": "application/json" },
201
- body: JSON.stringify({ expectedSha256 }),
202
- }));
203
- return { resourceId: resource.resourceId };
204
- }
205
- catch (error) {
206
- await controlApiFetch(addr, `/uploads/${encodeURIComponent(created.uploadId)}`, {
207
- method: "DELETE",
208
- }).catch(() => undefined);
209
- throw error;
210
- }
211
- }
212
167
  /** Per-family reviewer model map from `--reviewer-model "openai=gpt-4o-mini,anthropic=claude-haiku"`. Fails loudly on malformed input. */
213
168
  function reviewerModels(args) {
214
169
  return parseReviewerModelFlags(flagValues(args, "reviewer-model"));
@@ -221,7 +176,7 @@ function reviewerEfforts(args) {
221
176
  function reviewerPanel(args) {
222
177
  return parseReviewerPanelFlags(flagValues(args, "reviewer-panel"));
223
178
  }
224
- async function orchestrate(args, mode, json, forced = {}) {
179
+ async function orchestrate(args, mode, outputMode, forced = {}) {
225
180
  let rawPrompt = args._.slice(1).join(" ").trim();
226
181
  // Headless prompt sources (W13): `-` reads the prompt from stdin; a file
227
182
  // beats retyping. Exactly ONE source — ambiguity is a usage error, never a
@@ -229,34 +184,34 @@ async function orchestrate(args, mode, json, forced = {}) {
229
184
  const promptFile = flagStr(args, "prompt-file");
230
185
  if (promptFile !== undefined) {
231
186
  if (rawPrompt && rawPrompt !== "-") {
232
- return printUsageError(json, "claudexor: pass either an inline prompt or --prompt-file, not both");
187
+ return renderOutputUsageFailure(outputMode, "claudexor: pass either an inline prompt or --prompt-file, not both");
233
188
  }
234
189
  try {
235
190
  rawPrompt = readFileSync(promptFile, "utf8").trim();
236
191
  }
237
192
  catch (err) {
238
- return printUsageError(json, `claudexor: --prompt-file: cannot read ${promptFile}: ${err instanceof Error ? err.message : String(err)}`);
193
+ return renderOutputUsageFailure(outputMode, `claudexor: --prompt-file: cannot read ${promptFile}: ${err instanceof Error ? err.message : String(err)}`);
239
194
  }
240
195
  }
241
196
  else if (rawPrompt === "-") {
242
197
  rawPrompt = readFileSync(0, "utf8").trim();
243
198
  if (!rawPrompt) {
244
- return printUsageError(json, "claudexor: stdin prompt (`-`) was empty");
199
+ return renderOutputUsageFailure(outputMode, "claudexor: stdin prompt (`-`) was empty");
245
200
  }
246
201
  }
247
202
  const prompt = rawPrompt;
248
203
  if (!prompt) {
249
- return printUsageError(json, "claudexor: missing prompt");
204
+ return renderOutputUsageFailure(outputMode, "claudexor: missing prompt");
250
205
  }
251
206
  const portfolioRaw = flagStr(args, "portfolio");
252
207
  if (portfolioRaw !== undefined) {
253
- return printUsageError(json, "claudexor: --portfolio was removed in v2; use --routing-goal auto|quality|economy");
208
+ return renderOutputUsageFailure(outputMode, "claudexor: --portfolio was removed in v2; use --routing-goal auto|quality|economy");
254
209
  }
255
210
  const credentialProfileId = flagStr(args, "profile");
256
211
  const routingGoalRaw = flagStr(args, "routing-goal");
257
212
  const routingGoal = routingGoalRaw !== undefined ? RoutingGoal.safeParse(routingGoalRaw) : null;
258
213
  if (routingGoalRaw !== undefined && !routingGoal?.success) {
259
- return printUsageError(json, `claudexor: unknown --routing-goal '${routingGoalRaw}'`);
214
+ return renderOutputUsageFailure(outputMode, `claudexor: unknown --routing-goal '${routingGoalRaw}'`);
260
215
  }
261
216
  let reviewerEffortOverrides;
262
217
  let resolvedReviewerModels;
@@ -333,7 +288,10 @@ async function orchestrate(args, mode, json, forced = {}) {
333
288
  }
334
289
  catch (err) {
335
290
  // Projector: typed field errors / domain codes survive; a plain flag-parse Error is usage (exit 2).
336
- return renderCliFailure(json, err, { defaultCategory: "usage", messagePrefix: "claudexor:" });
291
+ return renderOutputFailure(outputMode, err, {
292
+ defaultCategory: "usage",
293
+ messagePrefix: "claudexor:",
294
+ });
337
295
  }
338
296
  let tests;
339
297
  try {
@@ -362,15 +320,18 @@ async function orchestrate(args, mode, json, forced = {}) {
362
320
  }
363
321
  catch (err) {
364
322
  // Preserves the typed `inline_secret_rejected` code (never echoing the token).
365
- return renderCliFailure(json, err, { defaultCategory: "usage", messagePrefix: "claudexor:" });
323
+ return renderOutputFailure(outputMode, err, {
324
+ defaultCategory: "usage",
325
+ messagePrefix: "claudexor:",
326
+ });
366
327
  }
367
328
  if (delegate && mode !== "agent") {
368
- return printUsageError(json, `claudexor: --delegate is an agent strategy (got mode '${mode}')`);
329
+ return renderOutputUsageFailure(outputMode, `claudexor: --delegate is an agent strategy (got mode '${mode}')`);
369
330
  }
370
331
  if (council && mode !== "plan") {
371
- return printUsageError(json, `claudexor: --council is a plan strategy (got mode '${mode}')`);
332
+ return renderOutputUsageFailure(outputMode, `claudexor: --council is a plan strategy (got mode '${mode}')`);
372
333
  }
373
- return daemonRun(args, json, {
334
+ return daemonRun(args, outputMode, {
374
335
  mode,
375
336
  delegate,
376
337
  council,
@@ -405,12 +366,10 @@ async function orchestrate(args, mode, json, forced = {}) {
405
366
  * All five product modes enter through the managed daemon and control API.
406
367
  * `--json` prints one stable `{ runId, runDir, status }` machine envelope.
407
368
  */
408
- async function daemonRun(args, json, p) {
369
+ async function daemonRun(args, outputMode, p) {
409
370
  const inPlace = flagBool(args, "in-place");
410
- const jsonStream = flagBool(args, "json-stream");
411
- if (json && jsonStream) {
412
- return printUsageError(json, "claudexor: --json prints exactly one object and --json-stream prints NDJSON; pass one, not both");
413
- }
371
+ const json = outputModeIsMachine(outputMode);
372
+ const jsonStream = outputModeIsStream(outputMode);
414
373
  let client;
415
374
  let addr;
416
375
  try {
@@ -504,6 +463,10 @@ async function daemonRun(args, json, p) {
504
463
  ...(p.reviewerModels ? { reviewerModels: p.reviewerModels } : {}),
505
464
  ...(p.reviewerEfforts ? { reviewerEfforts: p.reviewerEfforts } : {}),
506
465
  };
466
+ // The last run id this command bound, threaded into the failure envelope as
467
+ // a per-command extra: a run that DID start (or finish) must keep its handle
468
+ // even when a post-terminal read raises (e.g. 500 run_facts_invalid).
469
+ let terminalRunId = "";
507
470
  try {
508
471
  if (jsonStream) {
509
472
  // NDJSON machine surface (W13): an EARLY runId frame first, every run
@@ -513,18 +476,10 @@ async function daemonRun(args, json, p) {
513
476
  const started = await enqueueAndAwait(client, addr, body, { waitForTerminal: false });
514
477
  if (!started.runId) {
515
478
  // Even the early-failure path stays valid NDJSON (compact, one line).
516
- printJsonLine({
517
- frame: "run.terminal",
518
- runId: "",
519
- runDir: started.runDir,
520
- status: started.status,
521
- jobId: started.jobId,
522
- mode: p.mode,
523
- ...(started.error ? { error: started.error } : {}),
524
- ...daemonOutcomeProblemFields(started),
525
- });
479
+ printJsonLine(projectTerminalRunOutput(started, p.mode, null, { frame: "run.terminal" }));
526
480
  return exitCodeForState(started.status);
527
481
  }
482
+ terminalRunId = started.runId;
528
483
  printJsonLine({
529
484
  frame: "run.started",
530
485
  runId: started.runId,
@@ -538,43 +493,31 @@ async function daemonRun(args, json, p) {
538
493
  const final = started.jobId ? await client.status(started.jobId) : null;
539
494
  const out = mergeDaemonRunOutcome(started, final);
540
495
  const status = out.status;
541
- const reason = daemonOutcomeSummary({ ...started, status, error: out.error });
542
496
  const detail = await fetchRunDetail(addr, out.runId);
543
- printJsonLine({
497
+ const facts = projectRunOutcomeFacts(detail);
498
+ const reason = daemonOutcomeSummary({ ...out, outcomeFacts: facts ?? undefined });
499
+ printJsonLine(projectTerminalRunOutput(out, p.mode, detail, {
544
500
  frame: "run.terminal",
545
- runId: out.runId,
546
- runDir: out.runDir,
547
- status: out.status,
548
- jobId: out.jobId,
549
- mode: p.mode,
550
- ...(out.error ? { error: out.error } : {}),
551
- ...daemonOutcomeProblemFields(out),
552
- ...(reason ? { summary: reason } : {}),
553
- ...terminalDetailFields(detail),
554
- });
501
+ summary: reason,
502
+ }));
555
503
  return exitCodeForState(status, projectRunOutcomeFacts(detail));
556
504
  }
557
505
  if (json) {
558
506
  // Pure machine surface: await the terminal outcome and print one JSON object.
559
507
  const out = await enqueueAndAwait(client, addr, body, { waitForTerminal: true });
560
- // Preserve bench keys while adding mode and honest non-success detail.
561
- const reason = daemonOutcomeSummary(out);
508
+ terminalRunId = out.runId;
562
509
  // ADD-ONLY key (bench contract keeps {runId,runDir,status}): the derived
563
510
  // apply-gate verdict, so machine callers act on truth instead of
564
511
  // re-implying eligibility from status. ONE GET /runs/:id feeds all three
565
512
  // terminal projections (INV-120/122).
566
513
  const detail = await fetchRunDetail(addr, out.runId);
567
- printJson({
568
- runId: out.runId,
569
- runDir: out.runDir,
570
- status: out.status,
571
- jobId: out.jobId,
572
- mode: p.mode,
573
- ...(out.error ? { error: out.error } : {}),
574
- ...daemonOutcomeProblemFields(out),
575
- ...(reason ? { summary: reason } : {}),
576
- ...terminalDetailFields(detail),
514
+ // Preserve bench keys while deriving the human summary from the same
515
+ // canonical receipt that feeds the machine terminal fields.
516
+ const reason = daemonOutcomeSummary({
517
+ ...out,
518
+ outcomeFacts: projectRunOutcomeFacts(detail) ?? undefined,
577
519
  });
520
+ printJson(projectTerminalRunOutput(out, p.mode, detail, { summary: reason }));
578
521
  return exitCodeForState(out.status, projectRunOutcomeFacts(detail));
579
522
  }
580
523
  // Text mode: enqueue, then live-stream the run through the shared follow
@@ -583,11 +526,15 @@ async function daemonRun(args, json, p) {
583
526
  const started = await enqueueAndAwait(client, addr, body, { waitForTerminal: false });
584
527
  if (!started.runId) {
585
528
  print(`run did not start: ${started.status}${started.error ? ` — ${started.error}` : ""}`);
529
+ for (const line of terminalRunRequiredActionLines(started))
530
+ print(line);
586
531
  return exitCodeForState(started.status);
587
532
  }
533
+ terminalRunId = started.runId;
588
534
  await followRun(started.runId, false);
589
535
  const final = started.jobId ? await client.status(started.jobId) : null;
590
- const status = final?.state ?? started.status;
536
+ const out = mergeDaemonRunOutcome(started, final);
537
+ const status = out.status;
591
538
  const publicStatus = status;
592
539
  print("");
593
540
  print(`run ${started.runId} [${publicStatus}]`);
@@ -598,7 +545,11 @@ async function daemonRun(args, json, p) {
598
545
  print(` ${terminalBanner}`);
599
546
  for (const line of terminalDelegationLines(projectDelegation(terminalDetail)))
600
547
  print(line);
601
- print(` artifacts: ${final?.runDir ?? started.runDir}`);
548
+ print(` artifacts: ${out.runDir}`);
549
+ if (out.error)
550
+ print(` error: ${out.error}`);
551
+ for (const line of terminalRunRequiredActionLines(out))
552
+ print(line);
602
553
  // A succeeded lifecycle exits 0 — INCLUDING a "Done · needs review" run
603
554
  // (review blocked / checks failed). The apply-eligibility verdict (state
604
555
  // needs_review) is the ONE source for the unblock guidance (D8).
@@ -626,6 +577,7 @@ async function daemonRun(args, json, p) {
626
577
  runId: started.runId,
627
578
  interactive: !json && !jsonStream, // TTY plan turns answer inline
628
579
  });
580
+ terminalRunId = effectiveRunId;
629
581
  }
630
582
  // Offer apply only after a positive gate; otherwise print inspect/unblock guidance.
631
583
  const eligibility = await fetchApplyEligibility(addr, effectiveRunId);
@@ -647,9 +599,11 @@ async function daemonRun(args, json, p) {
647
599
  catch (err) {
648
600
  // D-7 projector: a typed control problem (code/retryable/context) survives
649
601
  // intact; --json-stream stays valid NDJSON via the compact-line stream option.
602
+ // A run that already started keeps its handle in the envelope (extras).
650
603
  return renderCliFailure(json || jsonStream, err, {
651
604
  messagePrefix: "claudexor:",
652
605
  stream: jsonStream,
606
+ ...(terminalRunId ? { extras: { runId: terminalRunId } } : {}),
653
607
  });
654
608
  }
655
609
  }
@@ -758,21 +712,12 @@ async function resolveRunStore(runId) {
758
712
  }
759
713
  return null;
760
714
  }
761
- async function controlJson(addr, path, init = {}) {
762
- const response = await controlApiFetch(addr, path, init);
763
- const text = await response.text();
764
- const body = text ? JSON.parse(text) : {};
765
- if (response.ok)
766
- return body;
767
- const detail = body && typeof body === "object" ? body : {};
768
- throw new Error(String(detail["message"] ?? detail["error"] ?? `HTTP ${response.status}`));
769
- }
770
- function printPreflightError(args, json, error) {
771
- if (json && (args._[0] ?? "help") === "plugin") {
715
+ function printPreflightError(args, outputMode, error) {
716
+ if (outputMode === "json" && (args._[0] ?? "help") === "plugin") {
772
717
  printJson(pluginCommandErrorResult(args._[1], args._[2], flagBool(args, "dry-run"), 2, error));
773
718
  return 2;
774
719
  }
775
- return printUsageError(json, error);
720
+ return renderOutputUsageFailure(outputMode, error);
776
721
  }
777
722
  function listCliArtifacts(root) {
778
723
  if (!existsSync(root))
@@ -794,8 +739,12 @@ function listCliArtifacts(root) {
794
739
  // KNOWN_FLAGS / VALUE_FLAGS (imported above) and the per-command scope check
795
740
  // are projections of the command registry. Unknown flags FAIL LOUDLY: `--harnes
796
741
  // codex` must never silently run all harnesses.
797
- async function dispatch(args, json) {
742
+ async function dispatch(args, outputMode) {
798
743
  const cmd = args._[0] ?? "help";
744
+ if (outputMode === "conflict") {
745
+ return renderOutputUsageFailure(outputMode, "claudexor: --json prints exactly one object and --json-stream prints NDJSON; pass one, not both");
746
+ }
747
+ const json = outputModeIsMachine(outputMode);
799
748
  // `--help` resolves the COMMAND first (QA-057): scoped usage for a known verb,
800
749
  // global help for a bare/`help` verb, a usage error (exit 2) for a typo.
801
750
  if (flagBool(args, "help"))
@@ -803,18 +752,21 @@ async function dispatch(args, json) {
803
752
  const unknownFlags = Object.keys(args.flags).filter((f) => !KNOWN_FLAGS.has(f));
804
753
  if (unknownFlags.length > 0) {
805
754
  const error = `claudexor: unknown flag(s): ${unknownFlags.map((f) => `--${f}`).join(", ")} (see \`claudexor help\`)`;
806
- return printPreflightError(args, json, error);
755
+ return printPreflightError(args, outputMode, error);
807
756
  }
808
757
  const valueFlagError = requiredStringFlagError(args, VALUE_FLAGS);
809
758
  if (valueFlagError)
810
- return printPreflightError(args, json, valueFlagError);
759
+ return printPreflightError(args, outputMode, valueFlagError);
811
760
  // Registry-enforced per-command flag scope: a KNOWN flag outside the
812
761
  // command's declared set (e.g. `plan --create`, `ask --force`) fails loudly
813
762
  // instead of being silently ignored. Data-driven from CLI_COMMANDS for
814
763
  // every verb (this replaced the old hand-listed plugin/--force special cases).
815
764
  const scopeError = commandFlagScopeError(cmd, Object.keys(args.flags));
816
765
  if (scopeError)
817
- return printPreflightError(args, json, scopeError);
766
+ return printPreflightError(args, outputMode, scopeError);
767
+ const positionalError = commandPositionalError(cmd, args._.slice(1));
768
+ if (positionalError)
769
+ return printPreflightError(args, outputMode, positionalError);
818
770
  // No arguments at all = the interactive REPL: a thread of turns over the
819
771
  // current project with native session continuity (chat is the normal loop).
820
772
  if (args._.length === 0 && process.stdin.isTTY) {
@@ -835,21 +787,28 @@ async function dispatch(args, json) {
835
787
  }
836
788
  case "project":
837
789
  return projectCommand(args, json);
790
+ case "remote":
791
+ return remoteCommand(args, json);
792
+ case "setup":
793
+ return setupCommand(args, json);
838
794
  case "agent": {
839
795
  const modeStr = flagStr(args, "mode");
840
796
  if (modeStr !== undefined) {
841
797
  const mode = normalizeMode(modeStr);
842
798
  if (!MODES.has(mode)) {
843
- return printUsageError(json, `claudexor: unknown --mode '${modeStr}'. valid: ${[...MODES].join(", ")}`);
799
+ return renderOutputUsageFailure(outputMode, `claudexor: unknown --mode '${modeStr}'. valid: ${[...MODES].join(", ")}`);
844
800
  }
845
- return orchestrate(args, mode, json);
801
+ const modeScopeError = runModeFlagScopeError(mode, Object.keys(args.flags));
802
+ if (modeScopeError)
803
+ return printPreflightError(args, outputMode, modeScopeError);
804
+ return orchestrate(args, mode, outputMode);
846
805
  }
847
- return orchestrate(args, "agent", json);
806
+ return orchestrate(args, "agent", outputMode);
848
807
  }
849
808
  case "ask":
850
- return orchestrate(args, "ask", json);
809
+ return orchestrate(args, "ask", outputMode);
851
810
  case "best-of":
852
- return orchestrate(args, "agent", json, { race: true });
811
+ return orchestrate(args, "agent", outputMode, { race: true });
853
812
  // RETIRED verb spellings hard-error with the new spelling — no silent
854
813
  // aliases (same doctrine as retired mode ids: stale scripts fail loudly).
855
814
  case "run":
@@ -868,12 +827,12 @@ async function dispatch(args, json) {
868
827
  : cmd === "spec"
869
828
  ? "claudexor plan <prompt> then Implement (the plan lifecycle surfaces open questions and freezes the plan on implement)"
870
829
  : "claudexor ask --deep-scan <prompt>";
871
- return printPreflightError(args, json, `claudexor: the '${cmd}' verb was retired; use ${replacement}`);
830
+ return printPreflightError(args, outputMode, `claudexor: the '${cmd}' verb was retired; use ${replacement}`);
872
831
  }
873
832
  case "plan":
874
- return orchestrate(args, "plan", json);
833
+ return orchestrate(args, "plan", outputMode);
875
834
  case "create":
876
- return orchestrate(args, "agent", json, { create: true });
835
+ return orchestrate(args, "agent", outputMode, { create: true });
877
836
  case "settings":
878
837
  return settingsCommand(args, json);
879
838
  case "quota":
@@ -922,12 +881,23 @@ async function dispatch(args, json) {
922
881
  const decision = store.readYaml(join(paths.arbitrationDir, "decision.yaml"));
923
882
  const workProduct = store.readYaml(join(paths.finalDir, "work_product.yaml"));
924
883
  const contract = TaskContract.safeParse(store.readYaml(join(paths.contextDir, "task.yaml")));
925
- const primary = primaryOutputForCli(paths.root, contract.success ? contract.data.mode.kind : undefined);
884
+ const parsedFailure = RunFailure.safeParse(store.readYaml(join(paths.finalDir, "failure.yaml")));
885
+ const failure = parsedFailure.success ? parsedFailure.data : null;
926
886
  // The CLI projects the orchestrator-owned telemetry artifact and NEVER
927
887
  // recomputes evidence from raw events (single-owner rule); a missing
928
888
  // artifact (legacy run) renders "telemetry unavailable".
929
889
  const parsedTelemetry = RunTelemetry.safeParse(store.readYaml(join(paths.finalDir, "telemetry.yaml")));
930
890
  const telemetry = parsedTelemetry.success ? parsedTelemetry.data : null;
891
+ // GH #29: inspect reads the same immutable receipt as GET and terminal JSON.
892
+ const runFacts = readRunFactsArtifact(store, paths.finalDir, {
893
+ runId,
894
+ ...(contract.success ? { taskId: contract.data.task_id } : {}),
895
+ });
896
+ const primary = primaryOutputForCli(paths.root, contract.success ? contract.data.mode.kind : undefined, {
897
+ failure,
898
+ lifecycle: runFacts?.outcome.lifecycle,
899
+ ...(runFacts?.presentation ? { presentation: runFacts.presentation } : {}),
900
+ });
931
901
  const toolErrors = telemetry
932
902
  ? telemetry.attempts.flatMap((a) => a.tool_errors
933
903
  .filter((e) => !e.recovered && e.kind === "web")
@@ -949,22 +919,26 @@ async function dispatch(args, json) {
949
919
  })))
950
920
  : [];
951
921
  const artifacts = listCliArtifacts(paths.root).filter((p) => !p.endsWith("/"));
952
- const outputReadyState = primary?.kind === "diagnostic"
953
- ? "diagnostic"
954
- : primary?.text.trim()
955
- ? "ready"
956
- : readTextSafe(join(paths.finalDir, "failure.yaml"))
957
- ? "diagnostic"
958
- : "finalizing";
922
+ const outputReadyState = runFacts?.presentation?.state ??
923
+ (primary?.kind === "diagnostic"
924
+ ? "diagnostic"
925
+ : primary?.text.trim()
926
+ ? "ready"
927
+ : (failure ?? readTextSafe(join(paths.finalDir, "failure.yaml")))
928
+ ? "diagnostic"
929
+ : "finalizing");
959
930
  const parsedDecision = DecisionRecord.safeParse(decision);
960
931
  const summary = readTextSafe(join(paths.finalDir, "summary.md"));
961
932
  if (json) {
962
933
  printJson({
963
934
  runId,
964
935
  runDir: paths.root,
936
+ lifecycle: runFacts?.outcome.lifecycle ?? null,
965
937
  outputReadyState,
966
938
  contract: contract.success ? contract.data : null,
967
939
  telemetry,
940
+ runFacts,
941
+ failure,
968
942
  toolErrors,
969
943
  toolWarnings,
970
944
  primaryOutput: primary,
@@ -976,6 +950,8 @@ async function dispatch(args, json) {
976
950
  return summary || primary ? 0 : 1;
977
951
  }
978
952
  print(`run ${runId} @ ${paths.root}`);
953
+ if (runFacts)
954
+ print(`lifecycle: ${runFacts.outcome.lifecycle}`);
979
955
  if (contract.success) {
980
956
  print(`mode: ${contract.data.mode.kind}`);
981
957
  print(`access: requested=${contract.data.access.requested_profile} effective=${contract.data.access.effective_profile}`);
@@ -992,6 +968,12 @@ async function dispatch(args, json) {
992
968
  print(`web: policy=${contract.data.external_context.policy} required=${contract.data.external_context.web_required} evidence=unavailable (no telemetry.yaml)`);
993
969
  }
994
970
  print(`output: ${outputReadyState}${primary ? ` ${primary.path}` : ""}`);
971
+ if (failure) {
972
+ print(`failure: ${failure.category}${failure.code ? `/${failure.code}` : ""} phase=${failure.phase}${failure.harnessId ? ` harness=${failure.harnessId}` : ""}`);
973
+ print(`failure message: ${failure.safeMessage}`);
974
+ for (const action of failure.nextActions)
975
+ print(`next action: ${action}`);
976
+ }
995
977
  {
996
978
  // Structured-output contract receipt (only present when the run was
997
979
  // started with --output-schema); projected, never re-validated here.
@@ -1148,20 +1130,8 @@ async function dispatch(args, json) {
1148
1130
  throw err;
1149
1131
  }
1150
1132
  }
1151
- case "harness": {
1152
- const sub = args._[1];
1153
- if (sub === "list") {
1154
- // Fakes are test fixtures, not real harnesses; `--all` reveals them.
1155
- const includeFakes = flagBool(args, "all");
1156
- const ids = [...buildRegistry({ includeFakes }).keys()];
1157
- if (json)
1158
- printJson({ harnesses: ids });
1159
- else
1160
- ids.forEach((id) => print(id));
1161
- return 0;
1162
- }
1163
- return printUsageError(json, "usage: claudexor harness list [--all]");
1164
- }
1133
+ case "harness":
1134
+ return harnessCommand(args, json);
1165
1135
  case "capabilities": {
1166
1136
  // The derived AgentCapabilityCatalog — same composer as the daemon's
1167
1137
  // GET /agent-capabilities and the MCP claudexor_capabilities tool.
@@ -1171,6 +1141,7 @@ async function dispatch(args, json) {
1171
1141
  else {
1172
1142
  print(`claudexor ${catalog.version} — capability catalog`);
1173
1143
  print(`modes: ${catalog.modes.join(", ")}`);
1144
+ print(`git: ${catalog.git.status}${catalog.git.version ? ` (${catalog.git.version})` : ""}${catalog.git.remediation ? ` — ${catalog.git.remediation}` : ""}`);
1174
1145
  print(`available harnesses: ${catalog.availableHarnesses.join(", ") || "(none)"}`);
1175
1146
  for (const h of catalog.harnesses) {
1176
1147
  const model = h.configuredModel
@@ -1207,7 +1178,7 @@ async function dispatch(args, json) {
1207
1178
  // callers get the ONE projector envelope (with message/code shape, no
1208
1179
  // longer a partial {ok,exitCode,error}); text mode prints the full help.
1209
1180
  if (json) {
1210
- return renderCliFailure(true, usageError(`claudexor: unknown command '${cmd}' (see \`claudexor help --json\`)`), {});
1181
+ return renderOutputFailure(outputMode, usageError(`claudexor: unknown command '${cmd}' (see \`claudexor help --json\`)`), {});
1211
1182
  }
1212
1183
  process.stderr.write(`claudexor: unknown command '${cmd}'\n\n${HELP}\n`);
1213
1184
  return 2;
@@ -1223,18 +1194,27 @@ async function main() {
1223
1194
  process.stdout.write(`${CLI_VERSION}\n`);
1224
1195
  return 0;
1225
1196
  }
1226
- const json = flagBool(args, "json");
1197
+ const outputMode = cliOutputMode(args);
1227
1198
  try {
1228
- return await dispatch(args, json);
1199
+ return await dispatch(args, outputMode);
1229
1200
  }
1230
1201
  catch (err) {
1231
- return renderCliFailure(json, err);
1202
+ return renderOutputFailure(outputMode, err);
1232
1203
  }
1233
1204
  }
1234
1205
  main()
1235
1206
  .then((code) => process.exit(code))
1236
1207
  .catch((err) => {
1237
- // Last-resort projector: still emit ONE envelope if the parse threw before json was known.
1238
- process.exit(renderCliFailure(process.argv.includes("--json"), err));
1208
+ // Last-resort projector: infer the complete mode even if parsing itself threw.
1209
+ const json = process.argv.includes("--json");
1210
+ const stream = process.argv.includes("--json-stream");
1211
+ const outputMode = json
1212
+ ? stream
1213
+ ? "conflict"
1214
+ : "json"
1215
+ : stream
1216
+ ? "json-stream"
1217
+ : "human";
1218
+ process.exit(renderOutputFailure(outputMode, err));
1239
1219
  });
1240
1220
  //# sourceMappingURL=cli.js.map