@astrosheep/keiyaku 4.0.5 → 4.0.6

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 (153) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +15 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -3
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +10 -4
  8. package/build/src/akuma/akuma.d.ts +2 -0
  9. package/build/src/akuma/akuma.js +26 -3
  10. package/build/src/akuma/archetype.d.ts +3 -1
  11. package/build/src/akuma/archetype.js +11 -6
  12. package/build/src/akuma/body-turn.d.ts +64 -0
  13. package/build/src/akuma/body-turn.js +449 -0
  14. package/build/src/akuma/body.d.ts +3 -7
  15. package/build/src/akuma/body.js +9 -420
  16. package/build/src/akuma/heart/facts.d.ts +2 -0
  17. package/build/src/akuma/heart/facts.js +3 -1
  18. package/build/src/akuma/heart/index.js +12 -2
  19. package/build/src/akuma/heart/rows.js +4 -1
  20. package/build/src/akuma/heart/schema.d.ts +1 -1
  21. package/build/src/akuma/heart/schema.js +3 -2
  22. package/build/src/akuma/heart/timeline.d.ts +1 -0
  23. package/build/src/akuma/heart/timeline.js +19 -0
  24. package/build/src/akuma/nuke.d.ts +2 -0
  25. package/build/src/akuma/nuke.js +152 -0
  26. package/build/src/akuma/provider.d.ts +3 -1
  27. package/build/src/akuma/providers/acp/config.d.ts +8 -0
  28. package/build/src/akuma/providers/acp/config.js +37 -0
  29. package/build/src/akuma/providers/acp/core.js +6 -0
  30. package/build/src/akuma/providers/acp/index.d.ts +1 -8
  31. package/build/src/akuma/providers/acp/index.js +2 -37
  32. package/build/src/akuma/providers/claude/index.js +5 -0
  33. package/build/src/akuma/providers/codex-app-server/index.js +5 -0
  34. package/build/src/akuma/providers/grok-build/index.js +1 -1
  35. package/build/src/akuma/providers/index.d.ts +3 -2
  36. package/build/src/akuma/providers/index.js +36 -36
  37. package/build/src/akuma/providers/opencode-sdk/index.js +6 -0
  38. package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
  39. package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
  40. package/build/src/akuma/providers/pi/index.d.ts +1 -1
  41. package/build/src/akuma/providers/pi/index.js +27 -11
  42. package/build/src/akuma/request-serve.d.ts +1 -0
  43. package/build/src/akuma/request-serve.js +31 -31
  44. package/build/src/akuma/request-wire.d.ts +3 -2
  45. package/build/src/akuma/request-wire.js +5 -2
  46. package/build/src/akuma-body.js +15 -1
  47. package/build/src/alias/index.d.ts +1 -0
  48. package/build/src/alias/index.js +34 -1
  49. package/build/src/cli/accepted.js +4 -0
  50. package/build/src/cli/commands/akuma-invoke.js +1 -0
  51. package/build/src/cli/commands/akuma.d.ts +1 -0
  52. package/build/src/cli/commands/akuma.js +11 -8
  53. package/build/src/cli/commands/contract.d.ts +25 -14
  54. package/build/src/cli/commands/contract.js +23 -17
  55. package/build/src/cli/commands/install.d.ts +2 -2
  56. package/build/src/cli/commands/install.js +6 -6
  57. package/build/src/cli/commands/nuke.d.ts +10 -0
  58. package/build/src/cli/commands/nuke.js +20 -0
  59. package/build/src/cli/commands/task-invoke.d.ts +15 -0
  60. package/build/src/cli/commands/task-invoke.js +57 -26
  61. package/build/src/cli/commands/task-query.d.ts +2 -2
  62. package/build/src/cli/commands/task-query.js +1 -2
  63. package/build/src/cli/commands/task.js +22 -22
  64. package/build/src/cli/coordinates.d.ts +5 -2
  65. package/build/src/cli/coordinates.js +5 -52
  66. package/build/src/cli/invoke.d.ts +5 -4
  67. package/build/src/cli/invoke.js +89 -59
  68. package/build/src/cli/main.d.ts +0 -2
  69. package/build/src/cli/main.js +4 -140
  70. package/build/src/cli/parse.d.ts +15 -2
  71. package/build/src/cli/parse.js +73 -8
  72. package/build/src/cli/render/akuma.js +55 -40
  73. package/build/src/cli/render/contract.js +49 -48
  74. package/build/src/cli/render/kanshi.js +15 -5
  75. package/build/src/cli/render/nuke.d.ts +3 -0
  76. package/build/src/cli/render/nuke.js +8 -0
  77. package/build/src/cli/render/refusal.d.ts +2 -1
  78. package/build/src/cli/render/refusal.js +31 -0
  79. package/build/src/cli/render/text.js +6 -1
  80. package/build/src/cli/result.d.ts +21 -2
  81. package/build/src/cli/runtime.d.ts +2 -0
  82. package/build/src/cli/runtime.js +140 -0
  83. package/build/src/cli/usage.d.ts +0 -2
  84. package/build/src/cli/usage.js +0 -4
  85. package/build/src/contract-worktree.d.ts +28 -12
  86. package/build/src/contract-worktree.js +129 -63
  87. package/build/src/git/hooks.d.ts +2 -0
  88. package/build/src/git/hooks.js +34 -1
  89. package/build/src/git/integration.d.ts +4 -0
  90. package/build/src/git/integration.js +38 -0
  91. package/build/src/git/nuke.d.ts +2 -0
  92. package/build/src/git/nuke.js +97 -0
  93. package/build/src/git/process.d.ts +2 -0
  94. package/build/src/git/reconcile.d.ts +2 -0
  95. package/build/src/git/reconcile.js +33 -2
  96. package/build/src/git/repository.js +4 -1
  97. package/build/src/git/target-placement.d.ts +2 -1
  98. package/build/src/git/target-placement.js +6 -7
  99. package/build/src/git/tender.d.ts +1 -0
  100. package/build/src/git/tender.js +2 -2
  101. package/build/src/git/workspace.d.ts +3 -0
  102. package/build/src/kanshi/read.js +5 -1
  103. package/build/src/library/akuma-creation.d.ts +1 -0
  104. package/build/src/library/akuma-creation.js +8 -1
  105. package/build/src/library/audit.d.ts +1 -0
  106. package/build/src/library/audit.js +1 -0
  107. package/build/src/library/contract.d.ts +6 -4
  108. package/build/src/library/contract.js +56 -34
  109. package/build/src/library/delivery.d.ts +3 -1
  110. package/build/src/library/keiyaku.d.ts +5 -1
  111. package/build/src/library/keiyaku.js +2 -0
  112. package/build/src/library/mutation.d.ts +1 -0
  113. package/build/src/library/mutation.js +3 -0
  114. package/build/src/library/nuke.d.ts +14 -0
  115. package/build/src/library/nuke.js +54 -0
  116. package/build/src/library/reconcile.js +5 -2
  117. package/build/src/library/refusal.d.ts +10 -1
  118. package/build/src/library/repo.d.ts +1 -0
  119. package/build/src/library/repo.js +2 -0
  120. package/build/src/protocol/audit.js +12 -5
  121. package/build/src/protocol/completion.d.ts +11 -1
  122. package/build/src/protocol/completion.js +68 -15
  123. package/build/src/protocol/deliver.d.ts +14 -2
  124. package/build/src/protocol/deliver.js +107 -6
  125. package/build/src/protocol/intent.d.ts +1 -0
  126. package/build/src/protocol/intent.js +7 -1
  127. package/build/src/protocol/operations.d.ts +30 -3
  128. package/build/src/protocol/operations.js +2 -2
  129. package/build/src/protocol/placement.d.ts +9 -1
  130. package/build/src/protocol/placement.js +5 -4
  131. package/build/src/protocol/read/status.d.ts +16 -4
  132. package/build/src/protocol/read/status.js +54 -12
  133. package/build/src/protocol/review.d.ts +1 -0
  134. package/build/src/protocol/review.js +8 -0
  135. package/build/src/runtime/proc/run.js +9 -5
  136. package/build/src/settlement/fence.d.ts +2 -0
  137. package/build/src/settlement/fence.js +24 -0
  138. package/build/src/settlement/settle.js +1 -2
  139. package/build/src/task/compose.d.ts +1 -1
  140. package/build/src/task/compose.js +2 -7
  141. package/build/src/task/context.d.ts +14 -2
  142. package/build/src/task/context.js +30 -7
  143. package/build/src/task/index.d.ts +6 -5
  144. package/build/src/task/index.js +15 -18
  145. package/build/src/task/mutation.d.ts +7 -2
  146. package/build/src/task/mutation.js +14 -6
  147. package/build/src/task/operations.d.ts +12 -6
  148. package/build/src/task/operations.js +22 -46
  149. package/build/src/task/store.d.ts +1 -0
  150. package/build/src/task/store.js +91 -1
  151. package/build/src/workspace-place.d.ts +2 -0
  152. package/build/src/workspace-place.js +27 -1
  153. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
- import { isTaskAction, parseTaskCommand, renderTaskUsage, } from "./commands/task.js";
2
- import { isAkumaAction, parseAkumaCatalogPath, parseAkumaCommand, renderAkumaRootRows, renderAkumaUsage, } from "./commands/akuma.js";
1
+ import { isTaskAction, parseTaskCommand, renderTaskHelp, renderTaskUsage, } from "./commands/task.js";
2
+ import { isAkumaAction, parseAkumaCatalogPath, parseAkumaCommand, renderAkumaHelp, renderAkumaRootRows, renderAkumaUsage, } from "./commands/akuma.js";
3
3
  import { INSTALL_USAGE, parseInstallCommand, renderInstallHelp } from "./commands/install.js";
4
- import { AMEND_MINIMAL_STDIN_HELP, CONTRACT_COMMAND_SPECS, } from "./commands/contract.js";
5
- import { CliUsageError, isBlankInput, usageLine, withJsonAutomationHelp } from "./usage.js";
4
+ import { AMEND_OPERATIONS_HELP, CONTRACT_COMMAND_SPECS, } from "./commands/contract.js";
5
+ import { CliUsageError, isBlankInput, usageLine } from "./usage.js";
6
6
  export { CliUsageError } from "./usage.js";
7
7
  const ROOT_USAGE = "usage: keiyaku [-C <path>] [--repo <path>] <command> [<contract>|@<contract>] [--flag ...] [-]";
8
8
  export function renderRootHelp() {
9
- return withJsonAutomationHelp([
9
+ return [
10
10
  ROOT_USAGE,
11
11
  "",
12
12
  "global options:",
@@ -20,12 +20,12 @@ export function renderRootHelp() {
20
20
  " task ...",
21
21
  " Task coordination; see `keiyaku task --help`.",
22
22
  ...renderAkumaRootRows(),
23
- ].join("\n"));
23
+ ].join("\n");
24
24
  }
25
25
  export function renderContractHelp(command) {
26
26
  const spec = CONTRACT_COMMAND_SPECS[command];
27
27
  const help = `${spec.purpose}\n\n${usageLine(spec.usage)}`;
28
- return withJsonAutomationHelp(command === "amend" ? `${help}\n\n${AMEND_MINIMAL_STDIN_HELP}` : help);
28
+ return command === "amend" ? `${help}\n\n${AMEND_OPERATIONS_HELP}` : help;
29
29
  }
30
30
  function contractUsage(command) {
31
31
  return usageLine(CONTRACT_COMMAND_SPECS[command].usage);
@@ -39,6 +39,65 @@ export function renderCommandUsage(command) {
39
39
  return renderAkumaUsage(command.command);
40
40
  return contractUsage(command.command);
41
41
  }
42
+ export function renderHelp(coordinate) {
43
+ switch (coordinate.kind) {
44
+ case "root": return renderRootHelp();
45
+ case "contract": return renderContractHelp(coordinate.command);
46
+ case "task": return renderTaskHelp(coordinate.action);
47
+ case "install": return renderInstallHelp();
48
+ case "akuma": return renderAkumaHelp(coordinate.action);
49
+ }
50
+ }
51
+ export function commandRepoPolicy(command) {
52
+ switch (command.command) {
53
+ case "bind":
54
+ case "amend":
55
+ case "deliver":
56
+ case "review":
57
+ case "arc":
58
+ case "abandon":
59
+ case "audit":
60
+ case "reconcile":
61
+ case "show":
62
+ case "region":
63
+ return { use: "required", acceptsExplicit: true };
64
+ case "ls":
65
+ return { use: command.query.kind === "contracts" ? "required" : "none", acceptsExplicit: false };
66
+ case "status":
67
+ case "tell":
68
+ return { use: "optional", acceptsExplicit: false };
69
+ case "history":
70
+ return "contract" in command
71
+ ? { use: "required", acceptsExplicit: true }
72
+ : { use: "optional", acceptsExplicit: false };
73
+ case "fork":
74
+ return { use: "optional", acceptsExplicit: true };
75
+ case "wait":
76
+ case "kill": {
77
+ const contractSelector = command.akuma.some((selector) => selector.startsWith("kei/"));
78
+ return { use: contractSelector ? "required" : "optional", acceptsExplicit: contractSelector };
79
+ }
80
+ case "call":
81
+ return {
82
+ use: command.contract === undefined ? "none" : "required",
83
+ acceptsExplicit: command.contract !== undefined,
84
+ };
85
+ case "settings":
86
+ case "nuke":
87
+ case "task":
88
+ case "install":
89
+ return { use: "none", acceptsExplicit: false };
90
+ }
91
+ }
92
+ function refuseUnusedRepo(command) {
93
+ if (command.command === "call")
94
+ throw new CliUsageError("--repo has no consumer without --contract");
95
+ throw new CliUsageError(`--repo has no consumer for ${command.command}`);
96
+ }
97
+ export function assertExplicitRepoUse(command, repo) {
98
+ if (repo !== undefined && !commandRepoPolicy(command).acceptsExplicit)
99
+ refuseUnusedRepo(command);
100
+ }
42
101
  function optionalFlag(flags, name) {
43
102
  const value = flags[name];
44
103
  return typeof value === "string" ? value : undefined;
@@ -175,6 +234,7 @@ function parseDeliver(parts) {
175
234
  ...(parts.actor === undefined ? {} : { actor: parts.actor }),
176
235
  ...(message === undefined ? {} : { message }),
177
236
  includeDirty: parts.flags["include-dirty"] === true,
237
+ materializeConflict: parts.flags["materialize-conflict"] === true,
178
238
  output: parts.output,
179
239
  };
180
240
  }
@@ -356,6 +416,10 @@ function parseCommand(parts) {
356
416
  output: parts.output,
357
417
  };
358
418
  }
419
+ case "nuke": {
420
+ const confirm = optionalFlag(parts.flags, "confirm");
421
+ return { command: "nuke", ...(confirm === undefined ? {} : { confirm }), output: parts.output };
422
+ }
359
423
  case "settings": return { command: "settings", output: parts.output };
360
424
  case "region": return parseRegion(parts);
361
425
  }
@@ -379,9 +443,10 @@ export function parseArgv(argv) {
379
443
  const akuma = isAkumaAction(invocation.commandArgv[0])
380
444
  ? parseAkumaCommand(invocation.commandArgv)
381
445
  : undefined;
382
- return {
446
+ const parsed = {
383
447
  ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
384
448
  ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
385
449
  command: task ?? akuma ?? install ?? parseCommand(scanArgv(invocation.commandArgv)),
386
450
  };
451
+ return parsed;
387
452
  }
@@ -1,5 +1,5 @@
1
1
  import { toolRepr } from "./akuma-tool.js";
2
- import { displayColumns, renderBoundedTextBlock, renderTextBlock, safeText, truncateMiddleDisplayText, } from "./terminal.js";
2
+ import { displayColumns, renderBoundedTextBlock, safeText, truncateMiddleDisplayText, } from "./terminal.js";
3
3
  const DEFAULT_CONTEXT = { columns: 80, color: false };
4
4
  const OPENING_STROKE = "─────";
5
5
  const TIME_WIDTH = 5;
@@ -30,7 +30,7 @@ function unobservedText(id, diagnostic) {
30
30
  }
31
31
  function lifeLabel(life) {
32
32
  if (life === "running")
33
- return "● running";
33
+ return "● STILL RUNNING";
34
34
  if (life === "asleep")
35
35
  return "○ asleep";
36
36
  if (life === "killed")
@@ -70,7 +70,7 @@ function mark(row) {
70
70
  return "?";
71
71
  return row.state.status === "ok" ? "✓" : "!";
72
72
  }
73
- return "·";
73
+ return "│";
74
74
  }
75
75
  function rowText(row) {
76
76
  if (row.kind === "said" || row.kind === "thought" || row.kind === "note" || row.kind === "call" || row.kind === "tell") {
@@ -160,47 +160,59 @@ function taskDispositionMark(disposition) {
160
160
  return "●";
161
161
  return disposition === "blocked" ? "‖" : "○";
162
162
  }
163
- function taskScanLine(row, indent = "") {
164
- return `${indent}${taskDispositionMark(row.disposition)} ${row.id} · P${row.priority} ${row.disposition}`;
165
- }
166
- function changeOp(op) {
167
- return op === "add" ? "write" : op === "delete" ? "delete" : "edit";
168
- }
169
- function renderReportedChangeLines(snapshot, columns) {
170
- const lines = [` changes ${snapshot.reportedChanges.length + snapshot.reportedChangesOmitted}`];
171
- for (const change of snapshot.reportedChanges) {
172
- const scan = ` ${changeOp(change.op)}`;
173
- const path = safeText(change.path);
174
- const stat = change.diffstat === undefined
175
- ? ""
176
- : ` — +${change.diffstat.added} -${change.diffstat.removed}`;
177
- const inline = `${scan} ${path}${stat}`;
178
- if (displayColumns(inline) <= columns)
179
- lines.push(inline);
180
- else
181
- lines.push(`${scan}`, ...renderTextBlock(`${path}${stat}`, " ", columns));
163
+ function changeStat(group) {
164
+ let added = 0;
165
+ let removed = 0;
166
+ for (const change of group) {
167
+ if (change.diffstat === undefined)
168
+ return "+? -?";
169
+ added += change.diffstat.added;
170
+ removed += change.diffstat.removed;
182
171
  }
183
- if (snapshot.reportedChangesOmitted > 0) {
184
- lines.push(` ⋮ ${snapshot.reportedChangesOmitted} earlier changes`);
172
+ return `+${added} -${removed}`;
173
+ }
174
+ function groupedChangeRows(changes) {
175
+ const groups = [];
176
+ const seen = new Map();
177
+ for (const change of changes) {
178
+ const index = seen.get(change.path);
179
+ if (index === undefined) {
180
+ seen.set(change.path, groups.length);
181
+ groups.push([change]);
182
+ }
183
+ else
184
+ groups[index].push(change);
185
185
  }
186
- return lines;
186
+ return groups.map((group) => ({ path: group[0].path, stat: changeStat(group) }));
187
+ }
188
+ function renderReportedChangeLines(snapshot) {
189
+ const rows = groupedChangeRows(snapshot.reportedChanges);
190
+ const width = rows.reduce((max, row) => Math.max(max, row.stat.length), 0);
191
+ return [
192
+ `changes ${snapshot.reportedChanges.length + snapshot.reportedChangesOmitted}`,
193
+ ...rows.map((row) => ` ${row.stat.padEnd(width)} ${safeText(row.path)}`),
194
+ ...(snapshot.reportedChangesOmitted > 0 ? [` ⋮ ${snapshot.reportedChangesOmitted} earlier changes`] : []),
195
+ ];
196
+ }
197
+ function renderTaskRow(row, columns) {
198
+ const prefix = ` ${taskDispositionMark(row.disposition)} ${row.id} · `;
199
+ const body = `${safeText(row.title)} · ${row.disposition} · P${row.priority}`;
200
+ const inline = `${prefix}${body}`;
201
+ if (displayColumns(inline) <= columns)
202
+ return [inline];
203
+ return renderBoundedTextBlock(body, {
204
+ first: prefix,
205
+ continuation: " ",
206
+ columns,
207
+ lines: Number.MAX_SAFE_INTEGER,
208
+ });
187
209
  }
188
210
  function renderTaskContextLines(created, columns) {
189
211
  if (created === undefined)
190
212
  return [];
191
213
  if (created.kind === "failed")
192
214
  return [`! tasks failed ${safeText(created.diagnostic)}`];
193
- const lines = [` tasks ${created.rows.length}`];
194
- for (const row of created.rows) {
195
- const scan = ` ${taskScanLine(row)}`;
196
- const title = safeText(row.title);
197
- const inline = `${scan} — ${title}`;
198
- if (displayColumns(inline) <= columns)
199
- lines.push(inline);
200
- else
201
- lines.push(`${scan} —`, ...renderTextBlock(title, " ", columns));
202
- }
203
- return lines;
215
+ return [`tasks ${created.rows.length}`, ...created.rows.flatMap((row) => renderTaskRow(row, columns))];
204
216
  }
205
217
  function snapshotText(view, context, options = {}) {
206
218
  const snapshot = view.status.timeline;
@@ -215,14 +227,14 @@ function snapshotText(view, context, options = {}) {
215
227
  ...contractFacts(view.contract),
216
228
  ...(options.facts ?? []),
217
229
  ];
218
- const footer = options.showLife === false ? [] : [` ${lifeLabel(view.status.life)}`];
230
+ const footer = options.showLife === false ? [] : ["", lifeLabel(view.status.life)];
219
231
  return [
220
232
  ...snapshotHeading(view.status.id, options.alias, view.contract),
221
233
  ...facts,
222
234
  ...activity,
223
- ...footer,
224
- ...renderReportedChangeLines(snapshot, context.columns),
225
235
  ...renderTaskContextLines(view.createdTasks, context.columns),
236
+ ...renderReportedChangeLines(snapshot),
237
+ ...footer,
226
238
  ].join("\n");
227
239
  }
228
240
  function observationStageText(id, observation, context, options = {}) {
@@ -291,6 +303,9 @@ export function akumaRawAnswer(result) {
291
303
  function executionCwdLine(result) {
292
304
  return result.execution.source === "contract-worktree" ? [`cwd ${result.execution.cwd}`] : [];
293
305
  }
306
+ function posixShellArgument(value) {
307
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
308
+ }
294
309
  function callText(result, context) {
295
310
  const alias = result.result.alias.kind === "aliased" ? result.result.alias.alias.alias : undefined;
296
311
  const contractId = result.result.dispatch.kind === "dispatched" ? result.result.dispatch.dispatch.contractId : undefined;
@@ -305,7 +320,7 @@ function callText(result, context) {
305
320
  : { kind: "associated", contractId }), ...cwd, ...restraint, ...facts];
306
321
  if (!callFailed(result.result)) {
307
322
  const selector = result.result.alias.kind === "aliased" ? result.result.alias.alias.alias : result.result.akuma;
308
- lines.push(`$ keiyaku -C ${result.world} wait ${selector} --timeout 5m`);
323
+ lines.push(`$ keiyaku -C ${posixShellArgument(result.world)} wait ${selector} --timeout 5m`);
309
324
  }
310
325
  return lines.join("\n");
311
326
  }
@@ -113,11 +113,20 @@ function acceptedRecord(result, columns) {
113
113
  const record = [];
114
114
  for (const fact of result.facts) {
115
115
  const contract = fact.contract === result.contract ? [] : [{ text: fact.contract, opaque: true }];
116
- receiptRow(record, " ", "journal", [
117
- ...contract,
118
- { text: fact.entry, opaque: true },
119
- { text: `· ${fact.kind}` },
120
- ], columns);
116
+ receiptRow(record, " ", "journal", fact.kind === "reintegrated"
117
+ ? [
118
+ ...contract,
119
+ { text: fact.entry, opaque: true },
120
+ { text: "· reintegrated" },
121
+ { text: fact.data.predecessor, opaque: true },
122
+ { text: "->" },
123
+ { text: fact.data.snapshot, opaque: true },
124
+ ]
125
+ : [
126
+ ...contract,
127
+ { text: fact.entry, opaque: true },
128
+ { text: `· ${fact.kind}` },
129
+ ], columns);
121
130
  }
122
131
  receiptRow(record, " ", "head", [{ text: result.head, opaque: true }], columns);
123
132
  if (result.verb === "deliver") {
@@ -176,11 +185,36 @@ function recordBlock(result, columns) {
176
185
  const rows = [...acceptedRecord(result, columns), ...acceptedLagRows(result, columns)];
177
186
  return [" record", ...indentRecord(rows)];
178
187
  }
179
- function verificationVerdictLine(verdict, columns) {
188
+ function completionLines(result, columns) {
180
189
  const lines = [];
181
- receiptRow(lines, verdict === "satisfied" ? "✓" : "!", "verification", [
182
- { text: verdict === "satisfied" ? "passed" : "failed" },
190
+ const completion = result.completion;
191
+ if (completion === undefined)
192
+ return lines;
193
+ const verification = completion.verification;
194
+ receiptRow(lines, " ", "target", [
195
+ { text: "->" },
196
+ { text: completion.integration, opaque: true },
197
+ ...(verification?.verdict === "satisfied"
198
+ ? [{ text: `· verified (${verification.mode})` }]
199
+ : []),
183
200
  ], columns);
201
+ if (verification?.verdict === "unsatisfied") {
202
+ receiptRow(lines, "!", "verification", [
203
+ { text: "unsatisfied" },
204
+ { text: `(${verification.mode})` },
205
+ ], columns);
206
+ if (result.verificationSummary !== undefined) {
207
+ receiptPayload(lines, " summary", result.verificationSummary);
208
+ }
209
+ }
210
+ return lines;
211
+ }
212
+ function movementLines(result, columns) {
213
+ const count = result.facts.filter((fact) => fact.kind === "reintegrated").length;
214
+ if (count === 0)
215
+ return [];
216
+ const lines = [];
217
+ receiptRow(lines, "~", "target", [{ text: `moved · re-integrated x${count}` }], columns);
184
218
  return lines;
185
219
  }
186
220
  function renderAcceptedBind(result, columns) {
@@ -201,51 +235,19 @@ function renderAcceptedAmend(result, columns) {
201
235
  lines.push(...acceptedDeviations(result, columns), ...recordBlock(result, columns));
202
236
  return lines.join("\n");
203
237
  }
204
- function reintegrationLines(result, complete, columns) {
205
- const reintegrated = result.facts.filter((fact) => fact.kind === "reintegrated");
206
- if (reintegrated.length === 0)
207
- return [];
208
- const lines = [];
209
- for (const fact of reintegrated) {
210
- receiptRow(lines, " ", "re-integrated", [
211
- { text: fact.data.predecessor, opaque: true },
212
- { text: "->" },
213
- { text: fact.data.snapshot, opaque: true },
214
- ], columns);
215
- }
216
- if (result.verificationVerdict !== undefined) {
217
- receiptRow(lines, " ", "verification re-run", [
218
- { text: "·" },
219
- { text: result.verificationVerdict },
220
- ], columns);
221
- }
222
- if (complete) {
223
- receiptRow(lines, " ", "target", [
224
- { text: "->" },
225
- { text: reintegrated.at(-1).data.snapshot, opaque: true },
226
- ], columns);
227
- }
228
- return lines;
229
- }
230
238
  function renderAcceptedDeliver(result, columns) {
231
- const complete = result.facts.some((fact) => fact.kind === "claimed");
239
+ const complete = result.completion !== undefined;
232
240
  const title = complete ? "delivered" : "deliver — not complete";
233
241
  const lines = titleLines("✓", title, result.contract, columns);
234
- lines.push(...reintegrationLines(result, complete, columns));
242
+ lines.push(...movementLines(result, columns), ...completionLines(result, columns));
235
243
  if (!complete)
236
244
  receiptRow(lines, " ", "candidate", [{ text: "kept" }], columns);
237
245
  if (result.verification !== undefined) {
238
246
  lines.push(...stopLines("verification", result.verification, columns, result.contract));
239
247
  }
240
- else if (result.verificationVerdict !== undefined) {
241
- lines.push(...verificationVerdictLine(result.verificationVerdict, columns));
242
- }
243
248
  if (!complete && result.placement !== undefined) {
244
249
  lines.push(...stopLines("completion", result.placement, columns, result.contract));
245
250
  }
246
- else if (!complete && (result.verification !== undefined || result.verificationVerdict === "unsatisfied")) {
247
- receiptRow(lines, "·", "completion", [{ text: "not attempted" }], columns);
248
- }
249
251
  if (result.cleanup !== undefined)
250
252
  pushBlock(lines, cleanupLines(result.cleanup, columns));
251
253
  if (result.leak !== undefined)
@@ -254,12 +256,11 @@ function renderAcceptedDeliver(result, columns) {
254
256
  return lines.join("\n");
255
257
  }
256
258
  function renderAcceptedReview(result, columns) {
257
- const complete = result.facts.some((fact) => fact.kind === "claimed");
258
- const lines = titleLines("✓", `review ${result.verdict}`, result.contract, columns);
259
- lines.push(...reintegrationLines(result, complete, columns));
260
- receiptRow(lines, complete ? "✓" : "!", "contract", [
261
- { text: complete ? "complete" : "not complete" },
262
- ], columns);
259
+ const complete = result.completion !== undefined;
260
+ const lines = titleLines("✓", `review ${result.verdict} — ${complete ? "complete" : "not complete"}`, result.contract, columns);
261
+ lines.push(...movementLines(result, columns), ...completionLines(result, columns));
262
+ if (!complete)
263
+ receiptRow(lines, " ", "candidate", [{ text: "kept" }], columns);
263
264
  if (result.placement !== undefined) {
264
265
  lines.push(...stopLines("completion", result.placement, columns, result.contract));
265
266
  }
@@ -85,8 +85,13 @@ function targetFacts(row) {
85
85
  }
86
86
  function workspaceState(row) {
87
87
  const observation = row.workspaceObservation;
88
+ if (row.workspace === "here" && observation.kind === "failed") {
89
+ return `workspace here · failed · ${observation.diagnostic}`;
90
+ }
88
91
  if (row.workspace === "here")
89
92
  return `workspace here · ${observation.kind}`;
93
+ if (observation.kind === "failed")
94
+ return `worktree unavailable · ${observation.diagnostic}`;
90
95
  if (observation.kind === "unappointed")
91
96
  return "worktree unappointed";
92
97
  if (observation.kind === "unavailable")
@@ -106,7 +111,7 @@ function showWorkspacePath(row, hot) {
106
111
  return false;
107
112
  if (row.workspaceObservation.kind === "unappointed")
108
113
  return false;
109
- if (row.workspaceObservation.kind !== "clean")
114
+ if (row.workspaceObservation.kind !== "clean" && row.workspaceObservation.kind !== "failed")
110
115
  return true;
111
116
  return hot;
112
117
  }
@@ -219,13 +224,15 @@ function renderContractRow(row, report, context, selection) {
219
224
  const compact = !hot && selection !== "contract";
220
225
  const title = row.title ?? "title unavailable";
221
226
  const phase = `${row.phase} · ${formatAge(row.phaseAt, report.observedAt)}`;
227
+ const journal = `journal ${formatAge(row.lastJournalAt, report.observedAt)}`;
222
228
  const lines = [identityLine(contractMark(row), row.id)];
223
- lines.push(...plumbFacts(compact ? [title, phase, ...targetFacts(row)] : [title], context.columns));
229
+ lines.push(...plumbFacts(compact ? [title, phase, journal, ...targetFacts(row)] : [title], context.columns));
224
230
  if (!compact)
225
- lines.push(...plumbFacts([phase, ...targetFacts(row)], context.columns));
231
+ lines.push(...plumbFacts([phase, journal, ...targetFacts(row)], context.columns));
226
232
  lines.push(...plumbFacts([workspaceState(row)], context.columns));
227
233
  if (showWorkspacePath(row, hot || selection === "contract")
228
234
  && row.workspaceObservation.kind !== "unappointed"
235
+ && row.workspaceObservation.kind !== "failed"
229
236
  && row.workspaceObservation.location.kind === "worktree") {
230
237
  lines.push(plumbPath(row.workspaceObservation.location.path));
231
238
  }
@@ -320,9 +327,12 @@ function renderAkuma(report, context) {
320
327
  lines.push(identityLine(akumaMark(row.life), row.id, akumaLabel(row)));
321
328
  const lifeAt = "lifeAt" in row ? row.lifeAt : null;
322
329
  const life = `${row.life} · ${formatAge(lifeAt, report.observedAt)}`;
330
+ const activity = "lastActivityAt" in row && row.lastActivityAt !== null
331
+ ? [`activity ${formatAge(row.lastActivityAt, report.observedAt)}`]
332
+ : [];
323
333
  const key = row.life === "stranded" && "strandedReason" in row && row.strandedReason === "resume-unsupported"
324
- ? [life, "resume unsupported"]
325
- : [life];
334
+ ? [life, ...activity, "resume unsupported"]
335
+ : [life, ...activity];
326
336
  const relation = row.contract === undefined
327
337
  ? ["unbound"]
328
338
  : [endpointFact(row.contract.id, row.contract.observed)];
@@ -0,0 +1,3 @@
1
+ import type { NukeResult } from "../../index.js";
2
+ export declare function renderNukeText(result: NukeResult): string;
3
+ export declare function nukeExitCode(result: NukeResult): number;
@@ -0,0 +1,8 @@
1
+ export function renderNukeText(result) {
2
+ return result.kind === "success"
3
+ ? `nuke success ${result.world}`
4
+ : `nuke failed ${result.world}\n${result.diagnostic}`;
5
+ }
6
+ export function nukeExitCode(result) {
7
+ return result.kind === "failed" ? 2 : 0;
8
+ }
@@ -1,5 +1,5 @@
1
1
  import type { BindDraftReceipt, RefusedResult } from "../result.js";
2
- import type { KeiyakuRefusal } from "../../index.js";
2
+ import type { IntegrationConflictMaterialized, KeiyakuRefusal } from "../../index.js";
3
3
  import { type TextRenderContext } from "./terminal.js";
4
4
  type DirtyWithOption = Extract<KeiyakuRefusal, {
5
5
  kind: "dirty-workspace";
@@ -12,5 +12,6 @@ type DirtyWithOption = Extract<KeiyakuRefusal, {
12
12
  export type RenderableRefusal = KeiyakuRefusal | DirtyWithOption;
13
13
  export declare function renderRefusalFacts(refusal: RenderableRefusal, indent: string, columns: number, addressed?: string): readonly string[];
14
14
  export declare function renderRefusal(result: RefusedResult, context?: TextRenderContext): string;
15
+ export declare function renderConflictMaterialized(result: IntegrationConflictMaterialized, context?: TextRenderContext): string;
15
16
  export declare function renderBindDraftReceipt(receipt: BindDraftReceipt): string;
16
17
  export {};
@@ -38,6 +38,18 @@ function renderDirtyRefusal(refusal, indent, columns, identity) {
38
38
  }
39
39
  export function renderRefusalFacts(refusal, indent, columns, addressed) {
40
40
  const identity = refusalIdentity(refusal, addressed);
41
+ if (refusal.kind === "nuke-confirmation-mismatch") {
42
+ return [
43
+ ...renderOpaqueBlock(`nuke confirmation mismatch world=${refusal.world} confirmation=${refusal.confirmation}`, indent, columns),
44
+ ...renderOpaqueBlock(`keiyaku nuke --confirm ${refusal.world}`, indent, columns),
45
+ ];
46
+ }
47
+ if (refusal.kind === "nuke-confirmation-required") {
48
+ return [
49
+ ...renderOpaqueBlock(`nuke confirmation required world=${refusal.world}`, indent, columns),
50
+ ...renderOpaqueBlock(`keiyaku nuke --confirm ${refusal.world}`, indent, columns),
51
+ ];
52
+ }
41
53
  if (refusal.kind === "dirty-workspace")
42
54
  return renderDirtyRefusal(refusal, indent, columns, identity);
43
55
  if (refusal.kind === "integration-failed") {
@@ -46,8 +58,18 @@ export function renderRefusalFacts(refusal, indent, columns, addressed) {
46
58
  ];
47
59
  if (refusal.conflictPaths !== undefined)
48
60
  lines.push(...collectionLines("conflictPaths", refusal.conflictPaths, indent, columns));
61
+ if ("recovery" in refusal && refusal.recovery !== undefined) {
62
+ wrap(lines, `recovery materialize conflicts · ${refusal.recovery.materialize}`, indent, columns);
63
+ wrap(lines, `recovery continue after resolve and commit · ${refusal.recovery.continue}`, indent, columns);
64
+ }
49
65
  return lines;
50
66
  }
67
+ if (refusal.kind === "merge-state-present") {
68
+ return renderOpaqueBlock(refusalHead(refusal.kind, identity, [
69
+ `workspace=${refusal.workspace.kind}`,
70
+ `path=${refusal.workspace.path}`,
71
+ ]), indent, columns);
72
+ }
51
73
  if (refusal.kind === "integration-unsupported") {
52
74
  return renderOpaqueBlock(refusalHead(refusal.kind, identity, [`requiredGit=${refusal.requiredGit}`]), indent, columns);
53
75
  }
@@ -81,6 +103,15 @@ export function renderRefusal(result, context) {
81
103
  const output = lines.join("\n");
82
104
  return result.draft === undefined ? output : `${output}\n${renderBindDraftReceipt(result.draft)}`;
83
105
  }
106
+ export function renderConflictMaterialized(result, context) {
107
+ const columns = context?.columns ?? 80;
108
+ const indent = " ";
109
+ return [
110
+ ...renderOpaqueBlock(`integration-conflict-materialized targetHead=${result.targetHead}`, "", columns),
111
+ ...collectionLines("conflictPaths", result.conflictPaths, indent, columns),
112
+ ...renderOpaqueBlock(`workspace ${result.workspace.kind} ${result.workspace.path}`, indent, columns),
113
+ ].join("\n");
114
+ }
84
115
  export function renderBindDraftReceipt(receipt) {
85
116
  return [
86
117
  ...(receipt.path === undefined ? [] : [`draft preserved: ${receipt.path}`]),
@@ -1,8 +1,9 @@
1
1
  import { renderObservation } from "./board.js";
2
2
  import { renderAccepted, renderContractHistory, renderRetry } from "./contract.js";
3
3
  import { renderKanshiText } from "./kanshi.js";
4
- import { renderRefusal } from "./refusal.js";
4
+ import { renderConflictMaterialized, renderRefusal } from "./refusal.js";
5
5
  import { renderCatalogText } from "./catalog.js";
6
+ import { renderNukeText } from "./nuke.js";
6
7
  import { renderRegionText } from "./region.js";
7
8
  export function renderText(result, context) {
8
9
  if (result.kind === "guidance")
@@ -15,11 +16,15 @@ export function renderText(result, context) {
15
16
  return renderContractHistory(result.history);
16
17
  if (result.kind === "region")
17
18
  return renderRegionText(result.region);
19
+ if (result.kind === "nuke")
20
+ return renderNukeText(result.result);
18
21
  if (result.kind === "accepted")
19
22
  return renderAccepted(result, context);
20
23
  if (result.kind === "refused")
21
24
  return renderRefusal(result, context);
22
25
  if (result.kind === "retry")
23
26
  return renderRetry(result, context);
27
+ if (result.kind === "integration-conflict-materialized")
28
+ return renderConflictMaterialized(result, context);
24
29
  return renderObservation(result);
25
30
  }