@astrosheep/keiyaku 2.9.3 → 2.9.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 (282) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +14 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +26 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +68 -5
  8. package/build/agents/harness/projection.js +179 -124
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +82 -53
  21. package/build/agents/providers/codex-app-server/events.js +90 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/events.js +4 -3
  36. package/build/agents/providers/pi/registration.js +145 -0
  37. package/build/agents/providers/pi/session-schema.js +6 -0
  38. package/build/agents/providers/pi/terms.js +1 -0
  39. package/build/agents/providers/registration.js +7 -0
  40. package/build/agents/providers/registry.js +76 -0
  41. package/build/agents/selector.js +2 -29
  42. package/build/agents/types.js +10 -0
  43. package/build/artifact-directories.js +22 -0
  44. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  45. package/build/cli/commands/akuma/akuma/meta.js +1 -1
  46. package/build/cli/commands/akuma/list/meta-ls.js +1 -1
  47. package/build/cli/commands/akuma/view/handler.js +5 -4
  48. package/build/cli/commands/akuma/view/meta.js +1 -1
  49. package/build/cli/commands/akuma.js +6 -0
  50. package/build/cli/commands/contract/amend/handler.js +8 -2
  51. package/build/cli/commands/contract/amend/meta.js +4 -4
  52. package/build/cli/commands/contract/arc/handler.js +8 -2
  53. package/build/cli/commands/contract/arc/meta.js +4 -4
  54. package/build/cli/commands/contract/audit/handler.js +16 -0
  55. package/build/cli/commands/contract/audit/meta.js +16 -0
  56. package/build/cli/commands/contract/bind/handler.js +4 -3
  57. package/build/cli/commands/contract/bind/meta.js +44 -8
  58. package/build/cli/commands/contract/catalog.js +2 -0
  59. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  60. package/build/cli/commands/contract/forfeit/meta.js +1 -1
  61. package/build/cli/commands/contract/log/handler.js +3 -3
  62. package/build/cli/commands/contract/log/meta.js +1 -1
  63. package/build/cli/commands/contract/petition/handler.js +5 -8
  64. package/build/cli/commands/contract/petition/meta.js +6 -6
  65. package/build/cli/commands/contract/renew/handler.js +20 -6
  66. package/build/cli/commands/contract/renew/meta.js +6 -5
  67. package/build/cli/commands/metadata.js +59 -0
  68. package/build/cli/commands/projection/call/handler.js +6 -5
  69. package/build/cli/commands/projection/call/meta.js +4 -1
  70. package/build/cli/commands/projection/kill/handler.js +16 -32
  71. package/build/cli/commands/projection/kill/meta.js +1 -1
  72. package/build/cli/commands/projection/revive/meta.js +4 -1
  73. package/build/cli/commands/projection/shared.js +28 -0
  74. package/build/cli/commands/projection/status/handler.js +1 -1
  75. package/build/cli/commands/projection/status/meta.js +1 -1
  76. package/build/cli/commands/projection/tell/handler.js +14 -30
  77. package/build/cli/commands/projection/tell/meta.js +4 -1
  78. package/build/cli/commands/projection/wait/handler.js +45 -22
  79. package/build/cli/commands/projection/wait/meta.js +8 -3
  80. package/build/cli/commands/registry.js +2 -54
  81. package/build/cli/commands/shared.js +10 -7
  82. package/build/cli/commands/skills/install/handler.js +2 -5
  83. package/build/cli/commands/skills/install/meta.js +1 -1
  84. package/build/cli/commands/skills/skills/handler.js +1 -1
  85. package/build/cli/commands/skills/skills/meta.js +1 -1
  86. package/build/cli/commands/system/completion/meta.js +1 -1
  87. package/build/cli/commands/system/dump-env/meta.js +1 -1
  88. package/build/cli/commands/system/guide/meta.js +1 -1
  89. package/build/cli/commands/task/add/handler.js +7 -15
  90. package/build/cli/commands/task/add/meta.js +4 -1
  91. package/build/cli/commands/task/catalog.js +2 -2
  92. package/build/cli/commands/task/doctor/meta.js +1 -1
  93. package/build/cli/commands/task/done/handler.js +4 -8
  94. package/build/cli/commands/task/done/meta.js +1 -1
  95. package/build/cli/commands/task/drop/handler.js +3 -6
  96. package/build/cli/commands/task/drop/meta.js +1 -1
  97. package/build/cli/commands/task/ls/handler.js +12 -8
  98. package/build/cli/commands/task/ls/meta.js +3 -3
  99. package/build/cli/commands/task/shared.js +14 -4
  100. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  101. package/build/cli/commands/task/show/meta.js +8 -0
  102. package/build/cli/commands/task/start/handler.js +3 -6
  103. package/build/cli/commands/task/start/meta.js +1 -1
  104. package/build/cli/commands/task/stop/handler.js +4 -8
  105. package/build/cli/commands/task/stop/meta.js +1 -1
  106. package/build/cli/commands/task/task/handler.js +1 -1
  107. package/build/cli/commands/task/task/meta.js +1 -1
  108. package/build/cli/commands/task/update/handler.js +6 -11
  109. package/build/cli/commands/task/update/meta.js +4 -1
  110. package/build/cli/completion.js +1 -1
  111. package/build/cli/flags.js +37 -6
  112. package/build/cli/help.js +7 -14
  113. package/build/cli/index.js +112 -154
  114. package/build/cli/missing-subcommand.js +7 -0
  115. package/build/cli/output-stream-error-policy.js +19 -5
  116. package/build/cli/parse-flags.js +33 -7
  117. package/build/cli/parse-metadata.js +3 -9
  118. package/build/cli/parse-selectors.js +2 -1
  119. package/build/cli/parse.js +35 -5
  120. package/build/cli/projection-address.js +12 -12
  121. package/build/cli/render/address.js +41 -37
  122. package/build/cli/render/arc.js +24 -12
  123. package/build/cli/render/audit.js +121 -0
  124. package/build/cli/render/call.js +11 -5
  125. package/build/cli/render/errors.js +36 -1
  126. package/build/cli/render/format.js +4 -0
  127. package/build/cli/render/kanshi.js +6 -12
  128. package/build/cli/render/kill.js +9 -0
  129. package/build/cli/render/misc.js +58 -13
  130. package/build/cli/render/petition.js +20 -15
  131. package/build/cli/render/projection-activity.js +15 -6
  132. package/build/cli/render/shared.js +60 -21
  133. package/build/cli/render/skills.js +10 -0
  134. package/build/cli/render/status.js +56 -23
  135. package/build/cli/render/success-response.js +78 -0
  136. package/build/cli/render/task.js +66 -57
  137. package/build/cli/render/tell.js +5 -0
  138. package/build/cli/render/terminal-failure.js +63 -0
  139. package/build/cli/render/types.js +1 -0
  140. package/build/cli/render/wait.js +68 -38
  141. package/build/cli/types.js +1 -1
  142. package/build/config/akuma-loader.js +0 -5
  143. package/build/config/env-keys.js +3 -1
  144. package/build/config/env.js +3 -0
  145. package/build/config/provider-policy-ownership.js +3 -32
  146. package/build/config/settings/disease.js +8 -1
  147. package/build/config/settings/loader.js +11 -3
  148. package/build/config/settings/provider-instance-fields.js +51 -0
  149. package/build/config/settings/schema.js +6 -176
  150. package/build/core/addressing.js +66 -16
  151. package/build/core/amend.js +61 -18
  152. package/build/core/arc.js +33 -73
  153. package/build/core/audit.js +650 -0
  154. package/build/core/bind.js +82 -35
  155. package/build/core/call/call.js +15 -9
  156. package/build/core/call/context.js +6 -51
  157. package/build/core/call/execution.js +22 -22
  158. package/build/core/call/prompt.js +14 -1
  159. package/build/core/command-markdown.js +45 -0
  160. package/build/core/context.js +13 -7
  161. package/build/core/contract-view.js +22 -6
  162. package/build/core/draft.js +139 -19
  163. package/build/core/entry.js +3 -4
  164. package/build/core/forfeit.js +5 -2
  165. package/build/core/hints.js +1 -75
  166. package/build/core/identifier-slug.js +66 -0
  167. package/build/core/ids.js +25 -11
  168. package/build/core/ledger.js +20 -68
  169. package/build/core/markdown/lex.js +2 -1
  170. package/build/core/markdown/parser.js +0 -68
  171. package/build/core/markdown/render.js +68 -0
  172. package/build/core/markdown/sections.js +2 -1
  173. package/build/core/outcome-base.js +4 -0
  174. package/build/core/projection/generation/database.js +41 -52
  175. package/build/core/projection/generation/model.js +63 -2
  176. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  177. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +36 -53
  178. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  179. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +9 -7
  180. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  181. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +19 -3
  182. package/build/core/projection/generation/store.js +3 -3
  183. package/build/core/projection/generation/transitions.js +19 -3
  184. package/build/core/projection/index.js +27 -0
  185. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  186. package/build/core/{projection-alias.js → projection/projection-alias.js} +13 -13
  187. package/build/core/projection/projection-coordinate.js +61 -0
  188. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  189. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +37 -78
  190. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  191. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  192. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +16 -2
  193. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  194. package/build/core/{projection-status.js → projection/projection-status.js} +163 -45
  195. package/build/core/projection/projection-terminal-failure.js +106 -0
  196. package/build/core/projection/projection-wait.js +275 -0
  197. package/build/core/projection/projection-wake.js +227 -0
  198. package/build/core/provider-loader-ports.js +4 -0
  199. package/build/core/ref-log.js +65 -40
  200. package/build/core/registry.js +63 -10
  201. package/build/core/renew-session.js +128 -0
  202. package/build/core/renew.js +448 -102
  203. package/build/core/scope.js +272 -68
  204. package/build/core/seal.js +165 -40
  205. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +8 -8
  206. package/build/core/{claim.js → settlement/claim.js} +32 -8
  207. package/build/core/settlement/index.js +19 -0
  208. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +24 -49
  209. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  210. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  211. package/build/core/settlement/petition-io.js +1 -0
  212. package/build/core/{petition-preview.js → settlement/petition-preview.js} +2 -2
  213. package/build/core/{petition.js → settlement/petition.js} +21 -12
  214. package/build/core/settlement/queue-read-model.js +143 -0
  215. package/build/core/settlement/queue-seat-allocation.js +156 -0
  216. package/build/core/settlement/queue.js +6 -0
  217. package/build/core/settlement/settlement.js +288 -0
  218. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  219. package/build/core/settlement/verification.js +59 -0
  220. package/build/core/status/board.js +62 -41
  221. package/build/core/status/ledger-batch.js +158 -0
  222. package/build/core/status/reconciliation.js +10 -4
  223. package/build/core/stored-agent-event.js +6 -2
  224. package/build/core/task/board.js +41 -4
  225. package/build/core/task/commands.js +45 -28
  226. package/build/core/task/document.js +5 -0
  227. package/build/core/task/index.js +12 -0
  228. package/build/core/task/model.js +8 -1
  229. package/build/core/task/query.js +172 -0
  230. package/build/core/task/settlement-git.js +265 -74
  231. package/build/core/task/settlement-policy.js +67 -29
  232. package/build/core/task/source-board.js +1 -0
  233. package/build/core/task/task-contract.js +444 -0
  234. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  235. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  236. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  237. package/build/core/{task-git-store.js → task/task-git-store.js} +90 -19
  238. package/build/core/task/task-repository.js +1 -0
  239. package/build/core/{task-store-repository.js → task/task-store-repository.js} +6 -4
  240. package/build/core/task/task-store.js +1 -0
  241. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  242. package/build/core/task/task.js +7 -0
  243. package/build/core/task/tree.js +37 -0
  244. package/build/core/task/validation.js +5 -2
  245. package/build/core/transcripts.js +15 -3
  246. package/build/core/worktree-path.js +43 -28
  247. package/build/flow-error.js +83 -39
  248. package/build/fs/path-coordinate.js +16 -0
  249. package/build/generated/version.js +3 -2
  250. package/build/git/branches.js +2 -1
  251. package/build/git/core.js +106 -2
  252. package/build/git/diff/parsers.js +23 -13
  253. package/build/git/diff/structured.js +127 -0
  254. package/build/git/path-coordinate.js +17 -0
  255. package/build/git/refs.js +57 -0
  256. package/build/git/streaming-batch.js +301 -0
  257. package/build/git/worktree.js +65 -0
  258. package/build/index.js +9 -9
  259. package/build/keiyaku.js +2 -36
  260. package/build/telemetry/logger.js +4 -2
  261. package/package.json +4 -10
  262. package/skills/keiyaku/SKILL.md +5 -4
  263. package/skills/keiyaku-akuma/SKILL.md +25 -11
  264. package/skills/keiyaku-task/SKILL.md +17 -8
  265. package/skills/keiyaku-workflow/SKILL.md +58 -9
  266. package/build/agents/providers/codex-sdk.js +0 -329
  267. package/build/cli/commands/task/view/meta.js +0 -8
  268. package/build/core/command-io.js +0 -174
  269. package/build/core/path-coordinate.js +0 -27
  270. package/build/core/petition-claim.js +0 -117
  271. package/build/core/petition-run.js +0 -83
  272. package/build/core/projection-coordinate.js +0 -63
  273. package/build/core/projection-wait.js +0 -165
  274. package/build/core/projection-wake.js +0 -136
  275. package/build/core/queue.js +0 -343
  276. package/build/core/task-contract.js +0 -237
  277. package/build/core/task.js +0 -4
  278. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  279. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  280. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  281. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  282. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -0,0 +1,301 @@
1
+ import { spawn } from "node:child_process";
2
+ import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
3
+ import { gitProcessEnv, resolveGitTimeoutMs, } from "./core.js";
4
+ /** Default long-lived batch argv. One-shot calls must not use this gate. */
5
+ export const GIT_STREAMING_BATCH_DEFAULT_ARGS = ["cat-file", "--batch"];
6
+ /** Safety cap for a single response header line before the body frame. */
7
+ export const GIT_STREAMING_BATCH_MAX_HEADER_BYTES = 64 * 1024;
8
+ /** Wire/framing failure while reading batch stdout. */
9
+ export class GitStreamingBatchFramingError extends Error {
10
+ name = "GitStreamingBatchFramingError";
11
+ }
12
+ /** Stdout ended before a complete frame arrived. */
13
+ export class GitStreamingBatchResponseEndedError extends Error {
14
+ name = "GitStreamingBatchResponseEndedError";
15
+ }
16
+ class BufferedReader {
17
+ #iterator;
18
+ #buffer = Buffer.alloc(0);
19
+ #ended = false;
20
+ constructor(stream) {
21
+ this.#iterator = stream[Symbol.asyncIterator]();
22
+ }
23
+ async #fill() {
24
+ if (this.#ended) {
25
+ throw new GitStreamingBatchResponseEndedError("git streaming batch response ended unexpectedly");
26
+ }
27
+ const next = await this.#iterator.next();
28
+ if (next.done) {
29
+ this.#ended = true;
30
+ throw new GitStreamingBatchResponseEndedError("git streaming batch response ended unexpectedly");
31
+ }
32
+ const chunk = Buffer.isBuffer(next.value) ? next.value : Buffer.from(next.value);
33
+ this.#buffer = this.#buffer.length === 0 ? chunk : Buffer.concat([this.#buffer, chunk]);
34
+ }
35
+ async readLine(maxBytes) {
36
+ for (;;) {
37
+ const newline = this.#buffer.indexOf(0x0a);
38
+ if (newline >= 0) {
39
+ if (newline > maxBytes) {
40
+ throw new GitStreamingBatchFramingError(`git streaming batch header exceeds ${maxBytes} bytes`);
41
+ }
42
+ const line = this.#buffer.subarray(0, newline).toString("ascii");
43
+ this.#buffer = this.#buffer.subarray(newline + 1);
44
+ return line;
45
+ }
46
+ if (this.#buffer.length > maxBytes) {
47
+ throw new GitStreamingBatchFramingError(`git streaming batch header exceeds ${maxBytes} bytes without a newline`);
48
+ }
49
+ try {
50
+ await this.#fill();
51
+ }
52
+ catch (error) {
53
+ if (error instanceof GitStreamingBatchResponseEndedError) {
54
+ throw new GitStreamingBatchFramingError("git streaming batch response ended before a header newline");
55
+ }
56
+ throw error;
57
+ }
58
+ }
59
+ }
60
+ async readExactly(length) {
61
+ while (this.#buffer.length < length)
62
+ await this.#fill();
63
+ const value = this.#buffer.subarray(0, length);
64
+ this.#buffer = this.#buffer.subarray(length);
65
+ return value;
66
+ }
67
+ async discardExactly(length) {
68
+ let remaining = length;
69
+ while (remaining > 0) {
70
+ if (this.#buffer.length === 0)
71
+ await this.#fill();
72
+ const consumed = Math.min(remaining, this.#buffer.length);
73
+ this.#buffer = this.#buffer.subarray(consumed);
74
+ remaining -= consumed;
75
+ }
76
+ }
77
+ }
78
+ /**
79
+ * Long-lived Git batch subprocess: raw request lines, framed response records,
80
+ * and exit/timeout/close facts only. No domain vocabulary or FlowError decisions.
81
+ */
82
+ export class GitStreamingBatchSession {
83
+ cwd;
84
+ #child;
85
+ #reader;
86
+ #stderr = [];
87
+ #timeout;
88
+ #closed;
89
+ #maxHeaderBytes;
90
+ #state = { kind: "open" };
91
+ #timedOut = false;
92
+ #aborted = false;
93
+ #inputError = null;
94
+ #requestInFlight = false;
95
+ #closePromise = null;
96
+ constructor(cwd, child, timeoutMs, maxHeaderBytes) {
97
+ this.cwd = cwd;
98
+ this.#child = child;
99
+ this.#reader = new BufferedReader(this.#child.stdout);
100
+ this.#maxHeaderBytes = maxHeaderBytes;
101
+ this.#child.stderr.on("data", (chunk) => this.#stderr.push(chunk));
102
+ this.#child.stdin.on("error", (error) => {
103
+ this.#inputError = error;
104
+ });
105
+ this.#closed = new Promise((resolve) => {
106
+ this.#child.once("error", (error) => {
107
+ resolve({ code: null, signal: null, processError: error });
108
+ });
109
+ this.#child.once("close", (code, signal) => {
110
+ resolve({ code, signal });
111
+ });
112
+ });
113
+ this.#timeout = setTimeout(() => {
114
+ this.#timedOut = true;
115
+ this.#child.kill("SIGKILL");
116
+ }, timeoutMs);
117
+ }
118
+ /**
119
+ * Open the sole long-lived Git batch process for `cwd`.
120
+ * Default argv is `cat-file --batch`; callers supply compatible batch modes only.
121
+ */
122
+ static open(cwd, options) {
123
+ const args = options?.args ?? GIT_STREAMING_BATCH_DEFAULT_ARGS;
124
+ const timeoutMs = options?.timeoutMs ?? resolveGitTimeoutMs();
125
+ const maxHeaderBytes = options?.maxHeaderBytes ?? GIT_STREAMING_BATCH_MAX_HEADER_BYTES;
126
+ const child = spawn("git", [...args], {
127
+ cwd: normalizeFilesystemCoordinate(cwd),
128
+ env: gitProcessEnv(),
129
+ shell: false,
130
+ stdio: ["pipe", "pipe", "pipe"],
131
+ });
132
+ return new GitStreamingBatchSession(cwd, child, timeoutMs, maxHeaderBytes);
133
+ }
134
+ /**
135
+ * Write one request line and read one framed response record.
136
+ * Honors stdin backpressure. Concurrent requests are rejected.
137
+ */
138
+ async request(expression, options) {
139
+ if (this.#state.kind !== "open") {
140
+ throw new Error("git streaming batch session is not open");
141
+ }
142
+ if (this.#requestInFlight) {
143
+ throw new Error("git streaming batch rejects concurrent requests");
144
+ }
145
+ this.#requestInFlight = true;
146
+ try {
147
+ if (this.#inputError)
148
+ throw this.#inputError;
149
+ const line = `${expression}\n`;
150
+ if (!this.#child.stdin.write(line)) {
151
+ await new Promise((resolve, reject) => {
152
+ const onDrain = () => {
153
+ this.#child.stdin.off("error", onError);
154
+ resolve();
155
+ };
156
+ const onError = (error) => {
157
+ this.#child.stdin.off("drain", onDrain);
158
+ reject(error);
159
+ };
160
+ this.#child.stdin.once("drain", onDrain);
161
+ this.#child.stdin.once("error", onError);
162
+ });
163
+ }
164
+ let header;
165
+ try {
166
+ header = await this.#reader.readLine(this.#maxHeaderBytes);
167
+ }
168
+ catch (error) {
169
+ if (error instanceof GitStreamingBatchFramingError
170
+ || error instanceof GitStreamingBatchResponseEndedError) {
171
+ this.abort();
172
+ }
173
+ throw error;
174
+ }
175
+ if (header === `${expression} missing`) {
176
+ return { kind: "missing", expression };
177
+ }
178
+ if (header === `${expression} ambiguous`) {
179
+ return { kind: "ambiguous", expression };
180
+ }
181
+ const parts = header.split(" ");
182
+ const [oid, type, sizeText, ...unexpected] = parts;
183
+ const size = Number(sizeText);
184
+ if (unexpected.length > 0
185
+ || !oid
186
+ || !/^[0-9a-f]{40,64}$/.test(oid)
187
+ || !type
188
+ || !Number.isSafeInteger(size)
189
+ || size < 0) {
190
+ this.abort();
191
+ throw new GitStreamingBatchFramingError(`git streaming batch returned invalid object header: ${header}`);
192
+ }
193
+ if (options?.maxContentBytes !== undefined && size > options.maxContentBytes) {
194
+ try {
195
+ await this.#reader.discardExactly(size + 1);
196
+ }
197
+ catch (error) {
198
+ this.abort();
199
+ throw error;
200
+ }
201
+ // Stream stays aligned after discard; oversized is a record limit, not a framing break.
202
+ throw new Error(`git streaming batch object ${oid} exceeds ${options.maxContentBytes} bytes`);
203
+ }
204
+ let content;
205
+ let terminator;
206
+ try {
207
+ content = await this.#reader.readExactly(size);
208
+ terminator = await this.#reader.readExactly(1);
209
+ }
210
+ catch (error) {
211
+ this.abort();
212
+ throw error;
213
+ }
214
+ if (terminator[0] !== 0x0a) {
215
+ this.abort();
216
+ throw new GitStreamingBatchFramingError(`git streaming batch object ${oid} has an invalid frame terminator`);
217
+ }
218
+ return {
219
+ kind: "object",
220
+ record: { oid, type, size, content },
221
+ };
222
+ }
223
+ finally {
224
+ this.#requestInFlight = false;
225
+ }
226
+ }
227
+ /**
228
+ * Idempotent kill of the child after a framing or stream-end failure.
229
+ * Immediately leaves `open` so no later request can write/read the destroyed child.
230
+ */
231
+ abort() {
232
+ if (this.#aborted)
233
+ return;
234
+ this.#aborted = true;
235
+ if (this.#state.kind === "open") {
236
+ this.#state = { kind: "closing" };
237
+ }
238
+ this.#child.stdin.destroy();
239
+ this.#child.kill("SIGKILL");
240
+ }
241
+ /**
242
+ * End stdin, await process exit, and return structured close facts.
243
+ * First close may reject on process spawn error after storing the closed fact;
244
+ * a later close returns that stored fact. Successful and aborted close stay idempotent.
245
+ */
246
+ close() {
247
+ if (this.#state.kind === "closed")
248
+ return Promise.resolve(this.#state.fact);
249
+ if (this.#closePromise)
250
+ return this.#closePromise;
251
+ this.#state = { kind: "closing" };
252
+ // Clear the in-flight promise after settlement so a later close() returns the
253
+ // stored fact instead of replaying a rejected process-error promise. The
254
+ // returned chain is what callers await; bookkeeping must not create a second
255
+ // unhandled rejection.
256
+ this.#closePromise = this.#closeOnce().then((fact) => {
257
+ this.#closePromise = null;
258
+ return fact;
259
+ }, (error) => {
260
+ this.#closePromise = null;
261
+ throw error;
262
+ });
263
+ return this.#closePromise;
264
+ }
265
+ async #closeOnce() {
266
+ if (!this.#aborted && this.#child.stdin?.writable) {
267
+ this.#child.stdin.end();
268
+ }
269
+ let exit;
270
+ try {
271
+ exit = await this.#closed;
272
+ }
273
+ finally {
274
+ clearTimeout(this.#timeout);
275
+ }
276
+ if (exit.processError && !this.#aborted) {
277
+ const fact = {
278
+ code: null,
279
+ signal: null,
280
+ timedOut: this.#timedOut,
281
+ aborted: this.#aborted,
282
+ stderr: Buffer.concat(this.#stderr),
283
+ };
284
+ this.#state = { kind: "closed", fact };
285
+ throw exit.processError;
286
+ }
287
+ const fact = {
288
+ code: exit.code,
289
+ signal: exit.signal,
290
+ timedOut: this.#timedOut,
291
+ aborted: this.#aborted,
292
+ stderr: Buffer.concat(this.#stderr),
293
+ };
294
+ this.#state = { kind: "closed", fact };
295
+ return fact;
296
+ }
297
+ }
298
+ /** Open the sole long-lived Git batch subprocess primitive. */
299
+ export function openGitStreamingBatch(cwd, options) {
300
+ return GitStreamingBatchSession.open(cwd, options);
301
+ }
@@ -1,3 +1,6 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { FlowError } from "../flow-error.js";
1
4
  import { createGit, wrapGitError } from "./core.js";
2
5
  export const DIRTY_FILE_CATEGORY = {
3
6
  modified: "modified",
@@ -8,6 +11,8 @@ export const DIRTY_FILE_CATEGORY = {
8
11
  copied: "copied",
9
12
  untracked: "untracked",
10
13
  };
14
+ const DIRTY_WORKTREE_LIST_LIMIT = 10;
15
+ const KEIYAKU_ARTIFACT_DIR_PREFIX = ".keiyaku/";
11
16
  function normalizeGitPath(filePath) {
12
17
  return filePath.trim().replace(/^\.\/+/, "");
13
18
  }
@@ -67,6 +72,28 @@ export async function getDirtyFiles(cwd) {
67
72
  category: deriveDirtyFileCategory(file, statusPathSets),
68
73
  }));
69
74
  }
75
+ function normalizePathForDirtyMatch(cwd, filePath) {
76
+ const normalizedPath = filePath.trim();
77
+ if (!normalizedPath)
78
+ return "";
79
+ const repoRelativePath = path.isAbsolute(normalizedPath) ? path.relative(cwd, normalizedPath) : normalizedPath;
80
+ const platformNormalizedPath = path.normalize(repoRelativePath.replace(/[\\/]+/g, path.sep));
81
+ return platformNormalizedPath.split(path.sep).join("/").replace(/^\.\/+/, "").replace(/^\/+/, "");
82
+ }
83
+ /** Refuse mutations that can overwrite tracked changes in this worktree. */
84
+ export async function assertCleanTrackedWorkingTree(cwd) {
85
+ const dirtyFiles = (await getDirtyFiles(cwd)).filter((file) => file.category !== DIRTY_FILE_CATEGORY.untracked);
86
+ if (dirtyFiles.length === 0)
87
+ return;
88
+ const shown = dirtyFiles.slice(0, DIRTY_WORKTREE_LIST_LIMIT);
89
+ const list = shown.map((file) => ` ${renderDirtyFileStatusLine(file)}`).join("\n");
90
+ const overflowCount = dirtyFiles.length - shown.length;
91
+ const overflowLine = overflowCount > 0 ? `\n ... (+${overflowCount} more)` : "";
92
+ const hasDirtyKeiyakuArtifacts = dirtyFiles.some((file) => normalizePathForDirtyMatch(cwd, file.path).startsWith(KEIYAKU_ARTIFACT_DIR_PREFIX));
93
+ throw new FlowError("DIRTY_WORKTREE", `Uncommitted changes detected in working tree:\n${list}${overflowLine}\n\nBlocking changes must be committed or stashed before proceeding.`, hasDirtyKeiyakuArtifacts
94
+ ? { facts: { kind: "dirty_worktree", hasKeiyakuArtifacts: true } }
95
+ : undefined);
96
+ }
70
97
  export async function getUntrackedFiles(cwd) {
71
98
  const git = createGit(cwd);
72
99
  let output;
@@ -82,6 +109,44 @@ export async function getUntrackedFiles(cwd) {
82
109
  .filter((line) => line.length > 0);
83
110
  return Array.from(new Set(files));
84
111
  }
112
+ /** Capture conflicted paths while the index still contains unmerged entries. */
113
+ export async function getUnmergedPaths(cwd) {
114
+ const git = createGit(cwd);
115
+ let output;
116
+ try {
117
+ output = await git.raw(["diff", "--name-only", "--diff-filter=U", "-z"]);
118
+ }
119
+ catch (err) {
120
+ throw wrapGitError("diff --name-only --diff-filter=U", err, cwd);
121
+ }
122
+ return Array.from(new Set(output.split("\0").filter((filePath) => filePath.length > 0)));
123
+ }
124
+ async function directoryExists(directory) {
125
+ try {
126
+ return (await fs.stat(directory)).isDirectory();
127
+ }
128
+ catch (error) {
129
+ if (error.code === "ENOENT")
130
+ return false;
131
+ throw error;
132
+ }
133
+ }
134
+ /** Git owns these paths; a lingering REBASE_HEAD alone is not active-rebase evidence. */
135
+ export async function hasActiveRebase(cwd) {
136
+ const git = createGit(cwd);
137
+ let paths;
138
+ try {
139
+ paths = await Promise.all([
140
+ git.raw(["rev-parse", "--git-path", "rebase-merge"]),
141
+ git.raw(["rev-parse", "--git-path", "rebase-apply"]),
142
+ ]);
143
+ }
144
+ catch (error) {
145
+ throw wrapGitError("rev-parse --git-path rebase state", error, cwd);
146
+ }
147
+ const directories = paths.map((candidate) => path.resolve(cwd, candidate.trim()));
148
+ return (await Promise.all(directories.map(directoryExists))).some(Boolean);
149
+ }
85
150
  export async function isPathTracked(cwd, filePath) {
86
151
  const git = createGit(cwd);
87
152
  let output;
package/build/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { VERSION } from "./generated/version.js";
2
+ import { VERSION, GIT_HASH } from "./generated/version.js";
3
3
  import * as fs from "node:fs/promises";
4
4
  import { getConfig, initializeConfig } from "./config/env.js";
5
5
  import { ENV_KEYS } from "./config/env-keys.js";
6
- import { logErrorWithCause } from "./telemetry/logger.js";
7
- import { runProjectionGenerationRuntime } from "./core/projection-generation-runtime.js";
6
+ import { formatErrorWithCause } from "./telemetry/logger.js";
7
+ import { runProjectionGenerationRuntime } from "./core/projection/index.js";
8
8
  import { renderCliHelp, runCliCommand } from "./cli/index.js";
9
- import { installCliOutputStreamErrorPolicy } from "./cli/output-stream-error-policy.js";
9
+ import { installCliOutputStreamErrorPolicy, writeCliOutput } from "./cli/output-stream-error-policy.js";
10
10
  import { assertSubagentCliCommandAllowed } from "./cli/subagent-guard.js";
11
11
  installCliOutputStreamErrorPolicy(process.stdout, process.stderr);
12
12
  const packageVersion = VERSION;
@@ -45,16 +45,16 @@ async function main() {
45
45
  args: cliArgs,
46
46
  });
47
47
  if (cliArgs.length === 1 && cliArgs[0] === "--version") {
48
- console.log(packageVersion);
48
+ await writeCliOutput(process.stdout, `${GIT_HASH ? `${packageVersion} (${GIT_HASH})` : packageVersion}\n`);
49
49
  return;
50
50
  }
51
51
  if (cliArgs.length === 0 || (cliArgs.length === 1 && (cliArgs[0] === "--help" || cliArgs[0] === "-h"))) {
52
- console.log(renderCliHelp(packageVersion));
52
+ await writeCliOutput(process.stdout, `${renderCliHelp(packageVersion)}\n`);
53
53
  return;
54
54
  }
55
55
  process.exitCode = await runCliCommand(cliArgs);
56
56
  }
57
- main().catch((err) => {
58
- logErrorWithCause("Fatal error:", err, { mirrorToDebugLog: false });
59
- process.exit(1);
57
+ await main().catch(async (err) => {
58
+ await writeCliOutput(process.stderr, `${formatErrorWithCause("Fatal error:", err)}\n`);
59
+ process.exitCode = 1;
60
60
  });
package/build/keiyaku.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import * as path from "path";
2
2
  import { ACTOR_IDENTITY, SETTINGS_PATH_SEGMENTS } from "./keiyaku-constants.js";
3
3
  export { ACTOR_IDENTITY } from "./keiyaku-constants.js";
4
+ export { DRAFT_ARTIFACT_NOTICE, formatArtifactDirectories, KEIYAKU_ARTIFACT_DIRS, KEIYAKU_DRAFT_DIR, RESPONSE_ARTIFACT_NOTICE, RESPONSE_HISTORY_DIR, } from "./artifact-directories.js";
4
5
  export const KEIYAKU_FILE = path.join(".keiyaku", "KEIYAKU.md");
5
- export const KEIYAKU_DRAFT_DIR = path.join(".keiyaku", "draft");
6
- export const RESPONSE_HISTORY_DIR = path.join(".keiyaku", "response");
7
- export const KEIYAKU_ARTIFACT_DIRS = [KEIYAKU_DRAFT_DIR, RESPONSE_HISTORY_DIR];
8
6
  export const TOOL_NAMES = {
9
7
  bind: "bind",
10
8
  arc: "arc",
@@ -16,44 +14,12 @@ export const TOOL_NAMES = {
16
14
  help: "help",
17
15
  };
18
16
  export const ACTOR_IDENTITY_PLURAL = `${ACTOR_IDENTITY}s`;
19
- function formatArtifactDirectory(directory) {
20
- return `\`${directory}/\``;
21
- }
22
- export function formatArtifactDirectories(directories) {
23
- const formatted = directories.map(formatArtifactDirectory);
24
- if (formatted.length === 0)
25
- return "";
26
- if (formatted.length === 1)
27
- return formatted[0];
28
- if (formatted.length === 2)
29
- return `${formatted[0]} and ${formatted[1]}`;
30
- return `${formatted.slice(0, -1).join(", ")}, and ${formatted[formatted.length - 1]}`;
31
- }
32
- function buildArtifactNotice(directory) {
33
- return `Auto-generated artifact. Add ${formatArtifactDirectory(directory)} to \`.gitignore\` if you do not want it tracked.`;
34
- }
35
- export const DRAFT_ARTIFACT_NOTICE = buildArtifactNotice(KEIYAKU_ARTIFACT_DIRS[0]);
36
- export const RESPONSE_ARTIFACT_NOTICE = buildArtifactNotice(KEIYAKU_ARTIFACT_DIRS[1]);
37
17
  export const KEIYAKU_BRANCH_PREFIX = "keiyaku/";
38
18
  export const BASE_RULES_FILE = path.join(".keiyaku", "base-rules.md");
39
19
  export const SETTINGS_FILE = path.join(...SETTINGS_PATH_SEGMENTS);
40
- export const SUBAGENT_PROVIDERS = [
41
- "codex-sdk",
42
- "codex-app-server",
43
- "claude-agent-sdk",
44
- "opencode-sdk",
45
- "pi",
46
- ];
47
- export function isSubagentProvider(value) {
48
- return SUBAGENT_PROVIDERS.includes(value);
49
- }
20
+ export { SUBAGENT_PROVIDERS, isSubagentProvider, BUILTIN_AGENT_PROFILES, BUILTIN_AGENT_NAMES, } from "./agents/providers/registry.js";
50
21
  export const CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY = "model_reasoning_effort";
51
- export const BUILTIN_AGENT_NAMES = ["akuma-codex", "akuma-claude"];
52
22
  export const DEFAULT_AGENT_NAME = "akuma-codex";
53
- export const BUILTIN_AGENT_PROFILES = {
54
- "akuma-codex": { provider: "codex-sdk", description: "Deprecated Codex SDK Akuma; use codex-app-server." },
55
- "akuma-claude": { provider: "claude-agent-sdk", description: "Default Claude Agent SDK Akuma." },
56
- };
57
23
  export const CLAUDE_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
58
24
  export const CLAUDE_SETTING_SOURCES = ["project", "user", "local"];
59
25
  export const CLAUDE_DEFAULT_SETTING_SOURCES = ["project"];
@@ -31,6 +31,9 @@ function stringifyUnknown(error) {
31
31
  return String(error);
32
32
  }
33
33
  }
34
+ export function formatErrorWithCause(message, error) {
35
+ return `${message} ${stringifyUnknown(error)}`;
36
+ }
34
37
  export function logInfo(message, options = {}) {
35
38
  log("info", message, options);
36
39
  }
@@ -41,6 +44,5 @@ export function logError(message, options = {}) {
41
44
  log("error", message, options);
42
45
  }
43
46
  export function logErrorWithCause(message, error, options = {}) {
44
- const details = stringifyUnknown(error);
45
- logError(`${message} ${details}`, options);
47
+ logError(formatErrorWithCause(message, error), options);
46
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "2.9.3",
3
+ "version": "2.9.5",
4
4
  "description": "CLI for running iterative keiyaku workflows with Codex subagents.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,12 +24,6 @@
24
24
  },
25
25
  "scripts": {
26
26
  "sync:version": "node scripts/generate-version.mjs",
27
- "predev": "npm run sync:version --silent",
28
- "prebuild": "npm run sync:version --silent",
29
- "pretest:unit": "npm run sync:version --silent",
30
- "pretest:typecheck": "npm run sync:version --silent",
31
- "pretest:integration": "npm run sync:version --silent",
32
- "prestart": "npm run sync:version --silent",
33
27
  "preversion": "npm test --silent",
34
28
  "version": "npm run sync:version --silent && git add src/generated/version.ts",
35
29
  "dev": "npx tsx src/index.ts",
@@ -37,12 +31,13 @@
37
31
  "test:typecheck": "npx tsc --project tsconfig.tests.json",
38
32
  "prepublishOnly": "npm run build",
39
33
  "start": "node build/index.js",
40
- "test:unit": "node --import ./tests/support/real-provider-path-sentinel.mjs --import tsx --test --test-concurrency=8 tests/unit/*.test.ts",
34
+ "test:unit": "node scripts/run-unit-tests.mjs",
35
+ "test:boundaries": "node scripts/check-import-boundaries.mjs",
41
36
  "test:integration": "node --import ./tests/support/real-provider-path-sentinel.mjs --import tsx --test tests/integration/integration.test.ts",
42
37
  "pretest:cli-expect": "npm run build",
43
38
  "test:cli-expect": "node --import ./tests/support/real-provider-path-sentinel.mjs --test --test-concurrency=8 tests/cli-expect/suite/*.test.mjs",
44
39
  "test:real-providers": "KEIYAKU_REAL_PROVIDERS=1 node --import tsx --test --test-concurrency=1 tests/real-provider/matrix.test.ts",
45
- "test": "npm run test:unit && npm run test:typecheck && npm run test:integration && npm run test:cli-expect",
40
+ "test": "npm run test:unit && npm run test:typecheck && npm run test:boundaries && npm run test:integration && npm run test:cli-expect",
46
41
  "release": "npm version patch && npm publish"
47
42
  },
48
43
  "dependencies": {
@@ -50,7 +45,6 @@
50
45
  "@anthropic-ai/sdk": "^0.110.0",
51
46
  "@earendil-works/pi-coding-agent": "^0.80.10",
52
47
  "@modelcontextprotocol/sdk": "^1.29.0",
53
- "@openai/codex-sdk": "^0.113.0",
54
48
  "@opencode-ai/sdk": "1.18.3",
55
49
  "simple-git": "^3.21.0",
56
50
  "yaml": "^2.9.0",
@@ -21,10 +21,11 @@ Three kinds of objects:
21
21
  ```bash
22
22
  keiyaku task add "fix the flaky pump test" # write the idea down → k-3f9a21c4b0d2
23
23
  keiyaku bind --task k-3f9a21c4b0d2 # start it: branch + worktree
24
- cd .keiyaku/wt/<place> # work and commit here — yourself,
25
- # or: keiyaku call <akuma> "fix it"
24
+ cd .keiyaku/wt/<place> # work here yourself, or call an Akuma
25
+ keiyaku call <akuma> "fix it" # worker leaves reviewed source as a dirty tree
26
+ # host coordinator reviews, then git -C <worktree> add/commit accepted paths
26
27
  keiyaku status # is anything stuck? what's next?
27
- keiyaku petition <<'EOF' # done: seal + settle; the oath is required
28
+ keiyaku petition - <<'EOF' # done: seal + settle; the oath is required
28
29
  ## Oath
29
30
  I fixed the flaky test; suite is green.
30
31
  EOF
@@ -32,7 +33,7 @@ EOF
32
33
  ```
33
34
 
34
35
  That's the whole loop. Skip the task when the work needs no planning:
35
- `keiyaku bind --place NAME --objective TEXT --scope GLOB --checks CHECK` (or the same as Markdown on stdin — `bind --help` shows the shape).
36
+ `keiyaku bind --place NAME --objective TEXT --scope GLOB --checks CHECK` (or the same as Markdown through `keiyaku bind -` — `bind --help` shows the shape).
36
37
 
37
38
  ## Getting oriented
38
39
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: keiyaku-akuma
3
- description: Use when you need an Akuma to work for you — call a devil, watch it, send more instructions, kill it, or revive it from its record.
3
+ description: Use whenever a request mentions an Akuma, akuma, devil, demon, or 恶魔 and the work may involve calling, watching, telling, killing, or reviving one; covers Keiyaku Akuma profiles, projections, artifacts, lifecycle control, and rejoin operations.
4
4
  allowed-tools: Bash(keiyaku *)
5
5
  ---
6
6
 
@@ -8,42 +8,56 @@ allowed-tools: Bash(keiyaku *)
8
8
 
9
9
  Running devils. Contract lifecycle = `keiyaku-workflow` skill.
10
10
 
11
- ## Mind model
11
+ ## Core ideas
12
12
 
13
- - **profile** — a devil you can call (`keiyaku akuma ls`). Markdown file: frontmatter = provider/model config, body = its instructions. Project `.keiyaku/akuma/` > user `~/.keiyaku/akuma/` > builtin.
13
+ - **profile** — callable configuration: frontmatter selects provider/model;
14
+ Markdown body gives instructions. Project overrides user, which overrides builtin.
14
15
  - **projection** (`name/8hex`) — one live run of a devil. This is the id for `wait` / `tell` / `kill`.
15
16
  - **artifact** (`rsp_…`) — what a finished run left behind. This is the id for `revive`.
16
17
 
17
- Rule of thumb: alive → projection id; want to continue a finished/dead one → artifact id.
18
+ Rule of thumb: continue the same conversation with its projection id. Use an
19
+ artifact id with `revive` only when deliberately starting a new projection;
20
+ failed/dead state alone is not a reason to revive.
18
21
 
19
22
  ## Common usage
20
23
 
21
24
  ```bash
22
25
  keiyaku akuma ls # who's callable
26
+ keiyaku akuma view NAME # resolved profile + live projections
23
27
  keiyaku call NAME "do the thing" # run in foreground until it finishes
24
28
  keiyaku call NAME --wait 10m "do the thing" # bounded: after 10m you get a snapshot, it keeps running
25
29
  keiyaku call NAME --detach "do the thing" # fire and forget; note the projection id
26
30
  keiyaku call NAME - < prompt.md # long prompt: literal `-` reads stdin
27
31
  keiyaku wait <proj-id> # join a running one; prints its activity
28
32
  keiyaku wait <proj-id> --timeout 5m # peek for 5m; timeout = snapshot, not death
29
- keiyaku tell <proj-id> "also do X" # drop instructions in its mailbox
30
- keiyaku tell <proj-id> --wait 5m "also do X" # tell, then watch the response
33
+ keiyaku tell <proj-id> [--wait DURATION] <BODY|-> # optionally tell, then bounded-join
31
34
  keiyaku revive rsp_XXXX "continue..." # new run continuing from a record
32
35
  keiyaku kill <proj-id> # stop it; record survives, revive still works
33
36
  keiyaku status # forgot an id? it's on the board
34
37
  ```
35
38
 
39
+ ## Configuration
40
+
41
+ ```text
42
+ <repo>/.keiyaku/akuma/<name>.md project profile
43
+ <KEIYAKU_HOME>/akuma/<name>.md user profile (normally ~/.keiyaku)
44
+ ```
45
+
46
+ Use `keiyaku akuma view NAME` to inspect the resolved source and configuration.
47
+
36
48
  ## Expectations
37
49
 
38
- - `tell` is silent on success: exit 0, zero output. Silence **is** the receipt — do not re-send. Effects show up in `wait`/`status`.
39
- - Body is required for `call`/`tell`. Piping stdin without a literal `-` does nothing.
50
+ - Omit tell's bracketed `--wait DURATION` option for silent success: exit 0,
51
+ zero output. Supply it to perform the ordinary bounded join after acceptance.
52
+ Silence **is** the receipt — do not re-send. Effects show up in `wait`/`status`.
53
+ - Body is required for `call`/`tell`. Literal `-` selects stdin; unselected
54
+ piped bytes are ignored and cannot change an argv body.
40
55
  - A `--wait`/`--timeout` expiry prints a live snapshot and exits 0 — the run continues; rejoin with `wait` anytime.
41
56
  - `wait` is repeatable: on a finished run it shows the final transcript again.
42
- - `--contract ADDR` attaches a call to a contract (by place, slug, or full id); `--bare` skips attachment explicitly. `call` only — `revive` takes neither.
43
57
 
44
58
  ## Occasional flags
45
59
 
46
60
  - `--incognito` — no artifact written, no revive possible (call)
47
61
  - `--effort LEVEL` — provider effort override; needs a model set (call, revive, tell)
48
- - `-C DIR` — run relative to another directory (all akuma commands)
49
- - `--repo DIR` — you almost never need this: the repo is inferred from where you stand (`-C` included). Only for the rare case where the contract ledger lives in a *different* repo. Never pass it the same value as `-C`.
62
+ - `--repo DIR` — override repository inference only when the contract ledger
63
+ lives in a different repository.