@astrosheep/keiyaku 1.0.0 → 1.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 (123) hide show
  1. package/README.md +73 -56
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/call-terms_v2.js +103 -0
  4. package/build/agents/harness/activity-values.js +54 -0
  5. package/build/agents/harness/event-channel.js +55 -20
  6. package/build/agents/harness/events.js +47 -1
  7. package/build/agents/harness/execution-handle.js +292 -61
  8. package/build/agents/harness/index.js +103 -121
  9. package/build/agents/harness/pump.js +36 -47
  10. package/build/agents/harness/runtime.js +3 -0
  11. package/build/agents/launch-snapshot_v2.js +348 -0
  12. package/build/agents/opencode-sdk.js +42 -169
  13. package/build/agents/providers/claude-agent-sdk.js +240 -79
  14. package/build/agents/providers/codex-app-server.js +280 -72
  15. package/build/agents/providers/codex-sdk.js +159 -41
  16. package/build/agents/providers/event-type-tail.js +16 -0
  17. package/build/agents/providers/opencode-sdk.js +165 -59
  18. package/build/agents/providers/pi.js +254 -42
  19. package/build/agents/selector_v2.js +122 -0
  20. package/build/cli/commands/akuma.js +120 -42
  21. package/build/cli/completion.js +6 -7
  22. package/build/cli/flags.js +36 -10
  23. package/build/cli/help.js +42 -26
  24. package/build/cli/index.js +225 -93
  25. package/build/cli/parse.js +194 -75
  26. package/build/cli/projection-address.js +11 -0
  27. package/build/cli/render/address.js +88 -0
  28. package/build/cli/render/arc.js +10 -10
  29. package/build/cli/render/call.js +59 -0
  30. package/build/cli/render/compact-text.js +3 -0
  31. package/build/cli/render/errors.js +4 -2
  32. package/build/cli/render/format.js +6 -0
  33. package/build/cli/render/line-width.js +70 -0
  34. package/build/cli/render/misc.js +7 -4
  35. package/build/cli/render/petition.js +4 -3
  36. package/build/cli/render/projection-activity.js +201 -0
  37. package/build/cli/render/shared.js +46 -12
  38. package/build/cli/render/status.js +59 -10
  39. package/build/cli/render/tool-ledger-rollup.js +74 -0
  40. package/build/cli/render/tool-presentation.js +97 -0
  41. package/build/cli/render/wait.js +229 -0
  42. package/build/cli/skills-install.js +42 -24
  43. package/build/cli/subagent-guard.js +1 -1
  44. package/build/config/akuma-loader_v2.js +222 -0
  45. package/build/config/env-keys.js +0 -4
  46. package/build/config/env.js +0 -6
  47. package/build/config/settings.js +325 -12
  48. package/build/core/addressing.js +224 -0
  49. package/build/core/amend.js +30 -47
  50. package/build/core/arc.js +68 -60
  51. package/build/core/bind.js +105 -25
  52. package/build/core/call-persist.js +69 -0
  53. package/build/core/call.js +658 -0
  54. package/build/core/claim.js +72 -44
  55. package/build/core/command-io.js +19 -19
  56. package/build/core/context.js +11 -36
  57. package/build/core/draft.js +2 -3
  58. package/build/core/entry.js +101 -6
  59. package/build/core/execution-coordinate.js +62 -0
  60. package/build/core/execution-pact.js +141 -0
  61. package/build/core/forfeit.js +2 -3
  62. package/build/core/hints.js +10 -10
  63. package/build/core/ids.js +6 -0
  64. package/build/core/ledger.js +16 -1
  65. package/build/core/petition-claim-gates.js +14 -12
  66. package/build/core/petition-claim.js +1 -0
  67. package/build/core/petition-run.js +6 -2
  68. package/build/core/petition.js +63 -21
  69. package/build/core/places.js +106 -13
  70. package/build/core/projection/generation.js +412 -0
  71. package/build/core/projection/heart.js +316 -0
  72. package/build/core/projection/identity.js +81 -0
  73. package/build/core/projection/leash.js +87 -0
  74. package/build/core/projection/mint.js +154 -0
  75. package/build/core/projection-activity.js +219 -0
  76. package/build/core/projection-coordinate.js +35 -0
  77. package/build/core/projection-core.js +69 -463
  78. package/build/core/projection-generation-continuation.js +31 -0
  79. package/build/core/projection-generation-execution.js +148 -0
  80. package/build/core/projection-generation-identity.js +17 -0
  81. package/build/core/projection-generation-launcher.js +90 -0
  82. package/build/core/projection-generation-process.js +43 -0
  83. package/build/core/projection-generation-runner.js +144 -0
  84. package/build/core/projection-generation-runtime.js +15 -0
  85. package/build/core/projection-generation-store.js +707 -0
  86. package/build/core/projection-identity.js +11 -0
  87. package/build/core/projection-life-observer.js +87 -0
  88. package/build/core/projection-life-protocol.js +95 -0
  89. package/build/core/projection-mint.js +96 -24
  90. package/build/core/projection-runner-lock.js +230 -0
  91. package/build/core/projection-status.js +207 -107
  92. package/build/core/projection-tells.js +401 -0
  93. package/build/core/projection-wait.js +155 -0
  94. package/build/core/projection-wake.js +109 -231
  95. package/build/core/queue_v2.js +342 -0
  96. package/build/core/registry.js +6 -3
  97. package/build/core/render.js +63 -1
  98. package/build/core/renew.js +80 -53
  99. package/build/core/scope.js +186 -137
  100. package/build/core/seal.js +16 -19
  101. package/build/core/status.js +88 -25
  102. package/build/core/stored-agent-event.js +101 -0
  103. package/build/core/transcripts.js +214 -228
  104. package/build/core/verdict.js +27 -15
  105. package/build/core/worktree-bootstrap.js +187 -0
  106. package/build/core/worktree-path.js +72 -18
  107. package/build/flow-error.js +4 -6
  108. package/build/generated/version.js +1 -1
  109. package/build/git/branches.js +6 -1
  110. package/build/index.js +8 -5
  111. package/build/keiyaku.js +1 -3
  112. package/package.json +6 -5
  113. package/skills/keiyaku/SKILL.md +6 -47
  114. package/skills/keiyaku-akuma/SKILL.md +75 -0
  115. package/build/agents/effective-policy.js +0 -32
  116. package/build/agents/index.js +0 -114
  117. package/build/agents/selector.js +0 -28
  118. package/build/cli/render/summon.js +0 -17
  119. package/build/cli/render/tell.js +0 -11
  120. package/build/config/akuma-loader.js +0 -268
  121. package/build/core/queue.js +0 -73
  122. package/build/core/summon-persist.js +0 -73
  123. package/build/core/summon.js +0 -377
@@ -1,19 +1,20 @@
1
1
  import * as path from "node:path";
2
+ import * as fs from "node:fs/promises";
2
3
  import { FlowError, asMessage } from "../flow-error.js";
3
4
  import { writeDraftArtifact } from "./draft-artifact.js";
4
5
  import { amendKeiyaku } from "../core/amend.js";
5
6
  import { arcKeiyaku, buildArcInput } from "../core/arc.js";
6
7
  import { bindContract } from "../core/bind.js";
7
8
  import { buildAmendInput, defaultActorClock } from "../core/command-io.js";
8
- import { resolveContractAddress } from "../core/context.js";
9
+ import { AddressResolutionError, buildResolutionAttempt, resolveContractAddress, withResolvedAddress, } from "../core/addressing.js";
9
10
  import { forfeitContractCommand } from "../core/forfeit.js";
10
11
  import { readContractLog } from "../core/log.js";
11
12
  import { buildPetitionClaimInput } from "../core/petition.js";
12
13
  import { petitionKeiyaku } from "../core/petition-run.js";
13
14
  import { renewKeiyaku } from "../core/renew.js";
14
- import { readKanshiBoard } from "../core/status.js";
15
- import { runSummonAndPersist } from "../core/summon-persist.js";
16
- import { resolveResumePath } from "../core/transcripts.js";
15
+ import { normalizeTargetRef, readKanshiBoard } from "../core/status.js";
16
+ import { runCallAndPersist } from "../core/call-persist.js";
17
+ import { locateResponseArtifact } from "../core/transcripts.js";
17
18
  import { renderEnvExample } from "../config/env-keys.js";
18
19
  import { assertSettingsKnobUsable, loadKeiyakuSettings } from "../config/settings.js";
19
20
  import { handleCliError } from "./render/errors.js";
@@ -21,121 +22,209 @@ import { renderStatusBoard } from "./render/status.js";
21
22
  import { VERSION } from "../generated/version.js";
22
23
  import { FLAG_SPECS } from "./flags.js";
23
24
  import { renderAkumaList, renderAkumaShow } from "./commands/akuma.js";
24
- import { loadAkumaCatalog } from "../config/akuma-loader.js";
25
+ import { loadAkumaCatalogV2 } from "../config/akuma-loader_v2.js";
25
26
  import { renderCompletionCandidates, renderCompletionScript } from "./completion.js";
26
27
  import { CLI_COMMAND_METADATA, buildKeiyakuGuide, missingSubcommandError, renderCliCommandHelp as renderCliCommandHelpText, renderCliHelp as renderCliHelpText, } from "./help.js";
27
- import { commandUsesPromptInput, isSummonCommand, parseCliArgs } from "./parse.js";
28
+ import { commandUsesPromptInput, isCallCommand, parseCliArgs } from "./parse.js";
28
29
  import { buildAmendResponse, buildArcResponse, buildRenewResponse } from "./render/arc.js";
29
30
  import { buildLogResponse } from "./render/misc.js";
30
31
  import { buildPetitionClaimResponse } from "./render/petition.js";
31
- import { buildBindDraftResponse, buildBindResponse, buildForfeitResponse, renderCoreResult, textResponse } from "./render/shared.js";
32
- import { buildSummonResponse } from "./render/summon.js";
33
- import { projectionDir, resolveProjectionPrefix, writeInboxTell } from "../core/projection-core.js";
34
- import { assertTellEffortSupported, resumeProjectionOverride, tellProjection } from "../core/projection-wake.js";
35
- import { renderTellReceipt } from "./render/tell.js";
32
+ import { shellQuote } from "./render/format.js";
33
+ import { buildBindDraftResponse, buildBindResponse, buildForfeitResponse, formatCallNextAction, renderCoreResult, textResponse } from "./render/shared.js";
34
+ import { buildCallResponse, renderAdoptedLaunchReceipt } from "./render/call.js";
35
+ import { projectionDir, writeInboxTell } from "../core/projection-core.js";
36
+ import { assertTellEffortSupported, tellProjection } from "../core/projection-wake.js";
37
+ import { resolveProjectionAddress } from "./projection-address.js";
38
+ import { waitForProjection } from "../core/projection-wait.js";
39
+ import { buildWaitResponse } from "./render/wait.js";
40
+ import { prependAddressReceipt, renderResolvedAddressReceipt } from "./render/address.js";
36
41
  import { installOfficialSkills } from "./skills-install.js";
37
- const SUMMON_INPUT_HINTS = [
38
- "Summon input can be a prompt argument, `-` to read stdin, or omitted to read stdin.",
39
- "Use `keiyaku summon NAME \"prompt\"`, `keiyaku summon NAME - < prompt.md`, or `keiyaku resume RESPONSE_PATH \"prompt\"`.",
42
+ import { isGitRepo } from "../git/branches.js";
43
+ import { stableRepoRoot } from "../core/worktree-path.js";
44
+ const CALL_INPUT_HINTS = [
45
+ "Call input can be a prompt argument, `-` to read stdin, or omitted to read stdin.",
46
+ "Use `keiyaku call NAME \"prompt\"`, `keiyaku call NAME - < prompt.md`, or `keiyaku revive ARTIFACT_ID`.",
40
47
  ];
41
- function titleFromSummonPrompt(prompt) {
48
+ function titleFromCallPrompt(prompt) {
42
49
  const firstLine = prompt
43
50
  .split(/\r?\n/)
44
51
  .map((line) => line.trim())
45
52
  .find((line) => line.length > 0);
46
53
  if (!firstLine)
47
- return "Summon";
48
- return firstLine.replace(/^#+\s*/, "").slice(0, 80) || "Summon";
54
+ return "Call";
55
+ return firstLine.replace(/^#+\s*/, "").slice(0, 80) || "Call";
49
56
  }
50
- function buildSummonInput(content, flags) {
57
+ function repositoryCoordinateFromAddress(address) {
58
+ return address?.repo.kind === "repository" ? address.repo.stableRoot : undefined;
59
+ }
60
+ function buildCallInput(content, flags, mode = "call") {
51
61
  const cwd = flags.cwd ?? process.cwd();
52
62
  const task = content.trim();
53
63
  if (!task) {
54
- throw new FlowError("EMPTY_PARAM", "summon requires a prompt argument, `-` with stdin, or prompt text on stdin.", {
55
- hints: SUMMON_INPUT_HINTS,
64
+ throw new FlowError("EMPTY_PARAM", "call requires a prompt argument, `-` with stdin, or prompt text on stdin.", {
65
+ hints: CALL_INPUT_HINTS,
56
66
  });
57
67
  }
58
68
  return {
59
69
  cwd,
60
- title: titleFromSummonPrompt(task),
70
+ repo: flags.repo,
71
+ mode,
72
+ contractId: flags.contractId,
73
+ contractAddressSource: flags.contractAddressSource,
74
+ title: titleFromCallPrompt(task),
61
75
  task,
62
- agent: flags.agent,
76
+ agent: flags.akuma,
63
77
  model: flags.model,
64
78
  effort: flags.effort,
65
- resumePath: flags.resumePath,
66
79
  incognito: flags.incognito,
67
80
  bare: flags.bare,
81
+ detach: flags.detach,
68
82
  };
69
83
  }
70
- function resumePrompt(stdin) {
84
+ function revivePrompt(stdin) {
71
85
  return stdin.trim() ? stdin : "Continue from the previous response.";
72
86
  }
73
87
  function commandSpec(command, run) {
74
88
  return { ...CLI_COMMAND_METADATA[command], run };
75
89
  }
90
+ function effectiveDirectory(flags) {
91
+ return flags.cwd ?? process.cwd();
92
+ }
93
+ async function repositoryDirectory(flags) {
94
+ const candidate = flags.repo ?? effectiveDirectory(flags);
95
+ if (!(await isGitRepo(candidate))) {
96
+ if (flags.contractId) {
97
+ const asTyped = flags.contractAddressSource === "at-address"
98
+ ? `@${flags.contractId}`
99
+ : flags.contractId;
100
+ throw new AddressResolutionError("EMPTY_PARAM", `${asTyped} is a commission address, but ${candidate} has no repository ledger; contract addressing requires a ledger`, buildResolutionAttempt({
101
+ repo: { kind: "user" },
102
+ workdir: effectiveDirectory(flags),
103
+ spelling: {
104
+ asTyped,
105
+ source: flags.contractAddressSource ?? "explicit-flag",
106
+ },
107
+ }));
108
+ }
109
+ throw new FlowError("NOT_GIT_REPO", `${candidate} is not a git repository`);
110
+ }
111
+ return flags.repo ? await stableRepoRoot(candidate) : candidate;
112
+ }
113
+ async function projectDirectory(flags) {
114
+ if (flags.repo)
115
+ return await repositoryDirectory(flags);
116
+ return effectiveDirectory(flags);
117
+ }
76
118
  const CLI_COMMAND_SPECS = {
77
119
  bind: commandSpec("bind", async (stdin, flags) => {
78
- const cwd = flags.cwd ?? process.cwd();
120
+ const cwd = await repositoryDirectory(flags);
79
121
  if (flags.draftOnly) {
80
122
  return buildBindDraftResponse(await writeDraftArtifact({ cwd, title: "bind-draft", content: stdin }));
81
123
  }
82
124
  const result = await bindContract({
83
- cwd, draft: stdin, ...defaultActorClock(), workspace: flags.worktree ? "worktree" : "here",
125
+ cwd, draft: stdin, ...defaultActorClock(), workspace: "worktree",
84
126
  after: flags.after ?? [], exclusive: flags.exclusive,
127
+ onProgress: (line) => {
128
+ process.stdout.write(`${line}\n`);
129
+ },
85
130
  });
86
131
  return buildBindResponse(result, cwd);
87
132
  }),
88
- arc: commandSpec("arc", async (stdin, flags) => renderCoreResult(arcKeiyaku(buildArcInput(stdin, flags.cwd ?? process.cwd())), buildArcResponse)),
89
- summon: commandSpec("summon", async (stdin, flags, signal) => {
90
- const outcome = await runSummonAndPersist({ ...buildSummonInput(stdin, flags), signal });
91
- return buildSummonResponse(outcome.result, { responsePath: outcome.responsePath });
133
+ arc: commandSpec("arc", async (stdin, flags) => {
134
+ const parsed = buildArcInput(stdin, effectiveDirectory(flags), flags.waivePaths, flags.contractId, flags.contractAddressSource);
135
+ const cwd = await repositoryDirectory(flags);
136
+ return renderCoreResult(arcKeiyaku({ ...parsed, cwd }), buildArcResponse);
92
137
  }),
93
- ask: commandSpec("ask", async (stdin, flags, signal) => CLI_COMMAND_SPECS.summon.run(stdin, flags, signal, [])),
94
- resume: commandSpec("resume", async (stdin, flags, signal, positional) => {
95
- const cwd = flags.cwd ?? process.cwd();
96
- if (flags.projection) {
97
- if (positional.length > 0)
98
- throw new FlowError("EMPTY_PARAM", "resume --projection cannot be combined with a response artifact path");
99
- const resolved = resolveProjectionPrefix(cwd, flags.projection);
100
- if (resolved.status !== "resolved")
101
- throw new FlowError("EMPTY_PARAM", `projection not resolved: ${flags.projection}`);
102
- const result = resumeProjectionOverride(projectionDir(cwd, resolved.id), undefined, flags.effort);
103
- if (result === "active")
104
- throw new FlowError("EMPTY_PARAM", "projection is active; use keiyaku tell instead");
105
- if (result === "done")
106
- throw new FlowError("EMPTY_PARAM", "projection is done; use keiyaku tell instead");
107
- return textResponse(`Projection ${resolved.id} recovery started.\n`);
138
+ call: commandSpec("call", async (stdin, flags, signal) => {
139
+ const outcome = await runCallAndPersist({
140
+ ...buildCallInput(stdin, flags, "call"),
141
+ signal,
142
+ ...(!flags.detach
143
+ ? {
144
+ onLaunch: ({ address, projection }) => {
145
+ process.stdout.write(`${renderAdoptedLaunchReceipt(address, projection.id)}\n`);
146
+ },
147
+ }
148
+ : {}),
149
+ });
150
+ if (outcome.result.foregroundWait && outcome.result.projection) {
151
+ return buildWaitResponse(outcome.result.projection.id, outcome.result.foregroundWait);
108
152
  }
109
- const resumePath = await resolveResumePath(cwd, positional[0]);
110
- return CLI_COMMAND_SPECS.summon.run(resumePrompt(stdin), { ...flags, cwd, resumePath }, signal, []);
153
+ return buildCallResponse(outcome.result, {
154
+ responsePath: outcome.responsePath,
155
+ artifactId: outcome.artifactId,
156
+ addressPlacement: flags.detach ? "stdout" : "none",
157
+ });
158
+ }),
159
+ revive: commandSpec("revive", async (stdin, flags, signal, positional) => {
160
+ const cwd = effectiveDirectory(flags);
161
+ const artifactCwd = await projectDirectory(flags);
162
+ const artifactId = positional[0];
163
+ const responseStoreCwd = await isGitRepo(artifactCwd) ? await stableRepoRoot(artifactCwd) : await fs.realpath(artifactCwd);
164
+ const artifact = await locateResponseArtifact(responseStoreCwd, artifactId);
165
+ let executionCwd;
166
+ let repo;
167
+ if (artifact.provenance.authority.kind === "repository") {
168
+ executionCwd = responseStoreCwd;
169
+ repo = responseStoreCwd;
170
+ }
171
+ else {
172
+ if (!("workdir" in artifact.provenance.evidence)) {
173
+ throw new FlowError("INVALID_RESPONSE_PATH", `Invalid response artifact '${artifactId}': field 'provenance.evidence.workdir' is missing.`);
174
+ }
175
+ executionCwd = artifact.provenance.evidence.workdir;
176
+ repo = undefined;
177
+ }
178
+ const outcome = await runCallAndPersist({
179
+ ...buildCallInput(revivePrompt(stdin), { ...flags, cwd: executionCwd, repo }, "revive"),
180
+ reviveArtifact: artifact,
181
+ signal,
182
+ });
183
+ return buildCallResponse(outcome.result, { responsePath: outcome.responsePath, artifactId: outcome.artifactId, addressPlacement: "footer" });
184
+ }),
185
+ wait: commandSpec("wait", async (_stdin, flags, signal, positional) => {
186
+ const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
187
+ const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
188
+ const operation = async () => {
189
+ const projectionId = resolveProjectionAddress(cwd, positional[0], address?.binding.commissionId);
190
+ return prependAddressReceipt(buildWaitResponse(projectionId, await waitForProjection({ projectionDirectory: projectionDir(cwd, projectionId), timeoutMs: flags.timeoutMs, signal })), address, { projectionId });
191
+ };
192
+ return address ? await withResolvedAddress(address, operation) : await operation();
111
193
  }),
112
194
  tell: commandSpec("tell", async (_stdin, flags, _signal, positional) => {
113
- const cwd = flags.cwd ?? process.cwd();
195
+ const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
114
196
  const [prefix, message] = positional;
115
197
  if (!prefix || !message)
116
198
  throw new FlowError("EMPTY_PARAM", "tell requires a projection and message");
117
- const resolved = resolveProjectionPrefix(cwd, prefix);
118
- if (resolved.status !== "resolved")
119
- throw new FlowError("EMPTY_PARAM", `projection not resolved: ${prefix}`);
120
- const directory = projectionDir(cwd, resolved.id);
121
- assertTellEffortSupported(directory, flags.effort);
122
- // Mailbox first: do not inspect phase before this immutable write.
123
- writeInboxTell(directory, message, Date.now(), { effort: flags.effort });
124
- return textResponse(renderTellReceipt(resolved.id, tellProjection(directory, flags.effort)));
199
+ const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
200
+ const operation = async () => {
201
+ const projectionId = resolveProjectionAddress(cwd, prefix, address?.binding.commissionId);
202
+ const directory = projectionDir(cwd, projectionId);
203
+ assertTellEffortSupported(directory, flags.effort);
204
+ // Mailbox first: do not inspect phase before this immutable write.
205
+ writeInboxTell(directory, message, Date.now(), { effort: flags.effort });
206
+ await tellProjection(directory, flags.effort);
207
+ return textResponse("");
208
+ };
209
+ return address ? await withResolvedAddress(address, operation) : await operation();
125
210
  }),
126
- log: commandSpec("log", async (_stdin, flags, _signal, positional) => {
127
- const cwd = flags.cwd ?? process.cwd();
128
- const contractId = await resolveContractAddress(cwd, flags.contractId ?? positional[0]);
129
- return renderCoreResult(readContractLog({ cwd, contractId }), buildLogResponse);
211
+ log: commandSpec("log", async (_stdin, flags) => {
212
+ const cwd = await repositoryDirectory(flags);
213
+ const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
214
+ return await withResolvedAddress(address, async () => {
215
+ const result = await readContractLog({ cwd, contractId: address.binding.commissionId });
216
+ return buildLogResponse({ ...result, address });
217
+ });
130
218
  }),
131
219
  akuma: commandSpec("akuma", async () => { throw missingSubcommandError("akuma", CLI_COMMAND_METADATA.akuma.subcommands ?? []); }),
132
220
  "akuma list": commandSpec("akuma list", async (_stdin, flags) => {
133
- const loaded = await loadKeiyakuSettings(flags.cwd ?? process.cwd());
221
+ const cwd = await projectDirectory(flags);
222
+ const loaded = await loadKeiyakuSettings(cwd);
134
223
  // Role markers need settings pointers; catalog consumers keep legacy agents disease actionable.
135
224
  assertSettingsKnobUsable(loaded, "agents");
136
225
  assertSettingsKnobUsable(loaded, "default");
137
226
  assertSettingsKnobUsable(loaded, "reviewer");
138
- return textResponse(renderAkumaList(await loadAkumaCatalog(flags.cwd ?? process.cwd()), loaded.knobs));
227
+ return textResponse(renderAkumaList(await loadAkumaCatalogV2(cwd), loaded.providerInstances, loaded.knobs));
139
228
  }),
140
229
  "akuma ls": commandSpec("akuma ls", async (_stdin, flags, signal, positional) => CLI_COMMAND_SPECS["akuma list"].run(_stdin, flags, signal, positional)),
141
230
  "akuma show": commandSpec("akuma show", async (_stdin, flags, _signal, positional) => {
@@ -143,48 +232,64 @@ const CLI_COMMAND_SPECS = {
143
232
  if (!name) {
144
233
  throw new FlowError("EMPTY_PARAM", "akuma show requires an Akuma name");
145
234
  }
146
- const loaded = await loadKeiyakuSettings(flags.cwd ?? process.cwd());
235
+ const cwd = await projectDirectory(flags);
236
+ const loaded = await loadKeiyakuSettings(cwd);
147
237
  // Role markers need settings pointers; catalog consumers keep legacy agents disease actionable.
148
238
  assertSettingsKnobUsable(loaded, "agents");
149
239
  assertSettingsKnobUsable(loaded, "default");
150
240
  assertSettingsKnobUsable(loaded, "reviewer");
151
- return textResponse(renderAkumaShow(await loadAkumaCatalog(flags.cwd ?? process.cwd()), loaded.knobs, name));
241
+ return textResponse(renderAkumaShow(await loadAkumaCatalogV2(cwd), loaded.providerInstances, loaded.knobs, name));
152
242
  }),
153
243
  skills: commandSpec("skills", async () => { throw missingSubcommandError("skills", CLI_COMMAND_METADATA.skills.subcommands ?? []); }),
154
244
  "skills install": commandSpec("skills install", async (_stdin, flags) => {
155
245
  const result = await installOfficialSkills(flags.force);
156
246
  return textResponse([
157
- "Installed official Keiyaku skill:",
158
- ...result.targets.map((target) => `- ${target.path} -> ${result.source} (${target.status})`),
247
+ "Installed official Keiyaku skills:",
248
+ ...result.targets.map((target) => `- ${target.path} -> ${target.source} (${target.status})`),
159
249
  ...(result.warnings.length > 0 ? ["", "## Warning", ...result.warnings.map((warning) => `- ${warning}`)] : []),
160
250
  ].join("\n"));
161
251
  }),
162
- amend: commandSpec("amend", async (stdin, flags) => renderCoreResult(amendKeiyaku(buildAmendInput(stdin, flags.cwd ?? process.cwd())), buildAmendResponse)),
252
+ amend: commandSpec("amend", async (stdin, flags) => {
253
+ const parsed = buildAmendInput(stdin, effectiveDirectory(flags), flags.contractId, flags.contractAddressSource);
254
+ const cwd = await repositoryDirectory(flags);
255
+ return renderCoreResult(amendKeiyaku({ ...parsed, cwd }), buildAmendResponse);
256
+ }),
163
257
  renew: commandSpec("renew", async (_stdin, flags) => {
164
- const cwd = flags.cwd ?? process.cwd();
165
- const contractId = await resolveContractAddress(cwd, flags.contractId);
166
- return renderCoreResult(renewKeiyaku({ cwd, contractId }), buildRenewResponse);
258
+ const cwd = await repositoryDirectory(flags);
259
+ const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
260
+ return renderCoreResult(renewKeiyaku({ cwd, address, waivePaths: flags.waivePaths }), buildRenewResponse);
167
261
  }),
168
262
  petition: commandSpec("petition", async (stdin, flags, signal) => {
169
- const cwd = flags.cwd ?? process.cwd();
170
- const contractId = await resolveContractAddress(cwd, flags.contractId);
171
- const input = buildPetitionClaimInput({ cwd, contractId, stdin, signal });
172
- return renderCoreResult(petitionKeiyaku(input), (result) => buildPetitionClaimResponse(result, input));
263
+ const cwd = await repositoryDirectory(flags);
264
+ const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
265
+ return await withResolvedAddress(address, async () => {
266
+ const input = buildPetitionClaimInput({ cwd, contractId: address.binding.commissionId, stdin, signal, waivePaths: flags.waivePaths });
267
+ return buildPetitionClaimResponse({ ...await petitionKeiyaku(input), address }, input);
268
+ });
173
269
  }),
174
270
  forfeit: commandSpec("forfeit", async (_stdin, flags) => {
175
- const cwd = flags.cwd ?? process.cwd();
176
- const contractId = await resolveContractAddress(cwd, flags.contractId);
177
- return buildForfeitResponse(await forfeitContractCommand({ cwd, contractId, ...defaultActorClock(), note: flags.reason }));
271
+ const cwd = await repositoryDirectory(flags);
272
+ const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
273
+ return await withResolvedAddress(address, async () => buildForfeitResponse({
274
+ ...await forfeitContractCommand({ cwd, contractId: address.binding.commissionId, ...defaultActorClock(), note: flags.reason }),
275
+ address,
276
+ }));
178
277
  }),
179
278
  guide: commandSpec("guide", async () => textResponse(buildKeiyakuGuide(VERSION))),
180
279
  completion: commandSpec("completion", async (_stdin, flags) => {
181
- const cwd = flags.cwd ?? process.cwd();
280
+ const cwd = await projectDirectory(flags);
182
281
  if (flags.complete) {
183
282
  return textResponse(await renderCompletionCandidates({ cwd, word: flags.word, previous: flags.previous }));
184
283
  }
185
284
  return textResponse(renderCompletionScript(flags.shell));
186
285
  }),
187
- status: commandSpec("status", async (_stdin, flags) => textResponse(renderStatusBoard(await readKanshiBoard(flags.cwd ?? process.cwd(), Date.now())))),
286
+ status: commandSpec("status", async (_stdin, flags) => {
287
+ // Status is read-only and must open outside Git. projectDirectory still
288
+ // honors --repo as a repository coordinate when the operator names one.
289
+ const cwd = await projectDirectory(flags);
290
+ const target = flags.target === undefined ? undefined : normalizeTargetRef(flags.target);
291
+ return textResponse(renderStatusBoard(await readKanshiBoard(cwd, Date.now(), target === undefined ? undefined : { target })));
292
+ }),
188
293
  "dump-env": commandSpec("dump-env", async () => textResponse(renderEnvExample())),
189
294
  };
190
295
  function getCliCommandSpec(command) {
@@ -194,7 +299,7 @@ function commandRequiresStdin(command) {
194
299
  return getCliCommandSpec(command).stdin === "required";
195
300
  }
196
301
  function commandSavesFailedStdin(command) {
197
- return commandRequiresStdin(command) && command !== "summon";
302
+ return commandRequiresStdin(command) && command !== "call";
198
303
  }
199
304
  async function readStdin() {
200
305
  if (process.stdin.isTTY)
@@ -219,9 +324,25 @@ function responseData(response) {
219
324
  return {};
220
325
  return payload;
221
326
  }
327
+ function isCallProjectionFacts(value) {
328
+ if (value === null || typeof value !== "object" || Array.isArray(value))
329
+ return false;
330
+ const facts = value;
331
+ return typeof facts.id === "string"
332
+ && (facts.phase === "active" || facts.phase === "done" || facts.phase === "failed"
333
+ || facts.phase === "dismissed" || facts.phase === "lost" || facts.phase === "quiescent");
334
+ }
222
335
  function conversationalFooter(response) {
223
336
  const data = responseData(response);
224
337
  const lines = [];
338
+ if (response.address) {
339
+ const projection = data.projection;
340
+ const projectionFacts = isCallProjectionFacts(projection) ? projection : undefined;
341
+ const receiptArtifactId = typeof data.revivedArtifactId === "string"
342
+ ? data.revivedArtifactId
343
+ : typeof data.artifactId === "string" ? data.artifactId : undefined;
344
+ lines.push(renderResolvedAddressReceipt(response.address, { artifactId: receiptArtifactId, projectionId: projectionFacts?.id }));
345
+ }
225
346
  const responsePath = typeof data.responsePath === "string" ? data.responsePath : undefined;
226
347
  const agent = typeof data.agent === "string" ? data.agent : undefined;
227
348
  const meta = data.meta && typeof data.meta === "object" && !Array.isArray(data.meta) ? data.meta : undefined;
@@ -230,15 +351,28 @@ function conversationalFooter(response) {
230
351
  lines.push(`Saved to ${responsePath}.`);
231
352
  }
232
353
  if (agent) {
233
- lines.push(`Agent: ${agent}`);
354
+ lines.push(`Akuma: ${agent}`);
234
355
  }
235
356
  if (warnings.length > 0) {
236
357
  lines.push("Warnings:", ...warnings.map((warning) => `- ${warning}`));
237
358
  }
359
+ if (typeof data.summary === "string") {
360
+ const projection = data.projection;
361
+ const projectionFacts = isCallProjectionFacts(projection) ? projection : undefined;
362
+ const artifactId = typeof data.artifactId === "string" ? data.artifactId : undefined;
363
+ lines.push(formatCallNextAction({
364
+ projection: projectionFacts,
365
+ artifactId,
366
+ repositoryCoordinate: repositoryCoordinateFromAddress(response.address),
367
+ }));
368
+ }
238
369
  return lines.join("\n");
239
370
  }
240
371
  function writeSuccessResponse(command, response) {
241
- if (command !== "ask" && command !== "resume") {
372
+ // Successful tell is always silent: zero stdout bytes, even if text regresses.
373
+ if (command === "tell")
374
+ return;
375
+ if (command !== "revive") {
242
376
  process.stdout.write(`${responseText(response)}\n`);
243
377
  return;
244
378
  }
@@ -259,11 +393,6 @@ async function saveFailedStdin(input) {
259
393
  content: input.content,
260
394
  });
261
395
  }
262
- function shellQuote(value) {
263
- if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
264
- return value;
265
- return `'${value.replace(/'/g, "'\\''")}'`;
266
- }
267
396
  function retryHint(command, draftPath, cwd, cwdFlag) {
268
397
  if (!cwdFlag) {
269
398
  return `Retry: keiyaku ${command} < ${shellQuote(draftPath)}`;
@@ -279,6 +408,9 @@ async function printFailure(error, cwd) {
279
408
  });
280
409
  process.stderr.write(`${responseText(response)}\n`);
281
410
  }
411
+ function failureExitCode(error) {
412
+ return error instanceof Error && error.name === "AbortError" ? 130 : 1;
413
+ }
282
414
  async function handleFailureAfterStdin(input) {
283
415
  if (input.response) {
284
416
  process.stderr.write(`${responseText(input.response)}\n`);
@@ -309,9 +441,9 @@ async function runResponse(command, flags, stdin, positional) {
309
441
  return await getCliCommandSpec(command).run(stdin, flags, signal, positional);
310
442
  }
311
443
  function positionalPrompt(command, positional) {
312
- if (isSummonCommand(command))
444
+ if (isCallCommand(command))
313
445
  return positional[0];
314
- if (command === "resume")
446
+ if (command === "revive")
315
447
  return positional[1];
316
448
  return undefined;
317
449
  }
@@ -323,11 +455,11 @@ async function readPromptInput(command, positional, cwd) {
323
455
  return prompt;
324
456
  const stdin = await readStdin();
325
457
  if (!stdin.trim()) {
326
- if (command === "resume") {
458
+ if (command === "revive") {
327
459
  return "";
328
460
  }
329
461
  await printFailure(new FlowError("EMPTY_PARAM", `${command} requires a prompt argument, \`-\` with stdin, or prompt text on stdin.`, {
330
- hints: SUMMON_INPUT_HINTS,
462
+ hints: CALL_INPUT_HINTS,
331
463
  }), cwd);
332
464
  return undefined;
333
465
  }
@@ -385,12 +517,12 @@ export async function runCliCommand(tokens) {
385
517
  });
386
518
  }
387
519
  await printFailure(error, cwd);
388
- return 1;
520
+ return failureExitCode(error);
389
521
  }
390
522
  const success = !response.payload || !("error" in response.payload);
391
523
  if (success) {
392
524
  writeSuccessResponse(parsed.command, response);
393
- return 0;
525
+ return response.exitCode ?? 0;
394
526
  }
395
527
  if (commandRequiresStdin(parsed.command)) {
396
528
  return await handleFailureAfterStdin({