@astrosheep/keiyaku 2.8.4 → 2.9.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 (229) hide show
  1. package/README.md +35 -15
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
  4. package/build/agents/harness/control-types.js +1 -0
  5. package/build/agents/harness/execution-handle.js +23 -3
  6. package/build/agents/harness/index.js +6 -607
  7. package/build/agents/harness/outcome.js +141 -0
  8. package/build/agents/harness/projection.js +310 -0
  9. package/build/agents/harness/router.js +201 -0
  10. package/build/agents/harness/runtime.js +2 -1
  11. package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
  12. package/build/agents/launch-snapshot/resolve.js +72 -0
  13. package/build/agents/opencode-sdk.js +24 -3
  14. package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
  15. package/build/agents/providers/claude-agent-sdk/events.js +218 -0
  16. package/build/agents/providers/claude-agent-sdk/session.js +158 -0
  17. package/build/agents/providers/codex-app-server/adapter.js +347 -0
  18. package/build/agents/providers/codex-app-server/events.js +352 -0
  19. package/build/agents/providers/codex-app-server/session.js +266 -0
  20. package/build/agents/providers/codex-sdk.js +23 -5
  21. package/build/agents/providers/opencode-sdk/adapter.js +344 -0
  22. package/build/agents/providers/opencode-sdk/events.js +354 -0
  23. package/build/agents/providers/opencode-sdk/session.js +180 -0
  24. package/build/agents/providers/pi/adapter.js +269 -0
  25. package/build/agents/providers/pi/checkpoint.js +41 -0
  26. package/build/agents/providers/pi/events.js +282 -0
  27. package/build/agents/{selector_v2.js → selector.js} +8 -6
  28. package/build/cli/commands/akuma/akuma/handler.js +4 -0
  29. package/build/cli/commands/akuma/akuma/meta.js +16 -0
  30. package/build/cli/commands/akuma/catalog.js +8 -0
  31. package/build/cli/commands/akuma/list/handler.js +15 -0
  32. package/build/cli/commands/akuma/list/meta-ls.js +9 -0
  33. package/build/cli/commands/akuma/view/handler.js +34 -0
  34. package/build/cli/commands/akuma/view/meta.js +8 -0
  35. package/build/cli/commands/akuma.js +1 -1
  36. package/build/cli/commands/contract/amend/handler.js +10 -0
  37. package/build/cli/commands/contract/amend/meta.js +12 -0
  38. package/build/cli/commands/contract/arc/handler.js +9 -0
  39. package/build/cli/commands/contract/arc/meta.js +26 -0
  40. package/build/cli/commands/contract/bind/handler.js +23 -0
  41. package/build/cli/commands/contract/bind/meta.js +30 -0
  42. package/build/cli/commands/contract/catalog.js +16 -0
  43. package/build/cli/commands/contract/forfeit/handler.js +13 -0
  44. package/build/cli/commands/contract/forfeit/meta.js +12 -0
  45. package/build/cli/commands/contract/log/handler.js +12 -0
  46. package/build/cli/commands/contract/log/meta.js +12 -0
  47. package/build/cli/commands/contract/petition/handler.js +26 -0
  48. package/build/cli/commands/contract/petition/meta.js +23 -0
  49. package/build/cli/commands/contract/renew/handler.js +10 -0
  50. package/build/cli/commands/contract/renew/meta.js +17 -0
  51. package/build/cli/commands/projection/call/handler.js +39 -0
  52. package/build/cli/commands/projection/call/meta.js +9 -0
  53. package/build/cli/commands/projection/catalog.js +14 -0
  54. package/build/cli/commands/projection/kill/handler.js +35 -0
  55. package/build/cli/commands/projection/kill/meta.js +11 -0
  56. package/build/cli/commands/projection/revive/handler.js +53 -0
  57. package/build/cli/commands/projection/revive/meta.js +9 -0
  58. package/build/cli/commands/projection/status/handler.js +23 -0
  59. package/build/cli/commands/projection/status/meta.js +9 -0
  60. package/build/cli/commands/projection/tell/handler.js +34 -0
  61. package/build/cli/commands/projection/tell/meta.js +9 -0
  62. package/build/cli/commands/projection/wait/handler.js +21 -0
  63. package/build/cli/commands/projection/wait/meta.js +11 -0
  64. package/build/cli/commands/registry.js +100 -0
  65. package/build/cli/commands/shared.js +71 -0
  66. package/build/cli/commands/skills/catalog.js +6 -0
  67. package/build/cli/commands/skills/install/handler.js +10 -0
  68. package/build/cli/commands/skills/install/meta.js +9 -0
  69. package/build/cli/commands/skills/skills/handler.js +4 -0
  70. package/build/cli/commands/skills/skills/meta.js +10 -0
  71. package/build/cli/commands/system/catalog.js +8 -0
  72. package/build/cli/commands/system/completion/handler.js +10 -0
  73. package/build/cli/commands/system/completion/meta.js +9 -0
  74. package/build/cli/commands/system/dump-env/handler.js +3 -0
  75. package/build/cli/commands/system/dump-env/meta.js +10 -0
  76. package/build/cli/commands/system/guide/handler.js +4 -0
  77. package/build/cli/commands/system/guide/meta.js +9 -0
  78. package/build/cli/commands/task/add/handler.js +21 -0
  79. package/build/cli/commands/task/add/meta.js +8 -0
  80. package/build/cli/commands/task/catalog.js +22 -0
  81. package/build/cli/commands/task/doctor/handler.js +4 -0
  82. package/build/cli/commands/task/doctor/meta.js +8 -0
  83. package/build/cli/commands/task/done/handler.js +17 -0
  84. package/build/cli/commands/task/done/meta.js +8 -0
  85. package/build/cli/commands/task/drop/handler.js +6 -0
  86. package/build/cli/commands/task/drop/meta.js +8 -0
  87. package/build/cli/commands/task/ls/handler.js +13 -0
  88. package/build/cli/commands/task/ls/meta.js +8 -0
  89. package/build/cli/commands/task/shared.js +131 -0
  90. package/build/cli/commands/task/start/handler.js +6 -0
  91. package/build/cli/commands/task/start/meta.js +8 -0
  92. package/build/cli/commands/task/stop/handler.js +17 -0
  93. package/build/cli/commands/task/stop/meta.js +8 -0
  94. package/build/cli/commands/task/task/handler.js +5 -0
  95. package/build/cli/commands/task/task/meta.js +11 -0
  96. package/build/cli/commands/task/update/handler.js +18 -0
  97. package/build/cli/commands/task/update/meta.js +8 -0
  98. package/build/cli/commands/task/view/handler.js +14 -0
  99. package/build/cli/commands/task/view/meta.js +8 -0
  100. package/build/cli/completion.js +26 -27
  101. package/build/cli/flags.js +79 -2
  102. package/build/cli/help.js +27 -49
  103. package/build/cli/index.js +121 -374
  104. package/build/cli/output-stream-error-policy.js +17 -0
  105. package/build/cli/parse.js +159 -72
  106. package/build/cli/projection-address.js +5 -0
  107. package/build/cli/render/arc.js +1 -1
  108. package/build/cli/render/errors.js +4 -3
  109. package/build/cli/render/format.js +14 -0
  110. package/build/cli/render/kanshi.js +106 -0
  111. package/build/cli/render/petition.js +24 -1
  112. package/build/cli/render/projection-activity.js +5 -1
  113. package/build/cli/render/shared.js +2 -2
  114. package/build/cli/render/status.js +79 -13
  115. package/build/cli/render/task.js +113 -0
  116. package/build/cli/render/wait.js +23 -3
  117. package/build/cli/types.js +1 -1
  118. package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
  119. package/build/config/env.js +9 -4
  120. package/build/config/settings/disease.js +26 -0
  121. package/build/config/settings/knobs.js +8 -0
  122. package/build/config/settings/loader.js +244 -0
  123. package/build/config/settings/schema.js +276 -0
  124. package/build/core/addressing.js +1 -1
  125. package/build/core/amend.js +3 -3
  126. package/build/core/arc.js +5 -4
  127. package/build/core/bind-workspace.js +123 -0
  128. package/build/core/bind.js +75 -155
  129. package/build/core/call/call.js +224 -0
  130. package/build/core/call/context.js +238 -0
  131. package/build/core/call/execution.js +217 -0
  132. package/build/core/call/prompt.js +42 -0
  133. package/build/core/call/reference.js +24 -0
  134. package/build/core/call-persist.js +1 -1
  135. package/build/core/claim-delivery.js +247 -0
  136. package/build/core/claim.js +39 -277
  137. package/build/core/context.js +2 -1
  138. package/build/core/{render.js → contract-view.js} +12 -140
  139. package/build/core/draft.js +107 -9
  140. package/build/core/entry.js +9 -1
  141. package/build/core/execution-coordinate.js +4 -20
  142. package/build/core/execution-pact.js +16 -31
  143. package/build/core/forfeit.js +35 -22
  144. package/build/core/hints.js +2 -2
  145. package/build/core/log.js +2 -2
  146. package/build/core/petition-claim-gates.js +8 -6
  147. package/build/core/petition-preview.js +97 -0
  148. package/build/core/petition-run.js +3 -2
  149. package/build/core/petition.js +39 -26
  150. package/build/core/projection/generation/database.js +306 -0
  151. package/build/core/projection/generation/identity.js +84 -0
  152. package/build/core/projection/generation/model.js +201 -0
  153. package/build/core/projection/generation/store.js +73 -0
  154. package/build/core/projection/generation/transitions.js +258 -0
  155. package/build/core/projection/heart.js +0 -307
  156. package/build/core/projection/tell/database.js +109 -0
  157. package/build/core/projection/tell/model.js +97 -0
  158. package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
  159. package/build/core/projection-activity.js +23 -2
  160. package/build/core/projection-core.js +2 -1
  161. package/build/core/projection-execution-observer.js +259 -0
  162. package/build/core/projection-generation-continuation.js +1 -1
  163. package/build/core/projection-generation-execution.js +90 -20
  164. package/build/core/projection-generation-launcher.js +1 -1
  165. package/build/core/projection-generation-runner.js +198 -4
  166. package/build/core/projection-identity.js +1 -1
  167. package/build/core/projection-kill.js +50 -17
  168. package/build/core/projection-life-observer.js +1 -1
  169. package/build/core/projection-life-protocol.js +10 -0
  170. package/build/core/projection-mint.js +1 -1
  171. package/build/core/projection-status.js +45 -32
  172. package/build/core/projection-wait.js +12 -2
  173. package/build/core/projection-wake.js +5 -2
  174. package/build/core/{queue_v2.js → queue.js} +2 -1
  175. package/build/core/registry.js +1 -1
  176. package/build/core/renew.js +7 -7
  177. package/build/core/response-artifact-catalog-sqlite.js +112 -0
  178. package/build/core/response-artifact-catalog.js +7 -0
  179. package/build/core/seal.js +44 -16
  180. package/build/core/status/board.js +286 -0
  181. package/build/core/status/drift.js +99 -0
  182. package/build/core/status/lifecycle.js +84 -0
  183. package/build/core/status/reconciliation.js +131 -0
  184. package/build/core/stored-agent-event.js +28 -4
  185. package/build/core/target-ref.js +10 -0
  186. package/build/core/task/board.js +175 -0
  187. package/build/core/task/commands.js +211 -0
  188. package/build/core/task/document.js +189 -0
  189. package/build/core/task/model.js +1 -0
  190. package/build/core/task/settlement-git.js +207 -0
  191. package/build/core/task/settlement-policy.js +82 -0
  192. package/build/core/task/source-board.js +84 -0
  193. package/build/core/task/source-model.js +1 -0
  194. package/build/core/task/validation.js +44 -0
  195. package/build/core/task-contract.js +224 -0
  196. package/build/core/task-doctor.js +14 -0
  197. package/build/core/task-git-actor.js +38 -0
  198. package/build/core/task-git-runtime.js +34 -0
  199. package/build/core/task-git-store.js +375 -0
  200. package/build/core/task-repository.js +1 -0
  201. package/build/core/task-store-repository.js +86 -0
  202. package/build/core/task-store.js +1 -0
  203. package/build/core/task-worktree.js +51 -0
  204. package/build/core/task.js +4 -0
  205. package/build/core/transcripts.js +138 -56
  206. package/build/core/validation.js +17 -0
  207. package/build/core/verdict.js +2 -2
  208. package/build/core/worktree-path.js +24 -6
  209. package/build/flow-error.js +3 -2
  210. package/build/generated/version.js +1 -1
  211. package/build/git/commits.js +3 -0
  212. package/build/git/diff/parsers.js +0 -94
  213. package/build/index.js +2 -0
  214. package/build/keiyaku-constants.js +2 -0
  215. package/build/keiyaku.js +4 -3
  216. package/package.json +3 -2
  217. package/skills/keiyaku/SKILL.md +1 -1
  218. package/skills/keiyaku-akuma/SKILL.md +11 -6
  219. package/build/agents/harness/pump.js +0 -158
  220. package/build/agents/providers/codex-app-server.js +0 -864
  221. package/build/agents/providers/opencode-sdk.js +0 -363
  222. package/build/agents/providers/pi.js +0 -557
  223. package/build/config/settings.js +0 -533
  224. package/build/core/call.js +0 -686
  225. package/build/core/projection/generation.js +0 -412
  226. package/build/core/projection/identity.js +0 -81
  227. package/build/core/projection/mint.js +0 -174
  228. package/build/core/projection-generation-store.js +0 -707
  229. package/build/core/status.js +0 -539
@@ -1,358 +1,67 @@
1
1
  import * as path from "node:path";
2
- import * as fs from "node:fs/promises";
3
2
  import { FlowError, asMessage } from "../flow-error.js";
4
3
  import { writeDraftArtifact } from "./draft-artifact.js";
5
- import { amendKeiyaku } from "../core/amend.js";
6
- import { arcKeiyaku, buildArcInput } from "../core/arc.js";
7
- import { bindContract } from "../core/bind.js";
8
- import { buildAmendInput, defaultActorClock } from "../core/command-io.js";
9
- import { AddressResolutionError, buildArtifactResolutionAttempt, buildResolutionAttempt, resolveContractAddress, withResolvedAddress, } from "../core/addressing.js";
10
- import { forfeitContractCommand } from "../core/forfeit.js";
11
- import { readContractLog } from "../core/log.js";
12
- import { buildPetitionClaimInput } from "../core/petition.js";
13
- import { petitionKeiyaku } from "../core/petition-run.js";
14
- import { renewKeiyaku } from "../core/renew.js";
15
- import { normalizeTargetRef, readKanshiBoard } from "../core/status.js";
16
- import { runCallAndPersist } from "../core/call-persist.js";
17
- import { isResponseArtifactId } from "../core/response-artifact-id.js";
18
- import { locateResponseArtifact } from "../core/transcripts.js";
19
- import { renderEnvExample } from "../config/env-keys.js";
20
- import { assertSettingsKnobUsable, loadKeiyakuSettings } from "../config/settings.js";
4
+ import { compileBindDraftFlags } from "../core/draft.js";
21
5
  import { handleCliError } from "./render/errors.js";
22
- import { renderStatusBoard } from "./render/status.js";
23
- import { VERSION } from "../generated/version.js";
24
6
  import { FLAG_SPECS } from "./flags.js";
25
- import { renderAkumaList, renderAkumaShow } from "./commands/akuma.js";
26
- import { loadAkumaCatalogV2 } from "../config/akuma-loader_v2.js";
27
- import { renderCompletionCandidates, renderCompletionScript } from "./completion.js";
28
- import { CLI_COMMAND_METADATA, buildKeiyakuGuide, missingSubcommandError, renderCliCommandHelp as renderCliCommandHelpText, renderCliHelp as renderCliHelpText, } from "./help.js";
29
- import { commandUsesPromptInput, isCallCommand, parseCliArgs } from "./parse.js";
30
- import { buildAmendResponse, buildArcResponse, buildRenewResponse } from "./render/arc.js";
31
- import { buildLogResponse } from "./render/misc.js";
32
- import { buildPetitionClaimResponse } from "./render/petition.js";
7
+ import { renderCliCommandHelp as renderCliCommandHelpText, renderCliHelp as renderCliHelpText, } from "./help.js";
8
+ import { commandUsesPromptInput, isCallCommand, isPetitionDryRunInvocation, parseCliArgs } from "./parse.js";
9
+ import { getCommandMetadata, loadCommandHandler } from "./commands/registry.js";
10
+ import { repositoryCoordinateFromAddress } from "./commands/shared.js";
33
11
  import { shellQuote } from "./render/format.js";
34
- import { buildBindDraftResponse, buildBindResponse, buildForfeitResponse, formatCallNextAction, renderCoreResult, textResponse } from "./render/shared.js";
35
- import { buildCallResponse, renderAdoptedLaunchReceipt } from "./render/call.js";
36
- import { projectionDir, writeInboxTell } from "../core/projection-core.js";
37
- import { assertTellEffortSupported, tellProjection } from "../core/projection-wake.js";
38
- import { killProjectionGeneration } from "../core/projection-kill.js";
39
- import { resolveProjectionAddress } from "./projection-address.js";
40
- import { waitForProjection } from "../core/projection-wait.js";
41
- import { buildWaitResponse } from "./render/wait.js";
42
- import { prependAddressReceipt, renderResolvedAddressReceipt } from "./render/address.js";
43
- import { installOfficialSkills } from "./skills-install.js";
44
- import { isGitRepo } from "../git/branches.js";
45
- import { stableRepoRoot } from "../core/worktree-path.js";
46
- import { assertProjectionAlias } from "../core/projection-alias.js";
47
- const CALL_INPUT_HINTS = [
48
- "Call input can be a prompt argument, `-` to read stdin, or omitted to read stdin.",
49
- "Use `keiyaku call NAME \"prompt\"`, `keiyaku call NAME - < prompt.md`, or `keiyaku revive ARTIFACT_ID`.",
50
- ];
51
- function titleFromCallPrompt(prompt) {
52
- const firstLine = prompt
53
- .split(/\r?\n/)
54
- .map((line) => line.trim())
55
- .find((line) => line.length > 0);
56
- if (!firstLine)
57
- return "Call";
58
- return firstLine.replace(/^#+\s*/, "").slice(0, 80) || "Call";
59
- }
60
- function repositoryCoordinateFromAddress(address) {
61
- return address?.repo.kind === "repository" ? address.repo.stableRoot : undefined;
62
- }
63
- function buildCallInput(content, flags, mode = "call") {
64
- const cwd = flags.cwd ?? process.cwd();
65
- const task = content.trim();
66
- if (!task) {
67
- throw new FlowError("EMPTY_PARAM", "call requires a prompt argument, `-` with stdin, or prompt text on stdin.", {
68
- hints: CALL_INPUT_HINTS,
69
- });
70
- }
71
- return {
72
- cwd,
73
- repo: flags.repo,
74
- mode,
75
- contractId: flags.contractId,
76
- contractAddressSource: flags.contractAddressSource,
77
- title: titleFromCallPrompt(task),
78
- task,
79
- agent: flags.akuma,
80
- model: flags.model,
81
- effort: flags.effort,
82
- incognito: flags.incognito,
83
- bare: flags.bare,
84
- detach: flags.detach,
85
- ...(mode === "call" && flags.alias !== undefined ? { alias: assertProjectionAlias(flags.alias) } : {}),
86
- };
12
+ import { formatCallNextAction } from "./render/shared.js";
13
+ import { renderResolvedAddressReceipt } from "./render/address.js";
14
+ function commandRequiresStdin(command) {
15
+ return getCommandMetadata(command).stdin === "required";
87
16
  }
88
- function revivePrompt(stdin) {
89
- return stdin.trim() ? stdin : "Continue from the previous response.";
17
+ function commandAcceptsOptionalStdin(command) {
18
+ return getCommandMetadata(command).stdin === "optional";
90
19
  }
91
- function commandSpec(command, run) {
92
- return { ...CLI_COMMAND_METADATA[command], run };
20
+ function commandSavesFailedStdin(command) {
21
+ return (commandRequiresStdin(command) || commandAcceptsOptionalStdin(command)) && command !== "call";
93
22
  }
94
- function effectiveDirectory(flags) {
95
- return flags.cwd ?? process.cwd();
23
+ function hasBindDomainFlags(flags) {
24
+ return (flags.bindPlace !== undefined
25
+ || flags.bindObjective !== undefined
26
+ || flags.bindScope !== undefined
27
+ || flags.bindChecks !== undefined);
96
28
  }
97
- async function repositoryDirectory(flags) {
98
- const candidate = flags.repo ?? effectiveDirectory(flags);
99
- if (!(await isGitRepo(candidate))) {
100
- if (flags.contractId) {
101
- const asTyped = flags.contractAddressSource === "at-address"
102
- ? `@${flags.contractId}`
103
- : `--contract ${flags.contractId}`;
104
- throw new AddressResolutionError("EMPTY_PARAM", `${asTyped} is a commission address, but ${candidate} has no repository ledger; contract addressing requires a ledger`, buildResolutionAttempt({
105
- repo: { kind: "user" },
106
- workdir: effectiveDirectory(flags),
107
- spelling: {
108
- asTyped,
109
- source: flags.contractAddressSource ?? "explicit-flag",
110
- },
111
- }));
112
- }
113
- throw new FlowError("NOT_GIT_REPO", `${candidate} is not a git repository`);
29
+ function prepareBindInput(stdin, flags) {
30
+ const nonblankStdin = stdin.trim().length > 0;
31
+ const domainFlags = hasBindDomainFlags(flags);
32
+ if (flags.dryRun && flags.draftOnly) {
33
+ throw new FlowError("EMPTY_PARAM", "--dry-run cannot be combined with --draft-only");
114
34
  }
115
- return flags.repo ? await stableRepoRoot(candidate) : candidate;
116
- }
117
- async function projectDirectory(flags) {
118
- if (flags.repo)
119
- return await repositoryDirectory(flags);
120
- return effectiveDirectory(flags);
121
- }
122
- const CLI_COMMAND_SPECS = {
123
- bind: commandSpec("bind", async (stdin, flags) => {
124
- const cwd = await repositoryDirectory(flags);
125
- if (flags.draftOnly) {
126
- return buildBindDraftResponse(await writeDraftArtifact({ cwd, title: "bind-draft", content: stdin }));
127
- }
128
- const result = await bindContract({
129
- cwd, draft: stdin, ...defaultActorClock(), workspace: "worktree",
130
- after: flags.after ?? [], exclusive: flags.exclusive,
131
- onProgress: (line) => {
132
- process.stdout.write(`${line}\n`);
133
- },
134
- });
135
- return buildBindResponse(result, cwd);
136
- }),
137
- arc: commandSpec("arc", async (stdin, flags) => {
138
- const parsed = buildArcInput(stdin, effectiveDirectory(flags), flags.waivePaths, flags.contractId, flags.contractAddressSource);
139
- const cwd = await repositoryDirectory(flags);
140
- return renderCoreResult(arcKeiyaku({ ...parsed, cwd }), buildArcResponse);
141
- }),
142
- call: commandSpec("call", async (stdin, flags, signal) => {
143
- const outcome = await runCallAndPersist({
144
- ...buildCallInput(stdin, flags, "call"),
145
- signal,
146
- ...(!flags.detach
147
- ? {
148
- onLaunch: ({ address, projection }) => {
149
- process.stdout.write(`${renderAdoptedLaunchReceipt(address, projection.id, projection.alias, projection.previousAliasTarget)}\n`);
150
- },
151
- }
152
- : {}),
153
- });
154
- if (outcome.result.foregroundWait && outcome.result.projection) {
155
- return buildWaitResponse(outcome.result.projection.id, outcome.result.foregroundWait, { alias: outcome.result.projection.alias });
156
- }
157
- return buildCallResponse(outcome.result, {
158
- responsePath: outcome.responsePath,
159
- artifactId: outcome.artifactId,
160
- addressPlacement: flags.detach ? "stdout" : "none",
161
- });
162
- }),
163
- revive: commandSpec("revive", async (stdin, flags, signal, positional) => {
164
- const cwd = effectiveDirectory(flags);
165
- const artifactCwd = await projectDirectory(flags);
166
- const artifactId = positional[0];
167
- const isRepo = await isGitRepo(artifactCwd);
168
- const responseStoreCwd = isRepo ? await stableRepoRoot(artifactCwd) : await fs.realpath(artifactCwd);
169
- const artifact = await locateResponseArtifact({
170
- cwd: responseStoreCwd,
171
- artifactId,
172
- attempt: buildArtifactResolutionAttempt({
173
- repo: isRepo
174
- ? { kind: "repository", stableRoot: responseStoreCwd }
175
- : { kind: "user" },
176
- workdir: cwd,
177
- supplied: artifactId,
178
- inference: ["response artifact identity lookup"],
179
- }),
180
- });
181
- let executionCwd;
182
- let repo;
183
- if (artifact.provenance.authority.kind === "repository") {
184
- executionCwd = responseStoreCwd;
185
- repo = responseStoreCwd;
186
- }
187
- else {
188
- if (!("workdir" in artifact.provenance.evidence)) {
189
- throw new FlowError("INVALID_RESPONSE_PATH", `Invalid response artifact '${artifactId}': field 'provenance.evidence.workdir' is missing.`);
190
- }
191
- executionCwd = artifact.provenance.evidence.workdir;
192
- repo = undefined;
193
- }
194
- const outcome = await runCallAndPersist({
195
- ...buildCallInput(revivePrompt(stdin), { ...flags, cwd: executionCwd, repo }, "revive"),
196
- reviveArtifact: artifact,
197
- signal,
198
- });
199
- return buildCallResponse(outcome.result, { responsePath: outcome.responsePath, artifactId: outcome.artifactId, addressPlacement: "footer" });
200
- }),
201
- wait: commandSpec("wait", async (_stdin, flags, signal, positional) => {
202
- const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
203
- const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
204
- const operation = async () => {
205
- const projection = await resolveProjectionAddress({
206
- cwd,
207
- address: positional[0],
208
- scope: address,
209
- verb: "wait",
210
- });
211
- return prependAddressReceipt(buildWaitResponse(projection.id, await waitForProjection({ projectionDirectory: projectionDir(cwd, projection.id), timeoutMs: flags.timeoutMs, signal }), { alias: projection.alias }), address, { projectionId: projection.id, projectionAlias: projection.alias });
212
- };
213
- return address ? await withResolvedAddress(address, operation) : await operation();
214
- }),
215
- tell: commandSpec("tell", async (_stdin, flags, _signal, positional) => {
216
- const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
217
- const [projectionAddress, message] = positional;
218
- if (!projectionAddress || !message)
219
- throw new FlowError("EMPTY_PARAM", "tell requires a projection address and message");
220
- const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
221
- const operation = async () => {
222
- const projection = await resolveProjectionAddress({
223
- cwd,
224
- address: projectionAddress,
225
- scope: address,
226
- verb: "tell",
227
- });
228
- const directory = projectionDir(cwd, projection.id);
229
- assertTellEffortSupported(directory, flags.effort);
230
- // Mailbox first: do not inspect phase before this immutable write.
231
- writeInboxTell(directory, message, Date.now(), { effort: flags.effort });
232
- await tellProjection(directory, flags.effort);
233
- return textResponse("");
234
- };
235
- return address ? await withResolvedAddress(address, operation) : await operation();
236
- }),
237
- kill: commandSpec("kill", async (_stdin, flags, _signal, positional) => {
238
- const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
239
- const [projectionAddress] = positional;
240
- if (!projectionAddress)
241
- throw new FlowError("EMPTY_PARAM", "kill requires a projection address");
242
- const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
243
- const operation = async () => {
244
- const projection = await resolveProjectionAddress({
245
- cwd,
246
- address: projectionAddress,
247
- scope: address,
248
- verb: "kill",
249
- });
250
- const result = killProjectionGeneration(projectionDir(cwd, projection.id));
251
- const line = result.status === "killed"
252
- ? `× killed ${projection.id}`
253
- : `· already resting ${projection.id}`;
254
- return textResponse(line);
255
- };
256
- return address ? await withResolvedAddress(address, operation) : await operation();
257
- }),
258
- log: commandSpec("log", async (_stdin, flags) => {
259
- const cwd = await repositoryDirectory(flags);
260
- const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
261
- return await withResolvedAddress(address, async () => {
262
- const result = await readContractLog({ cwd, contractId: address.binding.commissionId });
263
- return buildLogResponse({ ...result, address });
264
- });
265
- }),
266
- akuma: commandSpec("akuma", async () => { throw missingSubcommandError("akuma", CLI_COMMAND_METADATA.akuma.subcommands ?? []); }),
267
- "akuma list": commandSpec("akuma list", async (_stdin, flags) => {
268
- const cwd = await projectDirectory(flags);
269
- const loaded = await loadKeiyakuSettings(cwd);
270
- // Role markers need settings pointers; catalog consumers keep legacy agents disease actionable.
271
- assertSettingsKnobUsable(loaded, "agents");
272
- assertSettingsKnobUsable(loaded, "default");
273
- assertSettingsKnobUsable(loaded, "reviewer");
274
- return textResponse(renderAkumaList(await loadAkumaCatalogV2(cwd), loaded.providerInstances, loaded.knobs));
275
- }),
276
- "akuma ls": commandSpec("akuma ls", async (_stdin, flags, signal, positional) => CLI_COMMAND_SPECS["akuma list"].run(_stdin, flags, signal, positional)),
277
- "akuma show": commandSpec("akuma show", async (_stdin, flags, _signal, positional) => {
278
- const [name] = positional;
279
- if (!name) {
280
- throw new FlowError("EMPTY_PARAM", "akuma show requires an Akuma name");
281
- }
282
- if (isResponseArtifactId(name)) {
283
- throw new FlowError("EMPTY_PARAM", `artifact ${name} is a response artifact; akuma show expects an Akuma profile name`);
35
+ if (nonblankStdin && domainFlags) {
36
+ throw new FlowError("EMPTY_PARAM", "bind accepts either stdin Markdown or the four domain flags, not both");
37
+ }
38
+ if (domainFlags) {
39
+ const missing = [];
40
+ if (flags.bindPlace === undefined)
41
+ missing.push("--place");
42
+ if (flags.bindObjective === undefined)
43
+ missing.push("--objective");
44
+ if (flags.bindScope === undefined)
45
+ missing.push("--scope");
46
+ if (flags.bindChecks === undefined)
47
+ missing.push("--checks");
48
+ if (missing.length > 0) {
49
+ throw new FlowError("EMPTY_PARAM", `bind flag mode requires --place, --objective, --scope, and --checks; missing ${missing.join(", ")}`);
284
50
  }
285
- const cwd = await projectDirectory(flags);
286
- const loaded = await loadKeiyakuSettings(cwd);
287
- // Role markers need settings pointers; catalog consumers keep legacy agents disease actionable.
288
- assertSettingsKnobUsable(loaded, "agents");
289
- assertSettingsKnobUsable(loaded, "default");
290
- assertSettingsKnobUsable(loaded, "reviewer");
291
- return textResponse(renderAkumaShow(await loadAkumaCatalogV2(cwd), loaded.providerInstances, loaded.knobs, name));
292
- }),
293
- skills: commandSpec("skills", async () => { throw missingSubcommandError("skills", CLI_COMMAND_METADATA.skills.subcommands ?? []); }),
294
- "skills install": commandSpec("skills install", async (_stdin, flags) => {
295
- const result = await installOfficialSkills(flags.force);
296
- return textResponse([
297
- "Installed official Keiyaku skills:",
298
- ...result.targets.map((target) => `- ${target.path} -> ${target.source} (${target.status})`),
299
- ...(result.warnings.length > 0 ? ["", "## Warning", ...result.warnings.map((warning) => `- ${warning}`)] : []),
300
- ].join("\n"));
301
- }),
302
- amend: commandSpec("amend", async (stdin, flags) => {
303
- const parsed = buildAmendInput(stdin, effectiveDirectory(flags), flags.contractId, flags.contractAddressSource);
304
- const cwd = await repositoryDirectory(flags);
305
- return renderCoreResult(amendKeiyaku({ ...parsed, cwd }), buildAmendResponse);
306
- }),
307
- renew: commandSpec("renew", async (_stdin, flags) => {
308
- const cwd = await repositoryDirectory(flags);
309
- const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
310
- return renderCoreResult(renewKeiyaku({ cwd, address, waivePaths: flags.waivePaths }), buildRenewResponse);
311
- }),
312
- petition: commandSpec("petition", async (stdin, flags, signal) => {
313
- const cwd = await repositoryDirectory(flags);
314
- const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
315
- return await withResolvedAddress(address, async () => {
316
- const input = buildPetitionClaimInput({ cwd, contractId: address.binding.commissionId, stdin, signal, waivePaths: flags.waivePaths });
317
- return buildPetitionClaimResponse({ ...await petitionKeiyaku(input), address }, input);
51
+ return compileBindDraftFlags({
52
+ place: flags.bindPlace,
53
+ objective: flags.bindObjective,
54
+ scope: flags.bindScope,
55
+ checks: flags.bindChecks,
318
56
  });
319
- }),
320
- forfeit: commandSpec("forfeit", async (_stdin, flags) => {
321
- const cwd = await repositoryDirectory(flags);
322
- const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
323
- return await withResolvedAddress(address, async () => buildForfeitResponse({
324
- ...await forfeitContractCommand({ cwd, contractId: address.binding.commissionId, ...defaultActorClock(), note: flags.reason }),
325
- address,
326
- }));
327
- }),
328
- guide: commandSpec("guide", async () => textResponse(buildKeiyakuGuide(VERSION))),
329
- completion: commandSpec("completion", async (_stdin, flags) => {
330
- const cwd = await projectDirectory(flags);
331
- if (flags.complete) {
332
- return textResponse(await renderCompletionCandidates({ cwd, word: flags.word, previous: flags.previous }));
333
- }
334
- return textResponse(renderCompletionScript(flags.shell));
335
- }),
336
- status: commandSpec("status", async (_stdin, flags) => {
337
- // Status is read-only and must open outside Git. projectDirectory still
338
- // honors --repo as a repository coordinate when the operator names one.
339
- const cwd = await projectDirectory(flags);
340
- const target = flags.target === undefined ? undefined : normalizeTargetRef(flags.target);
341
- return textResponse(renderStatusBoard(await readKanshiBoard(cwd, Date.now(), {
342
- ...(target === undefined ? {} : { target }),
343
- showAllProjections: flags.all === true,
344
- })));
345
- }),
346
- "dump-env": commandSpec("dump-env", async () => textResponse(renderEnvExample())),
347
- };
348
- function getCliCommandSpec(command) {
349
- return CLI_COMMAND_SPECS[command];
350
- }
351
- function commandRequiresStdin(command) {
352
- return getCliCommandSpec(command).stdin === "required";
353
- }
354
- function commandSavesFailedStdin(command) {
355
- return commandRequiresStdin(command) && command !== "call";
57
+ }
58
+ if (flags.dryRun) {
59
+ throw new FlowError("EMPTY_PARAM", "--dry-run requires --place, --objective, --scope, and --checks");
60
+ }
61
+ if (!nonblankStdin) {
62
+ throw new FlowError("EMPTY_PARAM", `No Markdown input received on stdin.\n\nExample:\n ${usageExample("bind")}`);
63
+ }
64
+ return stdin;
356
65
  }
357
66
  async function readStdin() {
358
67
  if (process.stdin.isTTY)
@@ -421,10 +130,15 @@ function conversationalFooter(response) {
421
130
  }
422
131
  return lines.join("\n");
423
132
  }
424
- function writeSuccessResponse(command, response) {
425
- // Successful tell is always silent: zero stdout bytes, even if text regresses.
426
- if (command === "tell")
133
+ function writeSuccessResponse(command, flags, response) {
134
+ // Plain tell is always silent; tell --wait owns the ordinary wait response.
135
+ if (command === "tell" && flags.timeoutMs === undefined)
136
+ return;
137
+ if (command === "bind" && flags.dryRun) {
138
+ // Canonical Markdown already ends with exactly one newline.
139
+ process.stdout.write(responseText(response));
427
140
  return;
141
+ }
428
142
  if (command !== "revive") {
429
143
  process.stdout.write(`${responseText(response)}\n`);
430
144
  return;
@@ -471,7 +185,7 @@ async function handleFailureAfterStdin(input) {
471
185
  else {
472
186
  await printFailure(input.error ?? new Error("Unknown CLI failure"), input.cwd);
473
187
  }
474
- if (!commandSavesFailedStdin(input.command)) {
188
+ if (input.saveStdin === false || !commandSavesFailedStdin(input.command)) {
475
189
  return 1;
476
190
  }
477
191
  try {
@@ -491,36 +205,45 @@ async function handleFailureAfterStdin(input) {
491
205
  }
492
206
  async function runResponse(command, flags, stdin, positional) {
493
207
  const signal = makeAbortSignal();
494
- return await getCliCommandSpec(command).run(stdin, flags, signal, positional);
208
+ const handler = await loadCommandHandler(command);
209
+ return await handler(stdin, flags, signal, positional);
495
210
  }
496
- function positionalPrompt(command, positional) {
211
+ function bodyToken(command, positional) {
497
212
  if (isCallCommand(command))
498
213
  return positional[0];
214
+ if (command === "tell")
215
+ return positional[1];
499
216
  if (command === "revive")
500
217
  return positional[1];
501
218
  return undefined;
502
219
  }
503
- async function readPromptInput(command, positional, cwd) {
220
+ async function readBodyInput(command, positional, cwd) {
504
221
  if (!commandUsesPromptInput(command))
505
222
  return undefined;
506
- const prompt = positionalPrompt(command, positional);
507
- if (prompt !== undefined && prompt !== "-")
508
- return prompt;
509
- const stdin = await readStdin();
510
- if (!stdin.trim()) {
511
- if (command === "revive") {
223
+ const token = bodyToken(command, positional);
224
+ if (token === undefined) {
225
+ if (command === "revive")
512
226
  return "";
227
+ await printFailure(new FlowError("EMPTY_PARAM", `${command} body cannot be empty`), cwd);
228
+ return undefined;
229
+ }
230
+ if (token !== "-") {
231
+ if (!token.trim()) {
232
+ await printFailure(new FlowError("EMPTY_PARAM", `${command} body cannot be empty`), cwd);
233
+ return undefined;
513
234
  }
514
- await printFailure(new FlowError("EMPTY_PARAM", `${command} requires a prompt argument, \`-\` with stdin, or prompt text on stdin.`, {
515
- hints: CALL_INPUT_HINTS,
516
- }), cwd);
235
+ return token;
236
+ }
237
+ const stdin = await readStdin();
238
+ if (!stdin.trim()) {
239
+ await printFailure(new FlowError("EMPTY_PARAM", `${command} body cannot be empty`), cwd);
517
240
  return undefined;
518
241
  }
519
242
  return stdin;
520
243
  }
521
244
  function usageExample(command) {
522
- const spec = getCliCommandSpec(command);
523
- return spec.examples?.[0] ?? spec.usage[0] ?? `keiyaku ${command}`;
245
+ const meta = getCommandMetadata(command);
246
+ return meta.examples?.[0] ?? meta.usage[0] ?? `keiyaku ${command}`;
524
247
  }
525
248
  export async function runCliCommand(tokens) {
526
249
  let parsed;
@@ -529,7 +252,7 @@ export async function runCliCommand(tokens) {
529
252
  }
530
253
  catch (error) {
531
254
  await printFailure(error, process.cwd());
532
- return 1;
255
+ return isPetitionDryRunInvocation(tokens) ? 2 : 1;
533
256
  }
534
257
  if (parsed.helpRequested) {
535
258
  process.stdout.write(`${renderCliCommandHelp(resolveCliHelpCommand(parsed.command))}\n`);
@@ -537,65 +260,89 @@ export async function runCliCommand(tokens) {
537
260
  }
538
261
  const cwd = parsed.flags.cwd ?? process.cwd();
539
262
  let stdin = "";
540
- if (commandRequiresStdin(parsed.command)) {
263
+ if (parsed.command === "task add") {
264
+ if (parsed.positional[0] === "-")
265
+ stdin = await readStdin();
266
+ if (!(parsed.positional[0] === "-" ? stdin : parsed.positional[0]).trim()) {
267
+ await printFailure(new FlowError("EMPTY_PARAM", "task add input cannot be blank"), cwd);
268
+ return 1;
269
+ }
270
+ }
271
+ else if (parsed.command === "task update" && parsed.flags.taskBody === "-") {
272
+ stdin = await readStdin();
273
+ }
274
+ else if (commandRequiresStdin(parsed.command)) {
541
275
  stdin = await readStdin();
542
276
  if (!stdin.trim()) {
543
277
  await printFailure(new FlowError("EMPTY_PARAM", `No Markdown input received on stdin.\n\nExample:\n ${usageExample(parsed.command)}`), cwd);
544
278
  return 1;
545
279
  }
546
280
  }
547
- else if (parsed.command === "petition") {
548
- // Optional stdin: empty must reach the oath gate as OATH_MISSING, not EMPTY_PARAM.
281
+ else if (commandAcceptsOptionalStdin(parsed.command)) {
282
+ // Optional stdin: bind prepares domain flags or blank-stdin errors itself;
283
+ // petition lets an empty body reach the oath gate as OATH_MISSING.
549
284
  stdin = await readStdin();
550
285
  }
551
286
  else {
552
- const prompt = await readPromptInput(parsed.command, parsed.positional, cwd);
553
- if (prompt === undefined && commandUsesPromptInput(parsed.command)) {
287
+ const body = await readBodyInput(parsed.command, parsed.positional, cwd);
288
+ if (body === undefined && commandUsesPromptInput(parsed.command)) {
289
+ return 1;
290
+ }
291
+ stdin = body ?? "";
292
+ }
293
+ // Bind dry-run must compile and return before repository/cwd resolution or filesystem effects.
294
+ if (parsed.command === "bind") {
295
+ try {
296
+ stdin = prepareBindInput(stdin, parsed.flags);
297
+ }
298
+ catch (error) {
299
+ await printFailure(error, cwd);
554
300
  return 1;
555
301
  }
556
- stdin = prompt ?? "";
557
302
  }
558
303
  let response;
559
304
  try {
560
305
  response = await runResponse(parsed.command, parsed.flags, stdin, parsed.positional);
561
306
  }
562
307
  catch (error) {
563
- if (commandRequiresStdin(parsed.command)) {
308
+ if (commandRequiresStdin(parsed.command) || (parsed.command === "bind" && !parsed.flags.dryRun)) {
564
309
  return await handleFailureAfterStdin({
565
310
  error,
566
311
  cwd,
567
312
  cwdFlag: parsed.flags.cwd,
568
313
  command: parsed.command,
569
314
  stdin,
315
+ saveStdin: !(parsed.command === "bind" && hasBindDomainFlags(parsed.flags)),
570
316
  });
571
317
  }
572
318
  await printFailure(error, cwd);
319
+ if (parsed.command === "petition" && parsed.flags.dryRun)
320
+ return 2;
573
321
  return failureExitCode(error);
574
322
  }
575
323
  const success = !response.payload || !("error" in response.payload);
576
324
  if (success) {
577
- writeSuccessResponse(parsed.command, response);
325
+ writeSuccessResponse(parsed.command, parsed.flags, response);
578
326
  return response.exitCode ?? 0;
579
327
  }
580
- if (commandRequiresStdin(parsed.command)) {
328
+ if (commandRequiresStdin(parsed.command) || (parsed.command === "bind" && !parsed.flags.dryRun)) {
581
329
  return await handleFailureAfterStdin({
582
330
  response,
583
331
  cwd,
584
332
  cwdFlag: parsed.flags.cwd,
585
333
  command: parsed.command,
586
334
  stdin,
335
+ saveStdin: !(parsed.command === "bind" && hasBindDomainFlags(parsed.flags)),
587
336
  });
588
337
  }
589
338
  process.stderr.write(`${responseText(response)}\n`);
590
339
  return 1;
591
340
  }
592
341
  export function renderCliCommandHelp(command) {
593
- const spec = getCliCommandSpec(command);
594
- return renderCliCommandHelpText(command, spec, FLAG_SPECS);
342
+ const meta = getCommandMetadata(command);
343
+ return renderCliCommandHelpText(command, meta, FLAG_SPECS);
595
344
  }
596
345
  function resolveCliHelpCommand(command) {
597
- if (command === "akuma ls")
598
- return "akuma list";
599
346
  return command;
600
347
  }
601
348
  export function renderCliHelp(version) {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Install a process-level broken-pipe policy on each supplied stream.
3
+ * EPIPE is silent; every other emitted value is rethrown unchanged.
4
+ */
5
+ export function installCliOutputStreamErrorPolicy(...streams) {
6
+ for (const stream of streams) {
7
+ stream.on("error", (error) => {
8
+ if (typeof error === "object" &&
9
+ error !== null &&
10
+ "code" in error &&
11
+ error.code === "EPIPE") {
12
+ return;
13
+ }
14
+ throw error;
15
+ });
16
+ }
17
+ }