@astrosheep/keiyaku 4.6.15 → 4.6.17

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 (56) hide show
  1. package/build/plugins/claude/skills/keiyaku/SKILL.md +1 -1
  2. package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +13 -8
  3. package/build/plugins/codex/skills/keiyaku/SKILL.md +1 -1
  4. package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +13 -8
  5. package/build/plugins/opencode/skills/keiyaku/SKILL.md +1 -1
  6. package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +13 -8
  7. package/build/skills/keiyaku/SKILL.md +1 -1
  8. package/build/skills/keiyaku-akuma/SKILL.md +13 -8
  9. package/build/src/akuma/archetype.js +2 -2
  10. package/build/src/cli/commands/akuma-grammar.js +18 -14
  11. package/build/src/cli/commands/akuma.d.ts +2 -1
  12. package/build/src/cli/commands/akuma.js +24 -25
  13. package/build/src/cli/commands/contract-help.d.ts +11 -11
  14. package/build/src/cli/commands/contract-help.js +16 -16
  15. package/build/src/cli/commands/contract.d.ts +3 -2
  16. package/build/src/cli/commands/contract.js +45 -65
  17. package/build/src/cli/commands/debt-grammar.js +8 -15
  18. package/build/src/cli/commands/debt.d.ts +2 -1
  19. package/build/src/cli/commands/debt.js +22 -22
  20. package/build/src/cli/commands/install.d.ts +2 -1
  21. package/build/src/cli/commands/install.js +4 -3
  22. package/build/src/cli/commands/whoami.d.ts +46 -0
  23. package/build/src/cli/commands/whoami.js +127 -0
  24. package/build/src/cli/main.js +5 -4
  25. package/build/src/cli/parse.d.ts +7 -1
  26. package/build/src/cli/parse.js +23 -3
  27. package/build/src/cli/render/akuma-activity.d.ts +10 -23
  28. package/build/src/cli/render/akuma-activity.js +178 -212
  29. package/build/src/cli/render/akuma.d.ts +7 -6
  30. package/build/src/cli/render/akuma.js +22 -18
  31. package/build/src/cli/render/catalog.js +4 -3
  32. package/build/src/cli/render/contract-history.js +5 -16
  33. package/build/src/cli/render/debt.d.ts +2 -1
  34. package/build/src/cli/render/debt.js +6 -6
  35. package/build/src/cli/render/nuke.d.ts +2 -1
  36. package/build/src/cli/render/nuke.js +4 -2
  37. package/build/src/cli/render/receipt.js +8 -3
  38. package/build/src/cli/render/reconcile.js +8 -7
  39. package/build/src/cli/render/refusal.js +7 -3
  40. package/build/src/cli/render/terminal.d.ts +5 -2
  41. package/build/src/cli/render/terminal.js +74 -12
  42. package/build/src/cli/render/whoami.d.ts +2 -0
  43. package/build/src/cli/render/whoami.js +56 -0
  44. package/build/src/cli/runtime.d.ts +2 -1
  45. package/build/src/cli/runtime.js +59 -58
  46. package/build/src/cli/streams.d.ts +11 -1
  47. package/build/src/cli/streams.js +6 -2
  48. package/build/src/cli/usage.d.ts +1 -0
  49. package/build/src/cli/usage.js +7 -6
  50. package/build/src/contract-worktree.js +1 -1
  51. package/build/src/library/selection.d.ts +1 -0
  52. package/build/src/library/selection.js +1 -1
  53. package/build/src/runtime/proc/windows-launch.exe +0 -0
  54. package/package.json +3 -3
  55. package/skills/keiyaku/SKILL.md +1 -1
  56. package/skills/keiyaku-akuma/SKILL.md +13 -8
@@ -13,7 +13,7 @@ export const CONTRACT_COMMAND_SPECS = {
13
13
  "fork-of": "value",
14
14
  json: "boolean",
15
15
  },
16
- usage: "bind [--debt <debt/...>] [--target <ref> | --no-target] [--after <kei/...> ...] [--gates <name,...>] [--actor <actor>] - | bind --fork-of <kei/...> [--target <ref> | --no-target] [--actor <actor>]",
16
+ usage: "bind [--debt <debt/...>] [--target <ref> | --no-target] [--after <kei/...>]... [--gates <name,...>] [--actor <actor>] [--repo <path>] -\nbind --fork-of <kei/...> [--target <ref> | --no-target] [--actor <actor>] [--repo <path>]",
17
17
  purpose: "Create a Contract from stdin Markdown or copy an existing Contract as a starting point.",
18
18
  details: [
19
19
  "--target names one existing local branch literally; it is never created and never",
@@ -64,7 +64,7 @@ export const CONTRACT_COMMAND_SPECS = {
64
64
  positional: "optional",
65
65
  stdin: "optional",
66
66
  flags: { actor: "value", after: "repeat", "clear-after": "boolean", gates: "raw-value", json: "boolean" },
67
- usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name,...>] [--actor <actor>] [-]",
67
+ usage: "amend [<contract>|@<contract>] [--after <kei/...> [--after <kei/...>]... | --clear-after] [--gates <name,...>] [--actor <actor>] [--repo <path>] [-]",
68
68
  purpose: "Change a Contract's document, prerequisites, or acceptance gates.",
69
69
  details: [
70
70
  "Gates are acceptance obligations checked at placement: reviewed awaits a",
@@ -97,7 +97,7 @@ export const CONTRACT_COMMAND_SPECS = {
97
97
  overwrite: "boolean",
98
98
  json: "boolean",
99
99
  },
100
- usage: "deliver [<contract>|@<contract>] [--message <text>] [--include-dirty] [--materialize-conflict] [--overwrite] | deliver [<contract>|@<contract>] --dry-run [--include-dirty] [--show-diff]",
100
+ usage: "deliver [<contract>|@<contract>] [--message <text>] [--include-dirty] [--materialize-conflict] [--overwrite] [--repo <path>]\ndeliver [<contract>|@<contract>] --dry-run [--include-dirty] [--show-diff] [--repo <path>]",
101
101
  purpose: "Submit work for a Contract and integrate it when its requirements are met.",
102
102
  details: [
103
103
  "The Contract, not the current Arc, sets what delivery must satisfy. An Arc records progress; it does not narrow acceptance.",
@@ -122,7 +122,7 @@ export const CONTRACT_COMMAND_SPECS = {
122
122
  positional: "optional",
123
123
  stdin: "optional",
124
124
  flags: { actor: "value", satisfied: "boolean", unsatisfied: "boolean", summary: "value", json: "boolean" },
125
- usage: "review [<contract>|@<contract>] (--satisfied | --unsatisfied) (--summary <text> | -) [--actor <actor>]",
125
+ usage: "review [<contract>|@<contract>] (--satisfied | --unsatisfied) (--summary <text> | -) [--actor <actor>] [--repo <path>]",
126
126
  purpose: "Record whether a Contract meets its acceptance criteria.",
127
127
  details: [
128
128
  "A review checks the whole Contract, not just one Arc. After delivery it checks the candidate; before delivery it checks the current Contract and worktree, but cannot integrate.",
@@ -136,7 +136,7 @@ export const CONTRACT_COMMAND_SPECS = {
136
136
  positional: "optional",
137
137
  stdin: "required",
138
138
  flags: { actor: "value", json: "boolean" },
139
- usage: "arc [<contract>|@<contract>] [--actor <actor>] -",
139
+ usage: "arc [<contract>|@<contract>] [--actor <actor>] [--repo <path>] -",
140
140
  purpose: "Name the current chapter of a Contract's story (a story arc).",
141
141
  details: [
142
142
  "stdin is one nonblank H1 chapter name followed by any Markdown body, including none:",
@@ -149,14 +149,14 @@ export const CONTRACT_COMMAND_SPECS = {
149
149
  positional: "optional",
150
150
  stdin: "none",
151
151
  flags: { actor: "value", note: "value", json: "boolean" },
152
- usage: "abandon [<contract>|@<contract>] [--note <text>] [--actor <actor>]",
153
- purpose: "End work on a Contract without delivering it; optionally record why.",
152
+ usage: "abandon [<contract>|@<contract>] [--note <text>] [--actor <actor>] [--repo <path>]",
153
+ purpose: "End work on a Contract without accepting it; optionally record why.",
154
154
  },
155
155
  show: {
156
156
  positional: "optional",
157
157
  stdin: "none",
158
158
  flags: { json: "boolean" },
159
- usage: "show [<contract>|@<contract>]",
159
+ usage: "show [<contract>|@<contract>] [--repo <path>]",
160
160
  purpose: "Read one Contract's guidance.",
161
161
  details: [
162
162
  "Accepts a complete Contract identity or exact @<contract> short selector.",
@@ -183,12 +183,12 @@ export const CONTRACT_COMMAND_SPECS = {
183
183
  positional: "optional",
184
184
  stdin: "none",
185
185
  flags: { limit: "value", json: "boolean", mine: "boolean", caller: "value" },
186
- usage: 'ls debt[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls "aku/<name>/*" [--limit <count>]\nls "aku/*/*" [--limit <count>]\nls ["aku/*/*"] --mine\nls ["aku/*/*"] --caller <integration>:<id> | aku/<name>/<suffix>',
186
+ usage: 'ls debt[/] [--limit <count>]\nls debt/<namespace>/ [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls "aku/<name>/*" [--limit <count>]\nls "aku/*/*" [--limit <count>]\nls [aku/<name>[/] | "aku/<name>/*" | "aku/*/*"] (--mine | --caller (<integration>:<id> | aku/<name>/<suffix>)) [--limit <count>]',
187
187
  purpose: "List debts (ls debt), contracts (ls kei), or akumas (ls aku).",
188
188
  details: [
189
189
  "ls kei lists active Contracts; accepted and abandoned ones leave the list, and their record stays in history and Git.",
190
- "ls aku/ lists the callable Akuma-name catalog; ls aku/<name> lists the living Akumas under one name.",
191
- 'ls "aku/<name>/*" is the same living set in glob form; ls "aku/*/*" lists every living Akuma.',
190
+ "ls aku/ lists the callable Akuma-name catalog; ls aku/<name> lists recent retained Akuma instances under one name.",
191
+ 'ls "aku/<name>/*" is the same roster in glob form; ls "aku/*/*" lists recent retained Akuma instances across names.',
192
192
  "--mine selects the caller identity of this process and lists that caller's direct and transitive descendants in this World.",
193
193
  "--caller selects an explicit caller instead; both selectors are World-local and refuse for Contract, Debt, or Akuma-name catalogues.",
194
194
  "Descendant rows name their immediate calling Akuma beside the row.",
@@ -198,7 +198,7 @@ export const CONTRACT_COMMAND_SPECS = {
198
198
  positional: "optional",
199
199
  stdin: "none",
200
200
  flags: { "retry-hooks": "boolean", json: "boolean" },
201
- usage: "reconcile [<contract>|@<contract>] [--retry-hooks]",
201
+ usage: "reconcile [<contract>|@<contract>] [--retry-hooks] [--repo <path>]",
202
202
  purpose: "Restore a Contract's Git workspace from its recorded state, or all of this World's.",
203
203
  details: "--retry-hooks reruns configured setup commands even when a Contract workspace already exists; otherwise they run when reconciliation creates it.",
204
204
  },
@@ -221,7 +221,7 @@ export const CONTRACT_COMMAND_SPECS = {
221
221
  positional: "optional",
222
222
  stdin: "none",
223
223
  flags: { path: "repeat", json: "boolean" },
224
- usage: "region [<contract>]\nregion --path <pattern> [--path <pattern> ...]",
224
+ usage: "region [<contract>] [--repo <path>]\nregion --path <pattern> [--path <pattern>]... [--repo <path>]",
225
225
  purpose: "Show declared Contract write patterns — all, one Contract's, or those overlapping given paths.",
226
226
  details: "Check overlap before starting parallel work.",
227
227
  },
@@ -248,8 +248,8 @@ export function renderContractHelp(command) {
248
248
  "value's diagnostic states the expected shape.",
249
249
  "",
250
250
  "Akuma definitions are Markdown files, one per name:",
251
- " user ~/.keiyaku/akuma/<name>.md",
252
- " project <WorldRoot>/.keiyaku/akuma/<name>.md",
251
+ " user ~/.keiyaku/akumas/<name>.md",
252
+ " project <WorldRoot>/.keiyaku/akumas/<name>.md",
253
253
  "A project file wholly shadows the same-name user file. YAML",
254
254
  "frontmatter selects the provider and may declare base, model,",
255
255
  "effort, network, allowed, systemPromptMode, and description; the body is",
@@ -261,7 +261,7 @@ export function renderContractHelp(command) {
261
261
  ' "config": { "url": "ws://127.0.0.1:6767/ws", "provider": "claude" }',
262
262
  " } } }",
263
263
  "The native provider id is bare (claude, codex, pi), not provider/model.",
264
- "Example .keiyaku/akuma/paseo-worker.md frontmatter:",
264
+ "Example .keiyaku/akumas/paseo-worker.md frontmatter:",
265
265
  " ---",
266
266
  " provider: paseo",
267
267
  " model: <native-model-id>",
@@ -1,5 +1,6 @@
1
1
  import type { CliCoordinates } from "../coordinates.js";
2
2
  import type { CliRuntime } from "../runtime.js";
3
+ import { type CliOutcome } from "../streams.js";
3
4
  import type { ParsedContractCommand } from "./contract-grammar.js";
4
5
  /**
5
6
  * The one bind-document admission that runs before coordinate resolution, so a malformed document
@@ -16,6 +17,6 @@ type ParsedContractHistory = Readonly<{
16
17
  full: boolean;
17
18
  output: "text" | "json";
18
19
  }>;
19
- export declare function runContractHistoryCommand(command: ParsedContractHistory, coordinates: CliCoordinates): Promise<number>;
20
- export declare function runContractCommand(command: ParsedContractCommand, coordinates: CliCoordinates, runtime: CliRuntime): Promise<number>;
20
+ export declare function runContractHistoryCommand(command: ParsedContractHistory, coordinates: CliCoordinates): Promise<CliOutcome>;
21
+ export declare function runContractCommand(command: ParsedContractCommand, coordinates: CliCoordinates, runtime: CliRuntime): Promise<CliOutcome>;
21
22
  export {};
@@ -15,7 +15,7 @@ import { snapshotText } from "../render/akuma-activity.js";
15
15
  import { renderAkumaCatalogue, renderArchetypeCatalogue, renderContractCatalogue, renderDebtCatalogue, } from "../render/catalog.js";
16
16
  import { renderAccepted, renderContractHistory, renderRetry } from "../render/contract.js";
17
17
  import { renderKanshiCategoryText, renderKanshiText } from "../render/kanshi.js";
18
- import { nukeExitCode, renderNukeText } from "../render/nuke.js";
18
+ import { nukeOutcome, renderNukeText } from "../render/nuke.js";
19
19
  import { reconcileHasFailure, renderReconcile } from "../render/reconcile.js";
20
20
  import { renderConflictMaterialized, renderRefusal } from "../render/refusal.js";
21
21
  import { renderRegionText } from "../render/region.js";
@@ -92,9 +92,6 @@ function deliverRefusalProjection(refusal) {
92
92
  const submodules = "submodules" in refusal && Array.isArray(refusal.submodules) ? refusal.submodules : [];
93
93
  return { ...refusal, option: { flag: "--include-dirty", available: submodules.length === 0 } };
94
94
  }
95
- function contractExitCode(answer) {
96
- return answer.kind === "refused" ? 1 : answer.kind === "retry" ? 2 : 0;
97
- }
98
95
  function renderContractAnswer(answer, project, context) {
99
96
  switch (answer.kind) {
100
97
  case "handoff":
@@ -108,8 +105,8 @@ function renderContractAnswer(answer, project, context) {
108
105
  }
109
106
  }
110
107
  async function finishContractAnswer(answer, output, project) {
111
- const text = () => renderContractAnswer(answer, project, displayContext());
112
- return emit(output, answer, text, contractExitCode(answer));
108
+ const outcome = answer.kind === "refused" ? "refused" : answer.kind === "retry" ? "retryable" : "accepted";
109
+ return emit(output, answer, () => renderContractAnswer(answer, project, displayContext()), outcome);
113
110
  }
114
111
  async function selectedContract(repo, selector, scope) {
115
112
  const id = await resolveContractId(repo, selector, scope);
@@ -125,16 +122,6 @@ async function existingSeat(command, coordinates, runtime, library) {
125
122
  ...(actor === undefined ? {} : { actor }),
126
123
  };
127
124
  }
128
- /** One composition for every mutation leaf; gates, hooks, and freshness derive lazily inside it. */
129
- async function contractLibrary(command, execution, configuration, runtime) {
130
- const actor = executionChannel(execution).kind === "local" && ["deliver", "review"].includes(command.command)
131
- ? actorFromEdge(undefined, runtime.environment)
132
- : undefined;
133
- return composeContractLibrary(execution, createKeiyakuHandle, {
134
- ...(configuration === undefined ? {} : { settings: configuration }),
135
- ...(actor === undefined ? {} : { actor }),
136
- });
137
- }
138
125
  async function runBind(command, input, library) {
139
126
  const { coordinates, runtime } = input;
140
127
  const repo = requiredRepo(coordinates);
@@ -164,7 +151,7 @@ async function runBind(command, input, library) {
164
151
  return await finishContractAnswer(answer, command.output);
165
152
  const draft = await bindDraftReceipt(coordinates.establishWorld, markdown);
166
153
  const text = () => renderRefusal(refusedInvocation(answer), draft, displayContext());
167
- return emit(command.output, answer, text, 1);
154
+ return emit(command.output, answer, text, "refused");
168
155
  }
169
156
  catch (error) {
170
157
  if (error instanceof Error && "executionReceipt" in error)
@@ -193,18 +180,20 @@ async function runAmend(command, input, seat) {
193
180
  return await finishContractAnswer(answer, command.output);
194
181
  }
195
182
  async function runDeliver(command, runtime, seat) {
196
- if (command.dryRun)
197
- return await runDeliveryPreview(command, runtime, seat);
198
183
  const driver = command.output === "text" ? await runtime.progress() : undefined;
199
184
  let answer;
200
185
  try {
201
- answer = await seat.contract.deliver({
202
- ...(command.message === undefined ? {} : { message: command.message }),
203
- includeDirty: command.includeDirty,
204
- materializeConflict: command.materializeConflict,
205
- overwrite: command.overwrite,
206
- ...(runtime.signal === undefined ? {} : { signal: runtime.signal }),
207
- }, { ...(driver === undefined ? {} : { observe: driver.observe }) });
186
+ const progress = { ...(driver === undefined ? {} : { observe: driver.observe }) };
187
+ const signal = { ...(runtime.signal === undefined ? {} : { signal: runtime.signal }) };
188
+ answer = command.dryRun
189
+ ? await seat.contract.previewDelivery({ includeDirty: command.includeDirty, showDiff: command.showDiff, ...signal }, progress)
190
+ : await seat.contract.deliver({
191
+ ...(command.message === undefined ? {} : { message: command.message }),
192
+ includeDirty: command.includeDirty,
193
+ materializeConflict: command.materializeConflict,
194
+ overwrite: command.overwrite,
195
+ ...signal,
196
+ }, progress);
208
197
  }
209
198
  finally {
210
199
  if (driver !== undefined)
@@ -242,7 +231,7 @@ async function runArc(command, runtime, seat) {
242
231
  throw error;
243
232
  const refusal = { kind: "invalid-document", diagnostic: error.message };
244
233
  const refused = { kind: "refused", operation: "arc", contract: seat.id, refusal };
245
- return emit(command.output, refused, () => renderRefusal(refused, undefined, displayContext()), 1);
234
+ return emit(command.output, refused, () => renderRefusal(refused, undefined, displayContext()), "refused");
246
235
  }
247
236
  const answer = await seat.contract.arc({
248
237
  markdown,
@@ -257,24 +246,15 @@ async function runAbandon(command, seat) {
257
246
  });
258
247
  return await finishContractAnswer(answer, command.output);
259
248
  }
260
- async function runDeliveryPreview(command, runtime, seat) {
261
- const driver = command.output === "text" ? await runtime.progress() : undefined;
262
- let answer;
263
- try {
264
- answer = await seat.contract.previewDelivery({
265
- includeDirty: command.includeDirty,
266
- showDiff: command.showDiff,
267
- ...(runtime.signal === undefined ? {} : { signal: runtime.signal }),
268
- }, { ...(driver === undefined ? {} : { observe: driver.observe }) });
269
- }
270
- finally {
271
- if (driver !== undefined)
272
- await driver.finish().catch(() => undefined);
273
- }
274
- return await finishContractAnswer(answer, command.output, deliverRefusalProjection);
275
- }
276
249
  async function runContractMutation(command, input) {
277
- const library = await contractLibrary(command, input.execution, input.configuration, input.runtime);
250
+ const { execution, configuration, runtime } = input;
251
+ const actor = executionChannel(execution).kind === "local" && ["deliver", "review"].includes(command.command)
252
+ ? actorFromEdge(undefined, runtime.environment)
253
+ : undefined;
254
+ const library = composeContractLibrary(execution, createKeiyakuHandle, {
255
+ ...(configuration === undefined ? {} : { settings: configuration }),
256
+ ...(actor === undefined ? {} : { actor }),
257
+ });
278
258
  if (command.command === "bind")
279
259
  return await runBind(command, input, library);
280
260
  const seat = await existingSeat(command, input.coordinates, input.runtime, library);
@@ -296,7 +276,7 @@ function statusRefusal(operation, contract) {
296
276
  }
297
277
  async function runSettings(coordinates, home, output) {
298
278
  const value = await settingsAt(coordinates.world ?? undefined, home);
299
- return emit(output, settingsJsonValue(value), () => renderSettingsText(value, displayContext().columns), 0);
279
+ return emit(output, settingsJsonValue(value), () => renderSettingsText(value, displayContext().columns), "accepted");
300
280
  }
301
281
  async function runNuke(command, coordinates) {
302
282
  if (coordinates.world === null)
@@ -305,7 +285,7 @@ async function runNuke(command, coordinates) {
305
285
  world: coordinates.world,
306
286
  ...(command.confirm === undefined ? {} : { confirm: command.confirm }),
307
287
  });
308
- return emit(command.output, result, () => renderNukeText(result), nukeExitCode(result));
288
+ return emit(command.output, result, () => renderNukeText(result), nukeOutcome(result));
309
289
  }
310
290
  async function runLs(command, coordinates, home, runtime) {
311
291
  const query = command.query;
@@ -314,7 +294,7 @@ async function runLs(command, coordinates, home, runtime) {
314
294
  repo: requiredRepo(coordinates),
315
295
  ...(query.limit === undefined ? {} : { limit: query.limit }),
316
296
  });
317
- return emit(command.output, list, () => renderContractCatalogue(list), 0);
297
+ return emit(command.output, list, () => renderContractCatalogue(list), "accepted");
318
298
  }
319
299
  if (query.kind === "archetypes") {
320
300
  const list = await listArchetypeDefinitions({
@@ -322,7 +302,7 @@ async function runLs(command, coordinates, home, runtime) {
322
302
  ...(query.limit === undefined ? {} : { limit: query.limit }),
323
303
  ...(home === undefined ? {} : { home }),
324
304
  });
325
- return emit(command.output, list, () => renderArchetypeCatalogue(list), 0);
305
+ return emit(command.output, list, () => renderArchetypeCatalogue(list), "accepted");
326
306
  }
327
307
  if (coordinates.world === null)
328
308
  throw new CliUsageError("no Keiyaku world contains the invocation cwd");
@@ -334,7 +314,7 @@ async function runLs(command, coordinates, home, runtime) {
334
314
  });
335
315
  if (listed.kind !== "accepted")
336
316
  throw new Error("Debt list did not return an accepted observation");
337
- return emit(command.output, listed, () => renderDebtCatalogue(listed.value, query.namespace ?? []), 0);
317
+ return emit(command.output, listed, () => renderDebtCatalogue(listed.value, query.namespace ?? []), "accepted");
338
318
  }
339
319
  const caller = await lsCaller(query.caller, coordinates, runtime);
340
320
  const listed = await Akumas.of(coordinates.world).list({
@@ -342,7 +322,7 @@ async function runLs(command, coordinates, home, runtime) {
342
322
  ...(query.limit === undefined ? {} : { limit: query.limit }),
343
323
  ...(caller === undefined ? {} : { caller }),
344
324
  });
345
- return emit(command.output, listed, () => renderAkumaCatalogue(listed, query.archetype ?? null, caller), 0);
325
+ return emit(command.output, listed, () => renderAkumaCatalogue(listed, query.archetype ?? null, caller), "accepted");
346
326
  }
347
327
  /** `--mine` is the process caller identity; `--caller` names the same coordinate explicitly. */
348
328
  async function lsCaller(selection, coordinates, runtime) {
@@ -400,9 +380,9 @@ async function runShow(command, coordinates) {
400
380
  const guidance = await selected.contract.guidance();
401
381
  if (guidance === null) {
402
382
  const refusal = statusRefusal(command.command, selected.id);
403
- return emit(command.output, null, () => renderRefusal(refusal, undefined, displayContext()), 1);
383
+ return emit(command.output, null, () => renderRefusal(refusal, undefined, displayContext()), "refused");
404
384
  }
405
- return emit(command.output, guidance, () => guidance, 0);
385
+ return emit(command.output, guidance, () => guidance, "accepted");
406
386
  }
407
387
  async function runStatusSelection(command, coordinates) {
408
388
  const selectors = command.selectors;
@@ -426,7 +406,7 @@ async function runStatusSelection(command, coordinates) {
426
406
  if (command.output === "text")
427
407
  blocks.push(resolved.project());
428
408
  }
429
- return emit(command.output, values, () => blocks.join("\n\n"), 0);
409
+ return emit(command.output, values, () => blocks.join("\n\n"), "accepted");
430
410
  }
431
411
  async function runStatusCategory(command, coordinates) {
432
412
  if (command.category === "contract")
@@ -437,7 +417,7 @@ async function runStatusCategory(command, coordinates) {
437
417
  category: command.category,
438
418
  ...(command.limit === undefined ? {} : { limit: command.limit }),
439
419
  });
440
- return emit(command.output, report, () => renderKanshiCategoryText(report, displayContext()), 0);
420
+ return emit(command.output, report, () => renderKanshiCategoryText(report, displayContext()), "accepted");
441
421
  }
442
422
  async function runStatusIdentity(command, coordinates) {
443
423
  if (command.contract === undefined) {
@@ -445,11 +425,11 @@ async function runStatusIdentity(command, coordinates) {
445
425
  world: coordinates.world,
446
426
  ...(coordinates.repo === undefined ? {} : { repo: coordinates.repo }),
447
427
  });
448
- return emit(command.output, report, () => renderKanshiText(report, displayContext(), "world"), 0);
428
+ return emit(command.output, report, () => renderKanshiText(report, displayContext(), "world"), "accepted");
449
429
  }
450
430
  if (command.contract.startsWith("@")) {
451
431
  const resolved = await namedStatus(command.contract, coordinates, await observeNamedAddress({ world: coordinates.world, repo: coordinates.repo ?? null }));
452
- return emit(command.output, resolved.value, () => resolved.project(), 0);
432
+ return emit(command.output, resolved.value, () => resolved.project(), "accepted");
453
433
  }
454
434
  if (command.contract.startsWith("kei/")) {
455
435
  const repo = requiredRepo(coordinates);
@@ -457,9 +437,9 @@ async function runStatusIdentity(command, coordinates) {
457
437
  const report = await kanshi({ world: coordinates.world, repo, contract });
458
438
  if (report.contracts.kind === "present" && report.contracts.value.rows.length === 0) {
459
439
  const refusal = statusRefusal("status", contract);
460
- return emit(command.output, refusal.refusal, () => renderRefusal(refusal, undefined, displayContext()), 1);
440
+ return emit(command.output, refusal.refusal, () => renderRefusal(refusal, undefined, displayContext()), "refused");
461
441
  }
462
- return emit(command.output, report, () => renderKanshiText(report, displayContext(), "contract"), 0);
442
+ return emit(command.output, report, () => renderKanshiText(report, displayContext(), "contract"), "accepted");
463
443
  }
464
444
  throw new CliUsageError(`contract selector must use kei/: ${command.contract}`);
465
445
  }
@@ -470,13 +450,13 @@ async function runStatus(command, coordinates) {
470
450
  return await runStatusSelection({ ...command, selectors: command.selectors }, coordinates);
471
451
  if (command.akuma === true && command.contract !== undefined) {
472
452
  const resolved = await readAkumaStatus(command.contract, undefined, coordinates);
473
- return emit(command.output, resolved.value, () => resolved.project(), 0);
453
+ return emit(command.output, resolved.value, () => resolved.project(), "accepted");
474
454
  }
475
455
  return await runStatusIdentity(command, coordinates);
476
456
  }
477
457
  async function runRegion(command, coordinates) {
478
458
  const repo = requiredRepo(coordinates);
479
- const emitSection = (section) => emit(command.output, section, () => renderRegionText(section), 0);
459
+ const emitSection = (section) => emit(command.output, section, () => renderRegionText(section), "accepted");
480
460
  if (command.paths !== undefined) {
481
461
  const report = await kanshi({ world: coordinates.world, repo, region: { kind: "path", patterns: command.paths } });
482
462
  return emitSection(report.region ?? { kind: "absent" });
@@ -495,7 +475,7 @@ async function runRegion(command, coordinates) {
495
475
  writeJson(refusal);
496
476
  else
497
477
  writeStdout(renderRefusal({ operation: "region", contract, refusal }, undefined, displayContext()));
498
- return 1;
478
+ return "refused";
499
479
  }
500
480
  if (report.region?.kind !== "present" || report.region.value.kind !== "declarations")
501
481
  return emitSection(report.region ?? { kind: "absent" });
@@ -517,9 +497,9 @@ export async function runContractHistoryCommand(command, coordinates) {
517
497
  writeJson(refusal);
518
498
  else
519
499
  writeStdout(renderRefusal({ operation: "history", contract: selected.id, refusal }, undefined, displayContext()));
520
- return 1;
500
+ return "refused";
521
501
  }
522
- return emit(command.output, history, () => renderContractHistory(history, { full: command.full }), 0);
502
+ return emit(command.output, history, () => renderContractHistory(history, { full: command.full }), "accepted");
523
503
  }
524
504
  async function runReconcile(command, coordinates, execution, home) {
525
505
  const repo = requiredRepo(coordinates);
@@ -539,7 +519,7 @@ async function runReconcile(command, coordinates, execution, home) {
539
519
  writeJson(statusRefusal("reconcile", selected.id).refusal);
540
520
  else
541
521
  writeStdout(renderRefusal(statusRefusal("reconcile", selected.id), undefined, displayContext()));
542
- return 1;
522
+ return "refused";
543
523
  }
544
524
  report = await library.reconcile({ repo, contract: selected.id, retryHooks: command.retryHooks });
545
525
  }
@@ -547,7 +527,7 @@ async function runReconcile(command, coordinates, execution, home) {
547
527
  writeJson(report);
548
528
  else
549
529
  writeStdout(renderReconcile(report, displayContext()));
550
- return reconcileHasFailure(report) ? 1 : 0;
530
+ return reconcileHasFailure(report) ? "failed" : "accepted";
551
531
  }
552
532
  export async function runContractCommand(command, coordinates, runtime) {
553
533
  const execution = runtime.execution;
@@ -1,4 +1,4 @@
1
- import { CliUsageError, DEBT_FAMILY_USAGE_GUIDE, unknownCommandGuide } from "../usage.js";
1
+ import { CliUsageError, commonInvocationOptions, DEBT_FAMILY_USAGE_GUIDE, unknownCommandGuide, } from "../usage.js";
2
2
  import { scanArgs } from "../scan.js";
3
3
  import { parseDebtNamespaceSelector } from "../../debt/catalog.js";
4
4
  import { parseDebtId } from "../../debt/identity.js";
@@ -165,26 +165,19 @@ export function renderDebtHelp(action) {
165
165
  ].join("\n");
166
166
  }
167
167
  function renderDebtUsage(action) {
168
- return DEBT_COMMAND_SPECS[action].usage
168
+ return debtUsage(action)
169
169
  .split("\n")
170
- .map((line, index) => {
171
- if (index === 0 || line.trimStart().startsWith("debt ")) {
172
- const command = line.trimStart().startsWith("debt ") ? `keiyaku ${line.trim()}` : `keiyaku ${line}`;
173
- return `${index === 0 ? "usage " : " "}${command}`;
174
- }
175
- return ` ${line.trim()}`;
176
- })
170
+ .map((line, index) => `${index === 0 ? "usage " : " "}${line.trimStart()}`)
177
171
  .join("\n");
178
172
  }
179
173
  function debtUsage(action) {
180
- const lines = DEBT_COMMAND_SPECS[action].usage.split("\n");
181
- return lines
182
- .map((line, index) => {
174
+ const usage = DEBT_COMMAND_SPECS[action].usage;
175
+ const lines = usage.split("\n").map((line, index) => {
183
176
  if (index === 0 || line.trimStart().startsWith("debt "))
184
177
  return `keiyaku ${line.trim()}`;
185
- return ` ${line.trim()}`;
186
- })
187
- .join("\n");
178
+ return line.trim();
179
+ });
180
+ return [...lines, commonInvocationOptions(usage)].join("\n");
188
181
  }
189
182
  export function debtUsageGuide(action) {
190
183
  return {
@@ -1,4 +1,5 @@
1
1
  import type { ParsedDebtCommand } from "./debt-grammar.js";
2
+ import { type CliOutcome } from "../streams.js";
2
3
  import type { CliRuntime } from "../runtime.js";
3
4
  import type { CliCoordinates } from "../coordinates.js";
4
- export declare function runDebtCommand(command: ParsedDebtCommand, coordinates: CliCoordinates, runtime: CliRuntime): Promise<number>;
5
+ export declare function runDebtCommand(command: ParsedDebtCommand, coordinates: CliCoordinates, runtime: CliRuntime): Promise<CliOutcome>;
@@ -3,7 +3,7 @@ import { parseDebtNamespaceSelector } from "../../debt/catalog.js";
3
3
  import { debtsWithExecution, debtRowViewLimit, } from "../../debt/index.js";
4
4
  import { installNamespaceContext, resolveDebtNamespaceContext } from "../../debt/context.js";
5
5
  import { displayContext, emit, writeJson } from "../streams.js";
6
- import { renderDoctor, renderDebtContext, renderDebtLifecycle, renderDebtList, renderDebtMutation, renderDebtShow, renderDebtTree, renderDebtUpdate, debtExitCode, } from "../render/debt.js";
6
+ import { renderDoctor, renderDebtContext, renderDebtLifecycle, renderDebtList, renderDebtMutation, renderDebtShow, renderDebtTree, renderDebtUpdate, debtOutcome, } from "../render/debt.js";
7
7
  import { actorFromEdge } from "../actor.js";
8
8
  import { debtQuerySelection } from "../../debt/query.js";
9
9
  function value(command, name) {
@@ -121,15 +121,15 @@ async function runShow(command, debts) {
121
121
  for (const id of handles)
122
122
  details.push(await debts.debt({ id }).read());
123
123
  const missing = details.findIndex((detail) => detail === null);
124
- const code = missing >= 0 ? 1 : 0;
124
+ const outcome = missing >= 0 ? "refused" : "accepted";
125
125
  const projected = handles.length === 1 ? details[0] : details;
126
126
  if (missing >= 0) {
127
127
  const refusal = { kind: "refused", refusal: { kind: "debt-missing", debtId: handles[missing] } };
128
- return emit(command.output, projected, () => renderDebtShow(refusal, displayContext()), code);
128
+ return emit(command.output, projected, () => renderDebtShow(refusal, displayContext()), outcome);
129
129
  }
130
130
  const found = details.filter((detail) => detail !== null);
131
131
  const result = found.length === 1 ? found[0] : found;
132
- return emit(command.output, projected, () => renderDebtShow(result, displayContext()), code);
132
+ return emit(command.output, projected, () => renderDebtShow(result, displayContext()), outcome);
133
133
  }
134
134
  /** Presentation-only list scope the leaf acquired, never part of the native answer. */
135
135
  function listPresentation(command) {
@@ -151,7 +151,7 @@ async function runRead(input) {
151
151
  : { namespace: explicitListNamespace(command) }),
152
152
  ...(limit(command) === undefined ? {} : { limit: limit(command) }),
153
153
  });
154
- return emit(command.output, result, () => renderDebtList("ls", scope, result, displayContext()), debtExitCode(result));
154
+ return emit(command.output, result, () => renderDebtList("ls", scope, result, displayContext()), debtOutcome(result));
155
155
  }
156
156
  case "ready":
157
157
  case "blocked": {
@@ -161,7 +161,7 @@ async function runRead(input) {
161
161
  ...(value(command, "parent") === undefined ? {} : { parent: value(command, "parent") }),
162
162
  ...(limit(command) === undefined ? {} : { limit: limit(command) }),
163
163
  });
164
- return emit(command.output, result, () => renderDebtList(action, scope, result, displayContext()), debtExitCode(result));
164
+ return emit(command.output, result, () => renderDebtList(action, scope, result, displayContext()), debtOutcome(result));
165
165
  }
166
166
  case "query": {
167
167
  const result = await debts.query({
@@ -170,15 +170,15 @@ async function runRead(input) {
170
170
  ...(value(command, "sort") === undefined ? {} : { sort: value(command, "sort") }),
171
171
  ...(limit(command) === undefined ? {} : { limit: limit(command) }),
172
172
  });
173
- return emit(command.output, result, () => renderDebtList("query", scope, result, displayContext()), debtExitCode(result));
173
+ return emit(command.output, result, () => renderDebtList("query", scope, result, displayContext()), debtOutcome(result));
174
174
  }
175
175
  case "tree": {
176
176
  const result = await debts.debt({ id }).tree();
177
- return emit(command.output, result, () => renderDebtTree(result, displayContext()), debtExitCode(result));
177
+ return emit(command.output, result, () => renderDebtTree(result, displayContext()), debtOutcome(result));
178
178
  }
179
179
  case "doctor": {
180
180
  const result = await debts.doctor();
181
- return emit(command.output, result, () => renderDoctor(result), debtExitCode(result));
181
+ return emit(command.output, result, () => renderDoctor(result), debtOutcome(result));
182
182
  }
183
183
  default:
184
184
  throw new Error(`debt action is not a read: ${command.action}`);
@@ -204,32 +204,32 @@ function missingWorld(command) {
204
204
  // An addressed missing read keeps its native null in JSON; text refuses without partial output.
205
205
  if (output === "json") {
206
206
  writeJson(command.positionals.length === 1 ? null : command.positionals.map(() => null));
207
- return 1;
207
+ return "refused";
208
208
  }
209
209
  const refusal = missingDebt(command.positionals[0]);
210
- return emit(output, refusal, () => renderDebtShow(refusal, displayContext()), 1);
210
+ return emit(output, refusal, () => renderDebtShow(refusal, displayContext()), "refused");
211
211
  }
212
212
  if (isWorldObservationAction(command)) {
213
- return emit(output, { kind: "absent" }, () => "debt world absent", 1);
213
+ return emit(output, { kind: "absent" }, () => "debt world absent", "refused");
214
214
  }
215
215
  if (command.action === "context") {
216
216
  const result = { kind: "accepted", value: { namespace: [], source: "default-root" } };
217
- return emit(output, result, () => renderDebtContext(result, displayContext()), debtExitCode(result));
217
+ return emit(output, result, () => renderDebtContext(result, displayContext()), debtOutcome(result));
218
218
  }
219
219
  if (command.action === "tree") {
220
220
  const result = missingDebt(command.positionals[0]);
221
- return emit(output, result, () => renderDebtTree(result, displayContext()), debtExitCode(result));
221
+ return emit(output, result, () => renderDebtTree(result, displayContext()), debtOutcome(result));
222
222
  }
223
223
  if (command.action === "update") {
224
224
  const result = missingDebt(command.positionals[0]);
225
- return emit(output, result, () => renderDebtUpdate(result, displayContext()), debtExitCode(result));
225
+ return emit(output, result, () => renderDebtUpdate(result, displayContext()), debtOutcome(result));
226
226
  }
227
227
  if (command.action !== "done" && command.action !== "drop")
228
228
  throw new Error(`debt action has no missing-world answer: ${command.action}`);
229
229
  const result = {
230
230
  items: command.positionals.map((id) => ({ id: id, outcome: missingDebt(id) })),
231
231
  };
232
- return emit(output, result, () => renderDebtLifecycle(command.action, result, displayContext()), debtExitCode(result));
232
+ return emit(output, result, () => renderDebtLifecycle(command.action, result, displayContext()), debtOutcome(result));
233
233
  }
234
234
  /** One plural Debt lifecycle answer: every verb shares the same batch shape and renderer. */
235
235
  async function runBatchLifecycle(debts, command, verb) {
@@ -239,7 +239,7 @@ async function runBatchLifecycle(debts, command, verb) {
239
239
  ids: command.positionals,
240
240
  ...(note === undefined ? {} : { note }),
241
241
  });
242
- return emit(command.output, result, () => renderDebtLifecycle(verb, result, displayContext()), debtExitCode(result));
242
+ return emit(command.output, result, () => renderDebtLifecycle(verb, result, displayContext()), debtOutcome(result));
243
243
  }
244
244
  async function runMutation(input) {
245
245
  const { debts, command } = input;
@@ -248,11 +248,11 @@ async function runMutation(input) {
248
248
  switch (command.action) {
249
249
  case "add": {
250
250
  const result = await invokeAdd(input);
251
- return emit(command.output, result, () => renderDebtMutation("add", result, displayContext()), debtExitCode(result));
251
+ return emit(command.output, result, () => renderDebtMutation("add", result, displayContext()), debtOutcome(result));
252
252
  }
253
253
  case "update": {
254
254
  const result = await invokeUpdate(input);
255
- return emit(command.output, result, () => renderDebtUpdate(result, displayContext()), debtExitCode(result));
255
+ return emit(command.output, result, () => renderDebtUpdate(result, displayContext()), debtOutcome(result));
256
256
  }
257
257
  case "done":
258
258
  case "drop":
@@ -276,10 +276,10 @@ function refuseNamespace(command, refusal) {
276
276
  case "blocked":
277
277
  case "query": {
278
278
  const action = command.action;
279
- return emit(command.output, result, () => renderDebtList(action, listPresentation(command), result, displayContext()), debtExitCode(result));
279
+ return emit(command.output, result, () => renderDebtList(action, listPresentation(command), result, displayContext()), debtOutcome(result));
280
280
  }
281
281
  case "add":
282
- return emit(command.output, result, () => renderDebtMutation("add", result, displayContext()), debtExitCode(result));
282
+ return emit(command.output, result, () => renderDebtMutation("add", result, displayContext()), debtOutcome(result));
283
283
  default:
284
284
  throw new Error(`debt action has no namespace refusal: ${command.action}`);
285
285
  }
@@ -307,7 +307,7 @@ async function runContext(command, context) {
307
307
  const result = typeof resolved === "object" && "kind" in resolved
308
308
  ? { kind: "refused", refusal: resolved }
309
309
  : { kind: "accepted", value: resolved };
310
- return emit(command.output, result, () => renderDebtContext(result, displayContext()), debtExitCode(result));
310
+ return emit(command.output, result, () => renderDebtContext(result, displayContext()), debtOutcome(result));
311
311
  }
312
312
  // eslint-disable-next-line complexity -- this is the single CLI edge ordering world, context, forwarding, and local execution.
313
313
  export async function runDebtCommand(command, coordinates, runtime) {
@@ -1,3 +1,4 @@
1
+ import { type CliOutcome } from "../streams.js";
1
2
  import type { ProcessInput, ProcessOutcome } from "../../runtime/proc/run.js";
2
3
  declare const HARNESS_NAMES: readonly ["codex", "claude", "opencode", "pi"];
3
4
  type HarnessName = (typeof HARNESS_NAMES)[number];
@@ -22,5 +23,5 @@ export declare function parseInstallCommand(argv: readonly string[]): ParsedInst
22
23
  export declare function renderInstallHelp(): string;
23
24
  export declare function installHarnesses(harnesses: readonly HarnessName[], environment: NodeJS.ProcessEnv, runner?: InstallRunner): Promise<InstallInvocationResult>;
24
25
  export declare function renderInstallText(result: InstallInvocationResult): string;
25
- export declare function installExitCode(result: InstallInvocationResult): number;
26
+ export declare function installOutcome(result: InstallInvocationResult): CliOutcome;
26
27
  export {};