@astrosheep/keiyaku 2.9.6 → 2.9.7
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/harness/outcome.js +10 -0
- package/build/cli/commands/contract/bind/handler.js +10 -4
- package/build/cli/commands/contract/bind/meta.js +6 -6
- package/build/cli/commands/contract/petition/handler.js +16 -3
- package/build/cli/commands/contract/petition/meta.js +4 -4
- package/build/cli/commands/metadata.js +3 -2
- package/build/cli/commands/projection/tell/handler.js +10 -2
- package/build/cli/commands/projection/tell/meta.js +3 -3
- package/build/cli/commands/task/catalog.js +2 -0
- package/build/cli/commands/task/log/handler.js +12 -0
- package/build/cli/commands/task/log/meta.js +8 -0
- package/build/cli/flags.js +8 -0
- package/build/cli/index.js +10 -6
- package/build/cli/parse-flags.js +6 -0
- package/build/cli/parse-metadata.js +1 -1
- package/build/cli/render/line-width.js +33 -0
- package/build/cli/render/path-prefix-compaction.js +88 -0
- package/build/cli/render/petition.js +4 -0
- package/build/cli/render/projection-activity.js +93 -20
- package/build/cli/render/shared.js +34 -12
- package/build/cli/render/success-response.js +2 -0
- package/build/cli/render/tool-presentation.js +3 -3
- package/build/cli/render/wait.js +68 -48
- package/build/cli/subagent-guard.js +3 -0
- package/build/cli/types.js +1 -1
- package/build/core/bind.js +111 -6
- package/build/core/draft.js +1 -1
- package/build/core/projection/generation/database.js +21 -2
- package/build/core/projection/generation/model.js +24 -3
- package/build/core/projection/generation/projection-generation-continuation.js +75 -10
- package/build/core/projection/generation/projection-generation-execution.js +4 -3
- package/build/core/projection/generation/projection-generation-runner.js +96 -53
- package/build/core/projection/generation/projection-generation-runtime.js +19 -0
- package/build/core/projection/generation/store.js +9 -0
- package/build/core/projection/generation/transitions.js +105 -3
- package/build/core/projection/index.js +2 -2
- package/build/core/projection/projection-core.js +1 -1
- package/build/core/projection/projection-kill.js +31 -0
- package/build/core/projection/projection-life-protocol.js +10 -0
- package/build/core/projection/projection-wait.js +53 -15
- package/build/core/projection/projection-wake.js +35 -3
- package/build/core/projection/tell/database.js +18 -0
- package/build/core/projection/tell/model.js +1 -0
- package/build/core/projection/tell/store.js +102 -55
- package/build/core/registry.js +82 -69
- package/build/core/scope.js +9 -9
- package/build/core/task/index.js +2 -2
- package/build/core/task/task-contract.js +18 -0
- package/build/core/task/task-git-store.js +50 -0
- package/build/generated/version.js +2 -2
- package/package.json +1 -1
- package/skills/keiyaku-akuma/SKILL.md +10 -0
- package/skills/keiyaku-workflow/SKILL.md +76 -7
|
@@ -86,6 +86,16 @@ export function classifyFailure(shared, state, error, idleTimedOut, transportFai
|
|
|
86
86
|
warnings: [...state.warnings],
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
+
if (shared.explicitlyAborted && isProviderTerminalError(error) && error.fact.kind === "aborted") {
|
|
90
|
+
return mintOutcome({
|
|
91
|
+
status: "interrupted",
|
|
92
|
+
finalMessage: state.finalMessageCandidate ?? "",
|
|
93
|
+
output: renderCapturedText(state.outputBuffer),
|
|
94
|
+
diagnostics: renderCapturedText(state.diagnosticsBuffer),
|
|
95
|
+
session: state.session ?? fallbackSession(shared),
|
|
96
|
+
warnings: [...state.warnings],
|
|
97
|
+
});
|
|
98
|
+
}
|
|
89
99
|
if (transportFailure) {
|
|
90
100
|
const message = transportFailure instanceof Error ? transportFailure.message : `${shared.terms.provider} session transport probe failed`;
|
|
91
101
|
const timeoutMs = isProviderTerminalError(transportFailure) && transportFailure.fact.kind === "failed"
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { bindContract } from "../../../../core/bind.js";
|
|
1
|
+
import { bindContract, previewBindContract } from "../../../../core/bind.js";
|
|
2
2
|
import { defaultActorClock } from "../../../../core/outcome-base.js";
|
|
3
3
|
import { writeDraftArtifact } from "../../../draft-artifact.js";
|
|
4
|
-
import { buildBindDraftResponse,
|
|
4
|
+
import { buildBindDraftResponse, buildBindPreviewResponse, buildBindResponse } from "../../../render/shared.js";
|
|
5
5
|
import { repositoryDirectory } from "../../shared.js";
|
|
6
6
|
export const handler = async (stdin, flags, signal) => {
|
|
7
|
+
const cwd = await repositoryDirectory(flags, "bind");
|
|
7
8
|
if (flags.dryRun) {
|
|
8
|
-
|
|
9
|
+
const preview = await previewBindContract({
|
|
10
|
+
cwd, draft: stdin, ...defaultActorClock(), workspace: "worktree",
|
|
11
|
+
after: flags.after ?? [], exclusive: flags.exclusive,
|
|
12
|
+
...(flags.bindTaskIds === undefined ? {} : { taskIds: flags.bindTaskIds }),
|
|
13
|
+
signal,
|
|
14
|
+
});
|
|
15
|
+
return buildBindPreviewResponse(preview);
|
|
9
16
|
}
|
|
10
|
-
const cwd = await repositoryDirectory(flags, "bind");
|
|
11
17
|
if (flags.draftOnly) {
|
|
12
18
|
return buildBindDraftResponse(await writeDraftArtifact({ cwd, title: "bind-draft", content: stdin }));
|
|
13
19
|
}
|
|
@@ -17,10 +17,10 @@ export const meta = {
|
|
|
17
17
|
"<what the commission should accomplish>",
|
|
18
18
|
"",
|
|
19
19
|
"## Scope",
|
|
20
|
-
"
|
|
20
|
+
"~~~",
|
|
21
21
|
"<pattern>",
|
|
22
22
|
"<another-pattern>",
|
|
23
|
-
"
|
|
23
|
+
"~~~",
|
|
24
24
|
"",
|
|
25
25
|
"## Checks",
|
|
26
26
|
"- <verification>",
|
|
@@ -40,10 +40,10 @@ export const meta = {
|
|
|
40
40
|
"<what the commission should accomplish>",
|
|
41
41
|
"",
|
|
42
42
|
"## Scope",
|
|
43
|
-
"
|
|
43
|
+
"~~~",
|
|
44
44
|
"<pattern>",
|
|
45
45
|
"<another-pattern>",
|
|
46
|
-
"
|
|
46
|
+
"~~~",
|
|
47
47
|
"",
|
|
48
48
|
"## Checks",
|
|
49
49
|
"- <verification>",
|
|
@@ -52,10 +52,10 @@ export const meta = {
|
|
|
52
52
|
[
|
|
53
53
|
"keiyaku bind --task fix-flaky-pump - <<'KEIYAKU'",
|
|
54
54
|
"## Scope",
|
|
55
|
-
"
|
|
55
|
+
"~~~",
|
|
56
56
|
"<pattern>",
|
|
57
57
|
"<another-pattern>",
|
|
58
|
-
"
|
|
58
|
+
"~~~",
|
|
59
59
|
"",
|
|
60
60
|
"## Checks",
|
|
61
61
|
"- <verification>",
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { FlowError } from "../../../../flow-error.js";
|
|
2
2
|
import { resolveExistingContractAddress, withResolvedAddress } from "../../../../core/addressing.js";
|
|
3
3
|
import { buildPetitionClaimInput, petitionKeiyaku, previewPetition, } from "../../../../core/settlement/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { renewKeiyaku } from "../../../../core/renew.js";
|
|
5
|
+
import { buildPetitionClaimResponse, buildPetitionPreviewResponse, renderPetitionRenewedConsequence, } from "../../../render/petition.js";
|
|
6
|
+
import { writeCliOutput } from "../../../output-stream-error-policy.js";
|
|
5
7
|
import { effectiveDirectory, repositoryDirectory } from "../../shared.js";
|
|
6
8
|
export const handler = async (stdin, flags, signal) => {
|
|
7
9
|
if (flags.json && !flags.dryRun) {
|
|
8
10
|
throw new FlowError("EMPTY_PARAM", "petition --json requires --dry-run");
|
|
9
11
|
}
|
|
12
|
+
if (flags.renewBeforePetition && flags.dryRun) {
|
|
13
|
+
throw new FlowError("EMPTY_PARAM", "petition --renew cannot be used with --dry-run");
|
|
14
|
+
}
|
|
10
15
|
const cwd = await repositoryDirectory(flags, "petition");
|
|
11
16
|
const address = await resolveExistingContractAddress(cwd, flags.contractId, flags.contractAddressSource, effectiveDirectory(flags));
|
|
12
17
|
return await withResolvedAddress(address, async () => {
|
|
13
|
-
const input = buildPetitionClaimInput({ cwd, contractId: address.binding.commissionId, stdin, signal, appendScope: flags.appendScope });
|
|
14
18
|
if (flags.dryRun) {
|
|
19
|
+
const input = buildPetitionClaimInput({ cwd, contractId: address.binding.commissionId, stdin, signal });
|
|
15
20
|
return buildPetitionPreviewResponse(await previewPetition({
|
|
16
21
|
cwd,
|
|
17
22
|
contractId: address.binding.commissionId,
|
|
18
23
|
oath: input.oath,
|
|
19
24
|
}), flags.json === true);
|
|
20
25
|
}
|
|
21
|
-
|
|
26
|
+
const renewed = flags.renewBeforePetition
|
|
27
|
+
? await renewKeiyaku({ cwd, address, appendScope: flags.appendScope })
|
|
28
|
+
: undefined;
|
|
29
|
+
if (renewed && !renewed.alreadyFresh) {
|
|
30
|
+
await writeCliOutput(process.stdout, `${renderPetitionRenewedConsequence(renewed)}\n`);
|
|
31
|
+
}
|
|
32
|
+
const input = buildPetitionClaimInput({ cwd, contractId: address.binding.commissionId, stdin, signal, appendScope: flags.appendScope });
|
|
33
|
+
const petitionInput = renewed ? { ...input, appendScope: undefined } : input;
|
|
34
|
+
return buildPetitionClaimResponse({ ...await petitionKeiyaku(petitionInput), address }, input);
|
|
22
35
|
});
|
|
23
36
|
};
|
|
@@ -2,12 +2,12 @@ export const meta = {
|
|
|
2
2
|
command: "petition",
|
|
3
3
|
summary: "Run or preview the gated settlement pipeline",
|
|
4
4
|
usage: [
|
|
5
|
-
"keiyaku petition [-C|--cwd DIR] [--repo DIR] [--contract ADDR] [--append-scope PATTERN] [- < petition.md]",
|
|
6
|
-
"keiyaku @<id> petition [-C|--cwd DIR] [--repo DIR] [--append-scope PATTERN] [- < petition.md]",
|
|
5
|
+
"keiyaku petition [-C|--cwd DIR] [--repo DIR] [--contract ADDR] [--renew] [--append-scope PATTERN] [- < petition.md]",
|
|
6
|
+
"keiyaku @<id> petition [-C|--cwd DIR] [--repo DIR] [--renew] [--append-scope PATTERN] [- < petition.md]",
|
|
7
7
|
"keiyaku @<id> petition --dry-run [--json] [- < petition.md]",
|
|
8
8
|
],
|
|
9
9
|
stdin: { mode: "document", selector: { kind: "positional", index: 0, required: false, rejectBlank: false } },
|
|
10
|
-
flags: ["cwd", "repo", "contract", "append-scope", "dry-run", "json"],
|
|
10
|
+
flags: ["cwd", "repo", "contract", "renew", "append-scope", "dry-run", "json"],
|
|
11
11
|
purpose: "Seal the current a0 or open explicit arc, queue the contract, and run verdict and claim processing; dry-run previews deterministic gates without mutation.",
|
|
12
12
|
input: [
|
|
13
13
|
"## Oath",
|
|
@@ -16,7 +16,7 @@ export const meta = {
|
|
|
16
16
|
notes: [
|
|
17
17
|
"Petition stdin is optional and read only when literal - is supplied; a missing, empty, or blank oath is rejected by the settlement gate with OATH_MISSING.",
|
|
18
18
|
"The machine checks only that the oath is present. Accepted oath text is persisted in the petition entry and hidden from the reviewer.",
|
|
19
|
-
"
|
|
19
|
+
"Use --renew to run ordinary renew before petition; renew failures stop the command before settlement.",
|
|
20
20
|
"Dry-run never runs verification or review and exits 0, 1, or 2.",
|
|
21
21
|
],
|
|
22
22
|
completionRank: 10,
|
|
@@ -23,6 +23,7 @@ import { meta as taskMeta } from "./task/task/meta.js";
|
|
|
23
23
|
import { meta as taskAddMeta } from "./task/add/meta.js";
|
|
24
24
|
import { meta as taskShowMeta } from "./task/show/meta.js";
|
|
25
25
|
import { meta as taskLsMeta } from "./task/ls/meta.js";
|
|
26
|
+
import { meta as taskLogMeta } from "./task/log/meta.js";
|
|
26
27
|
import { meta as taskStartMeta } from "./task/start/meta.js";
|
|
27
28
|
import { meta as taskStopMeta } from "./task/stop/meta.js";
|
|
28
29
|
import { meta as taskDoneMeta } from "./task/done/meta.js";
|
|
@@ -34,14 +35,14 @@ export const COMMAND_METADATA_ORDER = [
|
|
|
34
35
|
"bind", "arc", "call", "revive", "wait", "tell", "kill", "log",
|
|
35
36
|
"akuma", "akuma ls", "akuma view", "skills", "skills install", "amend", "audit",
|
|
36
37
|
"renew", "petition", "forfeit", "guide", "completion", "status", "task",
|
|
37
|
-
"task add", "task show", "task ls", "task start", "task stop", "task done",
|
|
38
|
+
"task add", "task show", "task ls", "task log", "task start", "task stop", "task done",
|
|
38
39
|
"task drop", "task update", "task doctor", "dump-env",
|
|
39
40
|
];
|
|
40
41
|
const COMMAND_METADATA = [
|
|
41
42
|
bindMeta, arcMeta, callMeta, reviveMeta, waitMeta, tellMeta, killMeta, logMeta,
|
|
42
43
|
akumaMeta, akumaLsMeta, akumaViewMeta, skillsMeta, skillsInstallMeta, amendMeta, auditMeta,
|
|
43
44
|
renewMeta, petitionMeta, forfeitMeta, guideMeta, completionMeta, statusMeta,
|
|
44
|
-
taskMeta, taskAddMeta, taskShowMeta, taskLsMeta, taskStartMeta, taskStopMeta,
|
|
45
|
+
taskMeta, taskAddMeta, taskShowMeta, taskLsMeta, taskLogMeta, taskStartMeta, taskStopMeta,
|
|
45
46
|
taskDoneMeta, taskDropMeta, taskUpdateMeta, taskDoctorMeta, dumpEnvMeta,
|
|
46
47
|
];
|
|
47
48
|
export const CLI_COMMAND_METADATA = Object.fromEntries(COMMAND_METADATA.map((meta) => [meta.command, meta]));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FlowError } from "../../../../flow-error.js";
|
|
2
|
-
import { assertTellEffortSupported, tellProjection, writeInboxTell } from "../../../../core/projection/index.js";
|
|
2
|
+
import { assertTellEffortSupported, interruptProjectionGeneration, tellProjection, writeInboxTell } from "../../../../core/projection/index.js";
|
|
3
3
|
import { waitForProjection } from "../../../../core/projection/index.js";
|
|
4
4
|
import { prependAddressReceipt } from "../../../render/address.js";
|
|
5
5
|
import { buildSilentTellResponse } from "../../../render/tell.js";
|
|
@@ -13,7 +13,15 @@ export const handler = async (body, flags, signal, positional) => runProjectionC
|
|
|
13
13
|
assertTellEffortSupported(projection.directory, flags.effort);
|
|
14
14
|
// Address admission and any effort validation precede the durable mailbox write.
|
|
15
15
|
writeInboxTell(projection.directory, body, Date.now(), { effort: flags.effort });
|
|
16
|
-
|
|
16
|
+
if (flags.interrupt) {
|
|
17
|
+
const interrupted = interruptProjectionGeneration(projection.directory);
|
|
18
|
+
if (interrupted.status === "already-closed") {
|
|
19
|
+
await tellProjection(projection.directory, projection.id, flags.effort);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
await tellProjection(projection.directory, projection.id, flags.effort);
|
|
24
|
+
}
|
|
17
25
|
if (flags.timeoutMs === undefined)
|
|
18
26
|
return buildSilentTellResponse();
|
|
19
27
|
return prependAddressReceipt(buildWaitResponse(projection.id, await waitForProjection({ projectionDirectory: projection.directory, timeoutMs: flags.timeoutMs, signal }), { alias: projection.alias }), ctx.address, { projectionId: projection.id, projectionAlias: projection.alias });
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export const meta = {
|
|
2
2
|
command: "tell",
|
|
3
3
|
summary: "Send intent to a projection mailbox",
|
|
4
|
-
usage: ["keiyaku tell PROJECTION [--wait DURATION] [--] <BODY|->"],
|
|
4
|
+
usage: ["keiyaku tell PROJECTION [--interrupt] [--wait DURATION] [--] <BODY|->"],
|
|
5
5
|
stdin: {
|
|
6
6
|
mode: "argument",
|
|
7
7
|
selector: { kind: "positional", index: 1, required: true, rejectBlank: true },
|
|
8
8
|
},
|
|
9
|
-
flags: ["cwd", "repo", "contract", "effort", "wait"],
|
|
10
|
-
purpose: "Write intent to the mailbox
|
|
9
|
+
flags: ["cwd", "repo", "contract", "effort", "interrupt", "wait"],
|
|
10
|
+
purpose: "Write intent to the mailbox; --interrupt terminates the active turn before successor wake.",
|
|
11
11
|
completionRank: 3,
|
|
12
12
|
};
|
|
@@ -2,6 +2,7 @@ import { meta as taskMeta } from "./task/meta.js";
|
|
|
2
2
|
import { meta as addMeta } from "./add/meta.js";
|
|
3
3
|
import { meta as showMeta } from "./show/meta.js";
|
|
4
4
|
import { meta as lsMeta } from "./ls/meta.js";
|
|
5
|
+
import { meta as logMeta } from "./log/meta.js";
|
|
5
6
|
import { meta as startMeta } from "./start/meta.js";
|
|
6
7
|
import { meta as stopMeta } from "./stop/meta.js";
|
|
7
8
|
import { meta as doneMeta } from "./done/meta.js";
|
|
@@ -13,6 +14,7 @@ export const taskCatalog = [
|
|
|
13
14
|
{ meta: addMeta, load: () => import("./add/handler.js") },
|
|
14
15
|
{ meta: showMeta, load: () => import("./show/handler.js") },
|
|
15
16
|
{ meta: lsMeta, load: () => import("./ls/handler.js") },
|
|
17
|
+
{ meta: logMeta, load: () => import("./log/handler.js") },
|
|
16
18
|
{ meta: startMeta, load: () => import("./start/handler.js") },
|
|
17
19
|
{ meta: stopMeta, load: () => import("./stop/handler.js") },
|
|
18
20
|
{ meta: doneMeta, load: () => import("./done/handler.js") },
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FlowError } from "../../../../flow-error.js";
|
|
2
|
+
import { readTaskBoard, readTaskHistory, resolveTaskReadId, } from "../../../../core/task/index.js";
|
|
3
|
+
import { textResponse } from "../../../render/shared.js";
|
|
4
|
+
import { withTaskCommandRepository } from "../shared.js";
|
|
5
|
+
export const handler = async (_stdin, flags, _signal, positional) => withTaskCommandRepository(flags, async (repository, cwd) => {
|
|
6
|
+
const board = await readTaskBoard(repository);
|
|
7
|
+
const task = board.byId.get(resolveTaskReadId(board, positional[0]));
|
|
8
|
+
if (!task)
|
|
9
|
+
throw new FlowError("INTERNAL_STATE", "resolved task disappeared from the board");
|
|
10
|
+
const rows = await readTaskHistory(cwd, task.id);
|
|
11
|
+
return textResponse(rows.map(({ timestamp, actor, subject }) => `${timestamp}\t${actor}\t${subject}`).join("\n"));
|
|
12
|
+
});
|
package/build/cli/flags.js
CHANGED
|
@@ -68,6 +68,10 @@ export const FLAG_SPECS = {
|
|
|
68
68
|
kind: "boolean",
|
|
69
69
|
description: "Return after durable launch instead of waiting for completion.",
|
|
70
70
|
},
|
|
71
|
+
interrupt: {
|
|
72
|
+
kind: "boolean",
|
|
73
|
+
description: "Interrupt the current projection turn after saving the tell.",
|
|
74
|
+
},
|
|
71
75
|
"draft-only": {
|
|
72
76
|
kind: "boolean",
|
|
73
77
|
description: "Write a draft artifact without creating a contract workspace.",
|
|
@@ -124,6 +128,10 @@ export const FLAG_SPECS = {
|
|
|
124
128
|
placeholder: "PATTERN",
|
|
125
129
|
description: "Append one ordered scope pattern to the contract before audit. May be repeated.",
|
|
126
130
|
},
|
|
131
|
+
renew: {
|
|
132
|
+
kind: "boolean",
|
|
133
|
+
description: "Renew onto the current target before petitioning.",
|
|
134
|
+
},
|
|
127
135
|
resolve: {
|
|
128
136
|
kind: "boolean",
|
|
129
137
|
description: "Open a durable Keiyaku-owned renew recovery session when the rewrite conflicts.",
|
package/build/cli/index.js
CHANGED
|
@@ -57,9 +57,6 @@ function prepareBindInput(stdin, flags) {
|
|
|
57
57
|
checks: flags.bindChecks,
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
|
-
if (flags.dryRun) {
|
|
61
|
-
throw new FlowError("EMPTY_PARAM", "--dry-run requires --place, --objective, --scope, and --checks");
|
|
62
|
-
}
|
|
63
60
|
if (!nonblankStdin) {
|
|
64
61
|
// --task may supply name/objective defaults; core merges and reports missing sections.
|
|
65
62
|
if (flags.bindTaskIds !== undefined && flags.bindTaskIds.length > 0) {
|
|
@@ -188,11 +185,17 @@ function selectedArgument(parsed) {
|
|
|
188
185
|
if (stdin.selector === undefined)
|
|
189
186
|
return undefined;
|
|
190
187
|
if (stdin.selector.kind === "positional") {
|
|
191
|
-
|
|
188
|
+
const token = parsed.positional[stdin.selector.index];
|
|
189
|
+
if (token === undefined && !stdin.selector.required)
|
|
190
|
+
return undefined;
|
|
191
|
+
return { token, replace: () => parsed.flags };
|
|
192
192
|
}
|
|
193
193
|
const flagName = stdin.selector.name;
|
|
194
|
+
const token = parsed.flags[flagName];
|
|
195
|
+
if (token === undefined && !stdin.selector.required)
|
|
196
|
+
return undefined;
|
|
194
197
|
return {
|
|
195
|
-
token
|
|
198
|
+
token,
|
|
196
199
|
replace: (value) => ({ ...parsed.flags, [flagName]: value }),
|
|
197
200
|
};
|
|
198
201
|
}
|
|
@@ -248,7 +251,8 @@ export async function runCliCommand(tokens) {
|
|
|
248
251
|
await printFailure(error, cwd);
|
|
249
252
|
return 1;
|
|
250
253
|
}
|
|
251
|
-
// Bind
|
|
254
|
+
// Bind input compilation is side-effect free. Dry-run then performs the same
|
|
255
|
+
// repository admission as bind but does not persist an artifact on failure.
|
|
252
256
|
if (parsed.command === "bind") {
|
|
253
257
|
try {
|
|
254
258
|
stdin = prepareBindInput(stdin, parsed.flags);
|
package/build/cli/parse-flags.js
CHANGED
|
@@ -54,6 +54,9 @@ export function assignFlag(flags, name, value) {
|
|
|
54
54
|
case "detach":
|
|
55
55
|
flags.detach = Boolean(value);
|
|
56
56
|
return;
|
|
57
|
+
case "interrupt":
|
|
58
|
+
flags.interrupt = Boolean(value);
|
|
59
|
+
return;
|
|
57
60
|
case "draft-only":
|
|
58
61
|
flags.draftOnly = Boolean(value);
|
|
59
62
|
return;
|
|
@@ -102,6 +105,9 @@ export function assignFlag(flags, name, value) {
|
|
|
102
105
|
case "append-scope":
|
|
103
106
|
flags.appendScope = [...(flags.appendScope ?? []), String(value)];
|
|
104
107
|
return;
|
|
108
|
+
case "renew":
|
|
109
|
+
flags.renewBeforePetition = Boolean(value);
|
|
110
|
+
return;
|
|
105
111
|
case "resolve":
|
|
106
112
|
flags.renewResolve = Boolean(value);
|
|
107
113
|
return;
|
|
@@ -83,7 +83,7 @@ export function commandPositionalRange(command) {
|
|
|
83
83
|
if (command === "task add") {
|
|
84
84
|
return { min: 1, max: 1, label: "<BODY|->" };
|
|
85
85
|
}
|
|
86
|
-
if (command === "task show" || command === "task start" || command === "task drop") {
|
|
86
|
+
if (command === "task show" || command === "task log" || command === "task start" || command === "task drop") {
|
|
87
87
|
return { min: 1, max: 1, label: "<ID>" };
|
|
88
88
|
}
|
|
89
89
|
if (command === "task update") {
|
|
@@ -54,6 +54,39 @@ export function truncateColumns(value, maxColumns) {
|
|
|
54
54
|
}
|
|
55
55
|
return `${output}…`;
|
|
56
56
|
}
|
|
57
|
+
/** Truncate descriptive command text in the middle while retaining both ends. */
|
|
58
|
+
export function truncateMiddleColumns(value, maxColumns) {
|
|
59
|
+
if (maxColumns <= 0)
|
|
60
|
+
return "";
|
|
61
|
+
if (displayColumns(value) <= maxColumns)
|
|
62
|
+
return value;
|
|
63
|
+
if (maxColumns === 1)
|
|
64
|
+
return "…";
|
|
65
|
+
const budget = maxColumns - 1;
|
|
66
|
+
const leftBudget = Math.ceil(budget / 2);
|
|
67
|
+
const rightBudget = Math.floor(budget / 2);
|
|
68
|
+
let left = "";
|
|
69
|
+
let width = 0;
|
|
70
|
+
for (const character of value) {
|
|
71
|
+
const next = characterColumns(character);
|
|
72
|
+
if (width + next > leftBudget)
|
|
73
|
+
break;
|
|
74
|
+
left += character;
|
|
75
|
+
width += next;
|
|
76
|
+
}
|
|
77
|
+
const chars = [...value];
|
|
78
|
+
let right = "";
|
|
79
|
+
width = 0;
|
|
80
|
+
for (let index = chars.length - 1; index >= 0; index -= 1) {
|
|
81
|
+
const character = chars[index];
|
|
82
|
+
const next = characterColumns(character);
|
|
83
|
+
if (width + next > rightBudget)
|
|
84
|
+
break;
|
|
85
|
+
right = character + right;
|
|
86
|
+
width += next;
|
|
87
|
+
}
|
|
88
|
+
return `${left}…${right}`;
|
|
89
|
+
}
|
|
57
90
|
export function resolveLineColumns(stream = process.stdout) {
|
|
58
91
|
return stream.isTTY === true && Number.isInteger(stream.columns) && stream.columns > 0
|
|
59
92
|
? stream.columns
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const MIN_COMPACTION_SAVINGS = 4;
|
|
2
|
+
const SAFE_PATTERN_COMPONENT = /^[A-Za-z0-9._*?\[\]-]+$/;
|
|
3
|
+
const SAFE_CONCRETE_PATH_COMPONENT = /^[A-Za-z0-9._-]+$/;
|
|
4
|
+
/** Compact one displayed pattern-pair without merging distinct overlap facts. */
|
|
5
|
+
export function compactPatternPair(left, right) {
|
|
6
|
+
const literal = `${left} × ${right}`;
|
|
7
|
+
const leftParts = safePatternComponents(left);
|
|
8
|
+
const rightParts = safePatternComponents(right);
|
|
9
|
+
if (!leftParts || !rightParts)
|
|
10
|
+
return literal;
|
|
11
|
+
let sharedCount = 0;
|
|
12
|
+
const maximumShared = Math.min(leftParts.length, rightParts.length) - 1;
|
|
13
|
+
while (sharedCount < maximumShared && leftParts[sharedCount] === rightParts[sharedCount]) {
|
|
14
|
+
sharedCount += 1;
|
|
15
|
+
}
|
|
16
|
+
if (sharedCount === 0)
|
|
17
|
+
return literal;
|
|
18
|
+
const prefix = leftParts.slice(0, sharedCount).join("/");
|
|
19
|
+
const compacted = `${prefix}/{${leftParts.slice(sharedCount).join("/")} × ${rightParts.slice(sharedCount).join("/")}}`;
|
|
20
|
+
return savesEnough(literal, compacted) ? compacted : literal;
|
|
21
|
+
}
|
|
22
|
+
/** Compact a concrete preview through a stable slash-component trie when unambiguous. */
|
|
23
|
+
export function compactPathPreview(paths) {
|
|
24
|
+
const literal = paths.join(", ");
|
|
25
|
+
if (paths.length < 2)
|
|
26
|
+
return literal;
|
|
27
|
+
const components = paths.map(safeConcretePathComponents);
|
|
28
|
+
if (components.some((parts) => !parts))
|
|
29
|
+
return literal;
|
|
30
|
+
const trie = createTrie();
|
|
31
|
+
for (const parts of components)
|
|
32
|
+
insertPath(trie, parts);
|
|
33
|
+
if (hasTerminalBranch(trie))
|
|
34
|
+
return literal;
|
|
35
|
+
if (!hasSharedDirectoryPrefix(trie))
|
|
36
|
+
return literal;
|
|
37
|
+
const compacted = renderTrie(trie);
|
|
38
|
+
return savesEnough(literal, compacted) ? compacted : literal;
|
|
39
|
+
}
|
|
40
|
+
function safePatternComponents(value) {
|
|
41
|
+
return safeSlashComponents(value, SAFE_PATTERN_COMPONENT);
|
|
42
|
+
}
|
|
43
|
+
function safeConcretePathComponents(value) {
|
|
44
|
+
return safeSlashComponents(value, SAFE_CONCRETE_PATH_COMPONENT);
|
|
45
|
+
}
|
|
46
|
+
function safeSlashComponents(value, safeComponent) {
|
|
47
|
+
const parts = value.split("/");
|
|
48
|
+
if (parts.length === 0 || parts.some((part) => part === "." || part === ".." || !safeComponent.test(part))) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return parts;
|
|
52
|
+
}
|
|
53
|
+
function createTrie() {
|
|
54
|
+
return { terminal: false, children: new Map() };
|
|
55
|
+
}
|
|
56
|
+
function insertPath(node, parts) {
|
|
57
|
+
let current = node;
|
|
58
|
+
for (const part of parts) {
|
|
59
|
+
let child = current.children.get(part);
|
|
60
|
+
if (!child) {
|
|
61
|
+
child = createTrie();
|
|
62
|
+
current.children.set(part, child);
|
|
63
|
+
}
|
|
64
|
+
current = child;
|
|
65
|
+
}
|
|
66
|
+
current.terminal = true;
|
|
67
|
+
}
|
|
68
|
+
function hasTerminalBranch(node) {
|
|
69
|
+
if (node.terminal && node.children.size > 0)
|
|
70
|
+
return true;
|
|
71
|
+
return [...node.children.values()].some(hasTerminalBranch);
|
|
72
|
+
}
|
|
73
|
+
function hasSharedDirectoryPrefix(node, isRoot = true) {
|
|
74
|
+
if (!isRoot && node.children.size > 1)
|
|
75
|
+
return true;
|
|
76
|
+
return [...node.children.values()].some((child) => hasSharedDirectoryPrefix(child, false));
|
|
77
|
+
}
|
|
78
|
+
function renderTrie(node) {
|
|
79
|
+
const entries = [...node.children.entries()].map(([part, child]) => {
|
|
80
|
+
if (child.children.size === 0)
|
|
81
|
+
return part;
|
|
82
|
+
return `${part}/${renderTrie(child)}`;
|
|
83
|
+
});
|
|
84
|
+
return entries.length === 1 ? entries[0] : `{${entries.join(",")}}`;
|
|
85
|
+
}
|
|
86
|
+
function savesEnough(literal, compacted) {
|
|
87
|
+
return literal.length - compacted.length >= MIN_COMPACTION_SAVINGS;
|
|
88
|
+
}
|
|
@@ -4,6 +4,10 @@ import { assembleResponse, buildPayload, buildSection, formatMaybe, formatWarnin
|
|
|
4
4
|
import { DIFF_OVERVIEW_SECTION_TITLE, RESPONSE_MARKERS, WARNING_SECTION_TITLE } from "./response-style.js";
|
|
5
5
|
import { INFO_COMMIT_MAX_CHARS, INFO_PLEA_MAX_CHARS, } from "./shared.js";
|
|
6
6
|
import { prependAddressReceipt } from "./address.js";
|
|
7
|
+
/** Public consequence for a completed renew phase of petition --renew. */
|
|
8
|
+
export function renderPetitionRenewedConsequence(input) {
|
|
9
|
+
return `renewed onto ${input.newBase.slice(0, 8)} · ${input.sealed?.receipts.length ?? 0} receipts replayed`;
|
|
10
|
+
}
|
|
7
11
|
function formatPetitionSealSource(source) {
|
|
8
12
|
return source.arc === null
|
|
9
13
|
? `no open arc · fence ${source.fence}`
|