@astrosheep/keiyaku 4.5.19 → 4.5.21

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 (197) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -4
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/src/akuma/akuma-handle.d.ts +5 -1
  9. package/build/src/akuma/akuma-handle.js +4 -1
  10. package/build/src/akuma/akuma-instance.d.ts +7 -2
  11. package/build/src/akuma/akuma-instance.js +14 -7
  12. package/build/src/akuma/akuma-observe.d.ts +11 -0
  13. package/build/src/akuma/akuma-observe.js +23 -0
  14. package/build/src/akuma/akuma-product.d.ts +2 -0
  15. package/build/src/akuma/akuma-product.js +6 -0
  16. package/build/src/akuma/akuma.d.ts +20 -1
  17. package/build/src/akuma/akuma.js +2 -0
  18. package/build/src/akuma/allowed.d.ts +20 -0
  19. package/build/src/akuma/allowed.js +11 -0
  20. package/build/src/akuma/archetype.js +3 -0
  21. package/build/src/akuma/body.d.ts +25 -5
  22. package/build/src/akuma/body.js +70 -31
  23. package/build/src/akuma/call-request.d.ts +5 -0
  24. package/build/src/akuma/call-request.js +14 -4
  25. package/build/src/akuma/fleet-execution.d.ts +28 -0
  26. package/build/src/akuma/fleet-execution.js +36 -4
  27. package/build/src/akuma/fleet-observation.d.ts +54 -0
  28. package/build/src/akuma/fleet-request.d.ts +10 -0
  29. package/build/src/akuma/fleet-request.js +13 -3
  30. package/build/src/akuma/heart/facts.d.ts +2 -0
  31. package/build/src/akuma/heart/index.d.ts +1 -0
  32. package/build/src/akuma/heart/index.js +4 -1
  33. package/build/src/akuma/heart/rows.d.ts +2 -0
  34. package/build/src/akuma/heart/rows.js +7 -5
  35. package/build/src/akuma/heart/schema.d.ts +1 -1
  36. package/build/src/akuma/heart/schema.js +3 -1
  37. package/build/src/akuma/heart/tells.js +6 -5
  38. package/build/src/akuma/heart/timeline.js +1 -1
  39. package/build/src/akuma/provider-recipe.d.ts +1 -0
  40. package/build/src/akuma/provider-recipe.js +5 -0
  41. package/build/src/akuma/provider.d.ts +1 -1
  42. package/build/src/akuma/provider.js +1 -1
  43. package/build/src/akuma/providers/acp/core.js +3 -6
  44. package/build/src/akuma/providers/acp/index.js +3 -0
  45. package/build/src/akuma/providers/claude/index.js +10 -10
  46. package/build/src/akuma/providers/codex-app-server/index.js +12 -11
  47. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  48. package/build/src/akuma/providers/execution-environment.js +24 -0
  49. package/build/src/akuma/providers/grok-build/index.js +3 -0
  50. package/build/src/akuma/providers/opencode-sdk/index.js +32 -23
  51. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  52. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  53. package/build/src/akuma/providers/pi/events.js +7 -2
  54. package/build/src/akuma/providers/pi/index.js +5 -2
  55. package/build/src/akuma/request-observation.d.ts +45 -0
  56. package/build/src/akuma/request-observation.js +162 -0
  57. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  58. package/build/src/akuma/request-rendezvous.js +80 -14
  59. package/build/src/akuma/request-serve.js +27 -4
  60. package/build/src/akuma/request-wire.d.ts +5 -0
  61. package/build/src/akuma/request-wire.js +2 -0
  62. package/build/src/akuma/turn-drive.d.ts +1 -0
  63. package/build/src/akuma/turn-drive.js +2 -0
  64. package/build/src/akuma-body.js +5 -0
  65. package/build/src/body/amend.js +6 -4
  66. package/build/src/body/decode.d.ts +3 -1
  67. package/build/src/body/decode.js +53 -17
  68. package/build/src/body/region.d.ts +9 -1
  69. package/build/src/body/region.js +87 -12
  70. package/build/src/body/render.js +1 -1
  71. package/build/src/body/verification.d.ts +3 -1
  72. package/build/src/body/verification.js +4 -1
  73. package/build/src/cli/accepted.js +19 -2
  74. package/build/src/cli/commands/akuma-invoke.d.ts +3 -1
  75. package/build/src/cli/commands/akuma-invoke.js +134 -14
  76. package/build/src/cli/commands/akuma.js +24 -12
  77. package/build/src/cli/commands/contract-help.d.ts +15 -15
  78. package/build/src/cli/commands/contract-help.js +45 -19
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  80. package/build/src/cli/commands/contract-invoke.js +25 -11
  81. package/build/src/cli/commands/contract.js +6 -4
  82. package/build/src/cli/commands/install.d.ts +1 -1
  83. package/build/src/cli/commands/install.js +1 -1
  84. package/build/src/cli/commands/status-set.js +15 -24
  85. package/build/src/cli/commands/task.js +18 -18
  86. package/build/src/cli/invoke.d.ts +3 -0
  87. package/build/src/cli/invoke.js +58 -23
  88. package/build/src/cli/main.js +5 -0
  89. package/build/src/cli/parse.d.ts +3 -0
  90. package/build/src/cli/parse.js +26 -4
  91. package/build/src/cli/render/akuma-activity.d.ts +100 -2
  92. package/build/src/cli/render/akuma-activity.js +485 -40
  93. package/build/src/cli/render/akuma.d.ts +10 -0
  94. package/build/src/cli/render/akuma.js +31 -21
  95. package/build/src/cli/render/audit.js +1 -1
  96. package/build/src/cli/render/board.js +1 -1
  97. package/build/src/cli/render/catalog.js +16 -13
  98. package/build/src/cli/render/contract-history.js +39 -10
  99. package/build/src/cli/render/contract-observation.js +10 -6
  100. package/build/src/cli/render/contract.js +109 -106
  101. package/build/src/cli/render/execution-progress.d.ts +26 -0
  102. package/build/src/cli/render/execution-progress.js +178 -0
  103. package/build/src/cli/render/kanshi-akuma.js +27 -39
  104. package/build/src/cli/render/kanshi.js +33 -26
  105. package/build/src/cli/render/nuke.js +1 -1
  106. package/build/src/cli/render/receipt.d.ts +1 -1
  107. package/build/src/cli/render/receipt.js +6 -6
  108. package/build/src/cli/render/refusal.js +10 -7
  109. package/build/src/cli/render/region.js +1 -1
  110. package/build/src/cli/render/settings.js +12 -2
  111. package/build/src/cli/render/status-line.d.ts +30 -0
  112. package/build/src/cli/render/status-line.js +58 -0
  113. package/build/src/cli/render/status-set.js +1 -1
  114. package/build/src/cli/render/task.d.ts +2 -0
  115. package/build/src/cli/render/task.js +11 -7
  116. package/build/src/cli/render/terminal.d.ts +0 -1
  117. package/build/src/cli/render/terminal.js +47 -15
  118. package/build/src/cli/result.d.ts +1 -0
  119. package/build/src/cli/runtime.d.ts +2 -0
  120. package/build/src/cli/runtime.js +36 -4
  121. package/build/src/cli/usage.js +2 -2
  122. package/build/src/cli/version.d.ts +1 -0
  123. package/build/src/cli/version.js +25 -0
  124. package/build/src/git/hooks.d.ts +13 -1
  125. package/build/src/git/hooks.js +2 -1
  126. package/build/src/git/workspace.d.ts +1 -1
  127. package/build/src/git/workspace.js +1 -1
  128. package/build/src/identity/mint.d.ts +13 -0
  129. package/build/src/identity/mint.js +26 -0
  130. package/build/src/identity/normalize.d.ts +4 -0
  131. package/build/src/identity/normalize.js +30 -0
  132. package/build/src/index.d.ts +2 -0
  133. package/build/src/index.js +1 -0
  134. package/build/src/kanshi/read.js +17 -4
  135. package/build/src/kanshi/report.d.ts +1 -0
  136. package/build/src/library/akuma-creation.d.ts +2 -0
  137. package/build/src/library/akuma-creation.js +34 -18
  138. package/build/src/library/audit.d.ts +2 -0
  139. package/build/src/library/audit.js +1 -0
  140. package/build/src/library/bind.js +18 -16
  141. package/build/src/library/composition.d.ts +1 -0
  142. package/build/src/library/contract-bind.d.ts +3 -1
  143. package/build/src/library/contract-bind.js +5 -3
  144. package/build/src/library/contract-execution.d.ts +2 -0
  145. package/build/src/library/contract-execution.js +15 -1
  146. package/build/src/library/contract-forwarding.d.ts +4 -0
  147. package/build/src/library/contract-forwarding.js +3 -0
  148. package/build/src/library/contract-handle.d.ts +7 -0
  149. package/build/src/library/contract-handle.js +25 -0
  150. package/build/src/library/contract-operations.d.ts +7 -2
  151. package/build/src/library/contract-operations.js +21 -1
  152. package/build/src/library/contract-types.d.ts +1 -0
  153. package/build/src/library/execution-result.d.ts +2 -2
  154. package/build/src/library/execution.d.ts +8 -0
  155. package/build/src/library/execution.js +102 -0
  156. package/build/src/library/fleet.d.ts +4 -1
  157. package/build/src/library/fleet.js +32 -8
  158. package/build/src/library/keiyaku.d.ts +2 -0
  159. package/build/src/library/mutation.js +15 -7
  160. package/build/src/plugin/akuma-signals.d.ts +6 -0
  161. package/build/src/plugin/akuma-signals.js +9 -1
  162. package/build/src/plugin/public.d.ts +6 -2
  163. package/build/src/plugin/runtime.d.ts +1 -3
  164. package/build/src/plugin/runtime.js +95 -103
  165. package/build/src/protocol/amend.js +4 -3
  166. package/build/src/protocol/attempt.d.ts +2 -0
  167. package/build/src/protocol/bind.js +41 -14
  168. package/build/src/protocol/completion.d.ts +3 -0
  169. package/build/src/protocol/completion.js +35 -11
  170. package/build/src/protocol/deliver.js +165 -92
  171. package/build/src/protocol/execution-observation.d.ts +181 -0
  172. package/build/src/protocol/execution-observation.js +45 -0
  173. package/build/src/protocol/intent.d.ts +18 -22
  174. package/build/src/protocol/intent.js +63 -41
  175. package/build/src/protocol/operations.d.ts +2 -0
  176. package/build/src/protocol/placement.js +5 -1
  177. package/build/src/protocol/progress.d.ts +5 -1
  178. package/build/src/protocol/progress.js +10 -1
  179. package/build/src/protocol/read/status.d.ts +1 -0
  180. package/build/src/protocol/read/status.js +10 -2
  181. package/build/src/protocol/reintegrate.d.ts +1 -1
  182. package/build/src/protocol/reintegrate.js +70 -43
  183. package/build/src/protocol/result-codec.js +27 -11
  184. package/build/src/protocol/review.js +53 -30
  185. package/build/src/protocol/run.d.ts +83 -36
  186. package/build/src/protocol/run.js +90 -75
  187. package/build/src/runtime/proc/run.d.ts +16 -4
  188. package/build/src/runtime/proc/run.js +64 -22
  189. package/build/src/runtime/proc/windows-launch.exe +0 -0
  190. package/build/src/settings.js +5 -14
  191. package/build/src/task/document.d.ts +5 -1
  192. package/build/src/task/document.js +6 -1
  193. package/build/src/verification/execution.d.ts +10 -2
  194. package/build/src/verification/execution.js +143 -42
  195. package/build/src/verification/observation.d.ts +41 -0
  196. package/build/src/verification/observation.js +29 -0
  197. package/package.json +3 -1
@@ -1,5 +1,15 @@
1
1
  import { resolveActor } from "../actor.js";
2
2
  import { CliUsageError } from "../usage.js";
3
+ async function consumeExecution(execution, edge) {
4
+ const observation = edge.progress === undefined ? undefined : Promise.resolve().then(() => edge.progress(execution.progress));
5
+ void observation?.catch(() => undefined);
6
+ try {
7
+ return await execution.result;
8
+ }
9
+ finally {
10
+ await observation?.catch(() => undefined);
11
+ }
12
+ }
3
13
  function actorFromEdge(actor, environment) {
4
14
  try {
5
15
  return resolveActor({ env: environment, ...(actor === undefined ? {} : { actor }) });
@@ -141,15 +151,16 @@ async function existingSeat(input, parsed, library) {
141
151
  const actor = "actor" in parsed ? actorFromEdge(parsed.actor, edge.environment) : undefined;
142
152
  return { contract, id, ...(actor === undefined ? {} : { actor }), ...(hooks === undefined ? {} : { hooks }) };
143
153
  }
144
- async function invokeDeliver(parsed, seat) {
154
+ async function invokeDeliver(parsed, seat, edge) {
145
155
  const { acceptedDeliver } = await import("../accepted.js");
146
156
  try {
147
- const delivered = await seat.contract.deliver({
157
+ const delivered = await consumeExecution(seat.contract.startDelivery({
148
158
  ...(parsed.message === undefined ? {} : { message: parsed.message }),
149
159
  includeDirty: parsed.includeDirty,
150
160
  materializeConflict: parsed.materializeConflict,
151
161
  overwrite: parsed.overwrite,
152
- });
162
+ ...(edge.signal === undefined ? {} : { signal: edge.signal }),
163
+ }), edge);
153
164
  if (!("facts" in delivered))
154
165
  return delivered;
155
166
  return acceptedDeliver(delivered, seat.id);
@@ -166,13 +177,15 @@ async function invokeDeliver(parsed, seat) {
166
177
  throw error;
167
178
  }
168
179
  }
169
- async function invokeReview(parsed, seat, readStdin) {
170
- const summary = parsed.summaryFromStdin === true ? await readStdin() : parsed.summary;
180
+ async function invokeReview(parsed, seat, edge) {
181
+ const summary = parsed.summaryFromStdin === true ? await edge.readStdin() : parsed.summary;
171
182
  const { acceptedReview, resultFromMutationCall } = await import("../accepted.js");
172
- return resultFromMutationCall("review", () => seat.contract.review({
183
+ const input = {
173
184
  verdict: parsed.verdict,
174
185
  ...(summary === undefined ? {} : { summary }),
175
- }), (result) => acceptedReview(result, seat.id), { coordinate: seat.id });
186
+ ...(edge.signal === undefined ? {} : { signal: edge.signal }),
187
+ };
188
+ return resultFromMutationCall("review", () => consumeExecution(seat.contract.startReview(input), edge), (result) => acceptedReview(result, seat.id), { coordinate: seat.id });
176
189
  }
177
190
  async function contractLibrary(input, parsed) {
178
191
  const { configuration, edge, hooks } = input;
@@ -214,9 +227,9 @@ export async function invokeContractMutation(input) {
214
227
  }), (result) => acceptedAmend(result, id), { coordinate: id });
215
228
  }
216
229
  case "deliver":
217
- return invokeDeliver(parsed, seat);
230
+ return invokeDeliver(parsed, seat, edge);
218
231
  case "review":
219
- return invokeReview(parsed, seat, edge.readStdin);
232
+ return invokeReview(parsed, seat, edge);
220
233
  case "arc": {
221
234
  const markdown = await edge.readStdin();
222
235
  const { acceptedArc, resultFromMutationCall } = await import("../accepted.js");
@@ -236,10 +249,11 @@ export async function invokeContractMutation(input) {
236
249
  }
237
250
  case "audit": {
238
251
  const { acceptedAudit, resultFromMutationCall } = await import("../accepted.js");
239
- return resultFromMutationCall("audit", () => contract.audit({
252
+ return resultFromMutationCall("audit", () => consumeExecution(contract.startAudit({
240
253
  includeDirty: parsed.includeDirty,
241
254
  showDiff: parsed.showDiff,
242
- }), (result) => acceptedAudit(result, id), { coordinate: id });
255
+ ...(edge.signal === undefined ? {} : { signal: edge.signal }),
256
+ }), edge), (result) => acceptedAudit(result, id), { coordinate: id });
243
257
  }
244
258
  }
245
259
  }
@@ -20,13 +20,15 @@ function optionalFlag(flags, name) {
20
20
  function refuse(command, message) {
21
21
  throw new CliUsageError(message, commandGuide(command, CONTRACT_COMMAND_SPECS[command].usage));
22
22
  }
23
- function parseGateBundleNames(parts, command) {
23
+ function parseGateNames(parts, command) {
24
24
  const value = optionalFlag(parts.flags, "gates");
25
25
  if (value === undefined)
26
26
  return undefined;
27
+ if (value === "")
28
+ return [];
27
29
  const names = value.split(",");
28
30
  if (names.some((name) => name.length === 0)) {
29
- refuse(command, "--gates requires nonempty comma-separated names");
31
+ refuse(command, '--gates requires comma-separated names or "" to clear gates');
30
32
  }
31
33
  return names;
32
34
  }
@@ -53,7 +55,7 @@ function parseBind(parts) {
53
55
  const task = optionalFlag(parts.flags, "task");
54
56
  const target = optionalFlag(parts.flags, "target");
55
57
  const after = parts.flags.after === undefined ? [] : Array.isArray(parts.flags.after) ? parts.flags.after : [parts.flags.after];
56
- const gates = parseGateBundleNames(parts, "bind");
58
+ const gates = parseGateNames(parts, "bind");
57
59
  return {
58
60
  command: "bind",
59
61
  ...(task === undefined ? {} : { task }),
@@ -69,7 +71,7 @@ function parseAmend(parts) {
69
71
  const after = parts.flags.after === undefined ? [] : Array.isArray(parts.flags.after) ? parts.flags.after : [parts.flags.after];
70
72
  if (parts.flags["clear-after"] === true && after.length > 0)
71
73
  refuse("amend", "--clear-after and --after are mutually exclusive");
72
- const gates = parseGateBundleNames(parts, "amend");
74
+ const gates = parseGateNames(parts, "amend");
73
75
  if (!parts.stdin && parts.flags.after === undefined && parts.flags["clear-after"] !== true && gates === undefined) {
74
76
  refuse("amend", "amend requires stdin or --after, --clear-after, or --gates");
75
77
  }
@@ -16,7 +16,7 @@ export type InstallInvocationResult = Readonly<{
16
16
  results: readonly HarnessInstallResult[];
17
17
  }>;
18
18
  export type InstallRunner = (input: ProcessInput) => Promise<ProcessOutcome>;
19
- export declare const INSTALL_USAGE = "install <codex|claude|opencode|pi> [--json]\ninstall --all [--json]";
19
+ export declare const INSTALL_USAGE = "install <codex|claude|opencode|pi>\ninstall --all";
20
20
  export declare const INSTALL_ROOT_PURPOSE = "Install Keiyaku into coding harnesses";
21
21
  export declare function parseInstallCommand(argv: readonly string[]): ParsedInstallCommand;
22
22
  export declare function renderInstallHelp(): string;
@@ -2,7 +2,7 @@ import { dirname, resolve } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { CliUsageError, commandGuide, usageLine } from "../usage.js";
4
4
  export const HARNESS_NAMES = ["codex", "claude", "opencode", "pi"];
5
- export const INSTALL_USAGE = "install <codex|claude|opencode|pi> [--json]\ninstall --all [--json]";
5
+ export const INSTALL_USAGE = "install <codex|claude|opencode|pi>\ninstall --all";
6
6
  export const INSTALL_ROOT_PURPOSE = "Install Keiyaku into coding harnesses";
7
7
  function isHarness(value) {
8
8
  return value !== undefined && HARNESS_NAMES.includes(value);
@@ -13,33 +13,25 @@ async function statusSetAkuma(selector, id, alias, world, repo) {
13
13
  ...(result.alias === undefined ? {} : { alias: result.alias }),
14
14
  };
15
15
  }
16
- async function statusSetEntry(selector, world, repo, observed) {
17
- let resolved = selector;
18
- let alias;
16
+ async function statusSetContract(selector, id, world, repo) {
17
+ if (repo === undefined)
18
+ throw new CliUsageError("cannot select a contract while the Contract world is absent");
19
+ const observation = await (await import("../../kanshi/index.js")).observeKanshi({ world, repo, contract: canonicalContractSelector(id) });
20
+ return { selector, kind: "contract", report: observation.report };
21
+ }
22
+ async function statusSetEntry(selector, world, repo, named) {
19
23
  if (selector.startsWith("@")) {
20
- if (observed === undefined)
24
+ if (named === undefined)
21
25
  throw new CliUsageError("cannot resolve a named status selector");
22
26
  const { resolveNamedAddress } = await import("../../library/address.js");
23
- const address = resolveNamedAddress({ selector, report: observed.report, aliases: observed.aliases });
27
+ const address = resolveNamedAddress({ selector, report: named.report, aliases: named.aliases });
24
28
  if (address.kind === "akuma")
25
29
  return await statusSetAkuma(selector, address.id, selector, world, repo);
26
- resolved = address.id;
27
- alias = selector;
30
+ return await statusSetContract(selector, address.id, world, repo);
28
31
  }
29
- if (resolved.startsWith("aku/"))
30
- return await statusSetAkuma(selector, resolved, alias, world, repo);
31
- if (repo === undefined)
32
- throw new CliUsageError("cannot select a contract while the Contract world is absent");
33
- if (observed === undefined)
34
- throw new CliUsageError("cannot observe Contract status without a Kanshi report");
35
- return {
36
- selector,
37
- kind: "contract",
38
- report: (await import("../../kanshi/index.js")).selectKanshi({
39
- report: observed.report,
40
- contract: canonicalContractSelector(resolved),
41
- }),
42
- };
32
+ if (selector.startsWith("aku/"))
33
+ return await statusSetAkuma(selector, selector, undefined, world, repo);
34
+ return await statusSetContract(selector, selector, world, repo);
43
35
  }
44
36
  export async function invokeStatusSet(selectors, world, repo) {
45
37
  if (selectors.length < 2)
@@ -47,8 +39,7 @@ export async function invokeStatusSet(selectors, world, repo) {
47
39
  if (selectors.some((selector) => !selector.startsWith("aku/") && !selector.startsWith("@")) && repo === undefined) {
48
40
  throw new CliUsageError("cannot select a contract while the Contract world is absent");
49
41
  }
50
- const needsKanshi = selectors.some((selector) => !selector.startsWith("aku/"));
51
- const observed = needsKanshi
42
+ const named = selectors.some((selector) => selector.startsWith("@"))
52
43
  ? await (await import("../../kanshi/index.js")).observeKanshi({
53
44
  world,
54
45
  ...(repo === undefined ? {} : { repo }),
@@ -56,6 +47,6 @@ export async function invokeStatusSet(selectors, world, repo) {
56
47
  : undefined;
57
48
  const entries = [];
58
49
  for (const selector of selectors)
59
- entries.push(await statusSetEntry(selector, world, repo, observed));
50
+ entries.push(await statusSetEntry(selector, world, repo, named));
60
51
  return { kind: "status-set", entries };
61
52
  }
@@ -19,39 +19,39 @@ const TASK_COMMAND_SPECS = {
19
19
  note: "value",
20
20
  actor: "value",
21
21
  },
22
- usage: `task add <TITLE> [--namespace <ns>] [--priority 0..3] [--json]
22
+ usage: `task add <TITLE> [--namespace <ns>] [--priority 0..3]
23
23
  [--state open|in_progress|on_hold|done|drop]
24
24
  [--note <text>] [--actor <actor>]
25
25
  [--needs <TaskId>]... [--parent <TaskId>]
26
26
  [--supersedes <TaskId>]... [--relates <TaskId>]...
27
27
  [--body <text>]
28
- task add [--namespace <ns>] [--actor <actor>] [--json] -`,
28
+ task add [--namespace <ns>] [--actor <actor>] -`,
29
29
  purpose: "Create one Task from flags or a canonical stdin document.",
30
30
  },
31
31
  show: {
32
32
  arity: [1, Number.POSITIVE_INFINITY],
33
33
  flags: COMMON,
34
- usage: "task show <TaskId>... [--json]",
34
+ usage: "task show <TaskId>...",
35
35
  purpose: "Read one or more Tasks and their relationships.",
36
36
  },
37
37
  ls: {
38
38
  arity: [0, 1],
39
39
  flags: { ...COMMON, closed: "boolean", all: "boolean", world: "boolean", limit: "value" },
40
- usage: "task ls [<namespace-selector>] [--closed | --all] [--world] [--limit <n>] [--json]",
40
+ usage: "task ls [<namespace-selector>] [--closed | --all] [--world] [--limit <n>]",
41
41
  purpose: "List Tasks in the selected scope.",
42
42
  details: "--world lists every namespace in the current Task world.",
43
43
  },
44
44
  ready: {
45
45
  arity: [0, 0],
46
46
  flags: { ...COMMON, world: "boolean", parent: "value", limit: "value" },
47
- usage: "task ready [--world] [--parent <TaskId>] [--limit <n>] [--json]",
47
+ usage: "task ready [--world] [--parent <TaskId>] [--limit <n>]",
48
48
  purpose: "List open Tasks whose every need is terminal.",
49
49
  details: "--world lists every namespace in the current Task world.",
50
50
  },
51
51
  blocked: {
52
52
  arity: [0, 0],
53
53
  flags: { ...COMMON, world: "boolean", parent: "value", limit: "value" },
54
- usage: "task blocked [--world] [--parent <TaskId>] [--limit <n>] [--json]",
54
+ usage: "task blocked [--world] [--parent <TaskId>] [--limit <n>]",
55
55
  purpose: "List Tasks blocked by dependencies.",
56
56
  details: "--world lists every namespace in the current Task world.",
57
57
  },
@@ -59,7 +59,7 @@ task add [--namespace <ns>] [--actor <actor>] [--json] -`,
59
59
  arity: [0, 0],
60
60
  flags: { ...COMMON, where: "value", world: "boolean", sort: "value", limit: "value" },
61
61
  usage: `task query [--where <expression>] [--world]
62
- [--sort priority|created|updated|id] [--limit <n>] [--json]`,
62
+ [--sort priority|created|updated|id] [--limit <n>]`,
63
63
  purpose: "Query Task facts with a typed boolean expression.",
64
64
  details: [
65
65
  "fields: state priority title id parent under needs blocks ready blocked created updated",
@@ -73,13 +73,13 @@ task add [--namespace <ns>] [--actor <actor>] [--json] -`,
73
73
  tree: {
74
74
  arity: [1, 1],
75
75
  flags: COMMON,
76
- usage: "task tree <TaskId> [--json]",
76
+ usage: "task tree <TaskId>",
77
77
  purpose: "Read one Task parent decomposition tree.",
78
78
  },
79
79
  doctor: {
80
80
  arity: [0, 0],
81
81
  flags: COMMON,
82
- usage: "task doctor [--json]",
82
+ usage: "task doctor",
83
83
  purpose: "Inspect Task authority without repairing it.",
84
84
  },
85
85
  update: {
@@ -105,56 +105,56 @@ task add [--namespace <ns>] [--actor <actor>] [--json] -`,
105
105
  [--priority 0..3] [--needs <TaskId>]... [--drop-needs <TaskId>]...
106
106
  [--parent <TaskId> | --no-parent]
107
107
  [--supersedes <TaskId>]... [--drop-supersedes <TaskId>]...
108
- [--relates <TaskId>]... [--drop-relates <TaskId>]... [--json]`,
108
+ [--relates <TaskId>]... [--drop-relates <TaskId>]...`,
109
109
  purpose: "Apply one or more patches to a Task.",
110
110
  },
111
111
  start: {
112
112
  arity: [1, Number.POSITIVE_INFINITY],
113
113
  flags: COMMON,
114
- usage: "task start <TaskId>... [--json]",
114
+ usage: "task start <TaskId>...",
115
115
  purpose: "Move one or more open Tasks into progress.",
116
116
  },
117
117
  stop: {
118
118
  arity: [1, 1],
119
119
  flags: COMMON,
120
- usage: "task stop <TaskId> [--json]",
120
+ usage: "task stop <TaskId>",
121
121
  purpose: "Return one in-progress Task to open.",
122
122
  },
123
123
  hold: {
124
124
  arity: [1, Number.POSITIVE_INFINITY],
125
125
  flags: COMMON,
126
- usage: "task hold <TaskId>... [--json]",
126
+ usage: "task hold <TaskId>...",
127
127
  purpose: "Put one or more Tasks on hold.",
128
128
  },
129
129
  resume: {
130
130
  arity: [1, 1],
131
131
  flags: COMMON,
132
- usage: "task resume <TaskId> [--json]",
132
+ usage: "task resume <TaskId>",
133
133
  purpose: "Return one held Task to open.",
134
134
  },
135
135
  done: {
136
136
  arity: [1, Number.POSITIVE_INFINITY],
137
137
  flags: { ...COMMON, note: "value" },
138
- usage: "task done <TaskId>... [--note <text>] [--json]",
138
+ usage: "task done <TaskId>... [--note <text>]",
139
139
  purpose: "Mark one or more Tasks done.",
140
140
  },
141
141
  drop: {
142
142
  arity: [1, Number.POSITIVE_INFINITY],
143
143
  flags: { ...COMMON, note: "value" },
144
- usage: "task drop <TaskId>... [--note <text>] [--json]",
144
+ usage: "task drop <TaskId>... [--note <text>]",
145
145
  purpose: "Drop one or more Tasks.",
146
146
  },
147
147
  context: {
148
148
  arity: [0, 1],
149
149
  flags: COMMON,
150
- usage: "task context [<namespace>] [--json]",
150
+ usage: "task context [<namespace>]",
151
151
  purpose: "Read or replace the directory Task context.",
152
152
  },
153
153
  compose: {
154
154
  arity: [0, 0],
155
155
  stdin: "compose",
156
156
  flags: { ...COMMON, actor: "value", plan: "boolean" },
157
- usage: "task compose [--actor <actor>] [--plan] [--json] -",
157
+ usage: "task compose [--actor <actor>] [--plan] -",
158
158
  purpose: "Plan or admit one explicit Task composition.",
159
159
  details: [
160
160
  "nodes: + <Title> creates; @task/<id> modifies a pre-existing Task",
@@ -1,3 +1,4 @@
1
+ import type { ExecutionEvent } from "../library/execution.js";
1
2
  import type { AkumaInvocationResult } from "./commands/akuma-invoke.js";
2
3
  import type { InstallInvocationResult } from "./commands/install.js";
3
4
  import type { TaskInvocationResult } from "./commands/task-invoke.js";
@@ -11,6 +12,8 @@ type InvokeRuntime = Readonly<{
11
12
  environment?: NodeJS.ProcessEnv;
12
13
  readStdin?: () => Promise<string>;
13
14
  actor?: ActorId;
15
+ signal?: AbortSignal;
16
+ progress?: (events: AsyncIterable<ExecutionEvent>) => Promise<void>;
14
17
  }>;
15
18
  export type SettingsInvocationResult = Readonly<{
16
19
  kind: "settings";
@@ -1,9 +1,11 @@
1
+ import { resolve } from "node:path";
1
2
  import { resolveActor } from "./actor.js";
2
3
  import { isParsedAkumaCommand } from "./commands/akuma.js";
3
4
  import { invokeContractMutation } from "./commands/contract-invoke.js";
4
5
  import { assertExplicitRepoUse, CliUsageError, usageGuideForCommand, } from "./parse.js";
5
6
  import { isBlankInput } from "./usage.js";
6
7
  import { AKUMA_REQUESTS_ENV } from "../akuma/provider.js";
8
+ import { canonicalBirthCwd } from "../akuma/call-input.js";
7
9
  import { bodyRequestExecution, executionChannel, localExecutionContext, } from "../akuma/requests.js";
8
10
  async function readStdin() {
9
11
  const chunks = [];
@@ -118,7 +120,7 @@ async function selectContract(repo, selector, scope) {
118
120
  }
119
121
  async function invokeAkumaFromEdge(parsed, input) {
120
122
  try {
121
- const { statedCwd, repo, edge } = input;
123
+ const { executionCwd, repo, edge } = input;
122
124
  const path = await akumaWorldFor(parsed, input.located, input.candidate, input.establish);
123
125
  const home = parsed.command === "call" ? input.home : undefined;
124
126
  const configuration = parsed.command === "call" ? await settingsAt(path, home) : undefined;
@@ -131,7 +133,7 @@ async function invokeAkumaFromEdge(parsed, input) {
131
133
  : undefined;
132
134
  return await invokeAkuma(parsed, {
133
135
  path,
134
- ...(statedCwd === undefined ? {} : { statedCwd }),
136
+ ...(executionCwd === undefined ? {} : { executionCwd }),
135
137
  ...(home === undefined ? {} : { home }),
136
138
  ...(configuration === undefined ? {} : { settings: configuration }),
137
139
  ...(contract === undefined ? (repo === undefined ? {} : { repo }) : { contract }),
@@ -326,13 +328,56 @@ async function resolveInvocationCoordinates(invocation, runtime) {
326
328
  command: invocation.command,
327
329
  });
328
330
  }
331
+ async function invokeTask(parsed, runtime, edge, execution, coordinates) {
332
+ const actor = taskActor(parsed, runtime, edge.environment);
333
+ return await (await import("./commands/task-invoke.js")).invokeTaskFromEdge({
334
+ parsed,
335
+ world: coordinates.world,
336
+ candidate: coordinates.candidateWorld,
337
+ context: coordinates.taskContext,
338
+ establish: coordinates.establishWorld,
339
+ readStdin: edge.readStdin,
340
+ actor,
341
+ execution,
342
+ });
343
+ }
344
+ async function callExecutionCwd(input) {
345
+ if (input.command.command !== "call")
346
+ return undefined;
347
+ if (input.workdir !== undefined) {
348
+ return await canonicalBirthCwd(resolve(input.cwd, input.workdir), `workdir is not an existing directory: ${input.workdir}`);
349
+ }
350
+ return input.command.contract === undefined ? input.cwd : undefined;
351
+ }
352
+ async function invokeParsedAkuma(input) {
353
+ const executionCwd = await callExecutionCwd({
354
+ command: input.parsed,
355
+ cwd: input.cwd,
356
+ ...(input.workdir === undefined ? {} : { workdir: input.workdir }),
357
+ });
358
+ return await invokeAkumaFromEdge(input.parsed, {
359
+ located: input.world,
360
+ candidate: input.candidate,
361
+ establish: input.establish,
362
+ ...(executionCwd === undefined ? {} : { executionCwd }),
363
+ ...(input.repo === undefined ? {} : { repo: input.repo }),
364
+ ...(input.home === undefined ? {} : { home: input.home }),
365
+ edge: input.edge,
366
+ execution: input.execution,
367
+ });
368
+ }
329
369
  // eslint-disable-next-line complexity -- command dispatch keeps the CLI's existing boundary in one place.
330
370
  async function invokeParsed(invocation, runtime) {
331
371
  const environment = runtime.environment ?? process.env;
332
372
  const execution = executionForEnvironment(environment);
333
373
  const coordinates = await resolveInvocationCoordinates(invocation, runtime);
334
- const { cwd, cwdSource, repo, world, candidateWorld, establishWorld, taskContext } = coordinates;
335
- const edge = { environment, readStdin: runtime.readStdin ?? readStdin };
374
+ const { cwd, repo, world, candidateWorld, establishWorld } = coordinates;
375
+ const edge = {
376
+ environment,
377
+ readStdin: runtime.readStdin ?? readStdin,
378
+ ...(runtime.signal === undefined ? {} : { signal: runtime.signal }),
379
+ ...(runtime.progress === undefined ? {} : { progress: runtime.progress }),
380
+ };
336
381
  const parsed = invocation.command;
337
382
  const mapped = edge.environment.KEIYAKU_HOME?.trim();
338
383
  const home = mapped === undefined || mapped.length === 0 ? undefined : mapped;
@@ -340,34 +385,23 @@ async function invokeParsed(invocation, runtime) {
340
385
  return { kind: "settings", value: await settingsAt(world ?? undefined, home) };
341
386
  if (parsed.command === "nuke")
342
387
  return await (await import("./commands/nuke.js")).invokeNuke(parsed, world);
343
- if (parsed.command === "task") {
344
- const actor = taskActor(parsed, runtime, edge.environment);
345
- return await (await import("./commands/task-invoke.js")).invokeTaskFromEdge({
346
- parsed,
347
- world,
348
- candidate: candidateWorld,
349
- context: taskContext,
350
- establish: coordinates.establishWorld,
351
- readStdin: edge.readStdin,
352
- actor,
353
- execution,
354
- });
355
- }
388
+ if (parsed.command === "task")
389
+ return await invokeTask(parsed, runtime, edge, execution, coordinates);
356
390
  if (parsed.command === "history" && "contract" in parsed)
357
391
  return await invokeContractHistory(repo, parsed.contract);
358
- if (isParsedAkumaCommand(parsed)) {
359
- return await invokeAkumaFromEdge(parsed, {
360
- located: world,
392
+ if (isParsedAkumaCommand(parsed))
393
+ return await invokeParsedAkuma({
394
+ parsed,
395
+ cwd,
396
+ ...(invocation.workdir === undefined ? {} : { workdir: invocation.workdir }),
397
+ world,
361
398
  candidate: candidateWorld,
362
399
  establish: coordinates.establishWorld,
363
- invocationCwd: cwd,
364
- ...(cwdSource === "input" ? { statedCwd: cwd } : {}),
365
400
  ...(repo === undefined ? {} : { repo }),
366
401
  ...(home === undefined ? {} : { home }),
367
402
  edge,
368
403
  execution,
369
404
  });
370
- }
371
405
  if (parsed.command === "ls")
372
406
  return await invokeCatalog(parsed, world, repo, home);
373
407
  if (parsed.command === "status")
@@ -434,6 +468,7 @@ export async function invoke(invocation, runtime = {}) {
434
468
  return await invokeParsed({
435
469
  ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
436
470
  ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
471
+ ...(invocation.workdir === undefined ? {} : { workdir: invocation.workdir }),
437
472
  command,
438
473
  }, acquiredRuntime);
439
474
  }
@@ -1,4 +1,5 @@
1
1
  import { CliUsageError, parseArgv, renderHelp } from "./parse.js";
2
+ import { installedPackageVersion } from "./version.js";
2
3
  export async function main(argv = process.argv.slice(2)) {
3
4
  try {
4
5
  const parsed = parseArgv(argv);
@@ -8,6 +9,10 @@ export async function main(argv = process.argv.slice(2)) {
8
9
  process.stdout.write(help.endsWith("\n") ? help : `${help}\n`);
9
10
  return 0;
10
11
  }
12
+ if ("version" in parsed) {
13
+ process.stdout.write(`${await installedPackageVersion()}\n`);
14
+ return 0;
15
+ }
11
16
  return await (await import("./runtime.js")).runCliCommand(parsed);
12
17
  }
13
18
  catch (error) {
@@ -29,10 +29,13 @@ export type CliHelpCoordinate = Readonly<{
29
29
  export type ParsedExecution = Readonly<{
30
30
  cwd?: string;
31
31
  repo?: string;
32
+ workdir?: string;
32
33
  command: ParsedCommand;
33
34
  }>;
34
35
  export type ParsedInvocation = ParsedExecution | Readonly<{
35
36
  help: CliHelpCoordinate;
37
+ }> | Readonly<{
38
+ version: true;
36
39
  }>;
37
40
  type RepoUse = "none" | "optional" | "required";
38
41
  export type CommandRepoPolicy = Readonly<{
@@ -10,6 +10,7 @@ const ROOT_USAGE = [
10
10
  "usage keiyaku <command> [options]",
11
11
  " keiyaku <command> --help shows that command's complete usage",
12
12
  ].join("\n");
13
+ const INVOCATION_PATH_OPTIONS = new Set(["-C", "--cwd", "--repo", "--workdir"]);
13
14
  export function renderRootHelp(columns) {
14
15
  return renderHelpText([
15
16
  "keiyaku — Contract, Task, and Akuma control for one repository",
@@ -31,9 +32,13 @@ export function renderRootHelp(columns) {
31
32
  ` install ${INSTALL_ROOT_PURPOSE}`,
32
33
  ` settings ${CONTRACT_COMMAND_SPECS.settings.purpose}`,
33
34
  "",
35
+ "Package",
36
+ " --version Print the running package version.",
37
+ "",
34
38
  "Global options:",
35
39
  " -C, --cwd <path> Set the invocation working directory.",
36
40
  " --repo <path> Select the Git repository coordinate.",
41
+ " --workdir <path> Set the execution directory for call only.",
37
42
  ].join("\n"), columns);
38
43
  }
39
44
  function renderHelpText(help, columns) {
@@ -219,25 +224,31 @@ function scanArgv(argv) {
219
224
  function invocationOptions(argv) {
220
225
  let cwd;
221
226
  let repo;
227
+ let workdir;
222
228
  const commandArgv = [];
223
229
  for (let index = 0; index < argv.length; index += 1) {
224
230
  const token = argv[index];
225
- if (token !== "-C" && token !== "--cwd" && token !== "--repo") {
231
+ if (!INVOCATION_PATH_OPTIONS.has(token)) {
226
232
  commandArgv.push(token);
227
233
  continue;
228
234
  }
229
235
  if (token === "--repo" && repo !== undefined) {
230
236
  throw new CliUsageError("--repo may appear only once", ROOT_USAGE_GUIDE);
231
237
  }
232
- if (token !== "--repo" && cwd !== undefined) {
238
+ if ((token === "-C" || token === "--cwd") && cwd !== undefined) {
233
239
  throw new CliUsageError("-C/--cwd may appear only once", ROOT_USAGE_GUIDE);
234
240
  }
241
+ if (token === "--workdir" && workdir !== undefined) {
242
+ throw new CliUsageError("--workdir may appear only once", ROOT_USAGE_GUIDE);
243
+ }
235
244
  const value = argv[index + 1];
236
- if (value === undefined || value === "-" || value.startsWith("-") || isBlankInput(value)) {
245
+ if (invalidInvocationPath(value)) {
237
246
  throw new CliUsageError(`${token} requires a path`, ROOT_USAGE_GUIDE);
238
247
  }
239
248
  if (token === "--repo")
240
249
  repo = value;
250
+ else if (token === "--workdir")
251
+ workdir = value;
241
252
  else
242
253
  cwd = value;
243
254
  index += 1;
@@ -245,9 +256,13 @@ function invocationOptions(argv) {
245
256
  return {
246
257
  ...(cwd === undefined ? {} : { cwd }),
247
258
  ...(repo === undefined ? {} : { repo }),
259
+ ...(workdir === undefined ? {} : { workdir }),
248
260
  commandArgv,
249
261
  };
250
262
  }
263
+ function invalidInvocationPath(value) {
264
+ return value === undefined || value === "-" || value.startsWith("-") || isBlankInput(value);
265
+ }
251
266
  function helpCoordinate(argv) {
252
267
  const help = argv.indexOf("--help");
253
268
  if (help < 0)
@@ -272,6 +287,8 @@ export function parseArgv(argv) {
272
287
  const help = helpCoordinate(invocation.commandArgv);
273
288
  if (help !== null)
274
289
  return { help };
290
+ if (invocation.commandArgv.length === 1 && invocation.commandArgv[0] === "--version")
291
+ return { version: true };
275
292
  if (invocation.commandArgv[0] === "ls" &&
276
293
  (invocation.commandArgv.length === 1 ||
277
294
  (invocation.commandArgv.length === 2 && invocation.commandArgv[1] === "--json"))) {
@@ -280,9 +297,14 @@ export function parseArgv(argv) {
280
297
  const task = invocation.commandArgv[0] === "task" ? parseTaskCommand(invocation.commandArgv.slice(1)) : undefined;
281
298
  const install = invocation.commandArgv[0] === "install" ? parseInstallCommand(invocation.commandArgv.slice(1)) : undefined;
282
299
  const akuma = isAkumaAction(invocation.commandArgv[0]) ? parseAkumaCommand(invocation.commandArgv) : undefined;
300
+ const command = task ?? akuma ?? install ?? parseContractCommand(scanArgv(invocation.commandArgv));
301
+ if (invocation.workdir !== undefined && command.command !== "call") {
302
+ throw new CliUsageError(`option --workdir is not valid for ${command.command}`, usageGuideForCommand(command));
303
+ }
283
304
  return {
284
305
  ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
285
306
  ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
286
- command: task ?? akuma ?? install ?? parseContractCommand(scanArgv(invocation.commandArgv)),
307
+ ...(invocation.workdir === undefined ? {} : { workdir: invocation.workdir }),
308
+ command,
287
309
  };
288
310
  }