@astrosheep/keiyaku 4.0.1 → 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 (245) hide show
  1. package/README.md +137 -5
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +98 -58
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +152 -53
  9. package/build/src/akuma/abort.d.ts +2 -0
  10. package/build/src/akuma/abort.js +50 -0
  11. package/build/src/akuma/akuma.d.ts +23 -14
  12. package/build/src/akuma/akuma.js +120 -104
  13. package/build/src/akuma/archetype.d.ts +7 -6
  14. package/build/src/akuma/archetype.js +68 -41
  15. package/build/src/akuma/body.d.ts +3 -5
  16. package/build/src/akuma/body.js +408 -213
  17. package/build/src/akuma/heart/facts.d.ts +25 -34
  18. package/build/src/akuma/heart/facts.js +18 -7
  19. package/build/src/akuma/heart/index.d.ts +39 -42
  20. package/build/src/akuma/heart/index.js +134 -93
  21. package/build/src/akuma/heart/rows.d.ts +9 -31
  22. package/build/src/akuma/heart/rows.js +26 -44
  23. package/build/src/akuma/heart/schema.d.ts +1 -1
  24. package/build/src/akuma/heart/schema.js +7 -15
  25. package/build/src/akuma/heart/soul.d.ts +8 -0
  26. package/build/src/akuma/heart/soul.js +136 -0
  27. package/build/src/akuma/heart/storage.d.ts +28 -14
  28. package/build/src/akuma/heart/storage.js +86 -26
  29. package/build/src/akuma/identity.d.ts +2 -2
  30. package/build/src/akuma/identity.js +9 -9
  31. package/build/src/akuma/index.d.ts +6 -1
  32. package/build/src/akuma/index.js +6 -0
  33. package/build/src/akuma/projection.d.ts +117 -38
  34. package/build/src/akuma/projection.js +208 -97
  35. package/build/src/akuma/provider-recipe.d.ts +24 -0
  36. package/build/src/akuma/provider-recipe.js +100 -0
  37. package/build/src/akuma/provider.d.ts +12 -4
  38. package/build/src/akuma/provider.js +117 -70
  39. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  40. package/build/src/akuma/providers/acp/core.js +159 -0
  41. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  42. package/build/src/akuma/providers/acp/events.js +73 -0
  43. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  44. package/build/src/akuma/providers/acp/index.js +94 -0
  45. package/build/src/akuma/providers/claude/events.js +66 -13
  46. package/build/src/akuma/providers/claude/index.d.ts +2 -1
  47. package/build/src/akuma/providers/claude/index.js +35 -21
  48. package/build/src/akuma/providers/codex-app-server/events.js +4 -1
  49. package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
  50. package/build/src/akuma/providers/codex-app-server/index.js +24 -31
  51. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  52. package/build/src/akuma/providers/grok-build/index.js +70 -0
  53. package/build/src/akuma/providers/index.d.ts +5 -3
  54. package/build/src/akuma/providers/index.js +24 -59
  55. package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
  56. package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
  57. package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
  58. package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
  59. package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
  60. package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
  61. package/build/src/akuma/providers/pi/events.js +49 -11
  62. package/build/src/akuma/providers/pi/index.d.ts +1 -1
  63. package/build/src/akuma/providers/pi/index.js +50 -21
  64. package/build/src/akuma/publication.d.ts +3 -3
  65. package/build/src/akuma/publication.js +28 -40
  66. package/build/src/akuma/requests.d.ts +9 -7
  67. package/build/src/akuma/requests.js +92 -65
  68. package/build/src/alias/index.d.ts +2 -2
  69. package/build/src/alias/index.js +10 -10
  70. package/build/src/body/decode.js +7 -1
  71. package/build/src/body/region.d.ts +2 -0
  72. package/build/src/body/region.js +77 -7
  73. package/build/src/cli/accepted.d.ts +17 -13
  74. package/build/src/cli/accepted.js +72 -34
  75. package/build/src/cli/actor.js +3 -3
  76. package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
  77. package/build/src/cli/commands/akuma-invoke.js +14 -9
  78. package/build/src/cli/commands/akuma.d.ts +13 -3
  79. package/build/src/cli/commands/akuma.js +67 -34
  80. package/build/src/cli/commands/contract.d.ts +18 -5
  81. package/build/src/cli/commands/contract.js +9 -3
  82. package/build/src/cli/commands/task-invoke.d.ts +17 -4
  83. package/build/src/cli/commands/task-invoke.js +43 -20
  84. package/build/src/cli/commands/task-query.d.ts +5 -0
  85. package/build/src/cli/commands/task-query.js +247 -0
  86. package/build/src/cli/commands/task.d.ts +3 -1
  87. package/build/src/cli/commands/task.js +51 -13
  88. package/build/src/cli/coordinates.d.ts +18 -0
  89. package/build/src/cli/coordinates.js +120 -0
  90. package/build/src/cli/draft.d.ts +8 -0
  91. package/build/src/cli/draft.js +96 -0
  92. package/build/src/cli/invoke.d.ts +4 -3
  93. package/build/src/cli/invoke.js +160 -179
  94. package/build/src/cli/main.js +34 -6
  95. package/build/src/cli/parse.d.ts +9 -2
  96. package/build/src/cli/parse.js +29 -7
  97. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  98. package/build/src/cli/render/akuma-tool-command.js +144 -0
  99. package/build/src/cli/render/akuma-tool.d.ts +2 -2
  100. package/build/src/cli/render/akuma-tool.js +30 -4
  101. package/build/src/cli/render/akuma.d.ts +1 -0
  102. package/build/src/cli/render/akuma.js +211 -116
  103. package/build/src/cli/render/audit.d.ts +3 -0
  104. package/build/src/cli/render/audit.js +104 -0
  105. package/build/src/cli/render/contract.d.ts +3 -3
  106. package/build/src/cli/render/contract.js +192 -79
  107. package/build/src/cli/render/kanshi.js +263 -168
  108. package/build/src/cli/render/receipt.d.ts +19 -0
  109. package/build/src/cli/render/receipt.js +106 -0
  110. package/build/src/cli/render/refusal.d.ts +16 -2
  111. package/build/src/cli/render/refusal.js +88 -20
  112. package/build/src/cli/render/region.d.ts +2 -0
  113. package/build/src/cli/render/region.js +17 -0
  114. package/build/src/cli/render/task.d.ts +2 -1
  115. package/build/src/cli/render/task.js +223 -60
  116. package/build/src/cli/render/terminal.d.ts +7 -0
  117. package/build/src/cli/render/terminal.js +46 -0
  118. package/build/src/cli/render/text.js +6 -3
  119. package/build/src/cli/result.d.ts +115 -20
  120. package/build/src/cli/usage.d.ts +1 -0
  121. package/build/src/cli/usage.js +3 -0
  122. package/build/src/contract-worktree.d.ts +6 -5
  123. package/build/src/contract-worktree.js +95 -57
  124. package/build/src/coordination/durable-file.d.ts +4 -3
  125. package/build/src/coordination/durable-file.js +29 -25
  126. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  127. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  128. package/build/src/core/facts/fold.js +1 -6
  129. package/build/src/core/facts/gate.d.ts +1 -0
  130. package/build/src/core/facts/gate.js +1 -0
  131. package/build/src/core/verbs/amend.d.ts +1 -1
  132. package/build/src/core/verbs/amend.js +0 -11
  133. package/build/src/core/verbs/deliver.d.ts +1 -1
  134. package/build/src/core/verbs/deliver.js +1 -7
  135. package/build/src/core/verbs/placement.d.ts +1 -1
  136. package/build/src/core/verbs/placement.js +4 -1
  137. package/build/src/dispatch/index.d.ts +2 -2
  138. package/build/src/dispatch/index.js +17 -17
  139. package/build/src/git/admission.d.ts +1 -1
  140. package/build/src/git/admission.js +11 -11
  141. package/build/src/git/hooks.js +13 -13
  142. package/build/src/git/integration.d.ts +14 -15
  143. package/build/src/git/integration.js +80 -44
  144. package/build/src/git/observe.d.ts +5 -3
  145. package/build/src/git/observe.js +18 -12
  146. package/build/src/git/read-observation.js +9 -9
  147. package/build/src/git/reconcile.d.ts +2 -0
  148. package/build/src/git/reconcile.js +138 -95
  149. package/build/src/git/repository.d.ts +27 -19
  150. package/build/src/git/repository.js +155 -83
  151. package/build/src/git/scratch.d.ts +7 -3
  152. package/build/src/git/scratch.js +44 -37
  153. package/build/src/git/target-placement.d.ts +40 -4
  154. package/build/src/git/target-placement.js +221 -101
  155. package/build/src/git/tender.d.ts +6 -5
  156. package/build/src/git/tender.js +24 -43
  157. package/build/src/git/terminal-seal.d.ts +1 -1
  158. package/build/src/git/terminal-seal.js +7 -5
  159. package/build/src/git/workspace.d.ts +36 -4
  160. package/build/src/git/workspace.js +53 -18
  161. package/build/src/identity/selector.js +1 -1
  162. package/build/src/index.d.ts +3 -3
  163. package/build/src/index.js +1 -1
  164. package/build/src/kanshi/index.d.ts +2 -2
  165. package/build/src/kanshi/index.js +1 -1
  166. package/build/src/kanshi/read.d.ts +2 -1
  167. package/build/src/kanshi/read.js +118 -27
  168. package/build/src/kanshi/report.d.ts +50 -0
  169. package/build/src/kanshi/select.d.ts +5 -0
  170. package/build/src/kanshi/select.js +32 -0
  171. package/build/src/library/address.d.ts +12 -3
  172. package/build/src/library/address.js +77 -46
  173. package/build/src/library/akuma-creation.d.ts +6 -1
  174. package/build/src/library/akuma-creation.js +86 -13
  175. package/build/src/library/audit.d.ts +18 -0
  176. package/build/src/library/audit.js +44 -0
  177. package/build/src/library/bind.js +4 -4
  178. package/build/src/library/catalog.d.ts +1 -1
  179. package/build/src/library/catalog.js +5 -8
  180. package/build/src/library/contract.d.ts +5 -6
  181. package/build/src/library/contract.js +11 -35
  182. package/build/src/library/delivery.d.ts +2 -1
  183. package/build/src/library/fleet.d.ts +2 -2
  184. package/build/src/library/fleet.js +35 -71
  185. package/build/src/library/keiyaku.d.ts +3 -2
  186. package/build/src/library/keiyaku.js +1 -0
  187. package/build/src/library/mutation.d.ts +7 -6
  188. package/build/src/library/mutation.js +23 -25
  189. package/build/src/library/reconcile.d.ts +29 -0
  190. package/build/src/library/reconcile.js +179 -0
  191. package/build/src/library/region.d.ts +12 -0
  192. package/build/src/library/region.js +15 -1
  193. package/build/src/library/repo.d.ts +4 -16
  194. package/build/src/library/repo.js +13 -38
  195. package/build/src/protocol/attempt.js +6 -6
  196. package/build/src/protocol/bind.js +4 -4
  197. package/build/src/protocol/intent.d.ts +15 -2
  198. package/build/src/protocol/intent.js +21 -6
  199. package/build/src/protocol/operations.d.ts +73 -22
  200. package/build/src/protocol/operations.js +292 -88
  201. package/build/src/protocol/outcome.d.ts +9 -3
  202. package/build/src/protocol/outcome.js +3 -1
  203. package/build/src/protocol/placement.d.ts +15 -4
  204. package/build/src/protocol/placement.js +58 -25
  205. package/build/src/protocol/read/status.d.ts +8 -1
  206. package/build/src/protocol/read/status.js +61 -14
  207. package/build/src/runtime/proc/line-rpc.d.ts +3 -8
  208. package/build/src/runtime/proc/line-rpc.js +16 -43
  209. package/build/src/runtime/proc/run.d.ts +15 -29
  210. package/build/src/runtime/proc/run.js +113 -105
  211. package/build/src/runtime/proc/stdio.d.ts +18 -0
  212. package/build/src/runtime/proc/stdio.js +58 -0
  213. package/build/src/settings.d.ts +2 -2
  214. package/build/src/settings.js +8 -8
  215. package/build/src/settlement/fence.d.ts +0 -4
  216. package/build/src/settlement/fence.js +0 -9
  217. package/build/src/settlement/holder.d.ts +11 -1
  218. package/build/src/settlement/holder.js +25 -0
  219. package/build/src/settlement/settle.d.ts +1 -1
  220. package/build/src/settlement/settle.js +52 -89
  221. package/build/src/task/board.d.ts +3 -3
  222. package/build/src/task/board.js +6 -9
  223. package/build/src/task/compose.d.ts +1 -1
  224. package/build/src/task/compose.js +18 -10
  225. package/build/src/task/context.d.ts +3 -3
  226. package/build/src/task/context.js +14 -14
  227. package/build/src/task/document.d.ts +2 -1
  228. package/build/src/task/document.js +15 -5
  229. package/build/src/task/index.d.ts +19 -7
  230. package/build/src/task/index.js +42 -23
  231. package/build/src/task/operations.d.ts +15 -9
  232. package/build/src/task/operations.js +78 -48
  233. package/build/src/task/query.d.ts +67 -0
  234. package/build/src/task/query.js +279 -0
  235. package/build/src/task/store.d.ts +2 -2
  236. package/build/src/task/store.js +40 -25
  237. package/build/src/verification/execution.d.ts +4 -2
  238. package/build/src/verification/execution.js +9 -5
  239. package/build/src/workspace-place.d.ts +41 -0
  240. package/build/src/workspace-place.js +386 -0
  241. package/build/src/world.d.ts +13 -2
  242. package/build/src/world.js +81 -48
  243. package/package.json +3 -2
  244. package/build/src/protocol/read/audit.d.ts +0 -31
  245. package/build/src/protocol/read/audit.js +0 -55
@@ -0,0 +1,8 @@
1
+ import type { WorldRoot } from "../world.js";
2
+ import type { BindDraftReceipt } from "./result.js";
3
+ export declare class BindDraftError extends Error {
4
+ readonly original: unknown;
5
+ readonly draft: BindDraftReceipt;
6
+ constructor(original: unknown, draft: BindDraftReceipt);
7
+ }
8
+ export declare function preserveBindDraft(world: WorldRoot, markdown: string, now?: number): Promise<BindDraftReceipt>;
@@ -0,0 +1,96 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstatSync, mkdirSync, readFileSync, readdirSync, unlinkSync, utimesSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { repairDerivedFile, replaceFileDurably } from "../coordination/durable-file.js";
5
+ const RETENTION_MS = 7 * 24 * 60 * 60 * 1_000;
6
+ const DRAFT_NAME = /^bind-[0-9a-f]{64}\.md$/u;
7
+ export class BindDraftError extends Error {
8
+ original;
9
+ draft;
10
+ constructor(original, draft) {
11
+ super(original instanceof Error ? original.message : String(original));
12
+ this.name = "BindDraftError";
13
+ this.original = original;
14
+ this.draft = draft;
15
+ }
16
+ }
17
+ function diagnostic(error) {
18
+ return error instanceof Error ? error.message : String(error);
19
+ }
20
+ function sweep(directory, now) {
21
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
22
+ if (!entry.isFile() || !DRAFT_NAME.test(entry.name))
23
+ continue;
24
+ const path = join(directory, entry.name);
25
+ try {
26
+ if (now - lstatSync(path).mtimeMs > RETENTION_MS)
27
+ unlinkSync(path);
28
+ }
29
+ catch (error) {
30
+ if (error.code !== "ENOENT")
31
+ throw error;
32
+ }
33
+ }
34
+ }
35
+ export async function preserveBindDraft(world, markdown, now = Date.now()) {
36
+ const digest = createHash("sha256").update(markdown, "utf8").digest("hex");
37
+ const name = `bind-${digest}.md`;
38
+ const relativePath = `.keiyaku/draft/${name}`;
39
+ const directory = join(world, ".keiyaku", "draft");
40
+ const path = join(directory, name);
41
+ const warnings = [];
42
+ try {
43
+ mkdirSync(directory, { recursive: true });
44
+ }
45
+ catch (error) {
46
+ return { warning: `bind draft could not be preserved: ${diagnostic(error)}` };
47
+ }
48
+ try {
49
+ await repairDerivedFile(join(directory, ".gitignore"), "*\n");
50
+ }
51
+ catch (error) {
52
+ warnings.push(`bind draft ignore could not be maintained: ${diagnostic(error)}`);
53
+ }
54
+ try {
55
+ let equal = false;
56
+ try {
57
+ const status = lstatSync(path);
58
+ equal = status.isFile() && !status.isSymbolicLink() && readFileSync(path).equals(Buffer.from(markdown));
59
+ }
60
+ catch (error) {
61
+ if (error.code !== "ENOENT")
62
+ throw error;
63
+ }
64
+ if (equal) {
65
+ try {
66
+ const touched = new Date(now);
67
+ utimesSync(path, touched, touched);
68
+ }
69
+ catch (error) {
70
+ if (error.code !== "ENOENT")
71
+ throw error;
72
+ await replaceFileDurably(path, markdown);
73
+ }
74
+ }
75
+ else {
76
+ await replaceFileDurably(path, markdown);
77
+ }
78
+ if (!readFileSync(path).equals(Buffer.from(markdown))) {
79
+ throw new Error(`bind draft bytes do not match their content address: ${relativePath}`);
80
+ }
81
+ }
82
+ catch (error) {
83
+ warnings.push(`bind draft could not be preserved: ${diagnostic(error)}`);
84
+ return { warning: warnings.join("; ") };
85
+ }
86
+ try {
87
+ sweep(directory, now);
88
+ }
89
+ catch (error) {
90
+ warnings.push(`bind draft sweep failed: ${diagnostic(error)}`);
91
+ }
92
+ return {
93
+ path: relativePath,
94
+ ...(warnings.length === 0 ? {} : { warning: warnings.join("; ") }),
95
+ };
96
+ }
@@ -1,14 +1,15 @@
1
- import { type ContractId, type Settings } from "../index.js";
1
+ import { type ActorId, type ContractId, type Settings } from "../index.js";
2
2
  import { type AkumaInvocationResult } from "./commands/akuma-invoke.js";
3
3
  import { type InstallInvocationResult } from "./commands/install.js";
4
4
  import { type TaskInvocationResult } from "./commands/task-invoke.js";
5
5
  import { type ParsedExecution } from "./parse.js";
6
6
  import type { InvocationResult } from "./result.js";
7
- export type { AcceptedFact, DiffUnavailable, InvocationResult, Lag } from "./result.js";
7
+ export type { AcceptedFact, InvocationResult, Lag } from "./result.js";
8
8
  type InvokeRuntime = Readonly<{
9
9
  cwd?: string;
10
10
  environment?: NodeJS.ProcessEnv;
11
- readStdin?: () => string;
11
+ readStdin?: () => Promise<string>;
12
+ actor?: ActorId;
12
13
  }>;
13
14
  export type SettingsInvocationResult = Readonly<{
14
15
  kind: "settings";
@@ -1,9 +1,7 @@
1
- import { readFileSync, realpathSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { gatesFrom, Keiyaku, NoGitWorldError, Repo, requireBranchesToBeUpToDateFrom, settings, SettingsError, worktreeHooksFrom } from "../index.js";
4
- import { kanshi, selectKanshi } from "../kanshi/index.js";
1
+ import { AkumaWorldScopeError, gatesFrom, Keiyaku, requireBranchesToBeUpToDateFrom, settings, SettingsError, worktreeHooksFrom } from "../index.js";
2
+ import { kanshi, selectKanshi, selectRegion } from "../kanshi/index.js";
5
3
  import { resolveActor } from "./actor.js";
6
- import { resultFromMutationCall } from "./accepted.js";
4
+ import { acceptedAbandon, acceptedAmend, acceptedArc, acceptedAudit, acceptedBind, acceptedDeliver, acceptedReview, resultFromMutationCall, } from "./accepted.js";
7
5
  import { amendFromCommand } from "./commands/amend.js";
8
6
  import { invokeAkuma, invokeAkumaStatus } from "./commands/akuma-invoke.js";
9
7
  import { isParsedAkumaCommand } from "./commands/akuma.js";
@@ -11,42 +9,53 @@ import { bindFromCommand } from "./commands/bind.js";
11
9
  import { installHarnesses } from "./commands/install.js";
12
10
  import { invokeTask } from "./commands/task-invoke.js";
13
11
  import { CliUsageError, renderCommandUsage } from "./parse.js";
12
+ import { isBlankInput } from "./usage.js";
14
13
  import { contractFromInput, resolveContextualContract, resolveKanshiContract } from "./selectors.js";
15
14
  import { resolveNamedAddress } from "../library/address.js";
16
- import { World, WorldError } from "../world.js";
17
- function settingsAt(root, environment) {
18
- const home = environment.KEIYAKU_HOME?.trim();
19
- return settings({ ...(root === undefined ? {} : { root }), ...(home === undefined || home.length === 0 ? {} : { home }) });
15
+ import { assertExplicitRepoUse, resolveCliCoordinates } from "./coordinates.js";
16
+ import { BindDraftError, preserveBindDraft } from "./draft.js";
17
+ async function readStdin() {
18
+ const chunks = [];
19
+ for await (const chunk of process.stdin)
20
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
21
+ return Buffer.concat(chunks).toString("utf8");
20
22
  }
21
- function locateWorld(coordinate) {
22
- try {
23
- return World.locate(coordinate);
24
- }
25
- catch (error) {
26
- if (error instanceof WorldError)
27
- throw new CliUsageError(error.message);
28
- throw error;
23
+ function selectedStdinDiagnostic(command) {
24
+ switch (command.command) {
25
+ case "bind":
26
+ case "amend":
27
+ case "arc":
28
+ return `${command.command} requires a nonblank stdin document`;
29
+ case "review":
30
+ return command.summaryFromStdin === true ? "review requires a nonblank summary" : undefined;
31
+ case "call":
32
+ case "tell":
33
+ return command.prompt.kind === "stdin" ? `${command.command} requires a nonblank prompt` : undefined;
34
+ case "task":
35
+ switch (command.stdin) {
36
+ case "document": return "task add requires a nonblank stdin document";
37
+ case "compose": return "task compose requires a nonblank stdin document";
38
+ case "body": return "task update --body requires a nonblank value";
39
+ case "append": return "task update --append requires a nonblank value";
40
+ case "note": return "task update --note requires a nonblank value";
41
+ default: return undefined;
42
+ }
43
+ default:
44
+ return undefined;
29
45
  }
30
46
  }
31
- function canonicalInvocationCwd(input) {
32
- try {
33
- return realpathSync(input);
34
- }
35
- catch {
36
- throw new CliUsageError(`invocation cwd is not an existing directory: ${input}`);
37
- }
47
+ async function withAcquiredStdin(command, runtime) {
48
+ const diagnostic = selectedStdinDiagnostic(command);
49
+ if (diagnostic === undefined)
50
+ return runtime;
51
+ const bytes = await (runtime.readStdin ?? readStdin)();
52
+ if (isBlankInput(bytes))
53
+ throw new CliUsageError(diagnostic);
54
+ return { ...runtime, readStdin: async () => bytes };
38
55
  }
39
- function establishWorld(coordinate, located) {
40
- if (located !== null)
41
- return located;
42
- try {
43
- return World.at(coordinate);
44
- }
45
- catch (error) {
46
- if (error instanceof WorldError)
47
- throw new CliUsageError(error.message);
48
- throw error;
49
- }
56
+ function settingsAt(root, environment) {
57
+ const home = environment.KEIYAKU_HOME?.trim();
58
+ return settings({ ...(root === undefined ? {} : { root }), ...(home === undefined || home.length === 0 ? {} : { home }) });
50
59
  }
51
60
  function selectedGates(value, name) {
52
61
  try {
@@ -88,93 +97,47 @@ function actorFromEdge(actor, environment) {
88
97
  }
89
98
  return resolved;
90
99
  }
91
- function repoAt(coordinate) {
92
- return Repo.at({ path: coordinate });
93
- }
94
- function optionalRepoAt(coordinate) {
95
- try {
96
- return repoAt(coordinate);
97
- }
98
- catch (error) {
99
- if (error instanceof NoGitWorldError)
100
- return undefined;
101
- throw error;
102
- }
103
- }
104
- function repoUse(command) {
105
- switch (command.command) {
106
- case "bind":
107
- case "amend":
108
- case "deliver":
109
- case "review":
110
- case "arc":
111
- case "abandon":
112
- case "audit":
113
- case "reconcile":
114
- case "show":
115
- return "required";
116
- case "ls":
117
- return command.query.kind === "contracts" ? "required" : "none";
118
- case "status":
119
- case "tell":
120
- case "history":
121
- case "fork":
122
- return "optional";
123
- case "wait":
124
- case "kill":
125
- return command.akuma.some((selector) => selector.startsWith("kei/")) ? "required" : "optional";
126
- case "call":
127
- return command.contract === undefined ? "none" : "required";
128
- case "settings":
129
- case "task":
130
- case "install":
131
- return "none";
132
- }
133
- }
134
- function refuseUnusedRepo(command) {
135
- if (command.command === "call")
136
- throw new CliUsageError("--repo has no consumer without --contract");
137
- throw new CliUsageError(`--repo has no consumer for ${command.command}`);
138
- }
139
- function repoFor(use, coordinate) {
140
- switch (use) {
141
- case "none": return undefined;
142
- case "optional": return optionalRepoAt(coordinate);
143
- case "required": return repoAt(coordinate);
144
- }
145
- }
146
100
  async function selectContract(repo, selector, scope) {
147
101
  if (selector !== undefined && !selector.startsWith("@"))
148
102
  return contractFromInput(repo, selector);
149
103
  const id = resolveContextualContract(await Keiyaku.list({ repo }), selector, scope);
150
104
  return contractFromInput(repo, id);
151
105
  }
152
- async function invokeBind(parsed, repo, edge, configuration, hooks) {
153
- const markdown = edge.readStdin();
106
+ async function bindDraftReceipt(establishWorld, markdown) {
107
+ try {
108
+ return preserveBindDraft(await establishWorld(), markdown);
109
+ }
110
+ catch (error) {
111
+ return { warning: `bind draft could not be preserved: ${error instanceof Error ? error.message : String(error)}` };
112
+ }
113
+ }
114
+ async function invokeBind({ parsed, repo, edge, configuration, hooks, establishWorld }) {
115
+ const markdown = await edge.readStdin();
154
116
  const gates = selectedGates(configuration, parsed.gates);
155
117
  const actor = actorFromEdge(parsed.actor, edge.environment);
156
- return resultFromMutationCall("bind", () => bindFromCommand({
157
- command: parsed,
158
- repo,
159
- markdown,
160
- gates,
161
- ...(actor === undefined ? {} : { actor }),
162
- hooks,
163
- }), {
164
- project: (result) => {
165
- const bound = result.facts.find((fact) => fact.kind === "bind");
118
+ try {
119
+ const result = await resultFromMutationCall("bind", () => bindFromCommand({
120
+ command: parsed,
121
+ repo,
122
+ markdown,
123
+ gates,
124
+ ...(actor === undefined ? {} : { actor }),
125
+ hooks,
126
+ }), (accepted) => {
127
+ const bound = accepted.facts.find((fact) => fact.kind === "bind");
166
128
  if (bound === undefined || bound.kind !== "bind")
167
129
  throw new Error("accepted bind is missing its bind fact");
168
- return { target: bound.data.coordinates.target ?? null };
169
- },
170
- });
171
- }
172
- function unavailableDiff(delivery) {
173
- return {
174
- reason: "git-unavailable",
175
- integrationSnapshot: delivery.integration.snapshot,
176
- changeId: delivery.integration.changeId,
177
- };
130
+ return acceptedBind(accepted, bound.data.coordinates.target ?? null);
131
+ });
132
+ if (result.kind !== "refused")
133
+ return result;
134
+ return { ...result, draft: await bindDraftReceipt(establishWorld, markdown) };
135
+ }
136
+ catch (error) {
137
+ if (!(error instanceof TypeError))
138
+ throw error;
139
+ throw new BindDraftError(error, await bindDraftReceipt(establishWorld, markdown));
140
+ }
178
141
  }
179
142
  function deliverRefusal(refusal) {
180
143
  if (typeof refusal !== "object" || refusal === null || !("kind" in refusal) || refusal.kind !== "dirty-workspace") {
@@ -190,50 +153,28 @@ async function invokeDeliver(parsed, seat, requireBranchesToBeUpToDate) {
190
153
  requireBranchesToBeUpToDate,
191
154
  includeDirty: parsed.includeDirty,
192
155
  hooks: seat.hooks,
193
- }), {
156
+ }), (result) => acceptedDeliver(result, seat.id), {
194
157
  coordinate: seat.id,
195
158
  projectRefusal: deliverRefusal,
196
- project: (result) => ({
197
- obligations: {
198
- ...(result.value.verification === undefined ? {} : { verification: result.value.verification }),
199
- ...(result.value.placement === undefined ? {} : { placement: result.value.placement }),
200
- ...(result.value.cleanup === undefined ? {} : { cleanup: result.value.cleanup }),
201
- ...(result.value.leak === undefined ? {} : { leak: result.value.leak }),
202
- },
203
- }),
204
159
  });
205
160
  }
206
161
  async function invokeReview(parsed, seat, readStdin) {
207
- const summary = parsed.summaryFromStdin === true ? readStdin() : parsed.summary;
162
+ const summary = parsed.summaryFromStdin === true ? await readStdin() : parsed.summary;
208
163
  return resultFromMutationCall("review", () => seat.contract.review({
209
164
  verdict: parsed.verdict,
210
165
  ...(seat.actor === undefined ? {} : { actor: seat.actor }),
211
166
  ...(summary === undefined ? {} : { summary }),
212
167
  hooks: seat.hooks,
213
- }), {
214
- coordinate: seat.id,
215
- project: (result) => ({
216
- obligations: result.value.placement === undefined ? {} : { placement: result.value.placement },
217
- workspace: result.value.workspace,
218
- }),
219
- });
168
+ }), (result) => acceptedReview(result, seat.id), { coordinate: seat.id });
220
169
  }
221
- async function invokeAudit(parsed, seat) {
222
- const delivery = parsed.showDiffBody ? await seat.contract.delivery() : null;
223
- return resultFromMutationCall("audit", () => seat.contract.audit({ ...(seat.actor === undefined ? {} : { actor: seat.actor }), hooks: seat.hooks }), {
224
- coordinate: seat.id,
225
- project: async (result) => {
226
- let renderedDiff;
227
- if (parsed.showDiffBody && delivery !== null) {
228
- const diff = await delivery.diff();
229
- renderedDiff = diff === null ? unavailableDiff(delivery) : diff;
230
- }
231
- return {
232
- report: result.value,
233
- ...(renderedDiff === undefined ? {} : { diff: renderedDiff }),
234
- };
235
- },
236
- });
170
+ async function invokeAudit(parsed, seat, requireBranchesToBeUpToDate) {
171
+ return resultFromMutationCall("audit", () => seat.contract.audit({
172
+ ...(seat.actor === undefined ? {} : { actor: seat.actor }),
173
+ includeDirty: parsed.includeDirty,
174
+ showDiff: parsed.showDiff,
175
+ requireBranchesToBeUpToDate,
176
+ hooks: seat.hooks,
177
+ }), (result) => acceptedAudit(result, seat.id), { coordinate: seat.id });
237
178
  }
238
179
  async function invokeExisting({ parsed, repo, edge, scope, configuration, hooks }) {
239
180
  const { id, contract } = await selectContract(repo, parsed.contract, scope);
@@ -241,32 +182,32 @@ async function invokeExisting({ parsed, repo, edge, scope, configuration, hooks
241
182
  const seat = { contract, id, ...(actor === undefined ? {} : { actor }), hooks };
242
183
  switch (parsed.command) {
243
184
  case "amend": {
244
- const markdown = edge.readStdin();
185
+ const markdown = await edge.readStdin();
245
186
  const gates = parsed.gates === undefined
246
187
  ? undefined
247
188
  : selectedGates(configuration, parsed.gates);
248
- return resultFromMutationCall("amend", () => amendFromCommand({ command: parsed, repo, contract, markdown, gates, ...(actor === undefined ? {} : { actor }), hooks }), { coordinate: id });
189
+ return resultFromMutationCall("amend", () => amendFromCommand({ command: parsed, repo, contract, markdown, gates, ...(actor === undefined ? {} : { actor }), hooks }), (result) => acceptedAmend(result, id), { coordinate: id });
249
190
  }
250
191
  case "deliver":
251
192
  return invokeDeliver(parsed, seat, selectedGitPolicy(configuration));
252
193
  case "review":
253
194
  return invokeReview(parsed, seat, edge.readStdin);
254
195
  case "arc": {
255
- const markdown = edge.readStdin();
196
+ const markdown = await edge.readStdin();
256
197
  return resultFromMutationCall("arc", () => contract.arc({
257
198
  markdown,
258
199
  ...(actor === undefined ? {} : { actor }),
259
200
  hooks,
260
- }), { coordinate: id });
201
+ }), (result) => acceptedArc(result, id), { coordinate: id });
261
202
  }
262
203
  case "abandon":
263
204
  return resultFromMutationCall("abandon", () => contract.abandon({
264
205
  ...(actor === undefined ? {} : { actor }),
265
206
  ...(parsed.note === undefined ? {} : { note: parsed.note }),
266
207
  hooks,
267
- }), { coordinate: id });
208
+ }), (result) => acceptedAbandon(result, id), { coordinate: id });
268
209
  case "audit":
269
- return invokeAudit(parsed, seat);
210
+ return invokeAudit(parsed, seat, selectedGitPolicy(configuration));
270
211
  default:
271
212
  return parsed;
272
213
  }
@@ -279,7 +220,7 @@ async function invokeAkumaCommand(parsed, input) {
279
220
  const selected = contractFromInput(repo, parsed.contract);
280
221
  return await invokeAkuma(parsed, {
281
222
  path,
282
- executionCwd,
223
+ ...(executionCwd === undefined ? {} : { executionCwd }),
283
224
  settings: configuration,
284
225
  contract: selected.contract,
285
226
  readStdin: edge.readStdin,
@@ -287,7 +228,7 @@ async function invokeAkumaCommand(parsed, input) {
287
228
  }
288
229
  return await invokeAkuma(parsed, {
289
230
  path,
290
- executionCwd,
231
+ ...(executionCwd === undefined ? {} : { executionCwd }),
291
232
  settings: configuration,
292
233
  ...(repo === undefined ? {} : { repo }),
293
234
  readStdin: edge.readStdin,
@@ -298,13 +239,15 @@ async function invokeAkumaFromEdge(parsed, input) {
298
239
  return await invokeAkumaCommand(parsed, input);
299
240
  }
300
241
  catch (error) {
242
+ if (error instanceof AkumaWorldScopeError)
243
+ throw error;
301
244
  if (error instanceof TypeError)
302
245
  throw new CliUsageError(error.message);
303
246
  throw error;
304
247
  }
305
248
  }
306
- function akumaWorldFor(parsed, cwd, located) {
307
- const world = parsed.command === "call" ? establishWorld(cwd, located) : located;
249
+ async function akumaWorldFor(parsed, located, candidate, establish) {
250
+ const world = parsed.command === "call" ? candidate ?? await establish() : located;
308
251
  if (world === null)
309
252
  throw new CliUsageError("no Keiyaku world contains the invocation cwd");
310
253
  return world;
@@ -319,7 +262,7 @@ async function invokeCatalog(parsed, world, repo, edge) {
319
262
  if (parsed.query.kind === "archetypes") {
320
263
  return {
321
264
  kind: "catalog",
322
- catalog: await Keiyaku.ls({ query: parsed.query, settings: settingsAt(undefined, edge.environment) }),
265
+ catalog: await Keiyaku.ls({ query: parsed.query, settings: await settingsAt(undefined, edge.environment) }),
323
266
  };
324
267
  }
325
268
  if (world === null)
@@ -336,7 +279,7 @@ async function invokeCatalog(parsed, world, repo, edge) {
336
279
  }
337
280
  }
338
281
  async function invokeStatus(parsed, world, repo, edge) {
339
- const configuration = settingsAt(world ?? undefined, edge.environment);
282
+ const configuration = await settingsAt(world ?? undefined, edge.environment);
340
283
  if (parsed.akuma === true) {
341
284
  if (world === null)
342
285
  throw new CliUsageError("no Keiyaku world contains the invocation cwd");
@@ -348,7 +291,7 @@ async function invokeStatus(parsed, world, repo, edge) {
348
291
  }
349
292
  if (parsed.contract.startsWith("@")) {
350
293
  try {
351
- const address = resolveNamedAddress({
294
+ const address = await resolveNamedAddress({
352
295
  path: world,
353
296
  selector: parsed.contract,
354
297
  contracts: repo === undefined ? [] : (await Keiyaku.list({ repo })).rows,
@@ -371,26 +314,59 @@ async function invokeStatus(parsed, world, repo, edge) {
371
314
  const contract = resolveKanshiContract(report, parsed.contract);
372
315
  return { kind: "status", report: selectKanshi({ report, contract }), selection: "contract" };
373
316
  }
374
- async function invokeParsed(invocation, runtime, use) {
375
- const processCwd = resolve(runtime.cwd ?? ".");
376
- const cwd = canonicalInvocationCwd(resolve(processCwd, invocation.cwd ?? "."));
317
+ async function invokeRegion(parsed, world, repo) {
318
+ const read = async (region) => {
319
+ try {
320
+ return await kanshi({ world, repo, region });
321
+ }
322
+ catch (error) {
323
+ if (error instanceof TypeError)
324
+ throw new CliUsageError(error.message);
325
+ throw error;
326
+ }
327
+ };
328
+ if (parsed.contract === undefined) {
329
+ const selection = parsed.path !== undefined
330
+ ? { kind: "path", path: parsed.path }
331
+ : parsed.overlap ? { kind: "overlap" } : { kind: "declarations" };
332
+ const report = await read(selection);
333
+ return { kind: "region", region: report.region ?? { kind: "absent" } };
334
+ }
335
+ const report = await read({ kind: "declarations" });
336
+ const contract = resolveKanshiContract(report, parsed.contract);
337
+ if (report.contracts.kind !== "present" || report.contracts.value.rows.every((row) => row.id !== contract || row.disposition !== "active")) {
338
+ throw new CliUsageError(`unknown contract selector: ${parsed.contract}`);
339
+ }
340
+ if (report.region?.kind !== "present" || report.region.value.kind !== "declarations") {
341
+ return { kind: "region", region: report.region ?? { kind: "absent" } };
342
+ }
343
+ const selection = parsed.overlap ? { kind: "overlap", contract } : { kind: "contract", contract };
344
+ return { kind: "region", region: { kind: "present", value: selectRegion({ declarations: report.region.value.declarations, selection }) } };
345
+ }
346
+ // eslint-disable-next-line complexity -- command dispatch keeps the CLI's existing boundary in one place.
347
+ async function invokeParsed(invocation, runtime) {
348
+ const coordinates = await resolveCliCoordinates({
349
+ ...(runtime.cwd === undefined ? {} : { processCwd: runtime.cwd }),
350
+ ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
351
+ ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
352
+ command: invocation.command,
353
+ });
354
+ const { cwd, repo, world, candidateWorld, establishWorld } = coordinates;
377
355
  const edge = {
378
356
  environment: runtime.environment ?? process.env,
379
- readStdin: runtime.readStdin ?? (() => readFileSync(0, "utf8")),
357
+ readStdin: runtime.readStdin ?? readStdin,
380
358
  };
381
359
  const parsed = invocation.command;
382
- const repoCoordinate = resolve(cwd, invocation.repo ?? ".");
383
- const repo = repoFor(use, repoCoordinate);
384
- const world = locateWorld(cwd);
385
360
  if (parsed.command === "settings") {
386
- return { kind: "settings", value: settingsAt(world ?? undefined, edge.environment) };
361
+ return { kind: "settings", value: await settingsAt(world ?? undefined, edge.environment) };
387
362
  }
388
363
  if (parsed.command === "task") {
389
364
  try {
390
365
  return await invokeTask(parsed, {
391
366
  world,
392
- establish: () => establishWorld(cwd, world),
367
+ establish: coordinates.establishWorld,
393
368
  readStdin: edge.readStdin,
369
+ ...(runtime.actor === undefined ? {} : { actor: runtime.actor }),
394
370
  });
395
371
  }
396
372
  catch (error) {
@@ -400,11 +376,13 @@ async function invokeParsed(invocation, runtime, use) {
400
376
  }
401
377
  }
402
378
  if (isParsedAkumaCommand(parsed)) {
403
- const akumaWorld = akumaWorldFor(parsed, cwd, world);
404
- const configuration = settingsAt(akumaWorld, edge.environment);
379
+ const akumaWorld = await akumaWorldFor(parsed, world, candidateWorld, coordinates.establishWorld);
380
+ const configuration = await settingsAt(akumaWorld, edge.environment);
405
381
  return await invokeAkumaFromEdge(parsed, {
406
382
  path: akumaWorld,
407
- executionCwd: cwd,
383
+ ...(parsed.command === "call" && parsed.contract !== undefined && invocation.cwd === undefined
384
+ ? {}
385
+ : { executionCwd: cwd }),
408
386
  ...(repo === undefined ? {} : { repo }),
409
387
  configuration,
410
388
  edge,
@@ -416,8 +394,10 @@ async function invokeParsed(invocation, runtime, use) {
416
394
  return await invokeStatus(parsed, world, repo, edge);
417
395
  if (repo === undefined)
418
396
  throw new Error(`${parsed.command} requires a resolved Repo`);
397
+ if (parsed.command === "region")
398
+ return invokeRegion(parsed, world, repo);
419
399
  const scope = cwd;
420
- const configuration = settingsAt(world ?? undefined, edge.environment);
400
+ const configuration = await settingsAt(world ?? undefined, edge.environment);
421
401
  const hooks = selectedHooks(configuration);
422
402
  if (parsed.command === "show") {
423
403
  const selected = await selectContract(repo, parsed.contract, scope);
@@ -432,27 +412,28 @@ async function invokeParsed(invocation, runtime, use) {
432
412
  return { kind: "observation", command: "reconcile", ...await contract.reconcile({ hooks, retryHooks: parsed.retryHooks }) };
433
413
  }
434
414
  case "bind":
435
- return invokeBind(parsed, repo, edge, configuration, hooks);
415
+ return invokeBind({ parsed, repo, edge, configuration, hooks, establishWorld });
436
416
  default:
437
417
  return invokeExisting({ parsed, repo, edge, scope, configuration, hooks });
438
418
  }
439
419
  }
440
- async function invokeInstall(command, runtime) {
441
- return installHarnesses(command.harnesses, runtime.environment ?? process.env);
420
+ function withResolvedTaskActor(command, runtime) {
421
+ if (command.command !== "task" || (command.action !== "add" && command.action !== "compose"))
422
+ return runtime;
423
+ const actor = actorFromEdge(typeof command.flags.actor === "string" ? command.flags.actor : undefined, runtime.environment ?? process.env);
424
+ return actor === undefined ? runtime : { ...runtime, actor };
442
425
  }
443
426
  export async function invoke(invocation, runtime = {}) {
444
427
  try {
445
428
  const command = invocation.command;
446
- const use = repoUse(command);
447
- if (invocation.repo !== undefined && use === "none")
448
- refuseUnusedRepo(command);
429
+ assertExplicitRepoUse(command, invocation.repo);
449
430
  if (command.command === "install")
450
- return await invokeInstall(command, runtime);
431
+ return installHarnesses(command.harnesses, runtime.environment ?? process.env);
451
432
  return await invokeParsed({
452
433
  ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
453
434
  ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
454
435
  command,
455
- }, runtime, use);
436
+ }, await withAcquiredStdin(command, withResolvedTaskActor(command, runtime)));
456
437
  }
457
438
  catch (error) {
458
439
  if (error instanceof CliUsageError && error.projection === undefined) {