@astrosheep/keiyaku 2.9.10 → 2.9.12
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/README.md +86 -45
- package/build/.tsbuildinfo +1 -1
- package/build/agents/providers/claude-agent-sdk/adapter.js +15 -2
- package/build/cli/commands/contract/bind/meta.js +4 -4
- package/build/cli/commands/metadata.js +3 -2
- package/build/cli/commands/projection/kill/handler.js +4 -5
- package/build/cli/commands/shared.js +1 -1
- package/build/cli/commands/task/catalog.js +2 -0
- package/build/cli/commands/task/compose/handler.js +18 -0
- package/build/cli/commands/task/compose/meta.js +10 -0
- package/build/cli/flags.js +5 -1
- package/build/cli/index.js +11 -6
- package/build/cli/parse-flags.js +21 -21
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +4 -4
- package/build/cli/parse.js +23 -23
- package/build/cli/projection-address.js +7 -7
- package/build/cli/render/task.js +27 -0
- package/build/cli/types.js +1 -1
- package/build/core/addressing.js +3 -3
- package/build/core/call/context.js +3 -3
- package/build/core/call/prompt.js +1 -1
- package/build/core/contract-view.js +1 -1
- package/build/core/draft.js +51 -22
- package/build/core/projection/generation/model.js +1 -1
- package/build/core/projection/generation/projection-generation-runner.js +13 -3
- package/build/core/projection/generation/transitions.js +10 -0
- package/build/core/projection/projection-history.js +1 -1
- package/build/core/projection/projection-kill.js +5 -4
- package/build/core/projection/projection-runner-lock.js +58 -229
- package/build/core/projection/projection-wait.js +2 -2
- package/build/core/projection/projection-wake.js +2 -0
- package/build/core/repository-ledger/claim-evidence.js +1 -1
- package/build/core/repository-ledger/current-state-store.js +455 -337
- package/build/core/repository-ledger/read-model.js +3 -3
- package/build/core/repository-ledger/write-transaction.js +15 -13
- package/build/core/sqlite-process-lifetime-lock.js +194 -0
- package/build/core/task/compose.js +327 -0
- package/build/core/task/index.js +1 -0
- package/build/core/task/settlement-git.js +69 -26
- package/build/core/task/task-git-store.js +1 -0
- package/build/core/task/task-store-repository.js +1 -0
- package/build/flow-error.js +6 -1
- package/build/generated/version.js +2 -2
- package/package.json +2 -2
- package/skills/keiyaku-akuma/SKILL.md +6 -1
- package/skills/keiyaku-task/SKILL.md +23 -1
- package/skills/keiyaku-workflow/SKILL.md +33 -18
|
@@ -3,7 +3,7 @@ import { SUBAGENT_SESSION_VERSION } from "../../session.js";
|
|
|
3
3
|
import { createProjectionDriver } from "../../harness/execution-handle.js";
|
|
4
4
|
import { exactLaunchInputReceipts } from "../../harness/control-types.js";
|
|
5
5
|
import { createProviderFailure } from "../../harness/provider-adapter.js";
|
|
6
|
-
import { buildSubagentEnv, createAbortError, emitProviderEvidence, linkAbortSignal, sha256Utf8 } from "../../harness/runtime.js";
|
|
6
|
+
import { awaitProviderAbortSettlement, buildSubagentEnv, createAbortError, emitProviderEvidence, linkAbortSignal, sha256Utf8 } from "../../harness/runtime.js";
|
|
7
7
|
import { claudeIngressDisposition, emitClaudeThoughts, emitClaudeToolResults, emitClaudeToolStarts, emitClaudeUsage, extractAssistantMessage, extractAssistantMessageId, extractAssistantMessageModel, extractOverloadFallbackWarning, extractResultText, extractResumeToken, isClaudeApiRetryEvent, isClaudeModelRefusalFallbackEvent, isClaudeModelRefusalNoFallbackEvent, mapClaudeApiRetryWarning, } from "./events.js";
|
|
8
8
|
import { CLAUDE_AGENT_SDK_PROVIDER, createPushableUserMessageStream, toClaudeUserMessage, } from "./session.js";
|
|
9
9
|
function toClaudeSdkEffort(effort) {
|
|
@@ -19,6 +19,10 @@ async function loadClaudeAgentSdk(loader) {
|
|
|
19
19
|
export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
20
20
|
const sessionId = options.sessionId?.trim();
|
|
21
21
|
let liveQuery;
|
|
22
|
+
let queryAbortController;
|
|
23
|
+
let runStarted = false;
|
|
24
|
+
let settleRun;
|
|
25
|
+
const runSettled = new Promise((resolve) => { settleRun = resolve; });
|
|
22
26
|
let pendingEvidenceDiagnostics = 0;
|
|
23
27
|
let builderDiagnostic = () => {
|
|
24
28
|
pendingEvidenceDiagnostics += 1;
|
|
@@ -117,9 +121,13 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
117
121
|
queryActive = false;
|
|
118
122
|
const abortError = createAbortError(`${CLAUDE_AGENT_SDK_PROVIDER} exec cancelled by client`);
|
|
119
123
|
if (mode === "force") {
|
|
124
|
+
queryAbortController?.abort(abortError);
|
|
120
125
|
inputStream?.end(abortError);
|
|
121
126
|
rejectPendingEnqueueWaiters(abortError);
|
|
122
127
|
liveQuery?.close();
|
|
128
|
+
if (runStarted) {
|
|
129
|
+
await awaitProviderAbortSettlement(CLAUDE_AGENT_SDK_PROVIDER, runSettled, "forced query teardown did not settle");
|
|
130
|
+
}
|
|
123
131
|
return;
|
|
124
132
|
}
|
|
125
133
|
try {
|
|
@@ -131,8 +139,12 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
131
139
|
liveQuery?.close();
|
|
132
140
|
inputStream?.end(abortError);
|
|
133
141
|
rejectPendingEnqueueWaiters(abortError);
|
|
142
|
+
if (runStarted) {
|
|
143
|
+
await awaitProviderAbortSettlement(CLAUDE_AGENT_SDK_PROVIDER, runSettled, "graceful query teardown did not settle");
|
|
144
|
+
}
|
|
134
145
|
},
|
|
135
146
|
async run(builder) {
|
|
147
|
+
runStarted = true;
|
|
136
148
|
builderDiagnostic = (text) => builder.emit({ type: "diagnostic", text });
|
|
137
149
|
emitTurnStarted = () => builder.emit({ type: "turn", phase: "started" });
|
|
138
150
|
while (pendingEvidenceDiagnostics > 0) {
|
|
@@ -140,7 +152,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
140
152
|
builderDiagnostic("provider evidence callback failed");
|
|
141
153
|
}
|
|
142
154
|
const sdk = await loadClaudeAgentSdk(options.loader);
|
|
143
|
-
|
|
155
|
+
queryAbortController = new AbortController();
|
|
144
156
|
const unlinkBuilderAbort = linkAbortSignal(builder.signal, queryAbortController, () => undefined);
|
|
145
157
|
const unlinkOuterAbort = linkAbortSignal(options.signal, queryAbortController, () => undefined);
|
|
146
158
|
try {
|
|
@@ -375,6 +387,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
|
375
387
|
inputStream.end(endError);
|
|
376
388
|
}
|
|
377
389
|
rejectPendingEnqueueWaiters(endError);
|
|
390
|
+
settleRun();
|
|
378
391
|
}
|
|
379
392
|
},
|
|
380
393
|
});
|
|
@@ -5,7 +5,7 @@ export const meta = {
|
|
|
5
5
|
"keiyaku @<full-contract-id> bind [-C|--cwd DIR] [--repo DIR]",
|
|
6
6
|
"keiyaku bind --contract <full-contract-id> [-C|--cwd DIR] [--repo DIR]",
|
|
7
7
|
"keiyaku bind [-C|--cwd DIR] [--repo DIR] [--task ID]... [- < contract.md]",
|
|
8
|
-
"keiyaku bind [-C|--cwd DIR] [--repo DIR] [--task ID]... --title TEXT --context TEXT --objective TEXT --design TEXT --scope GLOB --criteria CRITERION [--verification DECLARATION] [--dry-run]",
|
|
8
|
+
"keiyaku bind [-C|--cwd DIR] [--repo DIR] [--task ID]... --title TEXT --context TEXT --objective TEXT --design TEXT --scope GLOB [--scope GLOB]... --criteria CRITERION [--verification DECLARATION] [--dry-run]",
|
|
9
9
|
],
|
|
10
10
|
stdin: { mode: "document", selector: { kind: "positional", index: 0, required: false, rejectBlank: false } },
|
|
11
11
|
flags: ["cwd", "repo", "contract", "after", "exclusive", "draft-only", "title", "context", "objective", "design", "scope", "criteria", "verification", "task", "dry-run"],
|
|
@@ -21,7 +21,7 @@ export const meta = {
|
|
|
21
21
|
"## Objective",
|
|
22
22
|
"<what the commission should accomplish>",
|
|
23
23
|
"",
|
|
24
|
-
"## Design",
|
|
24
|
+
"## Design & Approach",
|
|
25
25
|
"<settled blueprint>",
|
|
26
26
|
"",
|
|
27
27
|
"## Scope",
|
|
@@ -54,7 +54,7 @@ export const meta = {
|
|
|
54
54
|
"## Objective",
|
|
55
55
|
"<what the commission should accomplish>",
|
|
56
56
|
"",
|
|
57
|
-
"## Design",
|
|
57
|
+
"## Design & Approach",
|
|
58
58
|
"<settled blueprint>",
|
|
59
59
|
"",
|
|
60
60
|
"## Scope",
|
|
@@ -75,7 +75,7 @@ export const meta = {
|
|
|
75
75
|
"## Context",
|
|
76
76
|
"<established facts and constraints>",
|
|
77
77
|
"",
|
|
78
|
-
"## Design",
|
|
78
|
+
"## Design & Approach",
|
|
79
79
|
"<settled blueprint>",
|
|
80
80
|
"",
|
|
81
81
|
"## Scope",
|
|
@@ -22,6 +22,7 @@ import { meta as statusMeta } from "./projection/status/meta.js";
|
|
|
22
22
|
import { meta as historyMeta } from "./projection/history/meta.js";
|
|
23
23
|
import { meta as taskMeta } from "./task/task/meta.js";
|
|
24
24
|
import { meta as taskAddMeta } from "./task/add/meta.js";
|
|
25
|
+
import { meta as taskComposeMeta } from "./task/compose/meta.js";
|
|
25
26
|
import { meta as taskShowMeta } from "./task/show/meta.js";
|
|
26
27
|
import { meta as taskLsMeta } from "./task/ls/meta.js";
|
|
27
28
|
import { meta as taskLogMeta } from "./task/log/meta.js";
|
|
@@ -38,14 +39,14 @@ export const COMMAND_METADATA_ORDER = [
|
|
|
38
39
|
"bind", "arc", "call", "revive", "wait", "tell", "kill", "log",
|
|
39
40
|
"akuma", "akuma ls", "akuma show", "skills", "skills install", "amend", "audit",
|
|
40
41
|
"renew", "petition", "forfeit", "guide", "completion", "status", "history", "task",
|
|
41
|
-
"task add", "task show", "task ls", "task log", "task start", "task stop", "task hold", "task resume", "task done",
|
|
42
|
+
"task add", "task compose", "task show", "task ls", "task log", "task start", "task stop", "task hold", "task resume", "task done",
|
|
42
43
|
"task drop", "task update", "task doctor", "dump-env",
|
|
43
44
|
];
|
|
44
45
|
const COMMAND_METADATA = [
|
|
45
46
|
bindMeta, arcMeta, callMeta, reviveMeta, waitMeta, tellMeta, killMeta, logMeta,
|
|
46
47
|
akumaMeta, akumaLsMeta, akumaShowMeta, skillsMeta, skillsInstallMeta, amendMeta, auditMeta,
|
|
47
48
|
renewMeta, petitionMeta, forfeitMeta, guideMeta, completionMeta, statusMeta, historyMeta,
|
|
48
|
-
taskMeta, taskAddMeta, taskShowMeta, taskLsMeta, taskLogMeta, taskStartMeta, taskStopMeta,
|
|
49
|
+
taskMeta, taskAddMeta, taskComposeMeta, taskShowMeta, taskLsMeta, taskLogMeta, taskStartMeta, taskStopMeta,
|
|
49
50
|
taskHoldMeta, taskResumeMeta,
|
|
50
51
|
taskDoneMeta, taskDropMeta, taskUpdateMeta, taskDoctorMeta, dumpEnvMeta,
|
|
51
52
|
];
|
|
@@ -10,12 +10,11 @@ export const handler = async (_stdin, flags, signal, positional) => runProjectio
|
|
|
10
10
|
throw new FlowError("EMPTY_PARAM", "kill requires a projection address");
|
|
11
11
|
const projection = await resolveProjectionCommandTarget(ctx, projectionAddress, "kill");
|
|
12
12
|
const result = killProjectionGeneration(projection.directory);
|
|
13
|
-
if (result.status === "already-closed") {
|
|
14
|
-
return buildKillAlreadyClosedResponse(projection.id);
|
|
15
|
-
}
|
|
16
13
|
if (flags.timeoutMs === undefined) {
|
|
17
|
-
return
|
|
14
|
+
return result.status === "already-closed"
|
|
15
|
+
? buildKillAlreadyClosedResponse(projection.id)
|
|
16
|
+
: buildKillAcceptedResponse(projection.id);
|
|
18
17
|
}
|
|
19
|
-
//
|
|
18
|
+
// --wait always replays ordinary wait, including already-terminal generations.
|
|
20
19
|
return buildWaitResponse(projection.id, await waitForProjection({ projectionDirectory: projection.directory, timeoutMs: flags.timeoutMs, signal }), { alias: projection.alias });
|
|
21
20
|
});
|
|
@@ -54,7 +54,7 @@ export async function repositoryDirectory(flags, operation) {
|
|
|
54
54
|
const asTyped = flags.contractAddressSource === "at-address"
|
|
55
55
|
? `@${flags.contractId}`
|
|
56
56
|
: `--contract ${flags.contractId}`;
|
|
57
|
-
throw new AddressResolutionError("
|
|
57
|
+
throw new AddressResolutionError("UNKNOWN_ADDRESS", `${asTyped} is a commission address, but ${candidate} has no repository ledger; contract addressing requires a ledger`, buildResolutionAttempt({
|
|
58
58
|
repo: { kind: "user" },
|
|
59
59
|
workdir: effectiveDirectory(flags),
|
|
60
60
|
spelling: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { meta as taskMeta } from "./task/meta.js";
|
|
2
2
|
import { meta as addMeta } from "./add/meta.js";
|
|
3
|
+
import { meta as composeMeta } from "./compose/meta.js";
|
|
3
4
|
import { meta as showMeta } from "./show/meta.js";
|
|
4
5
|
import { meta as lsMeta } from "./ls/meta.js";
|
|
5
6
|
import { meta as logMeta } from "./log/meta.js";
|
|
@@ -14,6 +15,7 @@ import { meta as doctorMeta } from "./doctor/meta.js";
|
|
|
14
15
|
export const taskCatalog = [
|
|
15
16
|
{ meta: taskMeta, load: () => import("./task/handler.js") },
|
|
16
17
|
{ meta: addMeta, load: () => import("./add/handler.js") },
|
|
18
|
+
{ meta: composeMeta, load: () => import("./compose/handler.js") },
|
|
17
19
|
{ meta: showMeta, load: () => import("./show/handler.js") },
|
|
18
20
|
{ meta: lsMeta, load: () => import("./ls/handler.js") },
|
|
19
21
|
{ meta: logMeta, load: () => import("./log/handler.js") },
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { composeTasks, openTaskMutationContext, planTaskComposition, resolveTaskCreator, } from "../../../../core/task/index.js";
|
|
2
|
+
import { renderTaskComposition, renderTaskCompositionJson } from "../../../render/task.js";
|
|
3
|
+
import { textResponse } from "../../../render/shared.js";
|
|
4
|
+
import { effectiveDirectory } from "../../shared.js";
|
|
5
|
+
export const handler = async (stdin, flags, signal) => {
|
|
6
|
+
const cwd = effectiveDirectory(flags);
|
|
7
|
+
const context = await openTaskMutationContext(cwd, signal);
|
|
8
|
+
const input = {
|
|
9
|
+
sketch: stdin,
|
|
10
|
+
creator: await resolveTaskCreator(cwd),
|
|
11
|
+
namespace: context.namespace,
|
|
12
|
+
now: () => new Date(),
|
|
13
|
+
};
|
|
14
|
+
const plan = flags.check
|
|
15
|
+
? planTaskComposition(await context.repository.read(), input)
|
|
16
|
+
: await composeTasks(context.repository, input);
|
|
17
|
+
return textResponse(flags.json ? renderTaskCompositionJson(plan, Boolean(flags.check)) : renderTaskComposition(plan, Boolean(flags.check)));
|
|
18
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
command: "task compose",
|
|
3
|
+
summary: "Atomically compose tasks from a tree sketch",
|
|
4
|
+
usage: ["keiyaku task compose [-C|--cwd DIR] [--check] - < sketch.md"],
|
|
5
|
+
stdin: { mode: "document", selector: { kind: "positional", index: 0, required: true, rejectBlank: true } },
|
|
6
|
+
flags: ["cwd", "check", "json"],
|
|
7
|
+
purpose: "Atomically create and rewire tasks from one validated tree sketch.",
|
|
8
|
+
input: ["Each task line is two-space indented + title or @COORDINATE, followed by whitespace-separated parent=, needs=@a,@b, supersedes=@c, or relates=@d declarations. Bare prose is allowed only in a nested + task body."],
|
|
9
|
+
notes: ["Nesting assigns parent. Top-level references preserve their parent; parent= clears it. Omitted relation declarations preserve their current complete set, while an explicit empty declaration clears that set."],
|
|
10
|
+
};
|
package/build/cli/flags.js
CHANGED
|
@@ -103,7 +103,7 @@ export const FLAG_SPECS = {
|
|
|
103
103
|
scope: {
|
|
104
104
|
kind: "value",
|
|
105
105
|
placeholder: "GLOB",
|
|
106
|
-
description: "
|
|
106
|
+
description: "Add an ordered scope pattern to flag-generated bind Markdown. May be repeated.",
|
|
107
107
|
},
|
|
108
108
|
criteria: {
|
|
109
109
|
kind: "value",
|
|
@@ -138,6 +138,10 @@ export const FLAG_SPECS = {
|
|
|
138
138
|
kind: "boolean",
|
|
139
139
|
description: "Preview bind input or petition preflight without mutation.",
|
|
140
140
|
},
|
|
141
|
+
check: {
|
|
142
|
+
kind: "boolean",
|
|
143
|
+
description: "Derive and render the task composition without changing the board or Git state.",
|
|
144
|
+
},
|
|
141
145
|
"append-scope": {
|
|
142
146
|
kind: "value",
|
|
143
147
|
placeholder: "PATTERN",
|
package/build/cli/index.js
CHANGED
|
@@ -37,10 +37,10 @@ function prepareBindInput(stdin, flags, selectsStdin) {
|
|
|
37
37
|
const nonblankStdin = stdin.trim().length > 0;
|
|
38
38
|
const domainFlags = hasBindDomainFlags(flags);
|
|
39
39
|
if (flags.dryRun && flags.draftOnly) {
|
|
40
|
-
throw new FlowError("
|
|
40
|
+
throw new FlowError("INVALID_USAGE", "--dry-run cannot be combined with --draft-only");
|
|
41
41
|
}
|
|
42
42
|
if (selectsStdin && domainFlags) {
|
|
43
|
-
throw new FlowError("
|
|
43
|
+
throw new FlowError("INVALID_USAGE", "bind accepts either stdin Markdown or domain flags, not both");
|
|
44
44
|
}
|
|
45
45
|
if (domainFlags) {
|
|
46
46
|
const missing = [];
|
|
@@ -74,7 +74,10 @@ function prepareBindInput(stdin, flags, selectsStdin) {
|
|
|
74
74
|
if (flags.bindTaskIds !== undefined && flags.bindTaskIds.length > 0) {
|
|
75
75
|
return "";
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
const message = selectsStdin
|
|
78
|
+
? `No Markdown input received on stdin.\n\nExample:\n ${usageExample("bind")}`
|
|
79
|
+
: "bind requires literal '-' for Markdown input or the required domain flags";
|
|
80
|
+
throw new FlowError(selectsStdin ? "STDIN_EMPTY" : "EMPTY_PARAM", message);
|
|
78
81
|
}
|
|
79
82
|
return stdin;
|
|
80
83
|
}
|
|
@@ -218,7 +221,7 @@ async function resolveCliInput(parsed) {
|
|
|
218
221
|
const token = selected?.token;
|
|
219
222
|
const selectsStdin = token === "-";
|
|
220
223
|
if (selectsStdin && process.stdin.isTTY) {
|
|
221
|
-
throw new FlowError("
|
|
224
|
+
throw new FlowError("INVALID_USAGE", `${parsed.command} literal '-' requires piped stdin`);
|
|
222
225
|
}
|
|
223
226
|
const piped = selectsStdin ? await readStdin() : "";
|
|
224
227
|
const input = selectsStdin ? piped : token ?? "";
|
|
@@ -231,8 +234,10 @@ async function resolveCliInput(parsed) {
|
|
|
231
234
|
if (token !== undefined && stdinMeta.selector?.rejectBlank && !input.trim()) {
|
|
232
235
|
const message = stdinMeta.mode === "document"
|
|
233
236
|
? `No Markdown input received on stdin.\n\nExample:\n ${usageExample(parsed.command)}`
|
|
234
|
-
:
|
|
235
|
-
|
|
237
|
+
: selectsStdin
|
|
238
|
+
? `${parsed.command} received no body on stdin`
|
|
239
|
+
: `${parsed.command} body cannot be empty`;
|
|
240
|
+
throw new FlowError(selectsStdin ? "STDIN_EMPTY" : "EMPTY_PARAM", message);
|
|
236
241
|
}
|
|
237
242
|
return { stdin: input, flags: selected?.replace(input) ?? parsed.flags, selectsStdin };
|
|
238
243
|
}
|
package/build/cli/parse-flags.js
CHANGED
|
@@ -11,7 +11,7 @@ export function parseFlagName(raw) {
|
|
|
11
11
|
}
|
|
12
12
|
export function assertAllowedFlag(command, name) {
|
|
13
13
|
if (!allowedFlagsForParse(command).includes(name)) {
|
|
14
|
-
throw new FlowError("
|
|
14
|
+
throw new FlowError("UNKNOWN_OPTION", `option --${name} is not valid for ${command}`);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
export function assignFlag(flags, name, value) {
|
|
@@ -65,43 +65,40 @@ export function assignFlag(flags, name, value) {
|
|
|
65
65
|
return;
|
|
66
66
|
case "place":
|
|
67
67
|
if (flags.bindPlace !== undefined) {
|
|
68
|
-
throw new FlowError("
|
|
68
|
+
throw new FlowError("INVALID_USAGE", "option --place may only be specified once");
|
|
69
69
|
}
|
|
70
70
|
flags.bindPlace = String(value);
|
|
71
71
|
return;
|
|
72
72
|
case "objective":
|
|
73
73
|
if (flags.bindObjective !== undefined) {
|
|
74
|
-
throw new FlowError("
|
|
74
|
+
throw new FlowError("INVALID_USAGE", "option --objective may only be specified once");
|
|
75
75
|
}
|
|
76
76
|
flags.bindObjective = String(value);
|
|
77
77
|
return;
|
|
78
78
|
case "context":
|
|
79
79
|
if (flags.bindContext !== undefined) {
|
|
80
|
-
throw new FlowError("
|
|
80
|
+
throw new FlowError("INVALID_USAGE", "option --context may only be specified once");
|
|
81
81
|
}
|
|
82
82
|
flags.bindContext = String(value);
|
|
83
83
|
return;
|
|
84
84
|
case "design":
|
|
85
85
|
if (flags.bindDesign !== undefined) {
|
|
86
|
-
throw new FlowError("
|
|
86
|
+
throw new FlowError("INVALID_USAGE", "option --design may only be specified once");
|
|
87
87
|
}
|
|
88
88
|
flags.bindDesign = String(value);
|
|
89
89
|
return;
|
|
90
90
|
case "scope":
|
|
91
|
-
|
|
92
|
-
throw new FlowError("EMPTY_PARAM", "option --scope may only be specified once");
|
|
93
|
-
}
|
|
94
|
-
flags.bindScope = String(value);
|
|
91
|
+
flags.bindScope = [...(flags.bindScope ?? []), String(value)];
|
|
95
92
|
return;
|
|
96
93
|
case "criteria":
|
|
97
94
|
if (flags.bindCriteria !== undefined) {
|
|
98
|
-
throw new FlowError("
|
|
95
|
+
throw new FlowError("INVALID_USAGE", "option --criteria may only be specified once");
|
|
99
96
|
}
|
|
100
97
|
flags.bindCriteria = String(value);
|
|
101
98
|
return;
|
|
102
99
|
case "verification":
|
|
103
100
|
if (flags.bindVerification !== undefined) {
|
|
104
|
-
throw new FlowError("
|
|
101
|
+
throw new FlowError("INVALID_USAGE", "option --verification may only be specified once");
|
|
105
102
|
}
|
|
106
103
|
flags.bindVerification = String(value);
|
|
107
104
|
return;
|
|
@@ -120,6 +117,9 @@ export function assignFlag(flags, name, value) {
|
|
|
120
117
|
case "dry-run":
|
|
121
118
|
flags.dryRun = Boolean(value);
|
|
122
119
|
return;
|
|
120
|
+
case "check":
|
|
121
|
+
flags.check = Boolean(value);
|
|
122
|
+
return;
|
|
123
123
|
case "append-scope":
|
|
124
124
|
flags.appendScope = [...(flags.appendScope ?? []), String(value)];
|
|
125
125
|
return;
|
|
@@ -152,7 +152,7 @@ export function assignFlag(flags, name, value) {
|
|
|
152
152
|
return;
|
|
153
153
|
case "target":
|
|
154
154
|
if (flags.target !== undefined) {
|
|
155
|
-
throw new FlowError("
|
|
155
|
+
throw new FlowError("INVALID_USAGE", "option --target may only be specified once");
|
|
156
156
|
}
|
|
157
157
|
flags.target = String(value);
|
|
158
158
|
return;
|
|
@@ -162,7 +162,7 @@ export function assignFlag(flags, name, value) {
|
|
|
162
162
|
case "diff-budget": {
|
|
163
163
|
const parsed = Number(String(value));
|
|
164
164
|
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
165
|
-
throw new FlowError("
|
|
165
|
+
throw new FlowError("INVALID_USAGE", "option --diff-budget requires a non-negative safe integer byte count");
|
|
166
166
|
}
|
|
167
167
|
flags.auditDiffBudgetBytes = parsed;
|
|
168
168
|
return;
|
|
@@ -175,17 +175,17 @@ export function assignFlag(flags, name, value) {
|
|
|
175
175
|
return;
|
|
176
176
|
case "sort":
|
|
177
177
|
if (flags.taskSort !== undefined) {
|
|
178
|
-
throw new FlowError("
|
|
178
|
+
throw new FlowError("INVALID_USAGE", "option --sort may only be specified once");
|
|
179
179
|
}
|
|
180
180
|
flags.taskSort = String(value);
|
|
181
181
|
return;
|
|
182
182
|
case "pri": {
|
|
183
183
|
const raw = String(value);
|
|
184
184
|
if (!/^[0-3]$/.test(raw)) {
|
|
185
|
-
throw new FlowError("
|
|
185
|
+
throw new FlowError("INVALID_USAGE", "task priority must be an integer from 0 through 3");
|
|
186
186
|
}
|
|
187
187
|
if (flags.taskPri !== undefined) {
|
|
188
|
-
throw new FlowError("
|
|
188
|
+
throw new FlowError("INVALID_USAGE", "option --pri may only be specified once");
|
|
189
189
|
}
|
|
190
190
|
flags.taskPri = Number(raw);
|
|
191
191
|
return;
|
|
@@ -213,7 +213,7 @@ export function assignFlag(flags, name, value) {
|
|
|
213
213
|
return;
|
|
214
214
|
case "parent":
|
|
215
215
|
if (flags.taskParent !== undefined) {
|
|
216
|
-
throw new FlowError("
|
|
216
|
+
throw new FlowError("INVALID_USAGE", "option --parent may only be specified once");
|
|
217
217
|
}
|
|
218
218
|
flags.taskParent = String(value);
|
|
219
219
|
return;
|
|
@@ -222,24 +222,24 @@ export function assignFlag(flags, name, value) {
|
|
|
222
222
|
return;
|
|
223
223
|
case "title":
|
|
224
224
|
if (flags.taskTitle !== undefined) {
|
|
225
|
-
throw new FlowError("
|
|
225
|
+
throw new FlowError("INVALID_USAGE", "option --title may only be specified once");
|
|
226
226
|
}
|
|
227
227
|
flags.taskTitle = String(value);
|
|
228
228
|
return;
|
|
229
229
|
case "body":
|
|
230
230
|
if (flags.taskBody !== undefined) {
|
|
231
|
-
throw new FlowError("
|
|
231
|
+
throw new FlowError("INVALID_USAGE", "option --body may only be specified once");
|
|
232
232
|
}
|
|
233
233
|
flags.taskBody = String(value);
|
|
234
234
|
return;
|
|
235
235
|
case "append":
|
|
236
236
|
if (flags.taskAppend !== undefined) {
|
|
237
|
-
throw new FlowError("
|
|
237
|
+
throw new FlowError("INVALID_USAGE", "option --append may only be specified once");
|
|
238
238
|
}
|
|
239
239
|
flags.taskAppend = String(value);
|
|
240
240
|
return;
|
|
241
241
|
default:
|
|
242
|
-
throw new FlowError("
|
|
242
|
+
throw new FlowError("UNKNOWN_OPTION", `unknown option: --${name}`);
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
export function readValueFlag(name, token, eq, tokens, index) {
|
|
@@ -45,13 +45,13 @@ export function isPetitionDryRunInvocation(tokens) {
|
|
|
45
45
|
export function parseWaitTimeout(value) {
|
|
46
46
|
const matched = /^(?<amount>[1-9][0-9]*)(?<unit>[smh])$/.exec(value);
|
|
47
47
|
if (!matched?.groups) {
|
|
48
|
-
throw new FlowError("
|
|
48
|
+
throw new FlowError("INVALID_USAGE", "wait timeout must use <positive integer><s|m|h>, for example 45s, 5m, or 2h (maximum 24h)");
|
|
49
49
|
}
|
|
50
50
|
const amount = Number(matched.groups.amount);
|
|
51
51
|
const multiplier = matched.groups.unit === "s" ? 1_000 : matched.groups.unit === "m" ? 60_000 : 3_600_000;
|
|
52
52
|
const timeoutMs = amount * multiplier;
|
|
53
53
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs > 24 * 3_600_000) {
|
|
54
|
-
throw new FlowError("
|
|
54
|
+
throw new FlowError("INVALID_USAGE", "wait timeout must use <positive integer><s|m|h>, for example 45s, 5m, or 2h (maximum 24h)");
|
|
55
55
|
}
|
|
56
56
|
return timeoutMs;
|
|
57
57
|
}
|
|
@@ -86,6 +86,9 @@ export function commandPositionalRange(command) {
|
|
|
86
86
|
if (command === "task add") {
|
|
87
87
|
return { min: 1, max: 1, label: "<BODY|->" };
|
|
88
88
|
}
|
|
89
|
+
if (command === "task compose") {
|
|
90
|
+
return { min: 0, max: 0 };
|
|
91
|
+
}
|
|
89
92
|
if (command === "task show" || command === "task log" || command === "task start" || command === "task hold" || command === "task resume" || command === "task drop") {
|
|
90
93
|
return { min: 1, max: 1, label: "<ID>" };
|
|
91
94
|
}
|
|
@@ -26,13 +26,13 @@ function formatSelectorList(items) {
|
|
|
26
26
|
}
|
|
27
27
|
export function applyCommissionSelector(command, flags, atTokens, contractFlagRaws) {
|
|
28
28
|
if (atTokens.length > 0 && contractFlagRaws.length > 0) {
|
|
29
|
-
throw new FlowError("
|
|
29
|
+
throw new FlowError("INVALID_USAGE", `commission address supplied twice: ${atTokens[0]} and --contract ${contractFlagRaws[0]}; use exactly one form`);
|
|
30
30
|
}
|
|
31
31
|
if (atTokens.length > 1) {
|
|
32
|
-
throw new FlowError("
|
|
32
|
+
throw new FlowError("INVALID_USAGE", `${command} accepts one commission address; got ${formatSelectorList(atTokens)}`);
|
|
33
33
|
}
|
|
34
34
|
if (contractFlagRaws.length > 1) {
|
|
35
|
-
throw new FlowError("
|
|
35
|
+
throw new FlowError("INVALID_USAGE", `${command} accepts one commission address; got ${formatSelectorList(contractFlagRaws.map((raw) => `--contract ${raw}`))}`);
|
|
36
36
|
}
|
|
37
37
|
if (atTokens.length === 1) {
|
|
38
38
|
flags.contractId = normalizeContractAddress(atTokens[0]);
|
|
@@ -46,7 +46,7 @@ export function assertCommissionSelectorAllowed(command, flags) {
|
|
|
46
46
|
if (flags.contractId === undefined || COMMISSION_SELECTOR_COMMANDS.has(command)) {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
throw new FlowError("
|
|
49
|
+
throw new FlowError("INVALID_USAGE", `contract address @${flags.contractId} is not valid for ${command}`);
|
|
50
50
|
}
|
|
51
51
|
export function assertProjectionSelectorPresent(command, flags, atTokens, contractFlagRaws, restLength, payloadLength) {
|
|
52
52
|
if ((command !== "wait" && command !== "tell") || flags.contractId === undefined) {
|
package/build/cli/parse.js
CHANGED
|
@@ -31,7 +31,7 @@ export function parseCliArgs(tokens) {
|
|
|
31
31
|
const short = token.slice(1);
|
|
32
32
|
const name = SHORT_FLAG_ALIASES[short];
|
|
33
33
|
if (!name) {
|
|
34
|
-
throw new FlowError("
|
|
34
|
+
throw new FlowError("UNKNOWN_OPTION", `unknown option: ${token}`);
|
|
35
35
|
}
|
|
36
36
|
seenFlagNames.add(name);
|
|
37
37
|
if (VALUE_FLAGS.has(name)) {
|
|
@@ -52,7 +52,7 @@ export function parseCliArgs(tokens) {
|
|
|
52
52
|
assignFlag(flags, name, true);
|
|
53
53
|
continue;
|
|
54
54
|
}
|
|
55
|
-
throw new FlowError("
|
|
55
|
+
throw new FlowError("UNKNOWN_OPTION", `unknown option: ${token}`);
|
|
56
56
|
}
|
|
57
57
|
if (!token.startsWith("--")) {
|
|
58
58
|
free.push(token);
|
|
@@ -61,7 +61,7 @@ export function parseCliArgs(tokens) {
|
|
|
61
61
|
const eq = token.indexOf("=");
|
|
62
62
|
const name = parseFlagName(eq === -1 ? token : token.slice(0, eq));
|
|
63
63
|
if (!CONTROL_FLAGS.has(name)) {
|
|
64
|
-
throw new FlowError("
|
|
64
|
+
throw new FlowError("UNKNOWN_OPTION", `unknown option: --${name}`);
|
|
65
65
|
}
|
|
66
66
|
seenFlagNames.add(name);
|
|
67
67
|
if (VALUE_FLAGS.has(name)) {
|
|
@@ -77,7 +77,7 @@ export function parseCliArgs(tokens) {
|
|
|
77
77
|
}
|
|
78
78
|
if (BOOLEAN_FLAGS.has(name)) {
|
|
79
79
|
if (eq !== -1) {
|
|
80
|
-
throw new FlowError("
|
|
80
|
+
throw new FlowError("INVALID_USAGE", `option --${name} does not accept a value`);
|
|
81
81
|
}
|
|
82
82
|
assignFlag(flags, name, true);
|
|
83
83
|
}
|
|
@@ -113,9 +113,9 @@ export function parseCliArgs(tokens) {
|
|
|
113
113
|
}
|
|
114
114
|
if (seenFlagNames.has("place")) {
|
|
115
115
|
if (command === "bind") {
|
|
116
|
-
throw new FlowError("
|
|
116
|
+
throw new FlowError("UNKNOWN_OPTION", "bind no longer accepts --place; use --title <text>");
|
|
117
117
|
}
|
|
118
|
-
throw new FlowError("
|
|
118
|
+
throw new FlowError("UNKNOWN_OPTION", "unknown option --place; use @addr or --contract <addr>");
|
|
119
119
|
}
|
|
120
120
|
for (const name of seenFlagNames) {
|
|
121
121
|
assertAllowedFlag(command, name);
|
|
@@ -125,19 +125,19 @@ export function parseCliArgs(tokens) {
|
|
|
125
125
|
delete flags.taskTitle;
|
|
126
126
|
}
|
|
127
127
|
if (command === "call" && flags.detach && flags.timeoutMs !== undefined) {
|
|
128
|
-
throw new FlowError("
|
|
128
|
+
throw new FlowError("INVALID_USAGE", "call cannot combine --detach and --wait");
|
|
129
129
|
}
|
|
130
130
|
if (command === "task ls" && flags.all && flags.done) {
|
|
131
|
-
throw new FlowError("
|
|
131
|
+
throw new FlowError("INVALID_USAGE", "task ls cannot combine --all and --done");
|
|
132
132
|
}
|
|
133
133
|
if (command === "wait" && flags.any && flags.all) {
|
|
134
|
-
throw new FlowError("
|
|
134
|
+
throw new FlowError("INVALID_USAGE", WAIT_SET_SELECTION_ERROR);
|
|
135
135
|
}
|
|
136
136
|
if (command === "task update" && flags.taskParent !== undefined && flags.taskNoParent) {
|
|
137
|
-
throw new FlowError("
|
|
137
|
+
throw new FlowError("INVALID_USAGE", "task update cannot combine --parent and --no-parent");
|
|
138
138
|
}
|
|
139
139
|
if (command === "task update" && flags.taskBody !== undefined && flags.taskAppend !== undefined) {
|
|
140
|
-
throw new FlowError("
|
|
140
|
+
throw new FlowError("INVALID_USAGE", "task update cannot combine --body and --append");
|
|
141
141
|
}
|
|
142
142
|
if (command === "task update" && flags.taskTitle === undefined && flags.taskBody === undefined && flags.taskAppend === undefined
|
|
143
143
|
&& flags.taskPri === undefined && flags.taskNeeds === undefined && flags.taskDropNeeds === undefined
|
|
@@ -147,18 +147,18 @@ export function parseCliArgs(tokens) {
|
|
|
147
147
|
throw new FlowError("EMPTY_PARAM", "task update requires at least one change");
|
|
148
148
|
}
|
|
149
149
|
if (command === "petition" && flags.json && !flags.dryRun) {
|
|
150
|
-
throw new FlowError("
|
|
150
|
+
throw new FlowError("INVALID_USAGE", "petition --json requires --dry-run");
|
|
151
151
|
}
|
|
152
152
|
if (command === "petition" && flags.dryRun && (flags.appendScope?.length ?? 0) > 0) {
|
|
153
|
-
throw new FlowError("
|
|
153
|
+
throw new FlowError("INVALID_USAGE", "petition --dry-run does not accept --append-scope");
|
|
154
154
|
}
|
|
155
155
|
if (command === "renew") {
|
|
156
156
|
const recoveryModes = [flags.renewContinue, flags.renewAbort].filter(Boolean).length;
|
|
157
157
|
if (recoveryModes > 1) {
|
|
158
|
-
throw new FlowError("
|
|
158
|
+
throw new FlowError("INVALID_USAGE", "renew accepts only one of --continue or --abort");
|
|
159
159
|
}
|
|
160
160
|
if (flags.renewAbort && (flags.appendScope?.length ?? 0) > 0) {
|
|
161
|
-
throw new FlowError("
|
|
161
|
+
throw new FlowError("INVALID_USAGE", "renew --abort does not accept --append-scope");
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
assertCommissionSelectorAllowed(command, flags);
|
|
@@ -166,7 +166,7 @@ export function parseCliArgs(tokens) {
|
|
|
166
166
|
const positional = [...rest, ...payload];
|
|
167
167
|
if (command === "bind" && flags.contractId !== undefined) {
|
|
168
168
|
if (!isCommissionId(flags.contractId)) {
|
|
169
|
-
throw new FlowError("
|
|
169
|
+
throw new FlowError("UNKNOWN_ADDRESS", `bind replay requires a full canonical contract id; got ${flags.contractId}`);
|
|
170
170
|
}
|
|
171
171
|
const hasReplayInput = positional.length > 0
|
|
172
172
|
|| flags.bindTaskIds !== undefined
|
|
@@ -182,7 +182,7 @@ export function parseCliArgs(tokens) {
|
|
|
182
182
|
|| flags.bindCriteria !== undefined
|
|
183
183
|
|| flags.bindVerification !== undefined;
|
|
184
184
|
if (hasReplayInput) {
|
|
185
|
-
throw new FlowError("
|
|
185
|
+
throw new FlowError("INVALID_USAGE", "addressed bind is replay-only and accepts no draft or creation input");
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
if (command === "wait") {
|
|
@@ -190,13 +190,13 @@ export function parseCliArgs(tokens) {
|
|
|
190
190
|
throw new FlowError("EMPTY_PARAM", "wait requires <projection-address>");
|
|
191
191
|
}
|
|
192
192
|
if (positional.length > 1 && !flags.any && !flags.all) {
|
|
193
|
-
throw new FlowError("
|
|
193
|
+
throw new FlowError("INVALID_USAGE", WAIT_SET_SELECTION_ERROR);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
if (isCallCommand(command)) {
|
|
197
197
|
if (flags.akuma) {
|
|
198
198
|
if (positional.length > 1) {
|
|
199
|
-
throw new FlowError("
|
|
199
|
+
throw new FlowError("INVALID_USAGE", `${command} accepts at most one <BODY|-> when --akuma is used`);
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
else {
|
|
@@ -205,16 +205,16 @@ export function parseCliArgs(tokens) {
|
|
|
205
205
|
throw new FlowError("EMPTY_PARAM", `${command} requires an agent name; use \`keiyaku ${command} NAME <BODY|->\``);
|
|
206
206
|
}
|
|
207
207
|
if (isResponseArtifactId(name)) {
|
|
208
|
-
throw new FlowError("
|
|
208
|
+
throw new FlowError("UNKNOWN_ADDRESS", `artifact ${name} is a response artifact; call expects an Akuma profile name`);
|
|
209
209
|
}
|
|
210
210
|
const parsedName = agentNameSchema.safeParse(name);
|
|
211
211
|
if (!parsedName.success) {
|
|
212
|
-
throw new FlowError("
|
|
212
|
+
throw new FlowError("UNKNOWN_ADDRESS", `${command} requires a valid agent name before <BODY|->`);
|
|
213
213
|
}
|
|
214
214
|
flags.akuma = parsedName.data;
|
|
215
215
|
positional.shift();
|
|
216
216
|
if (positional.length > 1) {
|
|
217
|
-
throw new FlowError("
|
|
217
|
+
throw new FlowError("INVALID_USAGE", `${command} accepts at most one <BODY|-> after the agent name`);
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
}
|
|
@@ -231,7 +231,7 @@ export function parseCliArgs(tokens) {
|
|
|
231
231
|
const message = stdin.mode === "document"
|
|
232
232
|
? `${command} reads Markdown only through literal '-'; file path arguments are not supported`
|
|
233
233
|
: `${command} does not accept positional arguments`;
|
|
234
|
-
throw new FlowError("
|
|
234
|
+
throw new FlowError("INVALID_USAGE", message);
|
|
235
235
|
}
|
|
236
236
|
return { command, flags, positional };
|
|
237
237
|
}
|