@astrosheep/keiyaku 4.0.0 → 4.0.2

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 (308) hide show
  1. package/README.md +133 -8
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
  12. package/build/src/akuma/abort.d.ts +2 -0
  13. package/build/src/akuma/abort.js +50 -0
  14. package/build/src/akuma/akuma.d.ts +50 -33
  15. package/build/src/akuma/akuma.js +175 -127
  16. package/build/src/akuma/archetype.d.ts +14 -5
  17. package/build/src/akuma/archetype.js +85 -39
  18. package/build/src/akuma/body.d.ts +3 -5
  19. package/build/src/akuma/body.js +490 -183
  20. package/build/src/akuma/coordinate.d.ts +9 -0
  21. package/build/src/akuma/coordinate.js +22 -0
  22. package/build/src/akuma/heart/facts.d.ts +88 -45
  23. package/build/src/akuma/heart/facts.js +19 -8
  24. package/build/src/akuma/heart/index.d.ts +64 -55
  25. package/build/src/akuma/heart/index.js +172 -252
  26. package/build/src/akuma/heart/rows.d.ts +38 -83
  27. package/build/src/akuma/heart/rows.js +117 -155
  28. package/build/src/akuma/heart/schema.d.ts +1 -1
  29. package/build/src/akuma/heart/schema.js +61 -22
  30. package/build/src/akuma/heart/soul.d.ts +8 -0
  31. package/build/src/akuma/heart/soul.js +136 -0
  32. package/build/src/akuma/heart/storage.d.ts +47 -0
  33. package/build/src/akuma/heart/storage.js +224 -0
  34. package/build/src/akuma/heart/tells.d.ts +12 -0
  35. package/build/src/akuma/heart/tells.js +79 -0
  36. package/build/src/akuma/heart/timeline.d.ts +15 -0
  37. package/build/src/akuma/heart/timeline.js +86 -0
  38. package/build/src/akuma/identity.d.ts +2 -2
  39. package/build/src/akuma/identity.js +9 -9
  40. package/build/src/akuma/index.d.ts +10 -3
  41. package/build/src/akuma/index.js +7 -1
  42. package/build/src/akuma/projection.d.ts +176 -0
  43. package/build/src/akuma/projection.js +256 -0
  44. package/build/src/akuma/provider-recipe.d.ts +24 -0
  45. package/build/src/akuma/provider-recipe.js +100 -0
  46. package/build/src/akuma/provider.d.ts +67 -13
  47. package/build/src/akuma/provider.js +172 -105
  48. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  49. package/build/src/akuma/providers/acp/core.js +159 -0
  50. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  51. package/build/src/akuma/providers/acp/events.js +73 -0
  52. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  53. package/build/src/akuma/providers/acp/index.js +94 -0
  54. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  55. package/build/src/akuma/providers/claude/events.js +238 -0
  56. package/build/src/akuma/providers/claude/index.d.ts +47 -0
  57. package/build/src/akuma/providers/claude/index.js +279 -0
  58. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  59. package/build/src/akuma/providers/claude/input.js +109 -0
  60. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  61. package/build/src/akuma/providers/codex-app-server/events.js +15 -12
  62. package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
  63. package/build/src/akuma/providers/codex-app-server/index.js +77 -33
  64. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  65. package/build/src/akuma/providers/grok-build/index.js +70 -0
  66. package/build/src/akuma/providers/index.d.ts +5 -3
  67. package/build/src/akuma/providers/index.js +35 -56
  68. package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
  69. package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
  70. package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
  71. package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
  72. package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
  73. package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
  74. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  75. package/build/src/akuma/providers/pi/events.js +166 -0
  76. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  77. package/build/src/akuma/providers/pi/index.js +191 -0
  78. package/build/src/akuma/publication.d.ts +3 -3
  79. package/build/src/akuma/publication.js +28 -40
  80. package/build/src/akuma/requests.d.ts +9 -6
  81. package/build/src/akuma/requests.js +95 -65
  82. package/build/src/alias/index.d.ts +4 -3
  83. package/build/src/alias/index.js +12 -13
  84. package/build/src/body/decode.js +7 -1
  85. package/build/src/body/region.d.ts +2 -0
  86. package/build/src/body/region.js +77 -7
  87. package/build/src/body/render.js +2 -1
  88. package/build/src/body/shape.js +7 -1
  89. package/build/src/body/verification.js +19 -4
  90. package/build/src/cli/accepted.d.ts +18 -12
  91. package/build/src/cli/accepted.js +73 -34
  92. package/build/src/cli/actor.js +3 -3
  93. package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
  94. package/build/src/cli/commands/akuma-invoke.js +101 -52
  95. package/build/src/cli/commands/akuma.d.ts +27 -9
  96. package/build/src/cli/commands/akuma.js +151 -80
  97. package/build/src/cli/commands/contract.d.ts +155 -0
  98. package/build/src/cli/commands/contract.js +20 -0
  99. package/build/src/cli/commands/install.js +2 -0
  100. package/build/src/cli/commands/task-invoke.d.ts +19 -4
  101. package/build/src/cli/commands/task-invoke.js +65 -17
  102. package/build/src/cli/commands/task-query.d.ts +5 -0
  103. package/build/src/cli/commands/task-query.js +247 -0
  104. package/build/src/cli/commands/task.d.ts +3 -1
  105. package/build/src/cli/commands/task.js +52 -14
  106. package/build/src/cli/coordinates.d.ts +18 -0
  107. package/build/src/cli/coordinates.js +120 -0
  108. package/build/src/cli/draft.d.ts +8 -0
  109. package/build/src/cli/draft.js +96 -0
  110. package/build/src/cli/invoke.d.ts +9 -3
  111. package/build/src/cli/invoke.js +287 -108
  112. package/build/src/cli/main.js +54 -7
  113. package/build/src/cli/parse.d.ts +21 -117
  114. package/build/src/cli/parse.js +79 -84
  115. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  116. package/build/src/cli/render/akuma-tool-command.js +144 -0
  117. package/build/src/cli/render/akuma-tool.d.ts +4 -2
  118. package/build/src/cli/render/akuma-tool.js +35 -5
  119. package/build/src/cli/render/akuma.d.ts +3 -1
  120. package/build/src/cli/render/akuma.js +247 -212
  121. package/build/src/cli/render/audit.d.ts +3 -0
  122. package/build/src/cli/render/audit.js +104 -0
  123. package/build/src/cli/render/catalog.d.ts +2 -0
  124. package/build/src/cli/render/catalog.js +16 -0
  125. package/build/src/cli/render/contract.d.ts +3 -3
  126. package/build/src/cli/render/contract.js +200 -54
  127. package/build/src/cli/render/kanshi.js +272 -129
  128. package/build/src/cli/render/receipt.d.ts +19 -0
  129. package/build/src/cli/render/receipt.js +106 -0
  130. package/build/src/cli/render/refusal.d.ts +16 -2
  131. package/build/src/cli/render/refusal.js +88 -3
  132. package/build/src/cli/render/region.d.ts +2 -0
  133. package/build/src/cli/render/region.js +17 -0
  134. package/build/src/cli/render/task.d.ts +2 -1
  135. package/build/src/cli/render/task.js +223 -60
  136. package/build/src/cli/render/terminal.d.ts +20 -0
  137. package/build/src/cli/render/terminal.js +121 -0
  138. package/build/src/cli/render/text.js +11 -3
  139. package/build/src/cli/result.d.ts +126 -19
  140. package/build/src/cli/usage.d.ts +1 -0
  141. package/build/src/cli/usage.js +3 -0
  142. package/build/src/contract-worktree.d.ts +44 -0
  143. package/build/src/contract-worktree.js +251 -0
  144. package/build/src/coordination/durable-file.d.ts +5 -1
  145. package/build/src/coordination/durable-file.js +57 -10
  146. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  147. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  148. package/build/src/core/facts/codec.js +23 -4
  149. package/build/src/core/facts/eligibility.d.ts +1 -10
  150. package/build/src/core/facts/eligibility.js +0 -49
  151. package/build/src/core/facts/fold.js +1 -6
  152. package/build/src/core/facts/gate.d.ts +1 -0
  153. package/build/src/core/facts/gate.js +1 -0
  154. package/build/src/core/facts/observation.d.ts +3 -0
  155. package/build/src/core/facts/observation.js +12 -0
  156. package/build/src/core/facts/types.d.ts +10 -3
  157. package/build/src/core/subject.js +2 -2
  158. package/build/src/core/verbs/amend.d.ts +1 -1
  159. package/build/src/core/verbs/amend.js +3 -18
  160. package/build/src/core/verbs/bind.js +2 -16
  161. package/build/src/core/verbs/deliver.d.ts +1 -1
  162. package/build/src/core/verbs/deliver.js +14 -4
  163. package/build/src/core/verbs/placement.d.ts +1 -1
  164. package/build/src/core/verbs/placement.js +7 -9
  165. package/build/src/dispatch/index.d.ts +5 -3
  166. package/build/src/dispatch/index.js +30 -21
  167. package/build/src/duration.d.ts +10 -0
  168. package/build/src/duration.js +22 -0
  169. package/build/src/git/admission.d.ts +2 -2
  170. package/build/src/git/admission.js +33 -23
  171. package/build/src/git/hooks.d.ts +15 -0
  172. package/build/src/git/hooks.js +70 -18
  173. package/build/src/git/identity.d.ts +5 -1
  174. package/build/src/git/identity.js +23 -4
  175. package/build/src/git/integration.d.ts +48 -0
  176. package/build/src/git/integration.js +250 -0
  177. package/build/src/git/observe.d.ts +29 -15
  178. package/build/src/git/observe.js +185 -76
  179. package/build/src/git/read-observation.d.ts +41 -0
  180. package/build/src/git/read-observation.js +361 -0
  181. package/build/src/git/reconcile.d.ts +17 -5
  182. package/build/src/git/reconcile.js +305 -109
  183. package/build/src/git/repository.d.ts +41 -18
  184. package/build/src/git/repository.js +220 -114
  185. package/build/src/git/scratch.d.ts +17 -0
  186. package/build/src/git/scratch.js +73 -0
  187. package/build/src/git/target-placement.d.ts +92 -0
  188. package/build/src/git/target-placement.js +375 -0
  189. package/build/src/git/tender.d.ts +49 -0
  190. package/build/src/git/tender.js +63 -0
  191. package/build/src/git/terminal-seal.d.ts +24 -0
  192. package/build/src/git/terminal-seal.js +89 -0
  193. package/build/src/git/tree.d.ts +2 -2
  194. package/build/src/git/tree.js +4 -0
  195. package/build/src/git/workspace.d.ts +53 -0
  196. package/build/src/git/workspace.js +139 -0
  197. package/build/src/identity/selector.d.ts +6 -0
  198. package/build/src/identity/selector.js +19 -0
  199. package/build/src/index.d.ts +4 -2
  200. package/build/src/index.js +2 -1
  201. package/build/src/kanshi/index.d.ts +2 -2
  202. package/build/src/kanshi/index.js +1 -1
  203. package/build/src/kanshi/read.d.ts +7 -3
  204. package/build/src/kanshi/read.js +245 -45
  205. package/build/src/kanshi/report.d.ts +81 -6
  206. package/build/src/kanshi/select.d.ts +5 -0
  207. package/build/src/kanshi/select.js +33 -1
  208. package/build/src/library/address.d.ts +57 -0
  209. package/build/src/library/address.js +165 -0
  210. package/build/src/library/akuma-creation.d.ts +16 -7
  211. package/build/src/library/akuma-creation.js +96 -22
  212. package/build/src/library/audit.d.ts +18 -0
  213. package/build/src/library/audit.js +44 -0
  214. package/build/src/library/bind.d.ts +21 -0
  215. package/build/src/library/bind.js +56 -0
  216. package/build/src/library/catalog.d.ts +58 -0
  217. package/build/src/library/catalog.js +69 -0
  218. package/build/src/library/configuration.d.ts +6 -15
  219. package/build/src/library/configuration.js +14 -17
  220. package/build/src/library/contract.d.ts +23 -49
  221. package/build/src/library/contract.js +171 -185
  222. package/build/src/library/delivery.d.ts +24 -0
  223. package/build/src/library/delivery.js +21 -0
  224. package/build/src/library/fleet.d.ts +65 -0
  225. package/build/src/library/fleet.js +153 -0
  226. package/build/src/library/input.d.ts +3 -0
  227. package/build/src/library/input.js +19 -0
  228. package/build/src/library/keiyaku.d.ts +18 -3
  229. package/build/src/library/keiyaku.js +12 -1
  230. package/build/src/library/mutation.d.ts +39 -0
  231. package/build/src/library/mutation.js +37 -0
  232. package/build/src/library/reconcile.d.ts +29 -0
  233. package/build/src/library/reconcile.js +179 -0
  234. package/build/src/library/refusal.d.ts +21 -0
  235. package/build/src/library/refusal.js +29 -0
  236. package/build/src/library/region.d.ts +14 -1
  237. package/build/src/library/region.js +18 -4
  238. package/build/src/library/repo.d.ts +4 -13
  239. package/build/src/library/repo.js +14 -24
  240. package/build/src/protocol/attempt.d.ts +16 -3
  241. package/build/src/protocol/attempt.js +41 -10
  242. package/build/src/protocol/bind.d.ts +9 -2
  243. package/build/src/protocol/bind.js +48 -40
  244. package/build/src/protocol/intent.d.ts +57 -20
  245. package/build/src/protocol/intent.js +55 -79
  246. package/build/src/protocol/operations.d.ts +152 -43
  247. package/build/src/protocol/operations.js +482 -166
  248. package/build/src/protocol/outcome.d.ts +14 -3
  249. package/build/src/protocol/outcome.js +11 -3
  250. package/build/src/protocol/placement.d.ts +41 -0
  251. package/build/src/protocol/placement.js +147 -0
  252. package/build/src/protocol/read/documents.d.ts +2 -2
  253. package/build/src/protocol/read/documents.js +4 -4
  254. package/build/src/protocol/read/status.d.ts +18 -5
  255. package/build/src/protocol/read/status.js +73 -13
  256. package/build/src/protocol/run.d.ts +49 -7
  257. package/build/src/protocol/run.js +53 -25
  258. package/build/src/runtime/proc/line-rpc.d.ts +4 -8
  259. package/build/src/runtime/proc/line-rpc.js +22 -28
  260. package/build/src/runtime/proc/run.d.ts +23 -21
  261. package/build/src/runtime/proc/run.js +123 -89
  262. package/build/src/runtime/proc/stdio.d.ts +18 -0
  263. package/build/src/runtime/proc/stdio.js +58 -0
  264. package/build/src/settings.d.ts +7 -1
  265. package/build/src/settings.js +32 -17
  266. package/build/src/settlement/fence.d.ts +5 -0
  267. package/build/src/settlement/fence.js +14 -0
  268. package/build/src/settlement/holder.d.ts +35 -4
  269. package/build/src/settlement/holder.js +127 -28
  270. package/build/src/settlement/settle.d.ts +9 -1
  271. package/build/src/settlement/settle.js +74 -37
  272. package/build/src/task/board.d.ts +23 -5
  273. package/build/src/task/board.js +17 -10
  274. package/build/src/task/compose.d.ts +2 -2
  275. package/build/src/task/compose.js +18 -10
  276. package/build/src/task/context.d.ts +3 -3
  277. package/build/src/task/context.js +15 -20
  278. package/build/src/task/document.d.ts +2 -1
  279. package/build/src/task/document.js +15 -5
  280. package/build/src/task/index.d.ts +27 -15
  281. package/build/src/task/index.js +57 -35
  282. package/build/src/task/operations.d.ts +38 -13
  283. package/build/src/task/operations.js +80 -46
  284. package/build/src/task/query.d.ts +67 -0
  285. package/build/src/task/query.js +279 -0
  286. package/build/src/task/store.d.ts +5 -7
  287. package/build/src/task/store.js +43 -28
  288. package/build/src/verification/declaration.d.ts +1 -0
  289. package/build/src/verification/execution.d.ts +51 -0
  290. package/build/src/verification/execution.js +108 -0
  291. package/build/src/workspace-place.d.ts +41 -0
  292. package/build/src/workspace-place.js +386 -0
  293. package/build/src/world.d.ts +24 -0
  294. package/build/src/world.js +136 -0
  295. package/package.json +8 -4
  296. package/build/src/akuma/activity.d.ts +0 -66
  297. package/build/src/akuma/activity.js +0 -139
  298. package/build/src/akuma/providers/claude.js +0 -305
  299. package/build/src/context-root.d.ts +0 -4
  300. package/build/src/context-root.js +0 -15
  301. package/build/src/git/delivery.d.ts +0 -20
  302. package/build/src/git/delivery.js +0 -109
  303. package/build/src/git/verification.d.ts +0 -14
  304. package/build/src/git/verification.js +0 -56
  305. package/build/src/protocol/read/audit.d.ts +0 -25
  306. package/build/src/protocol/read/audit.js +0 -39
  307. package/build/src/verification/producer.d.ts +0 -23
  308. package/build/src/verification/producer.js +0 -54
@@ -1,9 +1,7 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { gatesFrom, Keiyaku, NoGitWorldError, Repo, settings, SettingsError, worktreeHooksFrom } from "../index.js";
4
- import { kanshi, selectKanshi } from "../kanshi/index.js";
1
+ import { AkumaWorldScopeError, gatesFrom, Keiyaku, requireBranchesToBeUpToDateFrom, settings, SettingsError, worktreeHooksFrom } from "../index.js";
2
+ import { kanshi, selectKanshi, selectRegion } from "../kanshi/index.js";
5
3
  import { resolveActor } from "./actor.js";
6
- import { resultFromMutationCall } from "./accepted.js";
4
+ import { acceptedAbandon, acceptedAmend, acceptedArc, acceptedAudit, acceptedBind, acceptedDeliver, acceptedReview, resultFromMutationCall, } from "./accepted.js";
7
5
  import { amendFromCommand } from "./commands/amend.js";
8
6
  import { invokeAkuma, invokeAkumaStatus } from "./commands/akuma-invoke.js";
9
7
  import { isParsedAkumaCommand } from "./commands/akuma.js";
@@ -11,10 +9,53 @@ import { bindFromCommand } from "./commands/bind.js";
11
9
  import { installHarnesses } from "./commands/install.js";
12
10
  import { invokeTask } from "./commands/task-invoke.js";
13
11
  import { CliUsageError, renderCommandUsage } from "./parse.js";
12
+ import { isBlankInput } from "./usage.js";
14
13
  import { contractFromInput, resolveContextualContract, resolveKanshiContract } from "./selectors.js";
14
+ import { resolveNamedAddress } from "../library/address.js";
15
+ import { assertExplicitRepoUse, resolveCliCoordinates } from "./coordinates.js";
16
+ import { BindDraftError, preserveBindDraft } from "./draft.js";
17
+ async function readStdin() {
18
+ const chunks = [];
19
+ for await (const chunk of process.stdin)
20
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
21
+ return Buffer.concat(chunks).toString("utf8");
22
+ }
23
+ function selectedStdinDiagnostic(command) {
24
+ switch (command.command) {
25
+ case "bind":
26
+ case "amend":
27
+ case "arc":
28
+ return `${command.command} requires a nonblank stdin document`;
29
+ case "review":
30
+ return command.summaryFromStdin === true ? "review requires a nonblank summary" : undefined;
31
+ case "call":
32
+ case "tell":
33
+ return command.prompt.kind === "stdin" ? `${command.command} requires a nonblank prompt` : undefined;
34
+ case "task":
35
+ switch (command.stdin) {
36
+ case "document": return "task add requires a nonblank stdin document";
37
+ case "compose": return "task compose requires a nonblank stdin document";
38
+ case "body": return "task update --body requires a nonblank value";
39
+ case "append": return "task update --append requires a nonblank value";
40
+ case "note": return "task update --note requires a nonblank value";
41
+ default: return undefined;
42
+ }
43
+ default:
44
+ return undefined;
45
+ }
46
+ }
47
+ async function withAcquiredStdin(command, runtime) {
48
+ const diagnostic = selectedStdinDiagnostic(command);
49
+ if (diagnostic === undefined)
50
+ return runtime;
51
+ const bytes = await (runtime.readStdin ?? readStdin)();
52
+ if (isBlankInput(bytes))
53
+ throw new CliUsageError(diagnostic);
54
+ return { ...runtime, readStdin: async () => bytes };
55
+ }
15
56
  function settingsAt(root, environment) {
16
57
  const home = environment.KEIYAKU_HOME?.trim();
17
- return settings({ root, ...(home === undefined || home.length === 0 ? {} : { home }) });
58
+ return settings({ ...(root === undefined ? {} : { root }), ...(home === undefined || home.length === 0 ? {} : { home }) });
18
59
  }
19
60
  function selectedGates(value, name) {
20
61
  try {
@@ -36,6 +77,16 @@ function selectedHooks(value) {
36
77
  throw error;
37
78
  }
38
79
  }
80
+ function selectedGitPolicy(value) {
81
+ try {
82
+ return requireBranchesToBeUpToDateFrom({ settings: value });
83
+ }
84
+ catch (error) {
85
+ if (error instanceof SettingsError)
86
+ throw new CliUsageError(error.message);
87
+ throw error;
88
+ }
89
+ }
39
90
  function actorFromEdge(actor, environment) {
40
91
  let resolved;
41
92
  try {
@@ -46,88 +97,84 @@ function actorFromEdge(actor, environment) {
46
97
  }
47
98
  return resolved;
48
99
  }
49
- function repoAt(coordinate) {
50
- return coordinate === undefined ? Repo.at() : Repo.at({ path: coordinate });
51
- }
52
100
  async function selectContract(repo, selector, scope) {
53
101
  if (selector !== undefined && !selector.startsWith("@"))
54
102
  return contractFromInput(repo, selector);
55
103
  const id = resolveContextualContract(await Keiyaku.list({ repo }), selector, scope);
56
104
  return contractFromInput(repo, id);
57
105
  }
58
- async function invokeBind(parsed, repo, edge, configuration, hooks) {
59
- const markdown = edge.readStdin();
106
+ async function bindDraftReceipt(establishWorld, markdown) {
107
+ try {
108
+ return preserveBindDraft(await establishWorld(), markdown);
109
+ }
110
+ catch (error) {
111
+ return { warning: `bind draft could not be preserved: ${error instanceof Error ? error.message : String(error)}` };
112
+ }
113
+ }
114
+ async function invokeBind({ parsed, repo, edge, configuration, hooks, establishWorld }) {
115
+ const markdown = await edge.readStdin();
60
116
  const gates = selectedGates(configuration, parsed.gates);
61
117
  const actor = actorFromEdge(parsed.actor, edge.environment);
62
- return resultFromMutationCall("bind", () => bindFromCommand({
63
- command: parsed,
64
- repo,
65
- markdown,
66
- gates,
67
- ...(actor === undefined ? {} : { actor }),
68
- hooks,
69
- }), {
70
- project: (result) => {
71
- const bound = result.facts.find((fact) => fact.kind === "bind");
118
+ try {
119
+ const result = await resultFromMutationCall("bind", () => bindFromCommand({
120
+ command: parsed,
121
+ repo,
122
+ markdown,
123
+ gates,
124
+ ...(actor === undefined ? {} : { actor }),
125
+ hooks,
126
+ }), (accepted) => {
127
+ const bound = accepted.facts.find((fact) => fact.kind === "bind");
72
128
  if (bound === undefined || bound.kind !== "bind")
73
129
  throw new Error("accepted bind is missing its bind fact");
74
- return { target: bound.data.coordinates.target ?? null };
75
- },
76
- });
130
+ return acceptedBind(accepted, bound.data.coordinates.target ?? null);
131
+ });
132
+ if (result.kind !== "refused")
133
+ return result;
134
+ return { ...result, draft: await bindDraftReceipt(establishWorld, markdown) };
135
+ }
136
+ catch (error) {
137
+ if (!(error instanceof TypeError))
138
+ throw error;
139
+ throw new BindDraftError(error, await bindDraftReceipt(establishWorld, markdown));
140
+ }
77
141
  }
78
- function unavailableDiff(delivery) {
79
- return {
80
- reason: "git-unavailable",
81
- snapshotId: delivery.snapshotId,
82
- changeId: delivery.changeId,
83
- };
142
+ function deliverRefusal(refusal) {
143
+ if (typeof refusal !== "object" || refusal === null || !("kind" in refusal) || refusal.kind !== "dirty-workspace") {
144
+ return refusal;
145
+ }
146
+ const submodules = "submodules" in refusal && Array.isArray(refusal.submodules) ? refusal.submodules : [];
147
+ return { ...refusal, option: { flag: "--include-dirty", available: submodules.length === 0 } };
84
148
  }
85
- async function invokeDeliver(parsed, seat) {
149
+ async function invokeDeliver(parsed, seat, requireBranchesToBeUpToDate) {
86
150
  return resultFromMutationCall("deliver", () => seat.contract.deliver({
87
151
  ...(seat.actor === undefined ? {} : { actor: seat.actor }),
88
152
  ...(parsed.message === undefined ? {} : { message: parsed.message }),
153
+ requireBranchesToBeUpToDate,
154
+ includeDirty: parsed.includeDirty,
89
155
  hooks: seat.hooks,
90
- }), {
156
+ }), (result) => acceptedDeliver(result, seat.id), {
91
157
  coordinate: seat.id,
92
- project: (result) => ({
93
- obligations: {
94
- ...(result.value.verification === undefined ? {} : { verification: result.value.verification }),
95
- ...(result.value.placement === undefined ? {} : { placement: result.value.placement }),
96
- ...(result.value.leak === undefined ? {} : { leak: result.value.leak }),
97
- },
98
- }),
158
+ projectRefusal: deliverRefusal,
99
159
  });
100
160
  }
101
161
  async function invokeReview(parsed, seat, readStdin) {
102
- const summary = parsed.summaryFromStdin === true ? readStdin() : parsed.summary;
162
+ const summary = parsed.summaryFromStdin === true ? await readStdin() : parsed.summary;
103
163
  return resultFromMutationCall("review", () => seat.contract.review({
104
164
  verdict: parsed.verdict,
105
165
  ...(seat.actor === undefined ? {} : { actor: seat.actor }),
106
166
  ...(summary === undefined ? {} : { summary }),
107
167
  hooks: seat.hooks,
108
- }), {
109
- coordinate: seat.id,
110
- project: (result) => ({
111
- obligations: result.value.placement === undefined ? {} : { placement: result.value.placement },
112
- }),
113
- });
168
+ }), (result) => acceptedReview(result, seat.id), { coordinate: seat.id });
114
169
  }
115
- async function invokeAudit(parsed, seat) {
116
- const delivery = parsed.showDiffBody ? await seat.contract.delivery() : null;
117
- return resultFromMutationCall("audit", () => seat.contract.audit({ ...(seat.actor === undefined ? {} : { actor: seat.actor }), hooks: seat.hooks }), {
118
- coordinate: seat.id,
119
- project: async (result) => {
120
- let renderedDiff;
121
- if (parsed.showDiffBody && delivery !== null) {
122
- const diff = await delivery.diff();
123
- renderedDiff = diff === null ? unavailableDiff(delivery) : diff;
124
- }
125
- return {
126
- report: result.value,
127
- ...(renderedDiff === undefined ? {} : { diff: renderedDiff }),
128
- };
129
- },
130
- });
170
+ async function invokeAudit(parsed, seat, requireBranchesToBeUpToDate) {
171
+ return resultFromMutationCall("audit", () => seat.contract.audit({
172
+ ...(seat.actor === undefined ? {} : { actor: seat.actor }),
173
+ includeDirty: parsed.includeDirty,
174
+ showDiff: parsed.showDiff,
175
+ requireBranchesToBeUpToDate,
176
+ hooks: seat.hooks,
177
+ }), (result) => acceptedAudit(result, seat.id), { coordinate: seat.id });
131
178
  }
132
179
  async function invokeExisting({ parsed, repo, edge, scope, configuration, hooks }) {
133
180
  const { id, contract } = await selectContract(repo, parsed.contract, scope);
@@ -135,77 +182,192 @@ async function invokeExisting({ parsed, repo, edge, scope, configuration, hooks
135
182
  const seat = { contract, id, ...(actor === undefined ? {} : { actor }), hooks };
136
183
  switch (parsed.command) {
137
184
  case "amend": {
138
- const markdown = edge.readStdin();
185
+ const markdown = await edge.readStdin();
139
186
  const gates = parsed.gates === undefined
140
187
  ? undefined
141
188
  : selectedGates(configuration, parsed.gates);
142
- return resultFromMutationCall("amend", () => amendFromCommand({ command: parsed, repo, contract, markdown, gates, ...(actor === undefined ? {} : { actor }), hooks }), { coordinate: id });
189
+ return resultFromMutationCall("amend", () => amendFromCommand({ command: parsed, repo, contract, markdown, gates, ...(actor === undefined ? {} : { actor }), hooks }), (result) => acceptedAmend(result, id), { coordinate: id });
143
190
  }
144
191
  case "deliver":
145
- return invokeDeliver(parsed, seat);
192
+ return invokeDeliver(parsed, seat, selectedGitPolicy(configuration));
146
193
  case "review":
147
194
  return invokeReview(parsed, seat, edge.readStdin);
148
195
  case "arc": {
149
- const markdown = edge.readStdin();
196
+ const markdown = await edge.readStdin();
150
197
  return resultFromMutationCall("arc", () => contract.arc({
151
198
  markdown,
152
199
  ...(actor === undefined ? {} : { actor }),
153
200
  hooks,
154
- }), { coordinate: id });
201
+ }), (result) => acceptedArc(result, id), { coordinate: id });
155
202
  }
156
203
  case "abandon":
157
204
  return resultFromMutationCall("abandon", () => contract.abandon({
158
205
  ...(actor === undefined ? {} : { actor }),
159
206
  ...(parsed.note === undefined ? {} : { note: parsed.note }),
160
207
  hooks,
161
- }), { coordinate: id });
208
+ }), (result) => acceptedAbandon(result, id), { coordinate: id });
162
209
  case "audit":
163
- return invokeAudit(parsed, seat);
210
+ return invokeAudit(parsed, seat, selectedGitPolicy(configuration));
164
211
  default:
165
212
  return parsed;
166
213
  }
167
214
  }
168
- async function invokeAkumaCommand(parsed, path, configuration, edge) {
215
+ async function invokeAkumaCommand(parsed, input) {
216
+ const { path, executionCwd, repo, configuration, edge } = input;
169
217
  if (parsed.command === "call" && parsed.contract !== undefined) {
170
- const selected = contractFromInput(repoAt(path), parsed.contract);
218
+ if (repo === undefined)
219
+ throw new Error("call with Contract requires a resolved Repo");
220
+ const selected = contractFromInput(repo, parsed.contract);
171
221
  return await invokeAkuma(parsed, {
172
222
  path,
223
+ ...(executionCwd === undefined ? {} : { executionCwd }),
173
224
  settings: configuration,
174
225
  contract: selected.contract,
175
226
  readStdin: edge.readStdin,
176
227
  });
177
228
  }
178
- if (parsed.command === "fork") {
179
- let repo;
229
+ return await invokeAkuma(parsed, {
230
+ path,
231
+ ...(executionCwd === undefined ? {} : { executionCwd }),
232
+ settings: configuration,
233
+ ...(repo === undefined ? {} : { repo }),
234
+ readStdin: edge.readStdin,
235
+ });
236
+ }
237
+ async function invokeAkumaFromEdge(parsed, input) {
238
+ try {
239
+ return await invokeAkumaCommand(parsed, input);
240
+ }
241
+ catch (error) {
242
+ if (error instanceof AkumaWorldScopeError)
243
+ throw error;
244
+ if (error instanceof TypeError)
245
+ throw new CliUsageError(error.message);
246
+ throw error;
247
+ }
248
+ }
249
+ async function akumaWorldFor(parsed, located, candidate, establish) {
250
+ const world = parsed.command === "call" ? candidate ?? await establish() : located;
251
+ if (world === null)
252
+ throw new CliUsageError("no Keiyaku world contains the invocation cwd");
253
+ return world;
254
+ }
255
+ async function invokeCatalog(parsed, world, repo, edge) {
256
+ try {
257
+ if (parsed.query.kind === "contracts") {
258
+ if (repo === undefined)
259
+ throw new Error("Contract catalog requires a resolved Repo");
260
+ return { kind: "catalog", catalog: await Keiyaku.ls({ query: parsed.query, repo }) };
261
+ }
262
+ if (parsed.query.kind === "archetypes") {
263
+ return {
264
+ kind: "catalog",
265
+ catalog: await Keiyaku.ls({ query: parsed.query, settings: await settingsAt(undefined, edge.environment) }),
266
+ };
267
+ }
268
+ if (world === null)
269
+ throw new CliUsageError("no Keiyaku world contains the invocation cwd");
270
+ if (parsed.query.kind === "tasks") {
271
+ return { kind: "catalog", catalog: await Keiyaku.ls({ query: parsed.query, path: world }) };
272
+ }
273
+ return { kind: "catalog", catalog: await Keiyaku.ls({ query: parsed.query, path: world }) };
274
+ }
275
+ catch (error) {
276
+ if (error instanceof TypeError)
277
+ throw new CliUsageError(error.message);
278
+ throw error;
279
+ }
280
+ }
281
+ async function invokeStatus(parsed, world, repo, edge) {
282
+ const configuration = await settingsAt(world ?? undefined, edge.environment);
283
+ if (parsed.akuma === true) {
284
+ if (world === null)
285
+ throw new CliUsageError("no Keiyaku world contains the invocation cwd");
286
+ return invokeAkumaStatus(world, parsed.contract, configuration, undefined, repo);
287
+ }
288
+ if (parsed.contract === undefined) {
289
+ const report = await kanshi({ world, ...(repo === undefined ? {} : { repo }) });
290
+ return { kind: "status", report, selection: "world" };
291
+ }
292
+ if (parsed.contract.startsWith("@")) {
293
+ try {
294
+ const address = await resolveNamedAddress({
295
+ path: world,
296
+ selector: parsed.contract,
297
+ contracts: repo === undefined ? [] : (await Keiyaku.list({ repo })).rows,
298
+ });
299
+ if (address.kind === "akuma") {
300
+ if (world === null)
301
+ throw new CliUsageError("no Keiyaku world contains the invocation cwd");
302
+ return invokeAkumaStatus(world, address.id, configuration, parsed.contract, repo);
303
+ }
304
+ const report = await kanshi({ world, ...(repo === undefined ? {} : { repo }) });
305
+ return { kind: "status", report: selectKanshi({ report, contract: address.id }), selection: "contract" };
306
+ }
307
+ catch (error) {
308
+ if (error instanceof TypeError)
309
+ throw new CliUsageError(error.message);
310
+ throw error;
311
+ }
312
+ }
313
+ const report = await kanshi({ world, ...(repo === undefined ? {} : { repo }) });
314
+ const contract = resolveKanshiContract(report, parsed.contract);
315
+ return { kind: "status", report: selectKanshi({ report, contract }), selection: "contract" };
316
+ }
317
+ async function invokeRegion(parsed, world, repo) {
318
+ const read = async (region) => {
180
319
  try {
181
- repo = repoAt(path);
320
+ return await kanshi({ world, repo, region });
182
321
  }
183
322
  catch (error) {
184
- if (!(error instanceof NoGitWorldError))
185
- throw error;
323
+ if (error instanceof TypeError)
324
+ throw new CliUsageError(error.message);
325
+ throw error;
186
326
  }
187
- return await invokeAkuma(parsed, {
188
- path,
189
- settings: configuration,
190
- ...(repo === undefined ? {} : { repo }),
191
- readStdin: edge.readStdin,
192
- });
327
+ };
328
+ if (parsed.contract === undefined) {
329
+ const selection = parsed.path !== undefined
330
+ ? { kind: "path", path: parsed.path }
331
+ : parsed.overlap ? { kind: "overlap" } : { kind: "declarations" };
332
+ const report = await read(selection);
333
+ return { kind: "region", region: report.region ?? { kind: "absent" } };
334
+ }
335
+ const report = await read({ kind: "declarations" });
336
+ const contract = resolveKanshiContract(report, parsed.contract);
337
+ if (report.contracts.kind !== "present" || report.contracts.value.rows.every((row) => row.id !== contract || row.disposition !== "active")) {
338
+ throw new CliUsageError(`unknown contract selector: ${parsed.contract}`);
339
+ }
340
+ if (report.region?.kind !== "present" || report.region.value.kind !== "declarations") {
341
+ return { kind: "region", region: report.region ?? { kind: "absent" } };
193
342
  }
194
- return await invokeAkuma(parsed, { path, settings: configuration, readStdin: edge.readStdin });
343
+ const selection = parsed.overlap ? { kind: "overlap", contract } : { kind: "contract", contract };
344
+ return { kind: "region", region: { kind: "present", value: selectRegion({ declarations: report.region.value.declarations, selection }) } };
195
345
  }
346
+ // eslint-disable-next-line complexity -- command dispatch keeps the CLI's existing boundary in one place.
196
347
  async function invokeParsed(invocation, runtime) {
197
- const coordinate = invocation.cwd ?? runtime.cwd;
348
+ const coordinates = await resolveCliCoordinates({
349
+ ...(runtime.cwd === undefined ? {} : { processCwd: runtime.cwd }),
350
+ ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
351
+ ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
352
+ command: invocation.command,
353
+ });
354
+ const { cwd, repo, world, candidateWorld, establishWorld } = coordinates;
198
355
  const edge = {
199
356
  environment: runtime.environment ?? process.env,
200
- readStdin: runtime.readStdin ?? (() => readFileSync(0, "utf8")),
357
+ readStdin: runtime.readStdin ?? readStdin,
201
358
  };
202
359
  const parsed = invocation.command;
203
360
  if (parsed.command === "settings") {
204
- return { kind: "settings", value: settingsAt(resolve(coordinate ?? "."), edge.environment) };
361
+ return { kind: "settings", value: await settingsAt(world ?? undefined, edge.environment) };
205
362
  }
206
363
  if (parsed.command === "task") {
207
364
  try {
208
- return await invokeTask(parsed, { ...(coordinate === undefined ? {} : { path: coordinate }), readStdin: edge.readStdin });
365
+ return await invokeTask(parsed, {
366
+ world,
367
+ establish: coordinates.establishWorld,
368
+ readStdin: edge.readStdin,
369
+ ...(runtime.actor === undefined ? {} : { actor: runtime.actor }),
370
+ });
209
371
  }
210
372
  catch (error) {
211
373
  if (error instanceof TypeError)
@@ -214,24 +376,33 @@ async function invokeParsed(invocation, runtime) {
214
376
  }
215
377
  }
216
378
  if (isParsedAkumaCommand(parsed)) {
217
- const path = resolve(coordinate ?? ".");
218
- const configuration = settingsAt(path, edge.environment);
219
- return await invokeAkumaCommand(parsed, path, configuration, edge);
379
+ const akumaWorld = await akumaWorldFor(parsed, world, candidateWorld, coordinates.establishWorld);
380
+ const configuration = await settingsAt(akumaWorld, edge.environment);
381
+ return await invokeAkumaFromEdge(parsed, {
382
+ path: akumaWorld,
383
+ ...(parsed.command === "call" && parsed.contract !== undefined && invocation.cwd === undefined
384
+ ? {}
385
+ : { executionCwd: cwd }),
386
+ ...(repo === undefined ? {} : { repo }),
387
+ configuration,
388
+ edge,
389
+ });
220
390
  }
221
- if (parsed.command === "status") {
222
- if (parsed.akuma === true) {
223
- return invokeAkumaStatus(coordinate ?? ".", parsed.contract);
224
- }
225
- const report = await kanshi(coordinate === undefined ? {} : { path: coordinate });
226
- if (parsed.contract === undefined)
227
- return { kind: "status", report, selection: "world" };
228
- const contract = resolveKanshiContract(report, parsed.contract);
229
- return { kind: "status", report: selectKanshi({ report, contract }), selection: "contract" };
230
- }
231
- const repo = repoAt(coordinate);
232
- const scope = coordinate ?? repo.root;
233
- const configuration = settingsAt(repo.root, edge.environment);
391
+ if (parsed.command === "ls")
392
+ return await invokeCatalog(parsed, world, repo, edge);
393
+ if (parsed.command === "status")
394
+ return await invokeStatus(parsed, world, repo, edge);
395
+ if (repo === undefined)
396
+ throw new Error(`${parsed.command} requires a resolved Repo`);
397
+ if (parsed.command === "region")
398
+ return invokeRegion(parsed, world, repo);
399
+ const scope = cwd;
400
+ const configuration = await settingsAt(world ?? undefined, edge.environment);
234
401
  const hooks = selectedHooks(configuration);
402
+ if (parsed.command === "show") {
403
+ const selected = await selectContract(repo, parsed.contract, scope);
404
+ return { kind: "guidance", contract: selected.id, guidance: await selected.contract.guidance() };
405
+ }
235
406
  switch (parsed.command) {
236
407
  case "reconcile": {
237
408
  if (parsed.contract === undefined) {
@@ -241,20 +412,28 @@ async function invokeParsed(invocation, runtime) {
241
412
  return { kind: "observation", command: "reconcile", ...await contract.reconcile({ hooks, retryHooks: parsed.retryHooks }) };
242
413
  }
243
414
  case "bind":
244
- return invokeBind(parsed, repo, edge, configuration, hooks);
415
+ return invokeBind({ parsed, repo, edge, configuration, hooks, establishWorld });
245
416
  default:
246
417
  return invokeExisting({ parsed, repo, edge, scope, configuration, hooks });
247
418
  }
248
419
  }
249
- async function invokeInstall(command, runtime) {
250
- return installHarnesses(command.harnesses, runtime.environment ?? process.env);
420
+ function withResolvedTaskActor(command, runtime) {
421
+ if (command.command !== "task" || (command.action !== "add" && command.action !== "compose"))
422
+ return runtime;
423
+ const actor = actorFromEdge(typeof command.flags.actor === "string" ? command.flags.actor : undefined, runtime.environment ?? process.env);
424
+ return actor === undefined ? runtime : { ...runtime, actor };
251
425
  }
252
426
  export async function invoke(invocation, runtime = {}) {
253
427
  try {
254
428
  const command = invocation.command;
429
+ assertExplicitRepoUse(command, invocation.repo);
255
430
  if (command.command === "install")
256
- return await invokeInstall(command, runtime);
257
- return await invokeParsed({ ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }), command }, runtime);
431
+ return installHarnesses(command.harnesses, runtime.environment ?? process.env);
432
+ return await invokeParsed({
433
+ ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
434
+ ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
435
+ command,
436
+ }, await withAcquiredStdin(command, withResolvedTaskActor(command, runtime)));
258
437
  }
259
438
  catch (error) {
260
439
  if (error instanceof CliUsageError && error.projection === undefined) {
@@ -1,12 +1,20 @@
1
+ import { AkumaWorldScopeError } from "../index.js";
1
2
  import { CliUsageError, parseArgv, renderContractHelp, renderRootHelp, } from "./parse.js";
2
3
  import { renderText } from "./render/text.js";
3
4
  import { renderTaskIncompleteDiagnostic, renderTaskText, taskExitCode } from "./render/task.js";
4
- import { akumaExitCode, renderAkumaJson, renderAkumaText } from "./render/akuma.js";
5
+ import { akumaExitCode, akumaRawAnswer, renderAkumaJson, renderAkumaText } from "./render/akuma.js";
5
6
  import { isParsedAkumaCommand, renderAkumaHelp } from "./commands/akuma.js";
6
7
  import { installExitCode, renderInstallHelp, renderInstallText } from "./commands/install.js";
7
8
  import { renderTaskHelp } from "./commands/task.js";
8
9
  import { renderSettingsText, settingsJsonValue } from "./render/settings.js";
10
+ import { renderCatalogText } from "./render/catalog.js";
11
+ import { BindDraftError } from "./draft.js";
12
+ import { renderBindDraftReceipt } from "./render/refusal.js";
9
13
  function writeTask(command, result) {
14
+ const context = {
15
+ columns: process.stdout.isTTY === true && Number.isInteger(process.stdout.columns) ? process.stdout.columns : 80,
16
+ color: false,
17
+ };
10
18
  if (command.output === "json")
11
19
  process.stdout.write(`${JSON.stringify(result)}\n`);
12
20
  else if (command.action === "compose" && typeof result === "object" && result !== null && "kind" in result && result.kind === "incomplete") {
@@ -16,13 +24,17 @@ function writeTask(command, result) {
16
24
  process.stdout.write(result.draft);
17
25
  }
18
26
  else
19
- process.stdout.write(`${renderTaskText(command, result)}\n`);
27
+ process.stdout.write(`${renderTaskText(command, result, context)}\n`);
20
28
  return taskExitCode(result);
21
29
  }
22
30
  function writeAkuma(command, result) {
23
- const output = command.output === "json" ? renderAkumaJson(command, result) : renderAkumaText(command, result);
24
- const rawLast = command.command === "history" && command.last && command.output === "text";
25
- process.stdout.write(rawLast ? output : `${output}\n`);
31
+ const output = command.output === "json" ? renderAkumaJson(command, result) : renderAkumaText(command, result, {
32
+ columns: process.stdout.isTTY === true && Number.isInteger(process.stdout.columns) ? process.stdout.columns : 80,
33
+ color: process.stdout.isTTY === true && process.env.NO_COLOR === undefined,
34
+ });
35
+ const raw = command.output === "text"
36
+ && ((command.command === "history" && command.last) || akumaRawAnswer(result) !== undefined);
37
+ process.stdout.write(raw ? output : `${output}\n`);
26
38
  return akumaExitCode(result);
27
39
  }
28
40
  function renderHelp(coordinate) {
@@ -34,6 +46,18 @@ function renderHelp(coordinate) {
34
46
  case "akuma": return renderAkumaHelp(coordinate.action);
35
47
  }
36
48
  }
49
+ function writeGuidance(command, result) {
50
+ if (command.output === "json")
51
+ process.stdout.write(`${JSON.stringify({ contract: result.contract, guidance: result.guidance })}\n`);
52
+ else
53
+ process.stdout.write(result.guidance.endsWith("\n") ? result.guidance : `${result.guidance}\n`);
54
+ return 0;
55
+ }
56
+ function writeCatalog(command, result) {
57
+ process.stdout.write(`${command.output === "json" ? JSON.stringify(result.catalog) : renderCatalogText(result.catalog)}\n`);
58
+ return 0;
59
+ }
60
+ // eslint-disable-next-line complexity -- output dispatch is the single CLI presentation boundary.
37
61
  function writeResult(command, result) {
38
62
  if (command.command === "install") {
39
63
  const value = result;
@@ -52,6 +76,15 @@ function writeResult(command, result) {
52
76
  return writeAkuma(command, result);
53
77
  }
54
78
  const contractResult = result;
79
+ if (contractResult.kind === "guidance")
80
+ return writeGuidance(command, contractResult);
81
+ if (contractResult.kind === "catalog")
82
+ return writeCatalog(command, contractResult);
83
+ if (contractResult.kind === "region") {
84
+ const output = command.output === "json" ? JSON.stringify(contractResult.region) : renderText(contractResult);
85
+ process.stdout.write(`${output}\n`);
86
+ return 0;
87
+ }
55
88
  const output = command.output === "json"
56
89
  ? JSON.stringify(contractResult.kind === "status" ? contractResult.report : contractResult)
57
90
  : renderText(contractResult, {
@@ -61,19 +94,33 @@ function writeResult(command, result) {
61
94
  process.stdout.write(`${output}\n`);
62
95
  return contractResult.kind === "refused" ? 1 : contractResult.kind === "retry" ? 2 : 0;
63
96
  }
97
+ function writeWorldScopeRefusal(error, output) {
98
+ const body = output === "json"
99
+ ? JSON.stringify(error.refusal)
100
+ : `${error.refusal.kind} ${error.refusal.world} ${error.refusal.ids.join(" ")}`;
101
+ process.stderr.write(`${body}\n`);
102
+ return 1;
103
+ }
64
104
  export async function main(argv = process.argv.slice(2)) {
105
+ let output = "text";
65
106
  try {
66
107
  const parsed = parseArgv(argv);
67
108
  if ("help" in parsed) {
68
109
  process.stdout.write(`${renderHelp(parsed.help)}\n`);
69
110
  return 0;
70
111
  }
112
+ output = parsed.command.output;
71
113
  const { invoke } = await import("./invoke.js");
72
114
  const result = await invoke(parsed, { cwd: process.cwd() });
73
115
  return writeResult(parsed.command, result);
74
116
  }
75
117
  catch (error) {
76
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
77
- return error instanceof CliUsageError ? 1 : 3;
118
+ if (error instanceof AkumaWorldScopeError)
119
+ return writeWorldScopeRefusal(error, output);
120
+ const original = error instanceof BindDraftError ? error.original : error;
121
+ process.stderr.write(`${original instanceof Error ? original.message : String(original)}\n`);
122
+ if (error instanceof BindDraftError)
123
+ process.stderr.write(`${renderBindDraftReceipt(error.draft)}\n`);
124
+ return original instanceof CliUsageError ? 1 : 3;
78
125
  }
79
126
  }