@astrosheep/keiyaku 2.9.8 → 2.9.10

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 (230) hide show
  1. package/README.md +50 -182
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/harness/control-types.js +18 -1
  4. package/build/agents/harness/index.js +2 -0
  5. package/build/agents/harness/outcome-error.js +81 -0
  6. package/build/agents/harness/projection.js +10 -7
  7. package/build/agents/harness/router.js +5 -1
  8. package/build/agents/providers/claude-agent-sdk/adapter.js +18 -4
  9. package/build/agents/providers/claude-agent-sdk/registration.js +11 -1
  10. package/build/agents/providers/claude-agent-sdk/session.js +19 -5
  11. package/build/agents/providers/codex-app-server/adapter.js +11 -6
  12. package/build/agents/providers/codex-app-server/events.js +67 -6
  13. package/build/agents/providers/codex-app-server/registration.js +11 -1
  14. package/build/agents/providers/opencode-sdk/adapter.js +8 -1
  15. package/build/agents/providers/opencode-sdk/events.js +24 -2
  16. package/build/agents/providers/opencode-sdk/registration.js +11 -1
  17. package/build/agents/providers/pi/checkpoint.js +9 -1
  18. package/build/agents/providers/pi/events.js +2 -0
  19. package/build/agents/providers/pi/registration.js +14 -1
  20. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  21. package/build/cli/commands/akuma/akuma/meta.js +3 -3
  22. package/build/cli/commands/akuma/catalog.js +2 -2
  23. package/build/cli/commands/akuma/{view → show}/handler.js +5 -5
  24. package/build/cli/commands/akuma/{view → show}/meta.js +3 -3
  25. package/build/cli/commands/akuma.js +1 -1
  26. package/build/cli/commands/contract/amend/handler.js +5 -4
  27. package/build/cli/commands/contract/arc/handler.js +5 -4
  28. package/build/cli/commands/contract/audit/handler.js +2 -2
  29. package/build/cli/commands/contract/bind/handler.js +12 -3
  30. package/build/cli/commands/contract/bind/meta.js +45 -15
  31. package/build/cli/commands/contract/forfeit/handler.js +9 -5
  32. package/build/cli/commands/contract/log/handler.js +2 -2
  33. package/build/cli/commands/contract/petition/handler.js +34 -7
  34. package/build/cli/commands/contract/renew/handler.js +7 -7
  35. package/build/cli/commands/contract/renew/meta.js +3 -3
  36. package/build/cli/commands/metadata.js +10 -6
  37. package/build/cli/commands/projection/call/meta.js +1 -1
  38. package/build/cli/commands/projection/catalog.js +2 -0
  39. package/build/cli/commands/projection/history/handler.js +21 -0
  40. package/build/cli/commands/projection/history/meta.js +9 -0
  41. package/build/cli/commands/projection/shared.js +3 -4
  42. package/build/cli/commands/projection/status/handler.js +3 -3
  43. package/build/cli/commands/projection/wait/handler.js +20 -11
  44. package/build/cli/commands/task/add/handler.js +14 -5
  45. package/build/cli/commands/task/add/meta.js +2 -2
  46. package/build/cli/commands/task/catalog.js +4 -0
  47. package/build/cli/commands/task/done/handler.js +1 -4
  48. package/build/cli/commands/task/done/meta.js +1 -1
  49. package/build/cli/commands/task/drop/meta.js +1 -1
  50. package/build/cli/commands/task/hold/handler.js +3 -0
  51. package/build/cli/commands/task/hold/meta.js +8 -0
  52. package/build/cli/commands/task/log/handler.js +1 -1
  53. package/build/cli/commands/task/log/meta.js +2 -2
  54. package/build/cli/commands/task/resume/handler.js +3 -0
  55. package/build/cli/commands/task/resume/meta.js +8 -0
  56. package/build/cli/commands/task/shared.js +39 -14
  57. package/build/cli/commands/task/show/handler.js +1 -1
  58. package/build/cli/commands/task/show/meta.js +1 -1
  59. package/build/cli/commands/task/start/meta.js +1 -1
  60. package/build/cli/commands/task/stop/handler.js +1 -4
  61. package/build/cli/commands/task/stop/meta.js +1 -1
  62. package/build/cli/commands/task/update/handler.js +11 -3
  63. package/build/cli/commands/task/update/meta.js +3 -3
  64. package/build/cli/completion.js +3 -2
  65. package/build/cli/flags.js +51 -14
  66. package/build/cli/help.js +4 -1
  67. package/build/cli/index.js +40 -25
  68. package/build/cli/parse-flags.js +40 -7
  69. package/build/cli/parse-metadata.js +5 -2
  70. package/build/cli/parse-selectors.js +2 -3
  71. package/build/cli/parse.js +40 -6
  72. package/build/cli/projection-address.js +12 -10
  73. package/build/cli/render/amendment-warning.js +8 -0
  74. package/build/cli/render/arc.js +6 -2
  75. package/build/cli/render/audit.js +400 -81
  76. package/build/cli/render/kanshi.js +242 -79
  77. package/build/cli/render/lifecycle.js +56 -0
  78. package/build/cli/render/misc.js +24 -28
  79. package/build/cli/render/petition.js +5 -0
  80. package/build/cli/render/projection-activity.js +65 -29
  81. package/build/cli/render/projection-history.js +64 -0
  82. package/build/cli/render/shared.js +23 -9
  83. package/build/cli/render/status.js +13 -35
  84. package/build/cli/render/task.js +51 -26
  85. package/build/cli/render/terminal-failure.js +3 -1
  86. package/build/cli/render/wait.js +45 -69
  87. package/build/cli/subagent-guard.js +1 -1
  88. package/build/cli/types.js +1 -1
  89. package/build/config/env-keys.js +3 -1
  90. package/build/config/env.js +24 -1
  91. package/build/config/settings/schema.js +3 -1
  92. package/build/core/address-carrier.js +89 -0
  93. package/build/core/addressing.js +255 -196
  94. package/build/core/amend.js +115 -38
  95. package/build/core/amendment-criteria.js +17 -0
  96. package/build/core/amendment-warning.js +10 -0
  97. package/build/core/arc.js +184 -83
  98. package/build/core/audit/candidate.js +110 -9
  99. package/build/core/audit/coordinates.js +120 -65
  100. package/build/core/audit/evidence.js +19 -23
  101. package/build/core/audit/facade.js +34 -11
  102. package/build/core/audit/report.js +5 -5
  103. package/build/core/bind-reconciliation.js +390 -0
  104. package/build/core/bind-workspace.js +12 -20
  105. package/build/core/bind.js +242 -273
  106. package/build/core/call/call.js +1 -0
  107. package/build/core/call/context.js +46 -71
  108. package/build/core/call/prompt.js +6 -3
  109. package/build/core/context.js +49 -35
  110. package/build/core/contract-carrier-runtime.js +408 -0
  111. package/build/core/contract-carrier.js +270 -0
  112. package/build/core/contract-view.js +33 -27
  113. package/build/core/contract.js +5 -1
  114. package/build/core/derived-replay.js +207 -0
  115. package/build/core/draft.js +125 -57
  116. package/build/core/forfeit.js +113 -56
  117. package/build/core/identifier-slug.js +63 -13
  118. package/build/core/ids.js +4 -4
  119. package/build/core/lifecycle-history.js +35 -0
  120. package/build/core/lifecycle-recovery.js +117 -0
  121. package/build/core/lifecycle-runner.js +377 -0
  122. package/build/core/log.js +37 -11
  123. package/build/core/outcome-base.js +181 -0
  124. package/build/core/process-group.js +139 -0
  125. package/build/core/projection/generation/database.js +82 -7
  126. package/build/core/projection/generation/model.js +9 -36
  127. package/build/core/projection/generation/projection-generation-continuation.js +10 -22
  128. package/build/core/projection/generation/projection-generation-execution.js +13 -6
  129. package/build/core/projection/index.js +5 -4
  130. package/build/core/projection/projection-activity.js +29 -8
  131. package/build/core/projection/projection-alias.js +23 -10
  132. package/build/core/projection/projection-core.js +21 -7
  133. package/build/core/projection/projection-execution-observer.js +91 -39
  134. package/build/core/projection/projection-history.js +474 -0
  135. package/build/core/projection/projection-runner-lock.js +12 -0
  136. package/build/core/projection/projection-status-observation.js +365 -0
  137. package/build/core/projection/projection-status.js +35 -337
  138. package/build/core/projection/projection-terminal-failure.js +13 -8
  139. package/build/core/projection/projection-wait.js +7 -2
  140. package/build/core/projection/tell/launch-store.js +244 -0
  141. package/build/core/projection/tell/model.js +3 -3
  142. package/build/core/projection/tell/store.js +70 -118
  143. package/build/core/registry.js +22 -87
  144. package/build/core/renew-build.js +424 -0
  145. package/build/core/renew-plan.js +239 -0
  146. package/build/core/renew-rewrite.js +77 -0
  147. package/build/core/renew.js +295 -520
  148. package/build/core/repository-ledger/accepted-fold-read.js +19 -0
  149. package/build/core/repository-ledger/accepted-tail.js +116 -0
  150. package/build/core/repository-ledger/admission.js +38 -0
  151. package/build/core/repository-ledger/authoritative-transaction.js +1 -0
  152. package/build/core/repository-ledger/claim-evidence.js +147 -0
  153. package/build/core/repository-ledger/codec.js +297 -0
  154. package/build/core/repository-ledger/current-state-store.js +853 -0
  155. package/build/core/repository-ledger/fold-repository.js +375 -0
  156. package/build/core/repository-ledger/fold.js +248 -0
  157. package/build/core/repository-ledger/git-object-store.js +243 -0
  158. package/build/core/repository-ledger/history-read.js +15 -0
  159. package/build/core/repository-ledger/incremental-read.js +153 -0
  160. package/build/core/repository-ledger/index.js +15 -0
  161. package/build/core/repository-ledger/inventory-plan.js +75 -0
  162. package/build/core/repository-ledger/inventory.js +310 -0
  163. package/build/core/repository-ledger/publication-admission.js +125 -0
  164. package/build/core/repository-ledger/publication.js +353 -0
  165. package/build/core/repository-ledger/read-model.js +469 -0
  166. package/build/core/repository-ledger/transaction-membership.js +78 -0
  167. package/build/core/repository-ledger/traversal.js +404 -0
  168. package/build/core/repository-ledger/write-transaction.js +339 -0
  169. package/build/core/seal.js +168 -161
  170. package/build/core/settlement/claim-delivery.js +42 -215
  171. package/build/core/settlement/claim.js +235 -153
  172. package/build/core/settlement/index.js +4 -4
  173. package/build/core/settlement/petition-claim-gates.js +52 -33
  174. package/build/core/settlement/petition-forfeit.js +12 -5
  175. package/build/core/settlement/petition-head-guard.js +8 -5
  176. package/build/core/settlement/petition-preview.js +68 -56
  177. package/build/core/settlement/petition-window.js +50 -0
  178. package/build/core/settlement/petition.js +252 -131
  179. package/build/core/settlement/queue-read-model.js +72 -95
  180. package/build/core/settlement/queue-seat-allocation.js +35 -126
  181. package/build/core/settlement/queue.js +1 -1
  182. package/build/core/settlement/settlement.js +172 -80
  183. package/build/core/settlement/verdict.js +112 -66
  184. package/build/core/settlement/verification-coordination.js +305 -0
  185. package/build/core/settlement/verification-supervisor.js +275 -0
  186. package/build/core/settlement/verification.js +398 -31
  187. package/build/core/status/board.js +125 -127
  188. package/build/core/status/lifecycle.js +7 -80
  189. package/build/core/structured-query.js +23 -0
  190. package/build/core/target-ref.js +1 -1
  191. package/build/core/task/board.js +59 -24
  192. package/build/core/task/commands.js +181 -33
  193. package/build/core/task/coordinate.js +56 -0
  194. package/build/core/task/document.js +25 -9
  195. package/build/core/task/index.js +7 -4
  196. package/build/core/task/query.js +17 -27
  197. package/build/core/task/settlement-git.js +164 -179
  198. package/build/core/task/settlement-policy.js +15 -11
  199. package/build/core/task/source-board.js +31 -13
  200. package/build/core/task/task-bind-preparation.js +193 -0
  201. package/build/core/task/task-contract.js +102 -193
  202. package/build/core/task/task-file-transaction.js +171 -0
  203. package/build/core/task/task-git-runtime.js +76 -14
  204. package/build/core/task/task-git-store.js +68 -169
  205. package/build/core/task/task-store-repository.js +13 -6
  206. package/build/core/task/task-worktree.js +6 -33
  207. package/build/core/task/task.js +1 -1
  208. package/build/core/task/tree.js +3 -3
  209. package/build/core/task/validation.js +4 -3
  210. package/build/core/transcripts.js +93 -1
  211. package/build/core/worktree-bootstrap.js +44 -36
  212. package/build/core/worktree-path.js +210 -77
  213. package/build/flow-error.js +7 -7
  214. package/build/generated/version.js +2 -2
  215. package/build/git/core.js +76 -13
  216. package/build/git/refs.js +168 -46
  217. package/build/git/streaming-batch.js +73 -13
  218. package/package.json +1 -1
  219. package/skills/keiyaku/SKILL.md +1 -1
  220. package/skills/keiyaku-akuma/SKILL.md +3 -3
  221. package/skills/keiyaku-task/SKILL.md +4 -2
  222. package/skills/keiyaku-workflow/SKILL.md +88 -203
  223. package/build/core/entry.js +0 -305
  224. package/build/core/ledger-batch.js +0 -194
  225. package/build/core/ledger.js +0 -84
  226. package/build/core/projection/leash.js +0 -87
  227. package/build/core/ref-log.js +0 -119
  228. package/build/core/renew-session.js +0 -128
  229. package/build/core/status/ledger-batch.js +0 -1
  230. package/build/core/status/reconciliation.js +0 -137
@@ -83,22 +83,37 @@ export const FLAG_SPECS = {
83
83
  place: {
84
84
  kind: "value",
85
85
  placeholder: "NAME",
86
- description: "Set the H1 contract name for flag-generated bind Markdown.",
86
+ description: "Legacy bind title spelling; bind rejects it with --title migration guidance.",
87
87
  },
88
88
  objective: {
89
89
  kind: "value",
90
90
  placeholder: "TEXT",
91
91
  description: "Set the objective for flag-generated bind Markdown.",
92
92
  },
93
+ context: {
94
+ kind: "value",
95
+ placeholder: "TEXT",
96
+ description: "Set established facts and constraints for flag-generated bind Markdown.",
97
+ },
98
+ design: {
99
+ kind: "value",
100
+ placeholder: "TEXT",
101
+ description: "Set the settled design for flag-generated bind Markdown.",
102
+ },
93
103
  scope: {
94
104
  kind: "value",
95
105
  placeholder: "GLOB",
96
106
  description: "Set the single scope pattern for flag-generated bind Markdown.",
97
107
  },
98
- checks: {
108
+ criteria: {
99
109
  kind: "value",
100
- placeholder: "CHECK",
101
- description: "Set the single acceptance check for flag-generated bind Markdown.",
110
+ placeholder: "CRITERION",
111
+ description: "Set the single declarative acceptance criterion for flag-generated bind Markdown.",
112
+ },
113
+ verification: {
114
+ kind: "value",
115
+ placeholder: "DECLARATION",
116
+ description: "Set the single executable verification declaration for flag-generated bind Markdown.",
102
117
  },
103
118
  task: {
104
119
  kind: "value",
@@ -132,17 +147,13 @@ export const FLAG_SPECS = {
132
147
  kind: "boolean",
133
148
  description: "Renew onto the current target before petitioning.",
134
149
  },
135
- resolve: {
136
- kind: "boolean",
137
- description: "Open a durable Keiyaku-owned renew recovery session when the rewrite conflicts.",
138
- },
139
150
  continue: {
140
151
  kind: "boolean",
141
- description: "Continue the open Keiyaku-owned renew recovery session.",
152
+ description: "Continue from the derived renew conflict scratch.",
142
153
  },
143
154
  abort: {
144
155
  kind: "boolean",
145
- description: "Restore and remove the open Keiyaku-owned renew recovery session.",
156
+ description: "Delete the derived renew conflict scratch without moving authoritative refs.",
146
157
  },
147
158
  shell: {
148
159
  kind: "value",
@@ -174,7 +185,7 @@ export const FLAG_SPECS = {
174
185
  },
175
186
  json: {
176
187
  kind: "boolean",
177
- description: "Render task data as JSON.",
188
+ description: "Render structured data as JSON.",
178
189
  },
179
190
  "diff-budget": {
180
191
  kind: "value",
@@ -188,12 +199,12 @@ export const FLAG_SPECS = {
188
199
  filter: {
189
200
  kind: "value",
190
201
  placeholder: TASK_FILTER_GRAMMAR,
191
- description: "Filter structured task facts. Repeat --filter for AND; comma values or | terms are OR.",
202
+ description: "Filter structured facts. Repeat --filter for AND; comma values or | terms are OR.",
192
203
  },
193
204
  sort: {
194
205
  kind: "value",
195
206
  placeholder: "KEY[,KEY...]",
196
- description: "Sort by priority ascending, modified descending, or created descending.",
207
+ description: "Sort the selected structured collection.",
197
208
  },
198
209
  pri: {
199
210
  kind: "value",
@@ -224,16 +235,41 @@ export const FLAG_SPECS = {
224
235
  placeholder: "ID",
225
236
  description: "Add a discovery-lineage task. May be repeated.",
226
237
  },
238
+ supersedes: {
239
+ kind: "value",
240
+ placeholder: "ID",
241
+ description: "Record that this task supersedes another task. May be repeated.",
242
+ },
243
+ "drop-supersedes": {
244
+ kind: "value",
245
+ placeholder: "ID",
246
+ description: "Remove a supersedes relation. May be repeated.",
247
+ },
248
+ "relates-to": {
249
+ kind: "value",
250
+ placeholder: "ID",
251
+ description: "Record related work. May be repeated.",
252
+ },
253
+ "drop-relates-to": {
254
+ kind: "value",
255
+ placeholder: "ID",
256
+ description: "Remove a related-work relation. May be repeated.",
257
+ },
227
258
  title: {
228
259
  kind: "value",
229
260
  placeholder: "TEXT",
230
- description: "Replace the task title.",
261
+ description: "Set the bind document title or replace the task title.",
231
262
  },
232
263
  body: {
233
264
  kind: "value",
234
265
  placeholder: "TEXT|-",
235
266
  description: "Replace the task body; literal - reads stdin.",
236
267
  },
268
+ append: {
269
+ kind: "value",
270
+ placeholder: "TEXT|-",
271
+ description: "Append a Markdown paragraph to the task body; literal - reads stdin.",
272
+ },
237
273
  };
238
274
  export const CONTROL_FLAGS = new Set(Object.keys(FLAG_SPECS));
239
275
  export const VALUE_FLAGS = new Set(Object.entries(FLAG_SPECS)
@@ -244,5 +280,6 @@ export const BOOLEAN_FLAGS = new Set(Object.entries(FLAG_SPECS)
244
280
  .map(([name]) => name));
245
281
  export const SHORT_FLAG_ALIASES = {
246
282
  C: "cwd",
283
+ d: "detach",
247
284
  f: "force",
248
285
  };
package/build/cli/help.js CHANGED
@@ -13,6 +13,9 @@ function flagUsage(name, specs) {
13
13
  if (name === "cwd") {
14
14
  return `-C, --cwd ${spec?.placeholder ?? "DIR"}`;
15
15
  }
16
+ if (name === "detach") {
17
+ return "-d, --detach";
18
+ }
16
19
  return spec?.kind === "value" ? `--${name} ${spec.placeholder ?? "VALUE"}` : `--${name}`;
17
20
  }
18
21
  export { CLI_COMMAND_METADATA };
@@ -69,7 +72,7 @@ export function renderCliHelp(version) {
69
72
  "Configuration:",
70
73
  " Akuma profiles <KEIYAKU_HOME>/akuma/*.md or .keiyaku/akuma/*.md",
71
74
  " Settings <KEIYAKU_HOME>/settings.json or .keiyaku/settings.json",
72
- " Inspect keiyaku akuma ls | keiyaku akuma view NAME",
75
+ " Inspect keiyaku akuma ls | keiyaku akuma show NAME",
73
76
  " Environment keiyaku dump-env",
74
77
  " Full format README Configuration",
75
78
  ].join("\n");
@@ -23,42 +23,54 @@ function commandSavesFailedStdin(command) {
23
23
  return (commandRequiresStdin(command) || commandAcceptsOptionalStdin(command)) && command !== "call";
24
24
  }
25
25
  function hasBindDomainFlags(flags) {
26
- return (flags.bindPlace !== undefined
26
+ return (flags.bindTitle !== undefined
27
+ || flags.bindContext !== undefined
27
28
  || flags.bindObjective !== undefined
29
+ || flags.bindDesign !== undefined
28
30
  || flags.bindScope !== undefined
29
- || flags.bindChecks !== undefined);
31
+ || flags.bindCriteria !== undefined
32
+ || flags.bindVerification !== undefined);
30
33
  }
31
- function prepareBindInput(stdin, flags) {
34
+ function prepareBindInput(stdin, flags, selectsStdin) {
35
+ if (flags.contractId !== undefined)
36
+ return "";
32
37
  const nonblankStdin = stdin.trim().length > 0;
33
38
  const domainFlags = hasBindDomainFlags(flags);
34
39
  if (flags.dryRun && flags.draftOnly) {
35
40
  throw new FlowError("EMPTY_PARAM", "--dry-run cannot be combined with --draft-only");
36
41
  }
37
- if (nonblankStdin && domainFlags) {
38
- throw new FlowError("EMPTY_PARAM", "bind accepts either stdin Markdown or the four domain flags, not both");
42
+ if (selectsStdin && domainFlags) {
43
+ throw new FlowError("EMPTY_PARAM", "bind accepts either stdin Markdown or domain flags, not both");
39
44
  }
40
45
  if (domainFlags) {
41
46
  const missing = [];
42
- if (flags.bindPlace === undefined)
43
- missing.push("--place");
47
+ if (flags.bindTitle === undefined)
48
+ missing.push("--title");
49
+ if (flags.bindContext === undefined)
50
+ missing.push("--context");
44
51
  if (flags.bindObjective === undefined)
45
52
  missing.push("--objective");
53
+ if (flags.bindDesign === undefined)
54
+ missing.push("--design");
46
55
  if (flags.bindScope === undefined)
47
56
  missing.push("--scope");
48
- if (flags.bindChecks === undefined)
49
- missing.push("--checks");
50
- if (missing.length > 0) {
51
- throw new FlowError("EMPTY_PARAM", `bind flag mode requires --place, --objective, --scope, and --checks; missing ${missing.join(", ")}`);
57
+ if (flags.bindCriteria === undefined)
58
+ missing.push("--criteria");
59
+ if (missing.length > 0 && (flags.bindTaskIds?.length ?? 0) === 0) {
60
+ throw new FlowError("EMPTY_PARAM", `bind flag mode requires --title, --context, --objective, --design, --scope, and --criteria; missing ${missing.join(", ")}`);
52
61
  }
53
62
  return compileBindDraftFlags({
54
- place: flags.bindPlace,
63
+ title: flags.bindTitle,
64
+ context: flags.bindContext,
55
65
  objective: flags.bindObjective,
66
+ design: flags.bindDesign,
56
67
  scope: flags.bindScope,
57
- checks: flags.bindChecks,
68
+ criteria: flags.bindCriteria,
69
+ verification: flags.bindVerification,
58
70
  });
59
71
  }
60
72
  if (!nonblankStdin) {
61
- // --task may supply name/objective defaults; core merges and reports missing sections.
73
+ // --task may supply title/objective defaults; core merges and reports missing sections.
62
74
  if (flags.bindTaskIds !== undefined && flags.bindTaskIds.length > 0) {
63
75
  return "";
64
76
  }
@@ -76,6 +88,7 @@ async function readStdin() {
76
88
  function makeAbortSignal() {
77
89
  const controller = new AbortController();
78
90
  process.once("SIGINT", () => controller.abort());
91
+ process.once("SIGTERM", () => controller.abort());
79
92
  return controller.signal;
80
93
  }
81
94
  function responseText(response) {
@@ -93,8 +106,8 @@ async function saveFailedStdin(input) {
93
106
  /**
94
107
  * Rebuild bind flags for a failed-stdin retry in deterministic shell-safe order.
95
108
  * Order follows bind usage/parse authority: cwd, repo, after, exclusive,
96
- * draft-only, task. Domain-flag bind does not save stdin, so place/objective/
97
- * scope/checks are not reconstructed. --dry-run never reaches this path.
109
+ * draft-only, task. Domain-flag bind does not save stdin, so title/objective/
110
+ * scope/criteria are not reconstructed. --dry-run never reaches this path.
98
111
  */
99
112
  function bindRetryFlagTokens(flags) {
100
113
  const tokens = [];
@@ -190,13 +203,13 @@ function selectedArgument(parsed) {
190
203
  return undefined;
191
204
  return { token, replace: () => parsed.flags };
192
205
  }
193
- const flagName = stdin.selector.name;
194
- const token = parsed.flags[flagName];
206
+ const flagName = stdin.selector.names.find((name) => parsed.flags[name] !== undefined);
207
+ const token = flagName === undefined ? undefined : parsed.flags[flagName];
195
208
  if (token === undefined && !stdin.selector.required)
196
209
  return undefined;
197
210
  return {
198
211
  token,
199
- replace: (value) => ({ ...parsed.flags, [flagName]: value }),
212
+ replace: (value) => flagName === undefined ? parsed.flags : ({ ...parsed.flags, [flagName]: value }),
200
213
  };
201
214
  }
202
215
  async function resolveCliInput(parsed) {
@@ -221,7 +234,7 @@ async function resolveCliInput(parsed) {
221
234
  : `${parsed.command} body cannot be empty`;
222
235
  throw new FlowError("EMPTY_PARAM", message);
223
236
  }
224
- return { stdin: input, flags: selected?.replace(input) ?? parsed.flags };
237
+ return { stdin: input, flags: selected?.replace(input) ?? parsed.flags, selectsStdin };
225
238
  }
226
239
  function usageExample(command) {
227
240
  const meta = getCommandMetadata(command);
@@ -242,9 +255,11 @@ export async function runCliCommand(tokens) {
242
255
  }
243
256
  const cwd = parsed.flags.cwd ?? process.cwd();
244
257
  let stdin;
258
+ let selectsStdin = false;
245
259
  try {
246
260
  const resolved = await resolveCliInput(parsed);
247
261
  stdin = resolved.stdin;
262
+ selectsStdin = resolved.selectsStdin;
248
263
  parsed = { ...parsed, flags: resolved.flags };
249
264
  }
250
265
  catch (error) {
@@ -255,7 +270,7 @@ export async function runCliCommand(tokens) {
255
270
  // repository admission as bind but does not persist an artifact on failure.
256
271
  if (parsed.command === "bind") {
257
272
  try {
258
- stdin = prepareBindInput(stdin, parsed.flags);
273
+ stdin = prepareBindInput(stdin, parsed.flags, selectsStdin);
259
274
  }
260
275
  catch (error) {
261
276
  await printFailure(error, cwd);
@@ -267,7 +282,7 @@ export async function runCliCommand(tokens) {
267
282
  response = await runResponse(parsed.command, parsed.flags, stdin, parsed.positional);
268
283
  }
269
284
  catch (error) {
270
- if (commandRequiresStdin(parsed.command) || (parsed.command === "bind" && !parsed.flags.dryRun)) {
285
+ if (commandRequiresStdin(parsed.command) || (parsed.command === "bind" && !parsed.flags.dryRun && parsed.flags.contractId === undefined)) {
271
286
  return await handleFailureAfterStdin({
272
287
  error,
273
288
  cwd,
@@ -282,12 +297,12 @@ export async function runCliCommand(tokens) {
282
297
  return 2;
283
298
  return failureExitCode(error);
284
299
  }
285
- const success = !response.payload || !("error" in response.payload);
300
+ const success = !response.isDomainFailure && (!response.payload || !("error" in response.payload));
286
301
  if (success) {
287
302
  await writeSuccessResponse(parsed.command, parsed.flags, response);
288
303
  return response.exitCode ?? 0;
289
304
  }
290
- if (commandRequiresStdin(parsed.command) || (parsed.command === "bind" && !parsed.flags.dryRun)) {
305
+ if (commandRequiresStdin(parsed.command) || (parsed.command === "bind" && !parsed.flags.dryRun && parsed.flags.contractId === undefined)) {
291
306
  return await handleFailureAfterStdin({
292
307
  response,
293
308
  cwd,
@@ -298,7 +313,7 @@ export async function runCliCommand(tokens) {
298
313
  });
299
314
  }
300
315
  await writeCliOutput(process.stderr, `${responseText(response)}\n`);
301
- return 1;
316
+ return response.exitCode ?? 1;
302
317
  }
303
318
  export function renderCliCommandHelp(command) {
304
319
  const meta = getCommandMetadata(command);
@@ -75,17 +75,35 @@ export function assignFlag(flags, name, value) {
75
75
  }
76
76
  flags.bindObjective = String(value);
77
77
  return;
78
+ case "context":
79
+ if (flags.bindContext !== undefined) {
80
+ throw new FlowError("EMPTY_PARAM", "option --context may only be specified once");
81
+ }
82
+ flags.bindContext = String(value);
83
+ return;
84
+ case "design":
85
+ if (flags.bindDesign !== undefined) {
86
+ throw new FlowError("EMPTY_PARAM", "option --design may only be specified once");
87
+ }
88
+ flags.bindDesign = String(value);
89
+ return;
78
90
  case "scope":
79
91
  if (flags.bindScope !== undefined) {
80
92
  throw new FlowError("EMPTY_PARAM", "option --scope may only be specified once");
81
93
  }
82
94
  flags.bindScope = String(value);
83
95
  return;
84
- case "checks":
85
- if (flags.bindChecks !== undefined) {
86
- throw new FlowError("EMPTY_PARAM", "option --checks may only be specified once");
96
+ case "criteria":
97
+ if (flags.bindCriteria !== undefined) {
98
+ throw new FlowError("EMPTY_PARAM", "option --criteria may only be specified once");
87
99
  }
88
- flags.bindChecks = String(value);
100
+ flags.bindCriteria = String(value);
101
+ return;
102
+ case "verification":
103
+ if (flags.bindVerification !== undefined) {
104
+ throw new FlowError("EMPTY_PARAM", "option --verification may only be specified once");
105
+ }
106
+ flags.bindVerification = String(value);
89
107
  return;
90
108
  case "task":
91
109
  flags.bindTaskIds = [...(flags.bindTaskIds ?? []), String(value)];
@@ -108,9 +126,6 @@ export function assignFlag(flags, name, value) {
108
126
  case "renew":
109
127
  flags.renewBeforePetition = Boolean(value);
110
128
  return;
111
- case "resolve":
112
- flags.renewResolve = Boolean(value);
113
- return;
114
129
  case "continue":
115
130
  flags.renewContinue = Boolean(value);
116
131
  return;
@@ -184,6 +199,18 @@ export function assignFlag(flags, name, value) {
184
199
  case "from":
185
200
  flags.taskFrom = [...(flags.taskFrom ?? []), String(value)];
186
201
  return;
202
+ case "supersedes":
203
+ flags.taskSupersedes = [...(flags.taskSupersedes ?? []), String(value)];
204
+ return;
205
+ case "drop-supersedes":
206
+ flags.taskDropSupersedes = [...(flags.taskDropSupersedes ?? []), String(value)];
207
+ return;
208
+ case "relates-to":
209
+ flags.taskRelatesTo = [...(flags.taskRelatesTo ?? []), String(value)];
210
+ return;
211
+ case "drop-relates-to":
212
+ flags.taskDropRelatesTo = [...(flags.taskDropRelatesTo ?? []), String(value)];
213
+ return;
187
214
  case "parent":
188
215
  if (flags.taskParent !== undefined) {
189
216
  throw new FlowError("EMPTY_PARAM", "option --parent may only be specified once");
@@ -205,6 +232,12 @@ export function assignFlag(flags, name, value) {
205
232
  }
206
233
  flags.taskBody = String(value);
207
234
  return;
235
+ case "append":
236
+ if (flags.taskAppend !== undefined) {
237
+ throw new FlowError("EMPTY_PARAM", "option --append may only be specified once");
238
+ }
239
+ flags.taskAppend = String(value);
240
+ return;
208
241
  default:
209
242
  throw new FlowError("EMPTY_PARAM", `unknown option: --${name}`);
210
243
  }
@@ -56,7 +56,7 @@ export function parseWaitTimeout(value) {
56
56
  return timeoutMs;
57
57
  }
58
58
  export function commandPositionalRange(command) {
59
- if (command === "akuma view") {
59
+ if (command === "akuma show") {
60
60
  return { min: 1, max: 1, label: "<name>" };
61
61
  }
62
62
  if (command === "log") {
@@ -80,10 +80,13 @@ export function commandPositionalRange(command) {
80
80
  if (command === "status") {
81
81
  return { min: 0, max: 1, label: "[projection-id]" };
82
82
  }
83
+ if (command === "history") {
84
+ return { min: 0, max: 1, label: "[projection|alias|rsp]" };
85
+ }
83
86
  if (command === "task add") {
84
87
  return { min: 1, max: 1, label: "<BODY|->" };
85
88
  }
86
- if (command === "task show" || command === "task log" || command === "task start" || command === "task drop") {
89
+ if (command === "task show" || command === "task log" || command === "task start" || command === "task hold" || command === "task resume" || command === "task drop") {
87
90
  return { min: 1, max: 1, label: "<ID>" };
88
91
  }
89
92
  if (command === "task update") {
@@ -2,6 +2,7 @@ import { FlowError } from "../flow-error.js";
2
2
  import { assignFlag } from "./parse-flags.js";
3
3
  import { normalizeContractAddress } from "./parse-metadata.js";
4
4
  const COMMISSION_SELECTOR_COMMANDS = new Set([
5
+ "bind",
5
6
  "arc",
6
7
  "amend",
7
8
  "call",
@@ -45,9 +46,7 @@ export function assertCommissionSelectorAllowed(command, flags) {
45
46
  if (flags.contractId === undefined || COMMISSION_SELECTOR_COMMANDS.has(command)) {
46
47
  return;
47
48
  }
48
- throw new FlowError("EMPTY_PARAM", command === "bind"
49
- ? `bind creates a commission and does not accept @${flags.contractId}; use -C/--cwd to select the repository`
50
- : `contract address @${flags.contractId} is not valid for ${command}`);
49
+ throw new FlowError("EMPTY_PARAM", `contract address @${flags.contractId} is not valid for ${command}`);
51
50
  }
52
51
  export function assertProjectionSelectorPresent(command, flags, atTokens, contractFlagRaws, restLength, payloadLength) {
53
52
  if ((command !== "wait" && command !== "tell") || flags.contractId === undefined) {
@@ -1,6 +1,7 @@
1
1
  import { FlowError } from "../flow-error.js";
2
2
  import { agentNameSchema } from "../config/settings/schema.js";
3
3
  import { isResponseArtifactId } from "../core/response-artifact-id.js";
4
+ import { isCommissionId } from "../core/ids.js";
4
5
  import { HELP_FLAGS } from "./flags.js";
5
6
  import { getCommandMetadata } from "./commands/metadata.js";
6
7
  import { assertAllowedFlag, assignFlag, BOOLEAN_FLAGS, CONTROL_FLAGS, parseFlagName, readValueFlag, SHORT_FLAG_ALIASES, VALUE_FLAGS, } from "./parse-flags.js";
@@ -102,7 +103,7 @@ export function parseCliArgs(tokens) {
102
103
  };
103
104
  }
104
105
  if (command === "akuma") {
105
- throw new FlowError("EMPTY_PARAM", "akuma requires subcommand: ls or view");
106
+ throw new FlowError("EMPTY_PARAM", "akuma requires subcommand: ls or show");
106
107
  }
107
108
  if (command === "skills") {
108
109
  throw new FlowError("EMPTY_PARAM", "skills requires subcommand: install");
@@ -110,12 +111,19 @@ export function parseCliArgs(tokens) {
110
111
  if (command === "task") {
111
112
  throw new FlowError("EMPTY_PARAM", `task requires subcommand: ${TASK_SUBCOMMANDS.join(", ")}`);
112
113
  }
113
- if (command !== "bind" && seenFlagNames.has("place")) {
114
+ if (seenFlagNames.has("place")) {
115
+ if (command === "bind") {
116
+ throw new FlowError("EMPTY_PARAM", "bind no longer accepts --place; use --title <text>");
117
+ }
114
118
  throw new FlowError("EMPTY_PARAM", "unknown option --place; use @addr or --contract <addr>");
115
119
  }
116
120
  for (const name of seenFlagNames) {
117
121
  assertAllowedFlag(command, name);
118
122
  }
123
+ if (command === "bind" && flags.taskTitle !== undefined) {
124
+ flags.bindTitle = flags.taskTitle;
125
+ delete flags.taskTitle;
126
+ }
119
127
  if (command === "call" && flags.detach && flags.timeoutMs !== undefined) {
120
128
  throw new FlowError("EMPTY_PARAM", "call cannot combine --detach and --wait");
121
129
  }
@@ -128,9 +136,14 @@ export function parseCliArgs(tokens) {
128
136
  if (command === "task update" && flags.taskParent !== undefined && flags.taskNoParent) {
129
137
  throw new FlowError("EMPTY_PARAM", "task update cannot combine --parent and --no-parent");
130
138
  }
131
- if (command === "task update" && flags.taskTitle === undefined && flags.taskBody === undefined
139
+ if (command === "task update" && flags.taskBody !== undefined && flags.taskAppend !== undefined) {
140
+ throw new FlowError("EMPTY_PARAM", "task update cannot combine --body and --append");
141
+ }
142
+ if (command === "task update" && flags.taskTitle === undefined && flags.taskBody === undefined && flags.taskAppend === undefined
132
143
  && flags.taskPri === undefined && flags.taskNeeds === undefined && flags.taskDropNeeds === undefined
133
- && flags.taskParent === undefined && !flags.taskNoParent && flags.taskFrom === undefined) {
144
+ && flags.taskParent === undefined && !flags.taskNoParent && flags.taskFrom === undefined
145
+ && flags.taskSupersedes === undefined && flags.taskDropSupersedes === undefined
146
+ && flags.taskRelatesTo === undefined && flags.taskDropRelatesTo === undefined) {
134
147
  throw new FlowError("EMPTY_PARAM", "task update requires at least one change");
135
148
  }
136
149
  if (command === "petition" && flags.json && !flags.dryRun) {
@@ -140,9 +153,9 @@ export function parseCliArgs(tokens) {
140
153
  throw new FlowError("EMPTY_PARAM", "petition --dry-run does not accept --append-scope");
141
154
  }
142
155
  if (command === "renew") {
143
- const recoveryModes = [flags.renewResolve, flags.renewContinue, flags.renewAbort].filter(Boolean).length;
156
+ const recoveryModes = [flags.renewContinue, flags.renewAbort].filter(Boolean).length;
144
157
  if (recoveryModes > 1) {
145
- throw new FlowError("EMPTY_PARAM", "renew accepts only one of --resolve, --continue, or --abort");
158
+ throw new FlowError("EMPTY_PARAM", "renew accepts only one of --continue or --abort");
146
159
  }
147
160
  if (flags.renewAbort && (flags.appendScope?.length ?? 0) > 0) {
148
161
  throw new FlowError("EMPTY_PARAM", "renew --abort does not accept --append-scope");
@@ -151,6 +164,27 @@ export function parseCliArgs(tokens) {
151
164
  assertCommissionSelectorAllowed(command, flags);
152
165
  assertProjectionSelectorPresent(command, flags, atTokens, contractFlagRaws, rest.length, payload.length);
153
166
  const positional = [...rest, ...payload];
167
+ if (command === "bind" && flags.contractId !== undefined) {
168
+ if (!isCommissionId(flags.contractId)) {
169
+ throw new FlowError("EMPTY_PARAM", `bind replay requires a full canonical contract id; got ${flags.contractId}`);
170
+ }
171
+ const hasReplayInput = positional.length > 0
172
+ || flags.bindTaskIds !== undefined
173
+ || flags.after !== undefined
174
+ || flags.exclusive === true
175
+ || flags.draftOnly === true
176
+ || flags.dryRun === true
177
+ || flags.bindTitle !== undefined
178
+ || flags.bindContext !== undefined
179
+ || flags.bindObjective !== undefined
180
+ || flags.bindDesign !== undefined
181
+ || flags.bindScope !== undefined
182
+ || flags.bindCriteria !== undefined
183
+ || flags.bindVerification !== undefined;
184
+ if (hasReplayInput) {
185
+ throw new FlowError("EMPTY_PARAM", "addressed bind is replay-only and accepts no draft or creation input");
186
+ }
187
+ }
154
188
  if (command === "wait") {
155
189
  if (positional.length === 0 && !flags.contractId) {
156
190
  throw new FlowError("EMPTY_PARAM", "wait requires <projection-address>");
@@ -1,9 +1,9 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { AddressResolutionError, buildObjectResolutionAttempt, } from "../core/addressing.js";
4
- import { assertProjectionTellable, resolveProjectionCoordinate } from "../core/projection/index.js";
5
- import { isProjectionId, projectionDir } from "../core/projection/index.js";
6
- import { isProjectionAlias, readProjectionAlias } from "../core/projection/index.js";
4
+ import { assertProjectionTellable, projectionAliasRootAtCoordinate, projectionDirAtCoordinate, resolveProjectionCoordinate, } from "../core/projection/index.js";
5
+ import { isProjectionId } from "../core/projection/index.js";
6
+ import { isProjectionAlias, readProjectionAliasAtRoot } from "../core/projection/index.js";
7
7
  import { readProjectionIdentity } from "../core/projection/index.js";
8
8
  import { isResponseArtifactId } from "../core/response-artifact-id.js";
9
9
  import { isErrnoException } from "../errno.js";
@@ -22,11 +22,14 @@ export async function resolveProjectionAddress(input) {
22
22
  // Scoped lookup reuses the commission carrier's repository root: zero Git probes.
23
23
  // Unscoped lookup performs exactly one coordinate probe.
24
24
  let repo;
25
- let physicalRoot;
25
+ let coordinate;
26
26
  let attempt;
27
27
  if (input.scope) {
28
28
  repo = input.scope.repo;
29
- physicalRoot = input.scope.repo.stableRoot;
29
+ coordinate = {
30
+ physicalRoot: input.scope.repo.stableRoot,
31
+ authority: { kind: "repository", stableRoot: input.scope.repo.stableRoot },
32
+ };
30
33
  attempt = buildObjectResolutionAttempt({
31
34
  repo,
32
35
  workdir,
@@ -40,11 +43,10 @@ export async function resolveProjectionAddress(input) {
40
43
  });
41
44
  }
42
45
  else {
43
- const coordinate = await resolveProjectionCoordinate(input.cwd);
46
+ coordinate = input.coordinate ?? await resolveProjectionCoordinate(input.cwd);
44
47
  repo = coordinate.authority.kind === "repository"
45
48
  ? { kind: "repository", stableRoot: coordinate.authority.stableRoot }
46
49
  : { kind: "user" };
47
- physicalRoot = coordinate.physicalRoot;
48
50
  attempt = buildObjectResolutionAttempt({
49
51
  repo,
50
52
  workdir,
@@ -69,13 +71,13 @@ export async function resolveProjectionAddress(input) {
69
71
  else {
70
72
  if (!isProjectionAlias(input.address))
71
73
  throw new AddressResolutionError("EMPTY_PARAM", `projection alias ${input.address} has no match ${suffix}`, attempt);
72
- const target = await readProjectionAlias(physicalRoot, input.address);
74
+ const target = await readProjectionAliasAtRoot(projectionAliasRootAtCoordinate(coordinate), input.address);
73
75
  if (!target)
74
76
  throw new AddressResolutionError("EMPTY_PARAM", `projection alias ${input.address} has no match ${suffix}`, attempt);
75
77
  id = target;
76
78
  alias = input.address;
77
79
  }
78
- const directory = await projectionDir(physicalRoot, id);
80
+ const directory = projectionDirAtCoordinate(coordinate, id);
79
81
  if (!isRealDirectory(directory)) {
80
82
  const subject = alias ? `projection alias ${alias} points to missing projection ${id}` : `projection ${id}`;
81
83
  throw new AddressResolutionError("EMPTY_PARAM", `${subject} has no match ${suffix}`, attempt);
@@ -99,5 +101,5 @@ export async function resolveProjectionAddress(input) {
99
101
  if (input.scope && (identity.binding.kind !== "commission" || identity.binding.commissionId !== input.scope.binding.commissionId)) {
100
102
  throw new AddressResolutionError("EMPTY_PARAM", `projection ${id} has no match ${suffix}`, attempt);
101
103
  }
102
- return { id, ...(alias ? { alias } : {}) };
104
+ return { id, directory, ...(alias ? { alias } : {}) };
103
105
  }
@@ -0,0 +1,8 @@
1
+ export function renderAmendmentWarning(warning) {
2
+ return [
3
+ `You have amended this contract ${warning.amendmentCount} times. Consider whether its intent has drifted.`,
4
+ "If the original intent is complete, move the remaining work to a task and start a new Keiyaku.",
5
+ "If this contract no longer fits the work, forfeit it and bind a new one.",
6
+ "If the amendments are intentional and the contract still fits, continue.",
7
+ ].join(" ");
8
+ }
@@ -4,6 +4,7 @@ import { assembleResponse, buildPayload, buildSection, formatHints, formatList,
4
4
  import { DIFF_OVERVIEW_SECTION_TITLE, RESPONSE_MARKERS, WARNING_SECTION_TITLE } from "./response-style.js";
5
5
  import { appendPersistenceSummary, CONTENT_SECTION_TITLE, HINTS_LIST_MAX_ITEMS, HINTS_SECTION_TITLE, INFO_COMMIT_MAX_CHARS, INFO_PLEA_MAX_CHARS, INFO_SECTION_TITLE, } from "./shared.js";
6
6
  import { prependAddressReceipt } from "./address.js";
7
+ import { renderAmendmentWarning } from "./amendment-warning.js";
7
8
  function sealLines(result) {
8
9
  if (!result.sealed)
9
10
  return ["nothing sealed"];
@@ -55,7 +56,7 @@ export function buildRenewAbortResponse(result) {
55
56
  const payload = {
56
57
  contractId: result.contractId,
57
58
  oldHead: result.oldHead,
58
- recoveryCleanup: result.recoveryCleanup,
59
+ scratchName: result.scratchName,
59
60
  summary: result.summary,
60
61
  };
61
62
  return prependAddressReceipt({ text: result.summary, payload }, result.address);
@@ -68,8 +69,11 @@ export function buildAmendResponse(result) {
68
69
  ...formatMaybe("Amendment", String(result.amendmentNumber), INFO_COMMIT_MAX_CHARS),
69
70
  ...formatMaybe("Ledger Commit", renderedLedgerCommit, INFO_COMMIT_MAX_CHARS),
70
71
  ]);
72
+ const warningSection = result.amendmentWarning
73
+ ? buildSection(WARNING_SECTION_TITLE, renderAmendmentWarning(result.amendmentWarning))
74
+ : null;
71
75
  return prependAddressReceipt({
72
- text: assembleResponse(`${RESPONSE_MARKERS.arc} Amended`, `Amended contract ledger with Amendment ${result.amendmentNumber} [${renderedLedgerCommit}].`, [buildSection(CONTENT_SECTION_TITLE, result.summary), infoSection].filter((section) => section !== null)),
76
+ text: assembleResponse(`${RESPONSE_MARKERS.arc} Amended`, `Amended contract ledger with Amendment ${result.amendmentNumber} [${renderedLedgerCommit}].`, [buildSection(CONTENT_SECTION_TITLE, result.summary), infoSection, warningSection].filter((section) => section !== null)),
73
77
  payload: buildPayload(payload),
74
78
  }, result.address);
75
79
  }