@astrosheep/keiyaku 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.md +133 -8
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
  12. package/build/src/akuma/abort.d.ts +2 -0
  13. package/build/src/akuma/abort.js +50 -0
  14. package/build/src/akuma/akuma.d.ts +50 -33
  15. package/build/src/akuma/akuma.js +175 -127
  16. package/build/src/akuma/archetype.d.ts +14 -5
  17. package/build/src/akuma/archetype.js +85 -39
  18. package/build/src/akuma/body.d.ts +3 -5
  19. package/build/src/akuma/body.js +490 -183
  20. package/build/src/akuma/coordinate.d.ts +9 -0
  21. package/build/src/akuma/coordinate.js +22 -0
  22. package/build/src/akuma/heart/facts.d.ts +88 -45
  23. package/build/src/akuma/heart/facts.js +19 -8
  24. package/build/src/akuma/heart/index.d.ts +64 -55
  25. package/build/src/akuma/heart/index.js +172 -252
  26. package/build/src/akuma/heart/rows.d.ts +38 -83
  27. package/build/src/akuma/heart/rows.js +117 -155
  28. package/build/src/akuma/heart/schema.d.ts +1 -1
  29. package/build/src/akuma/heart/schema.js +61 -22
  30. package/build/src/akuma/heart/soul.d.ts +8 -0
  31. package/build/src/akuma/heart/soul.js +136 -0
  32. package/build/src/akuma/heart/storage.d.ts +47 -0
  33. package/build/src/akuma/heart/storage.js +224 -0
  34. package/build/src/akuma/heart/tells.d.ts +12 -0
  35. package/build/src/akuma/heart/tells.js +79 -0
  36. package/build/src/akuma/heart/timeline.d.ts +15 -0
  37. package/build/src/akuma/heart/timeline.js +86 -0
  38. package/build/src/akuma/identity.d.ts +2 -2
  39. package/build/src/akuma/identity.js +9 -9
  40. package/build/src/akuma/index.d.ts +10 -3
  41. package/build/src/akuma/index.js +7 -1
  42. package/build/src/akuma/projection.d.ts +176 -0
  43. package/build/src/akuma/projection.js +256 -0
  44. package/build/src/akuma/provider-recipe.d.ts +24 -0
  45. package/build/src/akuma/provider-recipe.js +100 -0
  46. package/build/src/akuma/provider.d.ts +67 -13
  47. package/build/src/akuma/provider.js +172 -105
  48. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  49. package/build/src/akuma/providers/acp/core.js +159 -0
  50. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  51. package/build/src/akuma/providers/acp/events.js +73 -0
  52. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  53. package/build/src/akuma/providers/acp/index.js +94 -0
  54. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  55. package/build/src/akuma/providers/claude/events.js +238 -0
  56. package/build/src/akuma/providers/claude/index.d.ts +47 -0
  57. package/build/src/akuma/providers/claude/index.js +279 -0
  58. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  59. package/build/src/akuma/providers/claude/input.js +109 -0
  60. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  61. package/build/src/akuma/providers/codex-app-server/events.js +15 -12
  62. package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
  63. package/build/src/akuma/providers/codex-app-server/index.js +77 -33
  64. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  65. package/build/src/akuma/providers/grok-build/index.js +70 -0
  66. package/build/src/akuma/providers/index.d.ts +5 -3
  67. package/build/src/akuma/providers/index.js +35 -56
  68. package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
  69. package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
  70. package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
  71. package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
  72. package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
  73. package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
  74. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  75. package/build/src/akuma/providers/pi/events.js +166 -0
  76. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  77. package/build/src/akuma/providers/pi/index.js +191 -0
  78. package/build/src/akuma/publication.d.ts +3 -3
  79. package/build/src/akuma/publication.js +28 -40
  80. package/build/src/akuma/requests.d.ts +9 -6
  81. package/build/src/akuma/requests.js +95 -65
  82. package/build/src/alias/index.d.ts +4 -3
  83. package/build/src/alias/index.js +12 -13
  84. package/build/src/body/decode.js +7 -1
  85. package/build/src/body/region.d.ts +2 -0
  86. package/build/src/body/region.js +77 -7
  87. package/build/src/body/render.js +2 -1
  88. package/build/src/body/shape.js +7 -1
  89. package/build/src/body/verification.js +19 -4
  90. package/build/src/cli/accepted.d.ts +18 -12
  91. package/build/src/cli/accepted.js +73 -34
  92. package/build/src/cli/actor.js +3 -3
  93. package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
  94. package/build/src/cli/commands/akuma-invoke.js +101 -52
  95. package/build/src/cli/commands/akuma.d.ts +27 -9
  96. package/build/src/cli/commands/akuma.js +151 -80
  97. package/build/src/cli/commands/contract.d.ts +155 -0
  98. package/build/src/cli/commands/contract.js +20 -0
  99. package/build/src/cli/commands/install.js +2 -0
  100. package/build/src/cli/commands/task-invoke.d.ts +19 -4
  101. package/build/src/cli/commands/task-invoke.js +65 -17
  102. package/build/src/cli/commands/task-query.d.ts +5 -0
  103. package/build/src/cli/commands/task-query.js +247 -0
  104. package/build/src/cli/commands/task.d.ts +3 -1
  105. package/build/src/cli/commands/task.js +52 -14
  106. package/build/src/cli/coordinates.d.ts +18 -0
  107. package/build/src/cli/coordinates.js +120 -0
  108. package/build/src/cli/draft.d.ts +8 -0
  109. package/build/src/cli/draft.js +96 -0
  110. package/build/src/cli/invoke.d.ts +9 -3
  111. package/build/src/cli/invoke.js +287 -108
  112. package/build/src/cli/main.js +54 -7
  113. package/build/src/cli/parse.d.ts +21 -117
  114. package/build/src/cli/parse.js +79 -84
  115. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  116. package/build/src/cli/render/akuma-tool-command.js +144 -0
  117. package/build/src/cli/render/akuma-tool.d.ts +4 -2
  118. package/build/src/cli/render/akuma-tool.js +35 -5
  119. package/build/src/cli/render/akuma.d.ts +3 -1
  120. package/build/src/cli/render/akuma.js +247 -212
  121. package/build/src/cli/render/audit.d.ts +3 -0
  122. package/build/src/cli/render/audit.js +104 -0
  123. package/build/src/cli/render/catalog.d.ts +2 -0
  124. package/build/src/cli/render/catalog.js +16 -0
  125. package/build/src/cli/render/contract.d.ts +3 -3
  126. package/build/src/cli/render/contract.js +200 -54
  127. package/build/src/cli/render/kanshi.js +272 -129
  128. package/build/src/cli/render/receipt.d.ts +19 -0
  129. package/build/src/cli/render/receipt.js +106 -0
  130. package/build/src/cli/render/refusal.d.ts +16 -2
  131. package/build/src/cli/render/refusal.js +88 -3
  132. package/build/src/cli/render/region.d.ts +2 -0
  133. package/build/src/cli/render/region.js +17 -0
  134. package/build/src/cli/render/task.d.ts +2 -1
  135. package/build/src/cli/render/task.js +223 -60
  136. package/build/src/cli/render/terminal.d.ts +20 -0
  137. package/build/src/cli/render/terminal.js +121 -0
  138. package/build/src/cli/render/text.js +11 -3
  139. package/build/src/cli/result.d.ts +126 -19
  140. package/build/src/cli/usage.d.ts +1 -0
  141. package/build/src/cli/usage.js +3 -0
  142. package/build/src/contract-worktree.d.ts +44 -0
  143. package/build/src/contract-worktree.js +251 -0
  144. package/build/src/coordination/durable-file.d.ts +5 -1
  145. package/build/src/coordination/durable-file.js +57 -10
  146. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  147. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  148. package/build/src/core/facts/codec.js +23 -4
  149. package/build/src/core/facts/eligibility.d.ts +1 -10
  150. package/build/src/core/facts/eligibility.js +0 -49
  151. package/build/src/core/facts/fold.js +1 -6
  152. package/build/src/core/facts/gate.d.ts +1 -0
  153. package/build/src/core/facts/gate.js +1 -0
  154. package/build/src/core/facts/observation.d.ts +3 -0
  155. package/build/src/core/facts/observation.js +12 -0
  156. package/build/src/core/facts/types.d.ts +10 -3
  157. package/build/src/core/subject.js +2 -2
  158. package/build/src/core/verbs/amend.d.ts +1 -1
  159. package/build/src/core/verbs/amend.js +3 -18
  160. package/build/src/core/verbs/bind.js +2 -16
  161. package/build/src/core/verbs/deliver.d.ts +1 -1
  162. package/build/src/core/verbs/deliver.js +14 -4
  163. package/build/src/core/verbs/placement.d.ts +1 -1
  164. package/build/src/core/verbs/placement.js +7 -9
  165. package/build/src/dispatch/index.d.ts +5 -3
  166. package/build/src/dispatch/index.js +30 -21
  167. package/build/src/duration.d.ts +10 -0
  168. package/build/src/duration.js +22 -0
  169. package/build/src/git/admission.d.ts +2 -2
  170. package/build/src/git/admission.js +33 -23
  171. package/build/src/git/hooks.d.ts +15 -0
  172. package/build/src/git/hooks.js +70 -18
  173. package/build/src/git/identity.d.ts +5 -1
  174. package/build/src/git/identity.js +23 -4
  175. package/build/src/git/integration.d.ts +48 -0
  176. package/build/src/git/integration.js +250 -0
  177. package/build/src/git/observe.d.ts +29 -15
  178. package/build/src/git/observe.js +185 -76
  179. package/build/src/git/read-observation.d.ts +41 -0
  180. package/build/src/git/read-observation.js +361 -0
  181. package/build/src/git/reconcile.d.ts +17 -5
  182. package/build/src/git/reconcile.js +305 -109
  183. package/build/src/git/repository.d.ts +41 -18
  184. package/build/src/git/repository.js +220 -114
  185. package/build/src/git/scratch.d.ts +17 -0
  186. package/build/src/git/scratch.js +73 -0
  187. package/build/src/git/target-placement.d.ts +92 -0
  188. package/build/src/git/target-placement.js +375 -0
  189. package/build/src/git/tender.d.ts +49 -0
  190. package/build/src/git/tender.js +63 -0
  191. package/build/src/git/terminal-seal.d.ts +24 -0
  192. package/build/src/git/terminal-seal.js +89 -0
  193. package/build/src/git/tree.d.ts +2 -2
  194. package/build/src/git/tree.js +4 -0
  195. package/build/src/git/workspace.d.ts +53 -0
  196. package/build/src/git/workspace.js +139 -0
  197. package/build/src/identity/selector.d.ts +6 -0
  198. package/build/src/identity/selector.js +19 -0
  199. package/build/src/index.d.ts +4 -2
  200. package/build/src/index.js +2 -1
  201. package/build/src/kanshi/index.d.ts +2 -2
  202. package/build/src/kanshi/index.js +1 -1
  203. package/build/src/kanshi/read.d.ts +7 -3
  204. package/build/src/kanshi/read.js +245 -45
  205. package/build/src/kanshi/report.d.ts +81 -6
  206. package/build/src/kanshi/select.d.ts +5 -0
  207. package/build/src/kanshi/select.js +33 -1
  208. package/build/src/library/address.d.ts +57 -0
  209. package/build/src/library/address.js +165 -0
  210. package/build/src/library/akuma-creation.d.ts +16 -7
  211. package/build/src/library/akuma-creation.js +96 -22
  212. package/build/src/library/audit.d.ts +18 -0
  213. package/build/src/library/audit.js +44 -0
  214. package/build/src/library/bind.d.ts +21 -0
  215. package/build/src/library/bind.js +56 -0
  216. package/build/src/library/catalog.d.ts +58 -0
  217. package/build/src/library/catalog.js +69 -0
  218. package/build/src/library/configuration.d.ts +6 -15
  219. package/build/src/library/configuration.js +14 -17
  220. package/build/src/library/contract.d.ts +23 -49
  221. package/build/src/library/contract.js +171 -185
  222. package/build/src/library/delivery.d.ts +24 -0
  223. package/build/src/library/delivery.js +21 -0
  224. package/build/src/library/fleet.d.ts +65 -0
  225. package/build/src/library/fleet.js +153 -0
  226. package/build/src/library/input.d.ts +3 -0
  227. package/build/src/library/input.js +19 -0
  228. package/build/src/library/keiyaku.d.ts +18 -3
  229. package/build/src/library/keiyaku.js +12 -1
  230. package/build/src/library/mutation.d.ts +39 -0
  231. package/build/src/library/mutation.js +37 -0
  232. package/build/src/library/reconcile.d.ts +29 -0
  233. package/build/src/library/reconcile.js +179 -0
  234. package/build/src/library/refusal.d.ts +21 -0
  235. package/build/src/library/refusal.js +29 -0
  236. package/build/src/library/region.d.ts +14 -1
  237. package/build/src/library/region.js +18 -4
  238. package/build/src/library/repo.d.ts +4 -13
  239. package/build/src/library/repo.js +14 -24
  240. package/build/src/protocol/attempt.d.ts +16 -3
  241. package/build/src/protocol/attempt.js +41 -10
  242. package/build/src/protocol/bind.d.ts +9 -2
  243. package/build/src/protocol/bind.js +48 -40
  244. package/build/src/protocol/intent.d.ts +57 -20
  245. package/build/src/protocol/intent.js +55 -79
  246. package/build/src/protocol/operations.d.ts +152 -43
  247. package/build/src/protocol/operations.js +482 -166
  248. package/build/src/protocol/outcome.d.ts +14 -3
  249. package/build/src/protocol/outcome.js +11 -3
  250. package/build/src/protocol/placement.d.ts +41 -0
  251. package/build/src/protocol/placement.js +147 -0
  252. package/build/src/protocol/read/documents.d.ts +2 -2
  253. package/build/src/protocol/read/documents.js +4 -4
  254. package/build/src/protocol/read/status.d.ts +18 -5
  255. package/build/src/protocol/read/status.js +73 -13
  256. package/build/src/protocol/run.d.ts +49 -7
  257. package/build/src/protocol/run.js +53 -25
  258. package/build/src/runtime/proc/line-rpc.d.ts +4 -8
  259. package/build/src/runtime/proc/line-rpc.js +22 -28
  260. package/build/src/runtime/proc/run.d.ts +23 -21
  261. package/build/src/runtime/proc/run.js +123 -89
  262. package/build/src/runtime/proc/stdio.d.ts +18 -0
  263. package/build/src/runtime/proc/stdio.js +58 -0
  264. package/build/src/settings.d.ts +7 -1
  265. package/build/src/settings.js +32 -17
  266. package/build/src/settlement/fence.d.ts +5 -0
  267. package/build/src/settlement/fence.js +14 -0
  268. package/build/src/settlement/holder.d.ts +35 -4
  269. package/build/src/settlement/holder.js +127 -28
  270. package/build/src/settlement/settle.d.ts +9 -1
  271. package/build/src/settlement/settle.js +74 -37
  272. package/build/src/task/board.d.ts +23 -5
  273. package/build/src/task/board.js +17 -10
  274. package/build/src/task/compose.d.ts +2 -2
  275. package/build/src/task/compose.js +18 -10
  276. package/build/src/task/context.d.ts +3 -3
  277. package/build/src/task/context.js +15 -20
  278. package/build/src/task/document.d.ts +2 -1
  279. package/build/src/task/document.js +15 -5
  280. package/build/src/task/index.d.ts +27 -15
  281. package/build/src/task/index.js +57 -35
  282. package/build/src/task/operations.d.ts +38 -13
  283. package/build/src/task/operations.js +80 -46
  284. package/build/src/task/query.d.ts +67 -0
  285. package/build/src/task/query.js +279 -0
  286. package/build/src/task/store.d.ts +5 -7
  287. package/build/src/task/store.js +43 -28
  288. package/build/src/verification/declaration.d.ts +1 -0
  289. package/build/src/verification/execution.d.ts +51 -0
  290. package/build/src/verification/execution.js +108 -0
  291. package/build/src/workspace-place.d.ts +41 -0
  292. package/build/src/workspace-place.js +386 -0
  293. package/build/src/world.d.ts +24 -0
  294. package/build/src/world.js +136 -0
  295. package/package.json +8 -4
  296. package/build/src/akuma/activity.d.ts +0 -66
  297. package/build/src/akuma/activity.js +0 -139
  298. package/build/src/akuma/providers/claude.js +0 -305
  299. package/build/src/context-root.d.ts +0 -4
  300. package/build/src/context-root.js +0 -15
  301. package/build/src/git/delivery.d.ts +0 -20
  302. package/build/src/git/delivery.js +0 -109
  303. package/build/src/git/verification.d.ts +0 -14
  304. package/build/src/git/verification.js +0 -56
  305. package/build/src/protocol/read/audit.d.ts +0 -25
  306. package/build/src/protocol/read/audit.js +0 -39
  307. package/build/src/verification/producer.d.ts +0 -23
  308. package/build/src/verification/producer.js +0 -54
@@ -1,11 +1,22 @@
1
1
  import { displayColumns, renderTextBlock, safeText, tone } from "./terminal.js";
2
- function contractMark(phase) {
3
- if (phase === "claimed")
2
+ const RULE = "─";
3
+ const HORIZON = "───";
4
+ const PLUMB = " │ ";
5
+ const PATH_PREFIX = `${PLUMB}↳ `;
6
+ function contractMark(row) {
7
+ if (row.phase === "claimed")
4
8
  return "✓";
5
- if (phase === "abandoned")
9
+ if (row.phase === "abandoned")
6
10
  return "×";
7
- if (phase === "waiting" || phase === "pending-delivery")
8
- return "⧗";
11
+ if (row.title === null)
12
+ return "?";
13
+ if (row.gates.reports.some((report) => report.current.kind === "attested" && report.current.verdict === "unsatisfied")) {
14
+ return "!";
15
+ }
16
+ if (row.targetLag.kind === "unknown")
17
+ return "?";
18
+ if (row.phase === "waiting")
19
+ return "○";
9
20
  return "●";
10
21
  }
11
22
  function taskMark(row) {
@@ -14,18 +25,20 @@ function taskMark(row) {
14
25
  if (row.disposition === "drop")
15
26
  return "×";
16
27
  if (row.disposition === "on_hold")
17
- return "‖";
28
+ return "⧗";
18
29
  if (row.disposition === "in_progress")
19
30
  return "●";
20
- return row.disposition === "blocked" ? "⧗" : "○";
31
+ return row.disposition === "blocked" ? "‖" : "○";
21
32
  }
22
33
  function akumaMark(life) {
23
34
  if (life === "running")
24
35
  return "●";
25
36
  if (life === "stillborn")
26
37
  return "!";
27
- if (life === "asleep" || life === "dead")
38
+ if (life === "asleep")
28
39
  return "○";
40
+ if (life === "killed")
41
+ return "×";
29
42
  return "?";
30
43
  }
31
44
  function gateMark(report) {
@@ -33,35 +46,111 @@ function gateMark(report) {
33
46
  return "?";
34
47
  return report.current.verdict === "satisfied" ? "✓" : "!";
35
48
  }
36
- function hotFirst(rows, hot) {
37
- return [...rows.filter(hot), ...rows.filter((row) => !hot(row))];
49
+ function formatAge(source, observedAt) {
50
+ if (source === null || source === undefined)
51
+ return "—";
52
+ const sourceMs = Date.parse(source);
53
+ const observedMs = Date.parse(observedAt);
54
+ if (sourceMs > observedMs)
55
+ return "future";
56
+ const seconds = Math.floor((observedMs - sourceMs) / 1_000);
57
+ if (seconds < 60)
58
+ return `${seconds}s`;
59
+ const minutes = Math.floor(seconds / 60);
60
+ if (minutes < 60)
61
+ return `${minutes}m`;
62
+ const hours = Math.floor(minutes / 60);
63
+ return hours < 24 ? `${hours}h` : `${Math.floor(hours / 24)}d`;
38
64
  }
39
- function wrapChain(parts, columns) {
40
- if (parts.length === 0)
41
- return [];
42
- const lines = [];
43
- let current = " ";
44
- for (const part of parts.map(safeText)) {
45
- const candidate = current === " " ? `${current}${part}` : `${current} · ${part}`;
46
- if (current !== " " && displayColumns(candidate) > columns) {
47
- lines.push(current);
48
- current = ` ${part}`;
49
- }
50
- else
51
- current = candidate;
65
+ function isLost(life) {
66
+ return life === "stranded" || life === "hung" || life === "untidy";
67
+ }
68
+ function branchName(target) {
69
+ if (target === null)
70
+ return null;
71
+ return target.startsWith("refs/heads/") ? target.slice("refs/heads/".length) : target;
72
+ }
73
+ function targetFacts(row) {
74
+ const name = branchName(row.target);
75
+ if (name === null)
76
+ return ["no target"];
77
+ const facts = [`target ${name}`];
78
+ if (row.targetLag.kind === "unknown")
79
+ facts.push("behind unknown");
80
+ if (row.targetLag.kind === "counted")
81
+ facts.push(`behind ${row.targetLag.behind}`);
82
+ if (row.targetObservation?.drift === true)
83
+ facts.push("drift");
84
+ return facts;
85
+ }
86
+ function workspaceState(row) {
87
+ const observation = row.workspaceObservation;
88
+ if (row.workspace === "here")
89
+ return `workspace here · ${observation.kind}`;
90
+ if (observation.kind === "unappointed")
91
+ return "worktree unappointed";
92
+ if (observation.kind === "unavailable")
93
+ return "worktree unavailable";
94
+ if (observation.kind === "clean")
95
+ return "worktree clean";
96
+ const counts = [
97
+ ...(observation.counts.staged > 0 ? [`staged ${observation.counts.staged}`] : []),
98
+ ...(observation.counts.unstaged > 0 ? [`unstaged ${observation.counts.unstaged}`] : []),
99
+ ...(observation.counts.untracked > 0 ? [`untracked ${observation.counts.untracked}`] : []),
100
+ ...(observation.counts.submodules > 0 ? [`submodules ${observation.counts.submodules}`] : []),
101
+ ];
102
+ return counts.length === 0 ? "worktree dirty" : `worktree dirty · ${counts.join(" · ")}`;
103
+ }
104
+ function showWorkspacePath(row, hot) {
105
+ if (row.workspace !== "worktree")
106
+ return false;
107
+ if (row.workspaceObservation.kind === "unappointed")
108
+ return false;
109
+ if (row.workspaceObservation.kind !== "clean")
110
+ return true;
111
+ return hot;
112
+ }
113
+ function contractHot(row) {
114
+ if (row.title === null)
115
+ return true;
116
+ if (row.phase === "pending-delivery")
117
+ return true;
118
+ if (row.holder.kind === "unavailable" || row.holder.kind === "held")
119
+ return true;
120
+ if (row.fleet.length > 0)
121
+ return true;
122
+ if (row.targetLag.kind === "counted" && row.targetLag.behind > 0)
123
+ return true;
124
+ if (row.workspaceObservation.kind !== "clean" && row.workspaceObservation.kind !== "unappointed") {
125
+ return true;
52
126
  }
53
- lines.push(current);
54
- return lines;
127
+ return row.gates.reports.some((report) => report.current.kind === "stale"
128
+ || (report.current.kind === "attested" && report.current.verdict === "unsatisfied"));
129
+ }
130
+ function taskHot(row) {
131
+ return row.disposition === "blocked" || row.disposition === "in_progress";
132
+ }
133
+ function akumaHot(row) {
134
+ if (row.life === "running" || row.life === "stillborn" || isLost(row.life))
135
+ return true;
136
+ return row.contract?.observed === "missing" || row.contract?.observed === "unavailable";
55
137
  }
56
- function wrapHeading(primary, facts, columns) {
57
- const head = safeText(primary);
138
+ function aperture(label, columns) {
139
+ const prefix = `──[ ${label} ]`;
140
+ const fill = Math.max(1, columns - displayColumns(prefix));
141
+ return `${prefix}${RULE.repeat(fill)}`;
142
+ }
143
+ function plumbFacts(facts, columns) {
144
+ const clean = facts.map(safeText).filter((fact) => fact.length > 0);
145
+ if (clean.length === 0)
146
+ return [];
58
147
  const lines = [];
59
- let current = head;
60
- for (const fact of facts.map(safeText)) {
61
- const candidate = `${current} · ${fact}`;
62
- if (displayColumns(candidate) > columns) {
148
+ let current = PLUMB;
149
+ for (const fact of clean) {
150
+ const candidate = current === PLUMB ? `${PLUMB}${fact}` : `${current} · ${fact}`;
151
+ if (current !== PLUMB && displayColumns(candidate) > columns) {
63
152
  lines.push(current);
64
- current = ` ${fact}`;
153
+ current = `${PLUMB}${fact}`;
65
154
  }
66
155
  else
67
156
  current = candidate;
@@ -69,136 +158,190 @@ function wrapHeading(primary, facts, columns) {
69
158
  lines.push(current);
70
159
  return lines;
71
160
  }
72
- function renderGates(reports, context, includeSummaries) {
73
- const lines = [...wrapChain(reports.map((report) => `${gateMark(report)} ${safeText(report.gate)}`), context.columns)];
161
+ function plumbBlock(value, columns) {
162
+ return renderTextBlock(value, PLUMB, columns);
163
+ }
164
+ function plumbPath(path) {
165
+ return `${PATH_PREFIX}${safeText(path)}`;
166
+ }
167
+ function identityLine(mark, identity, extra = "") {
168
+ return extra.length === 0 ? `${mark} ${safeText(identity)}` : `${mark} ${safeText(identity)} ${safeText(extra)}`;
169
+ }
170
+ function renderGates(reports, observedAt, columns, includeSummaries) {
171
+ if (reports.length === 0)
172
+ return [];
173
+ const lines = [...plumbFacts([`gates: ${reports.map((report) => `${gateMark(report)} ${report.gate} ${formatAge(report.current.kind === "attested" ? report.current.at : null, observedAt)}`).join(" · ")}`], columns)];
74
174
  if (!includeSummaries)
75
175
  return lines;
76
176
  for (const report of reports) {
77
177
  if (report.current.kind !== "attested" || report.current.summary === undefined)
78
178
  continue;
79
- lines.push(...renderTextBlock(`${safeText(report.gate)}: ${report.current.summary}`, " ", context.columns)
80
- .map((line) => tone(line, "dim", context.color)));
179
+ lines.push(...plumbBlock(`${report.gate}: ${report.current.summary}`, columns));
81
180
  }
82
181
  return lines;
83
182
  }
84
183
  function failure(name, section, context) {
85
- return `${name} failed\n${tone(`! ${safeText(section.failure.message)}`, "alert", context.color)}`;
184
+ return [aperture(name, context.columns), `${tone(`! ${safeText(section.failure.message)}`, "alert", context.color)}`];
185
+ }
186
+ function sectionAbsent(name, columns) {
187
+ return [aperture(name, columns), `${PLUMB}${name.toLowerCase()} absent`];
188
+ }
189
+ function endpointFact(id, observed) {
190
+ if (observed === "missing")
191
+ return `-> ${id} (missing)`;
192
+ if (observed === "unavailable")
193
+ return `-> ${id} (unavailable)`;
194
+ return `-> ${id}`;
86
195
  }
87
196
  function renderContracts(report, context, selection) {
88
197
  const section = report.contracts;
89
198
  if (section.kind === "absent")
90
- return "keiyaku absent";
199
+ return sectionAbsent("KEIYAKU", context.columns);
91
200
  if (section.kind === "failed")
92
- return failure("keiyaku", section, context);
93
- const rows = hotFirst(section.value.rows, (row) => row.phase === "waiting" || row.phase === "pending-delivery");
94
- const lines = [`keiyaku ${rows.length}`];
201
+ return failure("KEIYAKU", section, context);
202
+ const rows = [
203
+ ...section.value.rows.filter(contractHot),
204
+ ...section.value.rows.filter((row) => !contractHot(row)),
205
+ ];
206
+ const lines = [aperture("KEIYAKU", context.columns)];
95
207
  for (const row of rows) {
96
- const facts = [
97
- row.workspace,
98
- ...(row.candidate === null ? [] : [`candidate ${safeText(row.candidate).slice(0, 8)}`]),
99
- ...(row.target === null ? [] : [`-> ${row.target}`]),
100
- ];
101
- lines.push(`${contractMark(row.phase)} ${safeText(row.id)} ${row.phase}`);
102
- lines.push(...wrapChain(facts, context.columns));
103
- lines.push(...renderGates(row.gates.reports, context, selection === "contract"));
208
+ const hot = contractHot(row);
209
+ const compact = !hot && selection !== "contract";
210
+ const title = row.title ?? "title unavailable";
211
+ const phase = `${row.phase} · ${formatAge(row.phaseAt, report.observedAt)}`;
212
+ lines.push(identityLine(contractMark(row), row.id));
213
+ lines.push(...plumbFacts(compact
214
+ ? [title, phase, ...targetFacts(row)]
215
+ : [title], context.columns));
216
+ if (!compact)
217
+ lines.push(...plumbFacts([phase, ...targetFacts(row)], context.columns));
218
+ lines.push(...plumbFacts([workspaceState(row)], context.columns));
219
+ if (showWorkspacePath(row, hot || selection === "contract")
220
+ && row.workspaceObservation.kind !== "unappointed"
221
+ && row.workspaceObservation.location.kind === "worktree") {
222
+ lines.push(plumbPath(row.workspaceObservation.location.path));
223
+ }
224
+ lines.push(...renderGates(row.gates.reports, report.observedAt, context.columns, selection === "contract"));
225
+ if (row.holder.kind === "held")
226
+ lines.push(...plumbFacts([`task ${row.holder.taskId}`], context.columns));
227
+ if (row.holder.kind === "unavailable")
228
+ lines.push(...plumbFacts(["holder unavailable"], context.columns));
229
+ for (const attached of row.fleet) {
230
+ const aliases = attached.aliases.length === 0 ? "" : ` (${attached.aliases.join(" ")})`;
231
+ lines.push(...plumbFacts([`akuma ${attached.id}${aliases}`], context.columns));
232
+ }
104
233
  }
105
- return lines.join("\n");
234
+ const attention = rows.filter(contractHot).length;
235
+ lines.push(aperture(`${rows.length} keiyaku · ${attention} attention`, context.columns));
236
+ return lines;
106
237
  }
107
238
  function renderTasks(report, context) {
108
239
  const section = report.tasks;
109
240
  if (section.kind === "absent")
110
- return "task absent";
241
+ return sectionAbsent("TASK", context.columns);
111
242
  if (section.kind === "failed")
112
- return failure("task", section, context);
113
- const active = section.value.rows.filter((row) => row.disposition !== "done" && row.disposition !== "drop");
114
- const ready = active.filter((row) => row.disposition === "ready").length;
115
- const onHold = active.filter((row) => row.disposition === "on_hold").length;
116
- const visible = hotFirst(active.filter((row) => row.disposition === "in_progress" || row.disposition === "blocked"), (row) => row.disposition === "blocked");
117
- const lines = [...wrapHeading(`task ${active.length}`, [`${ready} ready`, `${onHold} held`], context.columns)];
118
- for (const row of visible) {
119
- const contract = row.contract === undefined ? [] : [`keiyaku ${row.contract.id} (${row.contract.observed})`];
120
- lines.push(`${taskMark(row)} ${safeText(row.id)} ${row.disposition}`);
121
- lines.push(...renderTextBlock(row.title, " ", context.columns));
122
- lines.push(...wrapChain([`P${row.priority}`, ...contract], context.columns));
123
- }
124
- return lines.join("\n");
125
- }
126
- function isLost(life) {
127
- return life === "stranded" || life === "headless";
128
- }
129
- function akumaPriority(row) {
130
- if (isLost(row.life) || row.life === "stillborn")
131
- return 0;
132
- return row.life === "running" ? 1 : 2;
133
- }
134
- function firstLine(value) {
135
- return value.split(/[\r\n\u2028\u2029]/u, 1)[0] ?? "";
136
- }
137
- function truncateDisplay(value, columns) {
138
- if (displayColumns(value) <= columns)
139
- return value;
140
- if (columns <= 0)
141
- return "";
142
- const ellipsis = "…";
143
- if (columns <= displayColumns(ellipsis))
144
- return ellipsis;
145
- let result = "";
146
- let used = 0;
147
- for (const character of value) {
148
- const width = displayColumns(character);
149
- if (used + width + displayColumns(ellipsis) > columns)
150
- break;
151
- result += character;
152
- used += width;
153
- }
154
- return `${result}${ellipsis}`;
155
- }
156
- function bornFacts(row) {
157
- if (!("pending" in row) || (row.life !== "running" && !isLost(row.life)))
158
- return [];
159
- const confinement = row.confinement.kind === "unconfined"
160
- ? ["unconfined"]
161
- : row.confinement.writableRoots.map((root, index) => index === 0 ? `writes ${root}` : root);
162
- return [
163
- ...(row.pending.length === 0 ? [] : [`pending ${row.pending.length}`]),
164
- ...confinement,
243
+ return failure("TASK", section, context);
244
+ const rows = [
245
+ ...section.value.rows.filter(taskHot),
246
+ ...section.value.rows.filter((row) => !taskHot(row)),
165
247
  ];
248
+ const lines = [aperture("TASK", context.columns)];
249
+ for (const row of rows) {
250
+ const compact = !taskHot(row);
251
+ lines.push(identityLine(taskMark(row), row.id));
252
+ const relation = row.contract === undefined
253
+ ? ["unbound"]
254
+ : [endpointFact(row.contract.id, row.contract.observed)];
255
+ const facts = [row.title, row.disposition, `P${row.priority}`, ...relation];
256
+ if (compact)
257
+ lines.push(...plumbFacts(facts, context.columns));
258
+ else {
259
+ lines.push(...plumbBlock(row.title, context.columns));
260
+ lines.push(...plumbFacts([row.disposition, `P${row.priority}`], context.columns));
261
+ for (const blocker of row.blockers ?? []) {
262
+ lines.push(...plumbFacts([`blocked by ${blocker.id}`], context.columns));
263
+ }
264
+ lines.push(...plumbFacts(relation, context.columns));
265
+ }
266
+ }
267
+ const attention = rows.filter(taskHot).length;
268
+ lines.push(aperture(`${rows.length} task · ${attention} attention`, context.columns));
269
+ return lines;
166
270
  }
167
- function stillbornEvidence(row, columns) {
168
- if (row.life !== "stillborn" || !("seal" in row) || row.seal === undefined)
169
- return null;
170
- const prefix = " seal ";
171
- const evidence = safeText(firstLine(row.seal.evidence)).trim();
172
- if (evidence.length === 0)
173
- return null;
174
- return `${prefix}${truncateDisplay(evidence, Math.max(0, columns - displayColumns(prefix)))}`;
271
+ function akumaLabel(row) {
272
+ const aliases = row.aliases ?? [];
273
+ return aliases.length === 0 ? "" : `(${aliases.join(" ")})`;
175
274
  }
176
275
  function renderAkuma(report, context) {
177
276
  const section = report.akuma;
178
277
  if (section.kind === "absent")
179
- return "akuma absent";
278
+ return sectionAbsent("FLEET", context.columns);
180
279
  if (section.kind === "failed")
181
- return failure("akuma", section, context);
280
+ return failure("FLEET", section, context);
182
281
  const rows = [
183
- ...section.value.rows.filter((row) => akumaPriority(row) === 0),
184
- ...section.value.rows.filter((row) => akumaPriority(row) === 1),
185
- ...section.value.rows.filter((row) => akumaPriority(row) === 2),
282
+ ...section.value.rows.filter(akumaHot),
283
+ ...section.value.rows.filter((row) => !akumaHot(row)),
186
284
  ];
187
- const lines = [`akuma ${rows.length}`];
285
+ const lines = [aperture("FLEET", context.columns)];
188
286
  for (const row of rows) {
189
- lines.push(`${akumaMark(row.life)} ${safeText(row.id)} ${row.life}`);
190
- lines.push(...wrapChain(bornFacts(row), context.columns));
191
- const evidence = stillbornEvidence(row, context.columns);
192
- if (evidence !== null)
193
- lines.push(evidence);
287
+ lines.push(identityLine(akumaMark(row.life), row.id, akumaLabel(row)));
288
+ const lifeAt = "lifeAt" in row ? row.lifeAt : null;
289
+ const life = `${row.life} · ${formatAge(lifeAt, report.observedAt)}`;
290
+ const key = row.life === "stranded" && "strandedReason" in row && row.strandedReason === "resume-unsupported"
291
+ ? [life, "resume unsupported"]
292
+ : [life];
293
+ const relation = row.contract === undefined
294
+ ? ["unbound"]
295
+ : [endpointFact(row.contract.id, row.contract.observed)];
296
+ if (!akumaHot(row))
297
+ lines.push(...plumbFacts([...key, ...relation], context.columns));
298
+ else {
299
+ lines.push(...plumbFacts(key, context.columns));
300
+ lines.push(...plumbFacts(relation, context.columns));
301
+ }
194
302
  }
195
- return lines.join("\n");
303
+ const attention = rows.filter(akumaHot).length;
304
+ lines.push(aperture(`${rows.length} akuma · ${attention} attention`, context.columns));
305
+ return lines;
306
+ }
307
+ function sectionCount(name, section) {
308
+ if (section.kind === "present")
309
+ return `${section.value.rows.length} ${name}`;
310
+ return `${section.kind} ${name}`;
311
+ }
312
+ function worldCoordinate(report) {
313
+ const project = report.root;
314
+ const branch = report.branch?.startsWith("refs/heads/") === true
315
+ ? report.branch.slice("refs/heads/".length)
316
+ : report.branch;
317
+ const state = report.contracts.kind === "present" ? report.contracts.value.state ?? null : null;
318
+ return [project, branch, state].filter((part) => part !== null && part.length > 0).map(safeText).join(" ");
319
+ }
320
+ function splitHorizon(report, columns) {
321
+ if (report.root === null)
322
+ return ["✗ keiyaku world absent"];
323
+ const aggregate = [
324
+ sectionCount("keiyaku", report.contracts),
325
+ sectionCount("akuma", report.akuma),
326
+ sectionCount("task", report.tasks),
327
+ ].join(" · ");
328
+ const world = worldCoordinate(report);
329
+ const minimum = `kanshi ${HORIZON} ${aggregate} ${HORIZON} ${world}`;
330
+ if (displayColumns(minimum) > columns)
331
+ return [minimum];
332
+ const fill = columns - displayColumns(minimum);
333
+ const left = Math.floor(fill / 2);
334
+ const right = Math.ceil(fill / 2);
335
+ return [`kanshi ${HORIZON}${RULE.repeat(left)} ${aggregate} ${HORIZON}${RULE.repeat(right)} ${world}`];
196
336
  }
197
337
  export function renderKanshiText(report, context = { columns: 80, color: false }, selection = "world") {
198
338
  return [
199
- `kanshi ${safeText(report.root)}`,
200
- renderContracts(report, context, selection),
201
- renderTasks(report, context),
202
- renderAkuma(report, context),
203
- ].join("\n\n");
339
+ ...splitHorizon(report, context.columns),
340
+ "",
341
+ ...renderContracts(report, context, selection),
342
+ "",
343
+ ...renderTasks(report, context),
344
+ "",
345
+ ...renderAkuma(report, context),
346
+ ].join("\n");
204
347
  }
@@ -0,0 +1,19 @@
1
+ import type { PlacementStop, VerificationReuse, VerificationStop } from "../../index.js";
2
+ import type { AcceptedDeliverResult, Lag } from "../result.js";
3
+ export type ReceiptSegment = Readonly<{
4
+ text: string;
5
+ opaque?: boolean;
6
+ }>;
7
+ type HookFailure = Extract<Lag, {
8
+ kind: "worktree-hook-failed";
9
+ }>["failure"];
10
+ export declare function receiptRow(lines: string[], mark: string, label: string, segments: readonly ReceiptSegment[], columns: number): void;
11
+ export declare function receiptPayload(lines: string[], label: string, payload: string): void;
12
+ export declare function outcomeLines(mark: "✓" | "!" | "?", verb: string, word: "accepted" | "refused" | "retry", contract: string | undefined, columns?: number): string[];
13
+ export declare function hookFailureSummary(failure: HookFailure): string;
14
+ export declare function appendHookPayload(lines: string[], failure: HookFailure): void;
15
+ export declare function reuseLines(reuse: VerificationReuse | undefined, columns: number): readonly string[];
16
+ export declare function stopLines(label: "verification" | "claim", stop: VerificationStop | PlacementStop, columns: number, addressed: string): readonly string[];
17
+ export declare function cleanupLines(cleanup: NonNullable<AcceptedDeliverResult["cleanup"]>, columns: number): readonly string[];
18
+ export declare function leakLines(leak: NonNullable<AcceptedDeliverResult["leak"]>, columns: number): readonly string[];
19
+ export {};
@@ -0,0 +1,106 @@
1
+ import { displayColumns, renderOpaqueBlock, safeText } from "./terminal.js";
2
+ export function receiptRow(lines, mark, label, segments, columns) {
3
+ let current = `${mark} ${label}`;
4
+ for (const segment of segments) {
5
+ const text = segment.opaque === true ? safeText(segment.text) : segment.text;
6
+ const candidate = `${current} ${text}`;
7
+ if (displayColumns(candidate) <= columns) {
8
+ current = candidate;
9
+ continue;
10
+ }
11
+ if (current === `${mark} ${label}` && segment.opaque === true) {
12
+ lines.push(current);
13
+ current = ` ${text}`;
14
+ continue;
15
+ }
16
+ lines.push(current);
17
+ current = ` ${text}`;
18
+ }
19
+ lines.push(current);
20
+ }
21
+ export function receiptPayload(lines, label, payload) {
22
+ lines.push(label, "", payload, "");
23
+ }
24
+ export function outcomeLines(mark, verb, word, contract, columns = 80) {
25
+ const base = `${mark} ${verb} ${word}`;
26
+ if (contract === undefined)
27
+ return [base];
28
+ const inline = `${base} — ${contract}`;
29
+ if (displayColumns(inline) <= columns)
30
+ return [inline];
31
+ return [`${base} —`, ` ${safeText(contract)}`];
32
+ }
33
+ export function hookFailureSummary(failure) {
34
+ if (failure.kind === "timeout" || failure.kind === "unknown-exit")
35
+ return failure.kind;
36
+ if (failure.kind === "spawn-error")
37
+ return failure.kind;
38
+ return `exit=${failure.code} · truncated=${failure.truncated}`;
39
+ }
40
+ export function appendHookPayload(lines, failure) {
41
+ if (failure.kind === "spawn-error")
42
+ receiptPayload(lines, "diagnostic", failure.diagnostic);
43
+ if (!("stdout" in failure))
44
+ return;
45
+ if (failure.stdout.length > 0)
46
+ receiptPayload(lines, "stdout", failure.stdout);
47
+ if (failure.stderr.length > 0)
48
+ receiptPayload(lines, "stderr", failure.stderr);
49
+ }
50
+ export function reuseLines(reuse, columns) {
51
+ if (reuse === undefined)
52
+ return [];
53
+ return renderOpaqueBlock(`reuse verification ${reuse.entry} ${reuse.verdict}`, " ", columns);
54
+ }
55
+ export function stopLines(label, stop, columns, addressed) {
56
+ const detail = [];
57
+ if ("refusal" in stop && stop.refusal !== undefined) {
58
+ detail.push({ text: stop.refusal.kind });
59
+ if ("contractId" in stop.refusal && stop.refusal.contractId !== addressed) {
60
+ detail.push({ text: `contract=${stop.refusal.contractId}`, opaque: true });
61
+ }
62
+ }
63
+ else if ("retry" in stop && stop.retry !== undefined) {
64
+ detail.push({ text: stop.retry.kind });
65
+ }
66
+ else if ("failure" in stop) {
67
+ detail.push({ text: stop.failure });
68
+ if (stop.failure === "environment-failure" && "command" in stop) {
69
+ detail.push({ text: `command=${stop.command}` }, { text: hookFailureSummary(stop.detail), opaque: true });
70
+ }
71
+ else if (stop.failure === "target-moved") {
72
+ detail.push({ text: stop.target, opaque: true }, { text: `${stop.expected} -> ${stop.observed}`, opaque: true });
73
+ }
74
+ }
75
+ const lines = [];
76
+ receiptRow(lines, "!", label, detail, columns);
77
+ if ("failure" in stop && stop.failure === "environment-failure" && "command" in stop) {
78
+ appendHookPayload(lines, stop.detail);
79
+ }
80
+ if ("retry" in stop && stop.retry?.kind === "publication-failed") {
81
+ receiptPayload(lines, "diagnostic", stop.retry.diagnostic);
82
+ }
83
+ else if ("failure" in stop && "diagnostic" in stop) {
84
+ receiptPayload(lines, "diagnostic", stop.diagnostic);
85
+ }
86
+ return lines;
87
+ }
88
+ export function cleanupLines(cleanup, columns) {
89
+ const lines = [];
90
+ receiptRow(lines, "!", "cleanup", [
91
+ { text: cleanup.phase },
92
+ { text: `command=${cleanup.command}` },
93
+ { text: hookFailureSummary(cleanup.detail), opaque: true },
94
+ ], columns);
95
+ appendHookPayload(lines, cleanup.detail);
96
+ return lines;
97
+ }
98
+ export function leakLines(leak, columns) {
99
+ const lines = [];
100
+ receiptRow(lines, "!", "leak", [
101
+ { text: "worktree" },
102
+ { text: leak.path, opaque: true },
103
+ ], columns);
104
+ receiptPayload(lines, "diagnostic", leak.diagnostic);
105
+ return lines;
106
+ }
@@ -1,2 +1,16 @@
1
- import type { RefusedResult } from "../result.js";
2
- export declare function renderRefusal(result: RefusedResult): string;
1
+ import type { BindDraftReceipt, RefusedResult } from "../result.js";
2
+ import type { KeiyakuRefusal } from "../../index.js";
3
+ import { type TextRenderContext } from "./terminal.js";
4
+ type DirtyWithOption = Extract<KeiyakuRefusal, {
5
+ kind: "dirty-workspace";
6
+ }> & {
7
+ option?: Readonly<{
8
+ flag: string;
9
+ available: boolean;
10
+ }>;
11
+ };
12
+ export type RenderableRefusal = KeiyakuRefusal | DirtyWithOption;
13
+ export declare function renderRefusalFacts(refusal: RenderableRefusal, indent: string, columns: number, addressed?: string): readonly string[];
14
+ export declare function renderRefusal(result: RefusedResult, context?: TextRenderContext): string;
15
+ export declare function renderBindDraftReceipt(receipt: BindDraftReceipt): string;
16
+ export {};