@astrosheep/keiyaku 4.0.4 → 4.0.5

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 (173) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +14 -0
  6. package/build/src/akuma/akuma.d.ts +0 -1
  7. package/build/src/akuma/akuma.js +1 -6
  8. package/build/src/akuma/allowed.d.ts +1 -1
  9. package/build/src/akuma/allowed.js +3 -10
  10. package/build/src/akuma/body.d.ts +5 -2
  11. package/build/src/akuma/body.js +29 -23
  12. package/build/src/akuma/heart/facts.d.ts +35 -9
  13. package/build/src/akuma/heart/index.d.ts +4 -13
  14. package/build/src/akuma/heart/index.js +2 -133
  15. package/build/src/akuma/heart/request-authority.d.ts +12 -0
  16. package/build/src/akuma/heart/request-authority.js +119 -0
  17. package/build/src/akuma/heart/request-rows.d.ts +16 -0
  18. package/build/src/akuma/heart/request-rows.js +88 -0
  19. package/build/src/akuma/heart/rows.d.ts +1 -28
  20. package/build/src/akuma/heart/rows.js +0 -137
  21. package/build/src/akuma/heart/schema.d.ts +1 -1
  22. package/build/src/akuma/heart/schema.js +9 -2
  23. package/build/src/akuma/heart/soul.js +0 -36
  24. package/build/src/akuma/index.d.ts +1 -1
  25. package/build/src/akuma/provider-recipe.js +1 -11
  26. package/build/src/akuma/provider.d.ts +1 -6
  27. package/build/src/akuma/providers/acp/core.js +6 -2
  28. package/build/src/akuma/providers/acp/index.js +0 -1
  29. package/build/src/akuma/providers/claude/index.d.ts +0 -4
  30. package/build/src/akuma/providers/claude/index.js +7 -3
  31. package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
  32. package/build/src/akuma/providers/codex-app-server/index.js +0 -2
  33. package/build/src/akuma/providers/grok-build/index.js +0 -1
  34. package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
  35. package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
  36. package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
  37. package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
  38. package/build/src/akuma/providers/pi/index.js +11 -4
  39. package/build/src/akuma/request-serve.d.ts +87 -0
  40. package/build/src/akuma/request-serve.js +484 -0
  41. package/build/src/akuma/request-wire.d.ts +124 -0
  42. package/build/src/akuma/request-wire.js +380 -0
  43. package/build/src/akuma/requests.d.ts +28 -94
  44. package/build/src/akuma/requests.js +52 -562
  45. package/build/src/akuma-body.d.ts +7 -0
  46. package/build/src/akuma-body.js +54 -1
  47. package/build/src/body/arc.js +4 -29
  48. package/build/src/body/decode.js +5 -39
  49. package/build/src/body/envelope.d.ts +7 -0
  50. package/build/src/body/envelope.js +37 -0
  51. package/build/src/cli/accepted.js +17 -9
  52. package/build/src/cli/commands/akuma.js +2 -2
  53. package/build/src/cli/commands/contract.d.ts +6 -6
  54. package/build/src/cli/commands/contract.js +3 -3
  55. package/build/src/cli/commands/task-invoke.js +98 -9
  56. package/build/src/cli/coordinates.d.ts +1 -0
  57. package/build/src/cli/coordinates.js +7 -4
  58. package/build/src/cli/invoke.js +28 -4
  59. package/build/src/cli/parse.d.ts +2 -2
  60. package/build/src/cli/parse.js +21 -8
  61. package/build/src/cli/render/akuma.js +56 -13
  62. package/build/src/cli/render/audit.js +2 -6
  63. package/build/src/cli/render/contract.js +40 -0
  64. package/build/src/cli/render/receipt.js +28 -1
  65. package/build/src/cli/render/terminal.d.ts +0 -2
  66. package/build/src/cli/render/terminal.js +0 -29
  67. package/build/src/cli/result.d.ts +15 -7
  68. package/build/src/contract-worktree.d.ts +1 -2
  69. package/build/src/contract-worktree.js +4 -9
  70. package/build/src/core/facts/codec.js +9 -0
  71. package/build/src/core/facts/fold.js +13 -1
  72. package/build/src/core/facts/types.d.ts +8 -1
  73. package/build/src/core/subject.js +4 -3
  74. package/build/src/core/verbs/placement.d.ts +9 -1
  75. package/build/src/core/verbs/placement.js +24 -6
  76. package/build/src/core/verbs/reintegrate.d.ts +13 -0
  77. package/build/src/core/verbs/reintegrate.js +24 -0
  78. package/build/src/dispatch/index.d.ts +1 -1
  79. package/build/src/git/admission.d.ts +2 -1
  80. package/build/src/git/integration.d.ts +5 -2
  81. package/build/src/git/integration.js +54 -1
  82. package/build/src/git/observe.d.ts +2 -1
  83. package/build/src/git/observe.js +6 -5
  84. package/build/src/git/process.d.ts +26 -0
  85. package/build/src/git/process.js +94 -0
  86. package/build/src/git/read-observation.d.ts +2 -1
  87. package/build/src/git/read-observation.js +3 -2
  88. package/build/src/git/reconcile.d.ts +2 -1
  89. package/build/src/git/reconcile.js +9 -6
  90. package/build/src/git/repository.d.ts +2 -29
  91. package/build/src/git/repository.js +4 -118
  92. package/build/src/git/scratch.d.ts +1 -1
  93. package/build/src/git/scratch.js +1 -1
  94. package/build/src/git/target-placement.d.ts +1 -1
  95. package/build/src/git/target-placement.js +9 -7
  96. package/build/src/git/tender.d.ts +1 -1
  97. package/build/src/git/tender.js +2 -1
  98. package/build/src/git/terminal-seal.d.ts +2 -2
  99. package/build/src/git/terminal-seal.js +3 -6
  100. package/build/src/git/workspace.d.ts +1 -1
  101. package/build/src/git/workspace.js +1 -1
  102. package/build/src/index.d.ts +1 -2
  103. package/build/src/index.js +1 -1
  104. package/build/src/library/address.js +7 -1
  105. package/build/src/library/audit.d.ts +2 -1
  106. package/build/src/library/audit.js +1 -1
  107. package/build/src/library/bind.d.ts +2 -1
  108. package/build/src/library/bind.js +1 -1
  109. package/build/src/library/configuration.d.ts +1 -1
  110. package/build/src/library/configuration.js +52 -18
  111. package/build/src/library/contract.d.ts +55 -2
  112. package/build/src/library/contract.js +162 -23
  113. package/build/src/library/delivery.d.ts +1 -1
  114. package/build/src/library/fleet.d.ts +27 -7
  115. package/build/src/library/fleet.js +79 -39
  116. package/build/src/library/input.d.ts +0 -1
  117. package/build/src/library/input.js +0 -5
  118. package/build/src/library/keiyaku.d.ts +1 -1
  119. package/build/src/library/mutation.d.ts +2 -1
  120. package/build/src/library/reconcile.d.ts +2 -1
  121. package/build/src/library/reconcile.js +3 -1
  122. package/build/src/library/region.d.ts +1 -1
  123. package/build/src/library/repo.d.ts +3 -1
  124. package/build/src/library/repo.js +8 -4
  125. package/build/src/protocol/abandon.d.ts +9 -0
  126. package/build/src/protocol/abandon.js +15 -0
  127. package/build/src/protocol/amend.d.ts +16 -0
  128. package/build/src/protocol/amend.js +77 -0
  129. package/build/src/protocol/arc.d.ts +6 -0
  130. package/build/src/protocol/arc.js +12 -0
  131. package/build/src/protocol/attempt.d.ts +2 -1
  132. package/build/src/protocol/audit.d.ts +53 -0
  133. package/build/src/protocol/audit.js +140 -0
  134. package/build/src/protocol/bind.d.ts +1 -1
  135. package/build/src/protocol/completion.d.ts +32 -0
  136. package/build/src/protocol/completion.js +130 -0
  137. package/build/src/protocol/deliver.d.ts +37 -0
  138. package/build/src/protocol/deliver.js +160 -0
  139. package/build/src/protocol/intent.d.ts +1 -1
  140. package/build/src/protocol/intent.js +1 -1
  141. package/build/src/protocol/operations.d.ts +64 -200
  142. package/build/src/protocol/operations.js +21 -640
  143. package/build/src/protocol/placement.d.ts +4 -13
  144. package/build/src/protocol/placement.js +3 -15
  145. package/build/src/protocol/read/status.d.ts +1 -1
  146. package/build/src/protocol/read/status.js +3 -1
  147. package/build/src/protocol/reconcile.d.ts +35 -0
  148. package/build/src/protocol/reconcile.js +41 -0
  149. package/build/src/protocol/reintegrate.d.ts +31 -0
  150. package/build/src/protocol/reintegrate.js +74 -0
  151. package/build/src/protocol/review.d.ts +33 -0
  152. package/build/src/protocol/review.js +148 -0
  153. package/build/src/protocol/run.d.ts +1 -1
  154. package/build/src/settlement/fence.d.ts +1 -1
  155. package/build/src/settlement/holder.d.ts +1 -1
  156. package/build/src/settlement/settle.d.ts +1 -1
  157. package/build/src/task/board.d.ts +0 -1
  158. package/build/src/task/board.js +0 -3
  159. package/build/src/task/compose.d.ts +1 -1
  160. package/build/src/task/compose.js +11 -4
  161. package/build/src/task/index.d.ts +1 -1
  162. package/build/src/task/index.js +2 -127
  163. package/build/src/task/input.d.ts +18 -0
  164. package/build/src/task/input.js +189 -0
  165. package/build/src/task/mutation.d.ts +39 -0
  166. package/build/src/task/mutation.js +85 -0
  167. package/build/src/task/operations.d.ts +2 -0
  168. package/build/src/task/operations.js +35 -11
  169. package/build/src/task/store.js +0 -2
  170. package/build/src/verification/execution.d.ts +1 -1
  171. package/build/src/workspace-place.d.ts +1 -4
  172. package/build/src/workspace-place.js +0 -15
  173. package/package.json +3 -3
@@ -1,7 +1,12 @@
1
1
  import { runAkumaBody } from "./akuma/body.js";
2
2
  import { worldRootForAkumaPaths } from "./akuma/identity.js";
3
3
  import { executeKillAkuma, executeTellAkuma, executeWaitAkuma, } from "./library/fleet.js";
4
- function upstreamFor(launch) {
4
+ import { requireBranchesToBeUpToDateFrom, worktreeHooksFrom, } from "./library/configuration.js";
5
+ import { executeForwardedDeliver, executeForwardedReview, } from "./library/contract.js";
6
+ import { Repo } from "./library/repo.js";
7
+ import { settings } from "./settings.js";
8
+ import { executeTaskMutation } from "./task/mutation.js";
9
+ export function upstreamFor(launch, processConfiguration) {
5
10
  const path = worldRootForAkumaPaths(launch.paths);
6
11
  return {
7
12
  wait: async (input) => await executeWaitAkuma({
@@ -26,6 +31,54 @@ function upstreamFor(launch) {
26
31
  service: result.results.map(({ id, evidence }) => ({ id, evidence })),
27
32
  };
28
33
  },
34
+ deliver: async (input) => {
35
+ const [repo, configuration] = await Promise.all([
36
+ Repo.at({
37
+ path: input.repoRoot,
38
+ ...(processConfiguration.gitPath === undefined ? {} : { gitPath: processConfiguration.gitPath }),
39
+ }),
40
+ settings({
41
+ root: input.repoRoot,
42
+ ...(processConfiguration.home === undefined ? {} : { home: processConfiguration.home }),
43
+ }),
44
+ ]);
45
+ return await executeForwardedDeliver({
46
+ repo,
47
+ contractId: input.contractId,
48
+ requester: input.requester,
49
+ ...(input.message === undefined ? {} : { message: input.message }),
50
+ includeDirty: input.includeDirty,
51
+ requireBranchesToBeUpToDate: requireBranchesToBeUpToDateFrom({ settings: configuration }),
52
+ hooks: worktreeHooksFrom({ settings: configuration }),
53
+ signal: input.signal,
54
+ });
55
+ },
56
+ review: async (input) => {
57
+ const [repo, configuration] = await Promise.all([
58
+ Repo.at({
59
+ path: input.repoRoot,
60
+ ...(processConfiguration.gitPath === undefined ? {} : { gitPath: processConfiguration.gitPath }),
61
+ }),
62
+ settings({
63
+ root: input.repoRoot,
64
+ ...(processConfiguration.home === undefined ? {} : { home: processConfiguration.home }),
65
+ }),
66
+ ]);
67
+ return await executeForwardedReview({
68
+ repo,
69
+ contractId: input.contractId,
70
+ requester: input.requester,
71
+ verdict: input.verdict,
72
+ ...(input.summary === undefined ? {} : { summary: input.summary }),
73
+ hooks: worktreeHooksFrom({ settings: configuration }),
74
+ });
75
+ },
76
+ task: async (input) => await executeTaskMutation({
77
+ world: input.world,
78
+ request: input.request,
79
+ requester: input.requester,
80
+ signal: input.signal,
81
+ }),
29
82
  };
30
83
  }
31
84
  await runAkumaBody(upstreamFor);
@@ -1,15 +1,8 @@
1
- import { parseToAST } from "../markdown/parse.js";
2
- import { indexDocument, indexedHeadings, normalizeTitle, rawSlice, sectionContent, } from "../markdown/query.js";
1
+ import { sectionContent } from "../markdown/query.js";
2
+ import { decodeDocumentEnvelope } from "./envelope.js";
3
3
  function refusal(message) {
4
4
  throw new TypeError(message);
5
5
  }
6
- function nonblankRaw(document, node) {
7
- return rawSlice(document, node.span).trim().length > 0;
8
- }
9
- function topLevelSections(document) {
10
- return indexedHeadings(indexDocument(document), { level: 2 })
11
- .filter((node) => node.type === "section");
12
- }
13
6
  function requiredProse(document, sections, name) {
14
7
  const section = sections.get(name);
15
8
  if (section === undefined)
@@ -20,31 +13,13 @@ function requiredProse(document, sections, name) {
20
13
  return value;
21
14
  }
22
15
  export function decodeArcDocument(source) {
23
- const document = parseToAST(source);
24
- if (document.frontmatter !== undefined)
25
- refusal("arc document may not contain frontmatter");
26
- const titles = indexedHeadings(indexDocument(document), { level: 1 })
27
- .filter((node) => node.type === "section");
28
- if (titles.length !== 1)
29
- refusal("arc document requires exactly one H1 title");
30
- const title = titles[0];
16
+ const { document, title, sections } = decodeDocumentEnvelope(source, "arc");
31
17
  if (title.title.trim().length === 0)
32
18
  refusal("arc title must be nonblank");
33
- if (title.children.some((node) => nonblankRaw(document, node))) {
34
- refusal("arc title may not contain content before the first H2 section");
35
- }
36
- const stray = document.children.filter((node) => node.type !== "section" && nonblankRaw(document, node));
37
- if (stray.length > 0)
38
- refusal("arc document contains content outside an H1 or H2 section");
39
- const sections = new Map();
40
- for (const section of topLevelSections(document)) {
41
- const name = normalizeTitle(section.title);
42
- if (sections.has(name))
43
- refusal(`duplicate arc section '${section.title}'`);
19
+ for (const [name, section] of sections) {
44
20
  if (name !== "objective" && name !== "brief") {
45
21
  refusal(`arc document does not allow ## ${section.title}`);
46
22
  }
47
- sections.set(name, section);
48
23
  }
49
24
  return {
50
25
  title: title.title,
@@ -1,29 +1,12 @@
1
1
  import { mintDocumentKey, mintDocumentSegmentKey } from "./keys.js";
2
- import { parseToAST } from "../markdown/parse.js";
3
- import { directChildren, indexDocument, indexedHeadings, normalizeTitle, rawSlice, sectionContent, } from "../markdown/query.js";
2
+ import { decodeDocumentEnvelope } from "./envelope.js";
3
+ import { directChildren, normalizeTitle, rawSlice, sectionContent, } from "../markdown/query.js";
4
4
  import { CONTRACT_SECTIONS, RESERVED_SECTIONS } from "./shape.js";
5
5
  import { decodeRegion, RegionDocumentError } from "./region.js";
6
6
  import { decodeVerificationDeclarations, VerificationDocumentError } from "./verification.js";
7
7
  function refusal(message) {
8
8
  throw new TypeError(message);
9
9
  }
10
- function nonblankRaw(document, node) {
11
- return rawSlice(document, node.span).trim().length > 0;
12
- }
13
- function topLevelSections(document) {
14
- return indexedHeadings(indexDocument(document), { level: 2 })
15
- .filter((node) => node.type === "section");
16
- }
17
- function indexedSections(document) {
18
- const sections = new Map();
19
- for (const section of topLevelSections(document)) {
20
- const title = normalizeTitle(section.title);
21
- if (sections.has(title))
22
- refusal(`duplicate contract section '${section.title}'`);
23
- sections.set(title, section);
24
- }
25
- return sections;
26
- }
27
10
  function requireSections(sections) {
28
11
  for (const [name, spec] of Object.entries(CONTRACT_SECTIONS)) {
29
12
  if (spec.required && !sections.has(name))
@@ -72,14 +55,6 @@ function criteria(document, section) {
72
55
  return { title, body };
73
56
  });
74
57
  }
75
- function rejectUnownedBytes(document, title) {
76
- if (title.children.some((node) => nonblankRaw(document, node))) {
77
- refusal("contract title may not contain content before the first H2 section");
78
- }
79
- const stray = document.children.filter((node) => node.type !== "section" && nonblankRaw(document, node));
80
- if (stray.length > 0)
81
- refusal("contract document contains content outside an H1 or H2 section");
82
- }
83
58
  function verification(document, section) {
84
59
  try {
85
60
  return decodeVerificationDeclarations(document, section);
@@ -91,22 +66,14 @@ function verification(document, section) {
91
66
  }
92
67
  }
93
68
  export function decodeContractDocument(source) {
94
- let document;
69
+ let envelope;
95
70
  try {
96
- document = parseToAST(source);
71
+ envelope = decodeDocumentEnvelope(source, "contract");
97
72
  }
98
73
  catch (error) {
99
74
  refusal(error instanceof Error ? error.message : String(error));
100
75
  }
101
- if (document.frontmatter !== undefined)
102
- refusal("contract document may not contain frontmatter");
103
- const titles = indexedHeadings(indexDocument(document), { level: 1 })
104
- .filter((node) => node.type === "section");
105
- if (titles.length !== 1)
106
- refusal("contract document requires exactly one H1 title");
107
- const title = titles[0];
108
- rejectUnownedBytes(document, title);
109
- const sections = indexedSections(document);
76
+ const { document, title, sections, sectionNodes } = envelope;
110
77
  requireSections(sections);
111
78
  for (const name of RESERVED_SECTIONS) {
112
79
  if (sections.has(name))
@@ -131,7 +98,6 @@ export function decodeContractDocument(source) {
131
98
  verification: verificationSection === undefined ? [] : verification(document, verificationSection),
132
99
  extensions,
133
100
  };
134
- const sectionNodes = topLevelSections(document);
135
101
  const segments = sectionNodes.map((section) => mintDocumentSegmentKey(document.source, section.span));
136
102
  return {
137
103
  ...body,
@@ -0,0 +1,7 @@
1
+ import type { DocumentNode, SectionNode } from "../markdown/types.js";
2
+ export declare function decodeDocumentEnvelope(source: string, kind: "arc" | "contract"): Readonly<{
3
+ document: DocumentNode;
4
+ title: SectionNode;
5
+ sections: ReadonlyMap<string, SectionNode>;
6
+ sectionNodes: readonly SectionNode[];
7
+ }>;
@@ -0,0 +1,37 @@
1
+ import { parseToAST } from "../markdown/parse.js";
2
+ import { indexDocument, indexedHeadings, normalizeTitle, rawSlice } from "../markdown/query.js";
3
+ function refusal(message) {
4
+ throw new TypeError(message);
5
+ }
6
+ function nonblankRaw(document, node) {
7
+ return rawSlice(document, node.span).trim().length > 0;
8
+ }
9
+ function topLevelSections(document) {
10
+ return indexedHeadings(indexDocument(document), { level: 2 })
11
+ .filter((node) => node.type === "section");
12
+ }
13
+ export function decodeDocumentEnvelope(source, kind) {
14
+ const document = parseToAST(source);
15
+ if (document.frontmatter !== undefined)
16
+ refusal(`${kind} document may not contain frontmatter`);
17
+ const titles = indexedHeadings(indexDocument(document), { level: 1 })
18
+ .filter((node) => node.type === "section");
19
+ if (titles.length !== 1)
20
+ refusal(`${kind} document requires exactly one H1 title`);
21
+ const title = titles[0];
22
+ if (title.children.some((node) => nonblankRaw(document, node))) {
23
+ refusal(`${kind} title may not contain content before the first H2 section`);
24
+ }
25
+ if (document.children.some((node) => node.type !== "section" && nonblankRaw(document, node))) {
26
+ refusal(`${kind} document contains content outside an H1 or H2 section`);
27
+ }
28
+ const sectionNodes = topLevelSections(document);
29
+ const sections = new Map();
30
+ for (const section of sectionNodes) {
31
+ const name = normalizeTitle(section.title);
32
+ if (sections.has(name))
33
+ refusal(`duplicate ${kind} section '${section.title}'`);
34
+ sections.set(name, section);
35
+ }
36
+ return { document, title, sections, sectionNodes };
37
+ }
@@ -1,10 +1,11 @@
1
1
  import { KeiyakuRefused, KeiyakuRetry, } from "../index.js";
2
2
  function acceptedFacts(result) {
3
- return result.facts.map((fact) => ({
4
- contract: fact.contract,
5
- entry: fact.entry,
6
- kind: fact.kind,
7
- }));
3
+ return result.facts.map((fact) => fact.kind === "reintegrated"
4
+ ? { contract: fact.contract, entry: fact.entry, kind: fact.kind, data: fact.data }
5
+ : { contract: fact.contract, entry: fact.entry, kind: fact.kind });
6
+ }
7
+ function attestationFor(facts, gate) {
8
+ return facts.findLast((fact) => fact.kind === "attestation" && fact.data.gate === gate);
8
9
  }
9
10
  function acceptedEnvelope(result, coordinate) {
10
11
  const contract = coordinate ?? result.facts[0]?.contract;
@@ -45,7 +46,7 @@ export function acceptedAmend(result, coordinate) {
45
46
  }
46
47
  export function acceptedDeliver(result, coordinate) {
47
48
  const value = result.value;
48
- const attestation = result.facts.find((fact) => fact.kind === "attestation");
49
+ const attestation = attestationFor(result.facts, "verified");
49
50
  const verificationVerdict = attestation?.data.verdict ?? value.verificationReuse?.verdict;
50
51
  return {
51
52
  ...acceptedEnvelope(result, coordinate),
@@ -60,15 +61,22 @@ export function acceptedDeliver(result, coordinate) {
60
61
  }
61
62
  export function acceptedReview(result, coordinate) {
62
63
  const value = result.value;
63
- const attestation = result.facts.find((fact) => fact.kind === "attestation");
64
- if (attestation === undefined)
64
+ const reviewAttestation = attestationFor(result.facts, "reviewed");
65
+ if (reviewAttestation === undefined)
65
66
  throw new Error("accepted review is missing its attestation fact");
67
+ const verificationAttestation = attestationFor(result.facts, "verified");
68
+ const verificationVerdict = verificationAttestation?.data.verdict ?? value.verificationReuse?.verdict;
66
69
  return {
67
70
  ...acceptedEnvelope(result, coordinate),
68
71
  verb: "review",
69
- verdict: attestation.data.verdict,
72
+ verdict: reviewAttestation.data.verdict,
73
+ ...(verificationVerdict === undefined ? {} : { verificationVerdict }),
74
+ ...(value.verification === undefined ? {} : { verification: value.verification }),
75
+ ...(value.verificationReuse === undefined ? {} : { verificationReuse: value.verificationReuse }),
70
76
  ...(value.placement === undefined ? {} : { placement: value.placement }),
71
77
  ...(value.workspace === undefined ? {} : { workspace: value.workspace }),
78
+ ...(value.cleanup === undefined ? {} : { cleanup: value.cleanup }),
79
+ ...(value.leak === undefined ? {} : { leak: value.leak }),
72
80
  };
73
81
  }
74
82
  export function acceptedArc(result, coordinate) {
@@ -83,11 +83,11 @@ export function renderAkumaUsage(action) {
83
83
  return usageLine(AKUMA_COMMAND_SPECS[action].usage);
84
84
  }
85
85
  export function parseAkumaCatalogPath(value) {
86
- if (value === "aku/")
86
+ if (value === "aku" || value === "aku/")
87
87
  return { kind: "archetypes" };
88
88
  if (value === "aku/*/*")
89
89
  return { kind: "akuma" };
90
- const match = /^aku\/(.+)\/$/u.exec(value);
90
+ const match = /^aku\/([^/]+)\/?$/u.exec(value);
91
91
  if (match === null)
92
92
  return null;
93
93
  return { kind: "akuma", archetype: archetypeName(match[1]) };
@@ -1,4 +1,4 @@
1
- export type ContractFlagKind = "boolean" | "value" | "repeat-value";
1
+ export type ContractFlagKind = "boolean" | "value" | "raw-value" | "repeat-value";
2
2
  export type ContractCommandSpec = Readonly<{
3
3
  positional: "none" | "optional";
4
4
  stdin: "none" | "optional" | "required";
@@ -17,10 +17,10 @@ export declare const CONTRACT_COMMAND_SPECS: {
17
17
  readonly target: "value";
18
18
  readonly here: "boolean";
19
19
  readonly after: "repeat-value";
20
- readonly gates: "value";
20
+ readonly gates: "raw-value";
21
21
  readonly json: "boolean";
22
22
  };
23
- readonly usage: "bind [--task <task/...>] [--target <ref>] [--here] [--after <kei/...> ...] [--gates <name>] [--actor <actor>] [--json] -";
23
+ readonly usage: "bind [--task <task/...>] [--target <ref>] [--here] [--after <kei/...> ...] [--gates <name,...>] [--actor <actor>] [--json] -";
24
24
  readonly purpose: "Create one Contract from stdin Markdown.";
25
25
  };
26
26
  readonly amend: {
@@ -30,10 +30,10 @@ export declare const CONTRACT_COMMAND_SPECS: {
30
30
  readonly actor: "value";
31
31
  readonly after: "repeat-value";
32
32
  readonly "clear-after": "boolean";
33
- readonly gates: "value";
33
+ readonly gates: "raw-value";
34
34
  readonly json: "boolean";
35
35
  };
36
- readonly usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name>] [--actor <actor>] [--json] [-]";
36
+ readonly usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name,...>] [--actor <actor>] [--json] [-]";
37
37
  readonly purpose: "Amend one Contract's document operations or structured terms.";
38
38
  };
39
39
  readonly deliver: {
@@ -106,7 +106,7 @@ export declare const CONTRACT_COMMAND_SPECS: {
106
106
  readonly flags: {
107
107
  readonly json: "boolean";
108
108
  };
109
- readonly usage: "ls task/ [--json]\n keiyaku ls kei/ [--json]\n keiyaku ls aku/ [--json]\n keiyaku ls aku/<akuma>/ [--json]\n keiyaku ls \"aku/*/*\" [--json]";
109
+ readonly usage: "ls task[/] [--json]\n keiyaku ls kei[/] [--json]\n keiyaku ls aku[/] [--json]\n keiyaku ls aku/<akuma>[/] [--json]\n keiyaku ls \"aku/*/*\" [--json]";
110
110
  readonly purpose: "List one identity directory.";
111
111
  };
112
112
  readonly audit: {
@@ -4,15 +4,15 @@ export const AMEND_MINIMAL_STDIN_HELP = [
4
4
  " <complete replacement>",
5
5
  ].join("\n");
6
6
  export const CONTRACT_COMMAND_SPECS = {
7
- bind: { positional: "none", stdin: "required", flags: { actor: "value", task: "value", target: "value", here: "boolean", after: "repeat-value", gates: "value", json: "boolean" }, usage: "bind [--task <task/...>] [--target <ref>] [--here] [--after <kei/...> ...] [--gates <name>] [--actor <actor>] [--json] -", purpose: "Create one Contract from stdin Markdown." },
8
- amend: { positional: "optional", stdin: "optional", flags: { actor: "value", after: "repeat-value", "clear-after": "boolean", gates: "value", json: "boolean" }, usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name>] [--actor <actor>] [--json] [-]", purpose: "Amend one Contract's document operations or structured terms." },
7
+ bind: { positional: "none", stdin: "required", flags: { actor: "value", task: "value", target: "value", here: "boolean", after: "repeat-value", gates: "raw-value", json: "boolean" }, usage: "bind [--task <task/...>] [--target <ref>] [--here] [--after <kei/...> ...] [--gates <name,...>] [--actor <actor>] [--json] -", purpose: "Create one Contract from stdin Markdown." },
8
+ amend: { positional: "optional", stdin: "optional", flags: { actor: "value", after: "repeat-value", "clear-after": "boolean", gates: "raw-value", json: "boolean" }, usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name,...>] [--actor <actor>] [--json] [-]", purpose: "Amend one Contract's document operations or structured terms." },
9
9
  deliver: { positional: "optional", stdin: "none", flags: { actor: "value", message: "value", "include-dirty": "boolean", json: "boolean" }, usage: "deliver [<contract>|@<contract>] [--message <text>] [--include-dirty] [--actor <actor>] [--json]", purpose: "Deliver one Contract candidate." },
10
10
  review: { positional: "optional", stdin: "optional", flags: { actor: "value", satisfied: "boolean", unsatisfied: "boolean", summary: "value", json: "boolean" }, usage: "review [<contract>|@<contract>] (--satisfied | --unsatisfied) (--summary <text> | -) [--actor <actor>] [--json]", purpose: "Record one review verdict." },
11
11
  arc: { positional: "optional", stdin: "required", flags: { actor: "value", json: "boolean" }, usage: "arc [<contract>|@<contract>] [--actor <actor>] [--json] -", purpose: "Record stdin arc Markdown for one Contract." },
12
12
  abandon: { positional: "optional", stdin: "none", flags: { actor: "value", note: "value", json: "boolean" }, usage: "abandon [<contract>|@<contract>] [--note <text>] [--actor <actor>] [--json]", purpose: "Abandon one Contract with an optional note." },
13
13
  status: { positional: "optional", stdin: "none", flags: { json: "boolean" }, usage: "status [<contract>|@name|<aku/...>] [--json]", purpose: "Read the world status board or one Contract projection." },
14
14
  show: { positional: "optional", stdin: "none", flags: { json: "boolean" }, usage: "show [<contract>|@<contract>] [--json]", purpose: "Read one Contract guidance projection." },
15
- ls: { positional: "optional", stdin: "none", flags: { json: "boolean" }, usage: "ls task/ [--json]\n keiyaku ls kei/ [--json]\n keiyaku ls aku/ [--json]\n keiyaku ls aku/<akuma>/ [--json]\n keiyaku ls \"aku/*/*\" [--json]", purpose: "List one identity directory." },
15
+ ls: { positional: "optional", stdin: "none", flags: { json: "boolean" }, usage: "ls task[/] [--json]\n keiyaku ls kei[/] [--json]\n keiyaku ls aku[/] [--json]\n keiyaku ls aku/<akuma>[/] [--json]\n keiyaku ls \"aku/*/*\" [--json]", purpose: "List one identity directory." },
16
16
  audit: { positional: "optional", stdin: "none", flags: { "include-dirty": "boolean", diff: "boolean", actor: "value", json: "boolean" }, usage: "audit [<contract>|@<contract>] [--include-dirty] [--diff] [--actor <actor>] [--json]", purpose: "Ask what candidate preparation, Verification, and target placement would do." },
17
17
  reconcile: { positional: "optional", stdin: "none", flags: { "retry-hooks": "boolean", json: "boolean" }, usage: "reconcile [<contract>|@<contract>] [--retry-hooks] [--json]", purpose: "Reconcile one Contract or the invocation world." },
18
18
  settings: { positional: "none", stdin: "none", flags: { json: "boolean" }, usage: "settings [--json]", purpose: "Read user and project Settings resources." },
@@ -1,4 +1,6 @@
1
1
  import { Tasks, } from "../../task/index.js";
2
+ import { injectedBodyRequests, requestBodyTask } from "../../akuma/requests.js";
3
+ import { decodeTaskMutationRequest } from "../../task/mutation.js";
2
4
  function value(command, name) {
3
5
  const item = command.flags[name];
4
6
  return typeof item === "string" ? item : undefined;
@@ -54,6 +56,82 @@ async function invokeUpdate(tasks, command, readStdin) {
54
56
  ...(addRelates === undefined ? {} : { addRelates }), ...(dropRelates === undefined ? {} : { dropRelates }),
55
57
  });
56
58
  }
59
+ async function addMutationRequest(command, input) {
60
+ const selectedNamespace = namespace(value(command, "namespace"));
61
+ if (command.stdin === "document")
62
+ return { action: "task.addDocument", input: {
63
+ markdown: await input.readStdin(),
64
+ ...(selectedNamespace === undefined ? {} : { namespace: selectedNamespace }),
65
+ } };
66
+ const body = value(command, "body"), note = value(command, "note");
67
+ const initialState = state(value(command, "state")), selectedPriority = priority(value(command, "priority"));
68
+ const needs = ids(values(command, "needs")), parent = value(command, "parent");
69
+ const supersedes = ids(values(command, "supersedes")), relates = ids(values(command, "relates"));
70
+ return { action: "task.add", input: {
71
+ title: command.positionals[0], ...(selectedNamespace === undefined ? {} : { namespace: selectedNamespace }),
72
+ ...(body === undefined ? {} : { body }), ...(note === undefined ? {} : { note }),
73
+ ...(initialState === undefined ? {} : { state: initialState }),
74
+ ...(selectedPriority === undefined ? {} : { priority: selectedPriority }),
75
+ ...(needs === undefined ? {} : { needs }), ...(parent === undefined ? {} : { parent: parent }),
76
+ ...(supersedes === undefined ? {} : { supersedes }), ...(relates === undefined ? {} : { relates }),
77
+ } };
78
+ }
79
+ async function updateMutationRequest(command, input) {
80
+ const body = command.stdin === "body" ? await input.readStdin() : value(command, "body");
81
+ const appendBody = command.stdin === "append" ? await input.readStdin() : value(command, "append");
82
+ const note = command.stdin === "note" ? await input.readStdin() : value(command, "note");
83
+ const title = value(command, "title"), selectedPriority = priority(value(command, "priority"));
84
+ const addNeeds = ids(values(command, "needs")), dropNeeds = ids(values(command, "drop-needs"));
85
+ const parent = value(command, "parent"), addSupersedes = ids(values(command, "supersedes"));
86
+ const dropSupersedes = ids(values(command, "drop-supersedes")), addRelates = ids(values(command, "relates"));
87
+ const dropRelates = ids(values(command, "drop-relates"));
88
+ return { action: "task.update", id: command.positionals[0], input: {
89
+ ...(title === undefined ? {} : { title }), ...(body === undefined ? {} : { body }),
90
+ ...(appendBody === undefined ? {} : { appendBody }), ...(note === undefined ? {} : { note }),
91
+ ...(selectedPriority === undefined ? {} : { priority: selectedPriority }),
92
+ ...(addNeeds === undefined ? {} : { addNeeds }), ...(dropNeeds === undefined ? {} : { dropNeeds }),
93
+ ...(command.flags["no-parent"] === true ? { parent: null }
94
+ : parent === undefined ? {} : { parent: parent }),
95
+ ...(addSupersedes === undefined ? {} : { addSupersedes }),
96
+ ...(dropSupersedes === undefined ? {} : { dropSupersedes }),
97
+ ...(addRelates === undefined ? {} : { addRelates }), ...(dropRelates === undefined ? {} : { dropRelates }),
98
+ } };
99
+ }
100
+ function lifecycleMutationRequest(command) {
101
+ const id = command.positionals[0];
102
+ switch (command.action) {
103
+ case "start": return { action: "task.start", id };
104
+ case "stop": return { action: "task.stop", id };
105
+ case "resume": return { action: "task.resume", id };
106
+ case "hold": return { action: "task.hold", ids: command.positionals };
107
+ case "done": return { action: "task.done", ids: command.positionals, ...(value(command, "note") === undefined ? {} : { note: value(command, "note") }) };
108
+ case "drop": return { action: "task.drop", ids: command.positionals, ...(value(command, "note") === undefined ? {} : { note: value(command, "note") }) };
109
+ default: throw new Error(`task action cannot forward: ${command.action}`);
110
+ }
111
+ }
112
+ async function mutationRequest(command, input) {
113
+ if (command.action === "add")
114
+ return await addMutationRequest(command, input);
115
+ if (command.action === "compose")
116
+ return { action: "task.compose", markdown: await input.readStdin() };
117
+ if (command.action === "update")
118
+ return await updateMutationRequest(command, input);
119
+ return lifecycleMutationRequest(command);
120
+ }
121
+ function validatedMutationRequest(request) {
122
+ switch (request.action) {
123
+ case "task.add":
124
+ case "task.addDocument": return decodeTaskMutationRequest(request.action, { input: request.input });
125
+ case "task.compose": return decodeTaskMutationRequest(request.action, { markdown: request.markdown });
126
+ case "task.update": return decodeTaskMutationRequest(request.action, { id: request.id, input: request.input });
127
+ case "task.start":
128
+ case "task.stop":
129
+ case "task.resume": return decodeTaskMutationRequest(request.action, { id: request.id });
130
+ case "task.hold": return decodeTaskMutationRequest(request.action, { ids: request.ids });
131
+ case "task.done":
132
+ case "task.drop": return decodeTaskMutationRequest(request.action, { ids: request.ids, ...(request.note === undefined ? {} : { note: request.note }) });
133
+ }
134
+ }
57
135
  async function invokeRead(tasks, command) {
58
136
  const id = command.positionals[0];
59
137
  switch (command.action) {
@@ -97,15 +175,7 @@ function missingWorld(command) {
97
175
  }
98
176
  return missing(command.positionals[0]);
99
177
  }
100
- export async function invokeTask(command, input) {
101
- let world = input.world;
102
- if (world === null && (command.action === "add" || command.action === "compose"
103
- || (command.action === "namespace" && command.positionals.length > 0))) {
104
- world = await input.establish();
105
- }
106
- if (world === null)
107
- return missingWorld(command);
108
- const tasks = Tasks.of(world);
178
+ async function invokeLocalMutation(tasks, command, input) {
109
179
  if (isWorldObservation(command))
110
180
  return observeWorldRead(tasks, command);
111
181
  if (command.action === "show" || command.action === "tree")
@@ -138,3 +208,22 @@ export async function invokeTask(command, input) {
138
208
  default: throw new Error(`task action has no invocation: ${command.action}`);
139
209
  }
140
210
  }
211
+ function establishesWorld(command) {
212
+ return command.action === "add" || command.action === "compose"
213
+ || (command.action === "namespace" && command.positionals.length > 0);
214
+ }
215
+ function forwardsMutation(command) {
216
+ return command.action !== "show" && command.action !== "tree"
217
+ && command.action !== "namespace" && !isWorldObservation(command);
218
+ }
219
+ export async function invokeTask(command, input) {
220
+ const world = input.world ?? (establishesWorld(command) ? await input.establish() : null);
221
+ if (world === null)
222
+ return missingWorld(command);
223
+ const requests = injectedBodyRequests();
224
+ if (requests !== null && forwardsMutation(command)) {
225
+ const request = validatedMutationRequest(await mutationRequest(command, input));
226
+ return await requestBodyTask({ directory: requests, world, request });
227
+ }
228
+ return await invokeLocalMutation(Tasks.of(world), command, input);
229
+ }
@@ -13,6 +13,7 @@ export type CliCoordinateInput = Readonly<{
13
13
  processCwd?: string;
14
14
  cwd?: string;
15
15
  repo?: string;
16
+ gitPath?: string;
16
17
  command: ParsedCommand;
17
18
  }>;
18
19
  export declare function assertExplicitRepoUse(command: ParsedCommand, repo: string | undefined): void;
@@ -11,9 +11,12 @@ async function canonicalInvocationCwd(input) {
11
11
  throw new CliUsageError(`invocation cwd is not an existing directory: ${input}`);
12
12
  }
13
13
  }
14
- async function discoverRepoAt(coordinate) {
14
+ async function discoverRepoAt(coordinate, gitPath) {
15
15
  try {
16
- return { kind: "present", repo: await Repo.at({ path: coordinate }) };
16
+ return {
17
+ kind: "present",
18
+ repo: await Repo.at({ path: coordinate, ...(gitPath === undefined ? {} : { gitPath }) }),
19
+ };
17
20
  }
18
21
  catch (error) {
19
22
  if (error instanceof NoGitWorldError)
@@ -106,10 +109,10 @@ async function resolveWorld(cwd, repo) {
106
109
  export async function resolveCliCoordinates(input) {
107
110
  const processCwd = resolve(input.processCwd ?? ".");
108
111
  const cwd = await canonicalInvocationCwd(resolve(processCwd, input.cwd ?? "."));
109
- const invocationRepo = await discoverRepoAt(cwd);
112
+ const invocationRepo = await discoverRepoAt(cwd, input.gitPath);
110
113
  const selectedRepo = input.repo === undefined
111
114
  ? invocationRepo
112
- : await discoverRepoAt(resolve(cwd, input.repo));
115
+ : await discoverRepoAt(resolve(cwd, input.repo), input.gitPath);
113
116
  const worldRepo = invocationRepo.kind === "present" ? invocationRepo.repo : undefined;
114
117
  const repo = repoFor(repoPolicy(input.command).use, selectedRepo);
115
118
  const world = await resolveWorld(cwd, worldRepo);
@@ -12,6 +12,8 @@ import { CliUsageError, renderCommandUsage } from "./parse.js";
12
12
  import { isBlankInput } from "./usage.js";
13
13
  import { canonicalContractSelector, contractFromInput, resolveContextualContract, resolveKanshiContract, } from "./selectors.js";
14
14
  import { resolveNamedAddress } from "../library/address.js";
15
+ import { injectedBodyRequests } from "../akuma/requests.js";
16
+ import { EMPTY_WORKTREE_HOOKS } from "../library/configuration.js";
15
17
  import { assertExplicitRepoUse, resolveCliCoordinates } from "./coordinates.js";
16
18
  import { BindDraftError, preserveBindDraft } from "./draft.js";
17
19
  async function readStdin() {
@@ -67,8 +69,8 @@ function consumeSettings(run) {
67
69
  throw error;
68
70
  }
69
71
  }
70
- function selectedGates(value, name) {
71
- return consumeSettings(() => gatesFrom({ settings: value, ...(name === undefined ? {} : { name }) }));
72
+ function selectedGates(value, names) {
73
+ return consumeSettings(() => gatesFrom({ settings: value, ...(names === undefined ? {} : { names }) }));
72
74
  }
73
75
  function selectedGitPolicy(value) {
74
76
  return consumeSettings(() => requireBranchesToBeUpToDateFrom({ settings: value }));
@@ -83,6 +85,14 @@ function actorFromEdge(actor, environment) {
83
85
  }
84
86
  return resolved;
85
87
  }
88
+ function gitPathFromEdge(environment) {
89
+ const value = environment.KEIYAKU_GIT_PATH;
90
+ if (value === undefined)
91
+ return undefined;
92
+ if (value.trim().length === 0)
93
+ throw new CliUsageError("KEIYAKU_GIT_PATH requires a nonblank value");
94
+ return value;
95
+ }
86
96
  async function selectContract(repo, selector, scope) {
87
97
  if (selector !== undefined && !selector.startsWith("@"))
88
98
  return contractFromInput(repo, selector);
@@ -162,10 +172,15 @@ async function invokeAudit(parsed, seat, requireBranchesToBeUpToDate) {
162
172
  hooks: seat.hooks,
163
173
  }), (result) => acceptedAudit(result, seat.id), { coordinate: seat.id });
164
174
  }
165
- async function invokeExisting({ parsed, repo, edge, scope, configuration, hooks }) {
175
+ async function existingSeat({ parsed, repo, edge, scope, hooks }) {
166
176
  const { id, contract } = await selectContract(repo, parsed.contract, scope);
167
177
  const actor = actorFromEdge(parsed.actor, edge.environment);
168
- const seat = { contract, id, ...(actor === undefined ? {} : { actor }), hooks };
178
+ return { contract, id, ...(actor === undefined ? {} : { actor }), hooks };
179
+ }
180
+ async function invokeExisting(input) {
181
+ const { parsed, repo, edge, configuration, hooks } = input;
182
+ const seat = await existingSeat(input);
183
+ const { id, contract, actor } = seat;
169
184
  switch (parsed.command) {
170
185
  case "amend": {
171
186
  const markdown = parsed.stdin === true ? await edge.readStdin() : undefined;
@@ -355,10 +370,13 @@ async function invokeContractHistory(repo, contract) {
355
370
  }
356
371
  // eslint-disable-next-line complexity -- command dispatch keeps the CLI's existing boundary in one place.
357
372
  async function invokeParsed(invocation, runtime) {
373
+ const environment = runtime.environment ?? process.env;
374
+ const gitPath = gitPathFromEdge(environment);
358
375
  const coordinates = await resolveCliCoordinates({
359
376
  ...(runtime.cwd === undefined ? {} : { processCwd: runtime.cwd }),
360
377
  ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
361
378
  ...(invocation.repo === undefined ? {} : { repo: invocation.repo }),
379
+ ...(gitPath === undefined ? {} : { gitPath }),
362
380
  command: invocation.command,
363
381
  });
364
382
  const { cwd, cwdSource, repo, world, candidateWorld, establishWorld } = coordinates;
@@ -410,6 +428,12 @@ async function invokeParsed(invocation, runtime) {
410
428
  if (parsed.command === "region")
411
429
  return invokeRegion(parsed, world, repo);
412
430
  const scope = cwd;
431
+ if ((parsed.command === "deliver" || parsed.command === "review") && injectedBodyRequests() !== null) {
432
+ const seat = await existingSeat({ parsed, repo, edge, scope, hooks: EMPTY_WORKTREE_HOOKS });
433
+ return parsed.command === "deliver"
434
+ ? await invokeDeliver(parsed, seat, false)
435
+ : await invokeReview(parsed, seat, edge.readStdin);
436
+ }
413
437
  const configuration = await settingsAt(world ?? undefined, home);
414
438
  const hooks = consumeSettings(() => worktreeHooksFrom({ settings: configuration }));
415
439
  if (parsed.command === "show") {
@@ -20,14 +20,14 @@ export type ParsedBind = Output & Actor & Readonly<{
20
20
  target?: string;
21
21
  workspace?: "here";
22
22
  after?: readonly string[];
23
- gates?: string;
23
+ gates?: readonly string[];
24
24
  }>;
25
25
  export type ParsedAmend = Output & Actor & Readonly<{
26
26
  command: "amend";
27
27
  contract?: string;
28
28
  after?: readonly string[];
29
29
  clearAfter?: true;
30
- gates?: string;
30
+ gates?: readonly string[];
31
31
  stdin?: true;
32
32
  }>;
33
33
  export type ParsedDeliver = Output & Actor & Readonly<{