@astrosheep/keiyaku 2.9.2 → 2.9.4
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.
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +13 -94
- package/build/agents/harness/activity-values.js +16 -0
- package/build/agents/harness/event-persistence.js +218 -0
- package/build/agents/harness/execution-handle.js +25 -222
- package/build/agents/harness/index.js +3 -1
- package/build/agents/harness/outcome.js +47 -4
- package/build/agents/harness/projection.js +42 -59
- package/build/agents/harness/provider-adapter.js +58 -0
- package/build/agents/harness/router.js +11 -187
- package/build/agents/harness/runtime.js +17 -4
- package/build/agents/harness/session.js +15 -0
- package/build/agents/launch-snapshot/model.js +15 -236
- package/build/agents/launch-snapshot/parse-helpers.js +114 -0
- package/build/agents/launch-snapshot/resolve.js +17 -43
- package/build/agents/opencode-sdk.js +45 -12
- package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
- package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
- package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
- package/build/agents/providers/codex-app-server/adapter.js +50 -53
- package/build/agents/providers/codex-app-server/events.js +67 -11
- package/build/agents/providers/codex-app-server/identity.js +1 -0
- package/build/agents/providers/codex-app-server/policy.js +29 -0
- package/build/agents/providers/codex-app-server/registration.js +248 -0
- package/build/agents/providers/codex-app-server/session-schema.js +6 -0
- package/build/agents/providers/codex-app-server/session.js +23 -17
- package/build/agents/providers/codex-app-server/terms.js +1 -0
- package/build/agents/providers/opencode-sdk/adapter.js +94 -50
- package/build/agents/providers/opencode-sdk/events.js +8 -4
- package/build/agents/providers/opencode-sdk/registration.js +135 -0
- package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
- package/build/agents/providers/opencode-sdk/session.js +21 -14
- package/build/agents/providers/opencode-sdk/terms.js +1 -0
- package/build/agents/providers/pi/adapter.js +36 -30
- package/build/agents/providers/pi/registration.js +145 -0
- package/build/agents/providers/pi/session-schema.js +6 -0
- package/build/agents/providers/pi/terms.js +1 -0
- package/build/agents/providers/registration.js +7 -0
- package/build/agents/providers/registry.js +76 -0
- package/build/agents/selector.js +2 -29
- package/build/agents/types.js +8 -0
- package/build/artifact-directories.js +22 -0
- package/build/cli/commands/akuma/akuma/handler.js +1 -1
- package/build/cli/commands/akuma/view/handler.js +5 -4
- package/build/cli/commands/akuma.js +6 -0
- package/build/cli/commands/contract/amend/handler.js +8 -2
- package/build/cli/commands/contract/arc/handler.js +7 -1
- package/build/cli/commands/contract/bind/handler.js +4 -3
- package/build/cli/commands/contract/bind/meta.js +34 -7
- package/build/cli/commands/contract/forfeit/handler.js +4 -4
- package/build/cli/commands/contract/log/handler.js +3 -3
- package/build/cli/commands/contract/petition/handler.js +4 -6
- package/build/cli/commands/contract/renew/handler.js +20 -6
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +58 -0
- package/build/cli/commands/projection/call/handler.js +6 -5
- package/build/cli/commands/projection/kill/handler.js +16 -32
- package/build/cli/commands/projection/shared.js +28 -0
- package/build/cli/commands/projection/status/handler.js +1 -1
- package/build/cli/commands/projection/tell/handler.js +14 -30
- package/build/cli/commands/projection/wait/handler.js +45 -22
- package/build/cli/commands/projection/wait/meta.js +7 -2
- package/build/cli/commands/registry.js +2 -54
- package/build/cli/commands/shared.js +10 -7
- package/build/cli/commands/skills/install/handler.js +2 -5
- package/build/cli/commands/skills/skills/handler.js +1 -1
- package/build/cli/commands/task/add/handler.js +5 -13
- package/build/cli/commands/task/catalog.js +2 -2
- package/build/cli/commands/task/done/handler.js +4 -8
- package/build/cli/commands/task/drop/handler.js +3 -6
- package/build/cli/commands/task/ls/handler.js +3 -5
- package/build/cli/commands/task/shared.js +14 -4
- package/build/cli/commands/task/{view → show}/handler.js +7 -8
- package/build/cli/commands/task/show/meta.js +8 -0
- package/build/cli/commands/task/start/handler.js +3 -6
- package/build/cli/commands/task/stop/handler.js +4 -8
- package/build/cli/commands/task/task/handler.js +1 -1
- package/build/cli/commands/task/update/handler.js +4 -8
- package/build/cli/completion.js +1 -1
- package/build/cli/flags.js +19 -3
- package/build/cli/help.js +4 -11
- package/build/cli/index.js +57 -83
- package/build/cli/missing-subcommand.js +7 -0
- package/build/cli/parse-flags.js +14 -5
- package/build/cli/parse-metadata.js +3 -3
- package/build/cli/parse-selectors.js +1 -1
- package/build/cli/parse.js +21 -0
- package/build/cli/projection-address.js +7 -12
- package/build/cli/render/address.js +41 -37
- package/build/cli/render/arc.js +24 -12
- package/build/cli/render/call.js +11 -5
- package/build/cli/render/errors.js +36 -1
- package/build/cli/render/format.js +4 -0
- package/build/cli/render/kanshi.js +6 -12
- package/build/cli/render/kill.js +9 -0
- package/build/cli/render/misc.js +55 -12
- package/build/cli/render/petition.js +20 -15
- package/build/cli/render/projection-activity.js +3 -0
- package/build/cli/render/shared.js +60 -21
- package/build/cli/render/skills.js +10 -0
- package/build/cli/render/status.js +37 -14
- package/build/cli/render/success-response.js +77 -0
- package/build/cli/render/task.js +54 -14
- package/build/cli/render/tell.js +5 -0
- package/build/cli/render/terminal-failure.js +58 -0
- package/build/cli/render/types.js +1 -0
- package/build/cli/render/wait.js +38 -36
- package/build/cli/types.js +1 -1
- package/build/config/akuma-loader.js +0 -5
- package/build/config/env-keys.js +1 -1
- package/build/config/provider-policy-ownership.js +3 -32
- package/build/config/settings/disease.js +8 -1
- package/build/config/settings/loader.js +11 -3
- package/build/config/settings/provider-instance-fields.js +51 -0
- package/build/config/settings/schema.js +6 -176
- package/build/core/addressing.js +66 -16
- package/build/core/amend.js +135 -18
- package/build/core/arc.js +19 -67
- package/build/core/bind.js +82 -35
- package/build/core/call/call.js +15 -9
- package/build/core/call/context.js +6 -51
- package/build/core/call/execution.js +21 -22
- package/build/core/call/prompt.js +14 -1
- package/build/core/command-markdown.js +45 -0
- package/build/core/context.js +13 -7
- package/build/core/contract-view.js +20 -3
- package/build/core/draft.js +123 -16
- package/build/core/forfeit.js +5 -2
- package/build/core/hints.js +1 -75
- package/build/core/identifier-slug.js +66 -0
- package/build/core/ids.js +25 -11
- package/build/core/ledger.js +20 -68
- package/build/core/markdown/parser.js +0 -68
- package/build/core/markdown/render.js +68 -0
- package/build/core/markdown/sections.js +2 -1
- package/build/core/outcome-base.js +4 -0
- package/build/core/projection/generation/database.js +2 -38
- package/build/core/projection/generation/model.js +42 -1
- package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
- package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
- package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
- package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
- package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
- package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
- package/build/core/projection/generation/store.js +2 -2
- package/build/core/projection/generation/transitions.js +1 -1
- package/build/core/projection/index.js +27 -0
- package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
- package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
- package/build/core/projection/projection-coordinate.js +61 -0
- package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
- package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
- package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
- package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
- package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
- package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
- package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
- package/build/core/projection/projection-terminal-failure.js +104 -0
- package/build/core/projection/projection-wait.js +278 -0
- package/build/core/projection/projection-wake.js +203 -0
- package/build/core/provider-loader-ports.js +4 -0
- package/build/core/ref-log.js +65 -40
- package/build/core/registry.js +63 -10
- package/build/core/renew-session.js +128 -0
- package/build/core/renew.js +426 -99
- package/build/core/scope.js +143 -66
- package/build/core/seal.js +139 -35
- package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
- package/build/core/{claim.js → settlement/claim.js} +32 -8
- package/build/core/settlement/index.js +19 -0
- package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
- package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
- package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
- package/build/core/settlement/petition-io.js +1 -0
- package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
- package/build/core/{petition.js → settlement/petition.js} +12 -10
- package/build/core/settlement/queue-read-model.js +143 -0
- package/build/core/settlement/queue-seat-allocation.js +156 -0
- package/build/core/settlement/queue.js +6 -0
- package/build/core/settlement/settlement.js +295 -0
- package/build/core/{verdict.js → settlement/verdict.js} +9 -37
- package/build/core/status/board.js +24 -23
- package/build/core/status/ledger-batch.js +158 -0
- package/build/core/status/reconciliation.js +10 -4
- package/build/core/stored-agent-event.js +3 -0
- package/build/core/task/board.js +40 -4
- package/build/core/task/commands.js +30 -21
- package/build/core/task/index.js +12 -0
- package/build/core/task/settlement-git.js +157 -65
- package/build/core/task/settlement-policy.js +54 -24
- package/build/core/task/task-contract.js +370 -0
- package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
- package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
- package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
- package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
- package/build/core/task/task-repository.js +1 -0
- package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
- package/build/core/task/task-store.js +1 -0
- package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
- package/build/core/task/task.js +5 -0
- package/build/core/task/tree.js +37 -0
- package/build/core/task/validation.js +5 -2
- package/build/core/transcripts.js +15 -3
- package/build/core/worktree-path.js +43 -28
- package/build/flow-error.js +85 -39
- package/build/fs/durability.js +27 -0
- package/build/fs/path-coordinate.js +16 -0
- package/build/generated/version.js +3 -2
- package/build/git/branches.js +2 -1
- package/build/git/core.js +106 -2
- package/build/git/path-coordinate.js +17 -0
- package/build/git/refs.js +57 -0
- package/build/git/streaming-batch.js +301 -0
- package/build/git/worktree.js +65 -0
- package/build/index.js +3 -3
- package/build/keiyaku.js +2 -36
- package/package.json +4 -10
- package/skills/keiyaku/SKILL.md +3 -2
- package/skills/keiyaku-akuma/SKILL.md +10 -6
- package/skills/keiyaku-task/SKILL.md +1 -1
- package/skills/keiyaku-workflow/SKILL.md +25 -4
- package/build/agents/providers/codex-sdk.js +0 -329
- package/build/cli/commands/task/view/meta.js +0 -8
- package/build/core/command-io.js +0 -174
- package/build/core/path-coordinate.js +0 -27
- package/build/core/petition-claim.js +0 -117
- package/build/core/petition-run.js +0 -83
- package/build/core/projection-coordinate.js +0 -63
- package/build/core/projection-wait.js +0 -165
- package/build/core/projection-wake.js +0 -136
- package/build/core/queue.js +0 -343
- package/build/core/task-contract.js +0 -237
- package/build/core/task.js +0 -4
- /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
- /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
- /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
- /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
- /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, wrapGitError } from "./core.js";
|
|
2
|
+
const ZERO_OBJECT_ID = "0000000000000000000000000000000000000000";
|
|
3
|
+
async function resolveRefOrNull(cwd, ref) {
|
|
4
|
+
const git = createGit(cwd);
|
|
5
|
+
try {
|
|
6
|
+
return (await git.raw(["rev-parse", "--verify", ref])).trim();
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
if (errorContainsAnyPattern(error, MISSING_HEAD_PATTERNS))
|
|
10
|
+
return null;
|
|
11
|
+
throw wrapGitError(`rev-parse ${ref}`, error, cwd);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Create an immutable recovery ref, accepting an earlier identical attempt. */
|
|
15
|
+
export async function createRecoveryRef(cwd, ref, objectId) {
|
|
16
|
+
const existing = await resolveRefOrNull(cwd, ref);
|
|
17
|
+
if (existing === objectId)
|
|
18
|
+
return;
|
|
19
|
+
if (existing !== null) {
|
|
20
|
+
throw new Error(`recovery ref ${ref} already resolves to ${existing}, not ${objectId}`);
|
|
21
|
+
}
|
|
22
|
+
const git = createGit(cwd);
|
|
23
|
+
try {
|
|
24
|
+
await git.raw(["update-ref", ref, objectId, ZERO_OBJECT_ID]);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
// Another identical attempt may have won the create race.
|
|
28
|
+
if (await resolveRefOrNull(cwd, ref) === objectId)
|
|
29
|
+
return;
|
|
30
|
+
throw wrapGitError(`update-ref ${ref}`, error, cwd);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Remove only the exact attempt ref, returning truthful non-fatal cleanup facts. */
|
|
34
|
+
export async function deleteRecoveryRef(cwd, ref, objectId) {
|
|
35
|
+
try {
|
|
36
|
+
const existing = await resolveRefOrNull(cwd, ref);
|
|
37
|
+
if (existing === null)
|
|
38
|
+
return { ref, status: "already-absent" };
|
|
39
|
+
if (existing !== objectId) {
|
|
40
|
+
return {
|
|
41
|
+
ref,
|
|
42
|
+
status: "retained",
|
|
43
|
+
diagnostic: `ref resolves to ${existing}, not expected object ${objectId}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const git = createGit(cwd);
|
|
47
|
+
await git.raw(["update-ref", "-d", ref, objectId]);
|
|
48
|
+
return { ref, status: "removed" };
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
ref,
|
|
53
|
+
status: "retained",
|
|
54
|
+
diagnostic: wrapGitError(`update-ref -d ${ref}`, error, cwd).message,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -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
|
+
}
|
package/build/git/worktree.js
CHANGED
|
@@ -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,10 +1,10 @@
|
|
|
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
6
|
import { logErrorWithCause } from "./telemetry/logger.js";
|
|
7
|
-
import { runProjectionGenerationRuntime } from "./core/projection
|
|
7
|
+
import { runProjectionGenerationRuntime } from "./core/projection/index.js";
|
|
8
8
|
import { renderCliHelp, runCliCommand } from "./cli/index.js";
|
|
9
9
|
import { installCliOutputStreamErrorPolicy } from "./cli/output-stream-error-policy.js";
|
|
10
10
|
import { assertSubagentCliCommandAllowed } from "./cli/subagent-guard.js";
|
|
@@ -45,7 +45,7 @@ async function main() {
|
|
|
45
45
|
args: cliArgs,
|
|
46
46
|
});
|
|
47
47
|
if (cliArgs.length === 1 && cliArgs[0] === "--version") {
|
|
48
|
-
console.log(packageVersion);
|
|
48
|
+
console.log(GIT_HASH ? `${packageVersion} (${GIT_HASH})` : packageVersion);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
if (cliArgs.length === 0 || (cliArgs.length === 1 && (cliArgs[0] === "--help" || cliArgs[0] === "-h"))) {
|
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
|
|
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"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/keiyaku",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.4",
|
|
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
|
|
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",
|
package/skills/keiyaku/SKILL.md
CHANGED
|
@@ -21,8 +21,9 @@ 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
|
|
25
|
-
|
|
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
28
|
keiyaku petition <<'EOF' # done: seal + settle; the oath is required
|
|
28
29
|
## Oath
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-akuma
|
|
3
|
-
description: Use
|
|
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
|
|
|
@@ -14,7 +14,9 @@ Running devils. Contract lifecycle = `keiyaku-workflow` skill.
|
|
|
14
14
|
- **projection** (`name/8hex`) — one live run of a devil. This is the id for `wait` / `tell` / `kill`.
|
|
15
15
|
- **artifact** (`rsp_…`) — what a finished run left behind. This is the id for `revive`.
|
|
16
16
|
|
|
17
|
-
Rule of thumb:
|
|
17
|
+
Rule of thumb: continue the same conversation with its projection id. Use an
|
|
18
|
+
artifact id with `revive` only when deliberately starting a new projection;
|
|
19
|
+
failed/dead state alone is not a reason to revive.
|
|
18
20
|
|
|
19
21
|
## Common usage
|
|
20
22
|
|
|
@@ -26,8 +28,7 @@ keiyaku call NAME --detach "do the thing" # fire and forget; note the projec
|
|
|
26
28
|
keiyaku call NAME - < prompt.md # long prompt: literal `-` reads stdin
|
|
27
29
|
keiyaku wait <proj-id> # join a running one; prints its activity
|
|
28
30
|
keiyaku wait <proj-id> --timeout 5m # peek for 5m; timeout = snapshot, not death
|
|
29
|
-
keiyaku tell <proj-id>
|
|
30
|
-
keiyaku tell <proj-id> --wait 5m "also do X" # tell, then watch the response
|
|
31
|
+
keiyaku tell <proj-id> [--wait DURATION] <BODY|-> # optionally tell, then bounded-join
|
|
31
32
|
keiyaku revive rsp_XXXX "continue..." # new run continuing from a record
|
|
32
33
|
keiyaku kill <proj-id> # stop it; record survives, revive still works
|
|
33
34
|
keiyaku status # forgot an id? it's on the board
|
|
@@ -35,14 +36,17 @@ keiyaku status # forgot an id? it's on the board
|
|
|
35
36
|
|
|
36
37
|
## Expectations
|
|
37
38
|
|
|
38
|
-
-
|
|
39
|
+
- Omit tell's bracketed `--wait DURATION` option for silent success: exit 0,
|
|
40
|
+
zero output. Supply it to perform the ordinary bounded join after acceptance.
|
|
41
|
+
Silence **is** the receipt — do not re-send. Effects show up in `wait`/`status`.
|
|
39
42
|
- Body is required for `call`/`tell`. Piping stdin without a literal `-` does nothing.
|
|
40
43
|
- A `--wait`/`--timeout` expiry prints a live snapshot and exits 0 — the run continues; rejoin with `wait` anytime.
|
|
41
44
|
- `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`
|
|
45
|
+
- Only `--contract ADDR` attaches a new call to a contract (by place, slug, or full id). Without it, call is standalone; `--bare` remains an explicit synonym. `call` only — `revive` takes neither and follows artifact provenance.
|
|
43
46
|
|
|
44
47
|
## Occasional flags
|
|
45
48
|
|
|
46
49
|
- `--incognito` — no artifact written, no revive possible (call)
|
|
47
50
|
- `--effort LEVEL` — provider effort override; needs a model set (call, revive, tell)
|
|
48
51
|
- `-C DIR` — run relative to another directory (all akuma commands)
|
|
52
|
+
- `--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`.
|
|
@@ -24,7 +24,7 @@ open ⇄ in_progress → done | drop
|
|
|
24
24
|
keiyaku task add "fix the flaky pump test" # body: pipe stdin for details
|
|
25
25
|
keiyaku task add "title" --pri 1 --needs k-aaa # priority + dependency at birth
|
|
26
26
|
keiyaku task ls # all open, priority order
|
|
27
|
-
keiyaku task
|
|
27
|
+
keiyaku task show k-xxxx # full detail and parent-children tree
|
|
28
28
|
keiyaku task start k-xxxx # mark in_progress (start ≠ execute)
|
|
29
29
|
keiyaku task stop k-xxxx # back to open
|
|
30
30
|
keiyaku task done k-xxxx
|