@pmelab/gtd 27.8.0 → 28.0.0
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/.claude-plugin/plugin.json +1 -1
- package/README.md +59 -30
- package/claude/hooks/doors.ts +12 -0
- package/claude/hooks/register.tsx +2 -4
- package/claude/hooks/ship.ts +115 -20
- package/dist/chunks/{reserved-UdSuHDUJ.mjs → reserved-BIsngMGL.mjs} +168 -27
- package/dist/chunks/{tail-fj60rQ3y.mjs → tail-DHEaOHyp.mjs} +1 -1
- package/dist/chunks/{wire-C12dahrH.mjs → wire-D3Rc5r22.mjs} +14 -1
- package/dist/gtd.bundle.mjs +174 -48
- package/dist/testkit.mjs +158 -49
- package/dist/wire.mjs +2 -2
- package/dist/workflows/cleanup.mjs +3 -4
- package/dist/workflows/feature.mjs +2 -2
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +19 -4
- package/dist/workflows.mjs +186 -127
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +4 -3
- package/src/cli/Cli.test.ts +39 -3
- package/src/cli/Cli.ts +27 -6
- package/src/cli/Program.test.ts +3 -1
- package/src/cli/Program.ts +22 -0
- package/src/edge/Edge.test.ts +209 -54
- package/src/edge/Edge.ts +121 -42
- package/src/edge/index.ts +1 -0
- package/src/install/ConfigScaffold.ts +4 -1
- package/src/install/Install.test.ts +10 -0
- package/src/install/Install.ts +5 -2
- package/src/replay/Replay.test.ts +32 -0
- package/src/replay/Replay.ts +3 -1
- package/src/replay/Trailers.test.ts +11 -0
- package/src/replay/Trailers.ts +3 -1
- package/src/replay/index.ts +1 -1
- package/src/wire/Slices.ts +25 -0
- package/src/wire/WireSchema.test.ts +18 -0
- package/src/wire/golden/1/slices.json +11 -0
- package/src/wire/index.ts +1 -0
- package/src/workflow/load.test.ts +82 -5
- package/src/workflow/load.ts +40 -16
- package/src/workflows/cleanup/cleanup.feature +21 -0
- package/src/workflows/cleanup/cleanup.ts +1 -1
- package/src/workflows/index.ts +2 -1
- package/src/workflows/rollout/rollout.feature +282 -2
- package/src/workflows/rollout/rollout.test.ts +96 -2
- package/src/workflows/rollout/rollout.ts +41 -3
- package/src/workflows/shared/options.test.ts +25 -2
- package/src/workflows/shared/options.ts +7 -4
- package/src/workflows/shared/registry.test.ts +41 -1
- package/src/workflows/shared/registry.ts +26 -1
- package/src/workflows/shared/text.test.ts +252 -2
- package/src/workflows/shared/text.ts +44 -1
- package/src/workflows/shared/vars.ts +2 -0
- package/dist/chunks/registry-CP3YuV9j.mjs +0 -101
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, showScript, vars } from "../flows.mjs";
|
|
2
2
|
import { fixFindings, lensesOf, qualityLap as qualityLap$1, reviewToHandOff } from "../workflow-kit.mjs";
|
|
3
|
-
import { C as
|
|
3
|
+
import { C as reviewMissing, T as reviewing, U as handOffAwaitReviewMessage, _ as awaitReview, b as fixQuality, c as escalation, d as healthy, g as REVIEW, m as QUALITY, u as guarded, w as reviewQuality, x as fixRisks, y as fixCheck } from "./reserved-BIsngMGL.mjs";
|
|
4
4
|
//#region src/workflows/shared/tail.ts
|
|
5
5
|
/** The reviewer's own `.gtd/REVIEW.md`, kept beside the human's notes so `fix` can tell them apart. */
|
|
6
6
|
const REVIEW_ORIG = ".gtd/REVIEW.orig.md";
|
|
@@ -475,4 +475,17 @@ const compile = (glob) => {
|
|
|
475
475
|
};
|
|
476
476
|
const globMatches = (path, glob) => compile(glob).test(path);
|
|
477
477
|
//#endregion
|
|
478
|
-
|
|
478
|
+
//#region src/wire/Slices.ts
|
|
479
|
+
/** The ONLY place `gtd slices --json`'s shape is built; copies exactly the four fields, in order. */
|
|
480
|
+
const slicesDocument = (slices) => ({
|
|
481
|
+
schema: 1,
|
|
482
|
+
slices: slices.map(({ name, base, tip, closed }) => ({
|
|
483
|
+
name,
|
|
484
|
+
base,
|
|
485
|
+
tip,
|
|
486
|
+
closed
|
|
487
|
+
}))
|
|
488
|
+
});
|
|
489
|
+
const renderSlicesJson = (document) => JSON.stringify(document) + "\n";
|
|
490
|
+
//#endregion
|
|
491
|
+
export { WIRE_SCHEMA as A, EXIT_RUNTIME_ERROR as C, EXIT_USAGE_ERROR as D, EXIT_STRAYS_FOUND as E, ACCESS_REFUSAL as O, EXIT_OK as S, EXIT_SIGTERM as T, BEAT_KINDS as _, selectPath as a, stallDiagnosis as b, landProseText as c, renderLandPlain as d, DEMAND_BRIEFING as f, statusOf as g, renderBeatJson as h, resolveSession as i, renderFormat as j, UNATTRIBUTED_MODEL as k, noopText as l, beatDocument as m, slicesDocument as n, judgeJson as o, renderBeatPlain as p, globMatches as r, landFields as s, renderSlicesJson as t, renderLandJson as u, beatKindOf as v, EXIT_SIGINT as w, EXIT_CODES as x, demandOf as y };
|
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -21520,8 +21520,11 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
|
|
|
21520
21520
|
rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
|
|
21521
21521
|
straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
|
|
21522
21522
|
\`gtd doors\` lists the doors a repository offers (those of the workflows its
|
|
21523
|
-
config exports — the bundled \`fix\` and \`review [base]
|
|
21524
|
-
|
|
21523
|
+
config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
|
|
21524
|
+
\`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
|
|
21525
|
+
(remove it), which a config adds by exporting \`rollout\` from
|
|
21526
|
+
\`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
21527
|
+
\`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
|
|
21525
21528
|
\`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
|
|
21526
21529
|
missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
|
|
21527
21530
|
|
|
@@ -21631,7 +21634,10 @@ const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + D
|
|
|
21631
21634
|
const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped feature, review and fix.
|
|
21632
21635
|
// Every exported function is startable with \`gtd --workflow <name>\`; the
|
|
21633
21636
|
// default export is what a change in the tree starts. Each workflow brings
|
|
21634
|
-
// its own doors: \`gtd door review [base]\` and \`gtd door fix\`.
|
|
21637
|
+
// its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
|
|
21638
|
+
// from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
21639
|
+
// \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
|
|
21640
|
+
// \`gtd door cleanup <flag>\`.
|
|
21635
21641
|
export {
|
|
21636
21642
|
feature as default,
|
|
21637
21643
|
feature,
|
|
@@ -37652,6 +37658,22 @@ var init_Glob = __esmMin((() => {
|
|
|
37652
37658
|
globMatches = (path, glob) => compile(glob).test(path);
|
|
37653
37659
|
}));
|
|
37654
37660
|
//#endregion
|
|
37661
|
+
//#region src/wire/Slices.ts
|
|
37662
|
+
var slicesDocument, renderSlicesJson;
|
|
37663
|
+
var init_Slices = __esmMin((() => {
|
|
37664
|
+
init_constants();
|
|
37665
|
+
slicesDocument = (slices) => ({
|
|
37666
|
+
schema: 1,
|
|
37667
|
+
slices: slices.map(({ name, base, tip, closed }) => ({
|
|
37668
|
+
name,
|
|
37669
|
+
base,
|
|
37670
|
+
tip,
|
|
37671
|
+
closed
|
|
37672
|
+
}))
|
|
37673
|
+
});
|
|
37674
|
+
renderSlicesJson = (document) => JSON.stringify(document) + "\n";
|
|
37675
|
+
}));
|
|
37676
|
+
//#endregion
|
|
37655
37677
|
//#region src/wire/index.ts
|
|
37656
37678
|
var init_wire = __esmMin((() => {
|
|
37657
37679
|
init_constants();
|
|
@@ -37666,6 +37688,7 @@ var init_wire = __esmMin((() => {
|
|
|
37666
37688
|
init_Select();
|
|
37667
37689
|
init_Sessions();
|
|
37668
37690
|
init_Glob();
|
|
37691
|
+
init_Slices();
|
|
37669
37692
|
}));
|
|
37670
37693
|
//#endregion
|
|
37671
37694
|
//#region src/replay/RefName.ts
|
|
@@ -37867,7 +37890,7 @@ const TRAILER_READERS = {
|
|
|
37867
37890
|
if (value !== "") into.branch ??= value;
|
|
37868
37891
|
},
|
|
37869
37892
|
"Gtd-Version": (value, into) => {
|
|
37870
|
-
if (value !== "") into.version ??= value;
|
|
37893
|
+
if (value !== "" && !/[\u0000-\u001f\u007f-\u009f]/.test(value)) into.version ??= value;
|
|
37871
37894
|
},
|
|
37872
37895
|
"Gtd-Kept": (value, into) => {
|
|
37873
37896
|
if (HASH_RE.test(value)) into.kept ??= value;
|
|
@@ -38305,7 +38328,8 @@ const replay = async (input) => {
|
|
|
38305
38328
|
via,
|
|
38306
38329
|
trace,
|
|
38307
38330
|
landed,
|
|
38308
|
-
marks
|
|
38331
|
+
marks,
|
|
38332
|
+
slices: slices.map((s) => ({ ...s }))
|
|
38309
38333
|
};
|
|
38310
38334
|
const changesSince = (hash) => {
|
|
38311
38335
|
const tree = treeAt(hash);
|
|
@@ -38975,10 +38999,10 @@ const doorsFrom = (exports) => {
|
|
|
38975
38999
|
};
|
|
38976
39000
|
/**
|
|
38977
39001
|
* Each exported workflow's own doors, onto the name it is exported under. The
|
|
38978
|
-
* `doors` export adds to them and
|
|
39002
|
+
* `doors` export adds to them and declares a name several workflows clash on.
|
|
38979
39003
|
*/
|
|
38980
39004
|
const derivedDoors = (catalogue, declared) => {
|
|
38981
|
-
const
|
|
39005
|
+
const builders = /* @__PURE__ */ new Map();
|
|
38982
39006
|
for (const [workflow, loaded] of catalogue) for (const [name, spec] of Object.entries(loaded.doors ?? {})) {
|
|
38983
39007
|
const door = {
|
|
38984
39008
|
...spec,
|
|
@@ -38986,12 +39010,22 @@ const derivedDoors = (catalogue, declared) => {
|
|
|
38986
39010
|
};
|
|
38987
39011
|
const problem = doorProblem(name, door);
|
|
38988
39012
|
if (problem !== void 0) throw new Error(`workflow "${workflow}" builds door "${name}", which ${problem}`);
|
|
38989
|
-
|
|
38990
|
-
|
|
38991
|
-
|
|
39013
|
+
builders.set(name, [...builders.get(name) ?? [], door]);
|
|
39014
|
+
}
|
|
39015
|
+
const doors = /* @__PURE__ */ new Map();
|
|
39016
|
+
const clashes = /* @__PURE__ */ new Map();
|
|
39017
|
+
for (const [name, built] of builders) {
|
|
39018
|
+
const owner = ownerOf(name, built);
|
|
39019
|
+
if (owner !== void 0) doors.set(name, owner);
|
|
39020
|
+
else if (!declared.has(name)) clashes.set(name, built.map((door) => door.workflow));
|
|
38992
39021
|
}
|
|
38993
|
-
return
|
|
39022
|
+
return {
|
|
39023
|
+
doors: new Map([...doors, ...declared]),
|
|
39024
|
+
clashes
|
|
39025
|
+
};
|
|
38994
39026
|
};
|
|
39027
|
+
/** The export named after the door owns it, else the sole builder; a clash of several builds no door. */
|
|
39028
|
+
const ownerOf = (name, built) => built.find((door) => door.workflow === name) ?? (built.length === 1 ? built[0] : void 0);
|
|
38995
39029
|
/** Sort the doors and check each names a startable workflow. */
|
|
38996
39030
|
const checkDoors = (declared, catalogue) => {
|
|
38997
39031
|
const doors = new Map([...declared].sort(([a], [b]) => a.localeCompare(b)));
|
|
@@ -39022,16 +39056,28 @@ const loadWorkflows = (module, shipped) => {
|
|
|
39022
39056
|
return workflow === void 0 ? [] : [[name, workflow]];
|
|
39023
39057
|
}));
|
|
39024
39058
|
const defaultWorkflow = built.get(value);
|
|
39059
|
+
const derived = rejections.length > 0 ? {
|
|
39060
|
+
doors: /* @__PURE__ */ new Map(),
|
|
39061
|
+
clashes: /* @__PURE__ */ new Map()
|
|
39062
|
+
} : derivedDoors(catalogue, doorsFrom(exports));
|
|
39025
39063
|
return {
|
|
39026
39064
|
file,
|
|
39027
39065
|
catalogue,
|
|
39028
|
-
doors:
|
|
39066
|
+
doors: checkDoors(derived.doors, catalogue),
|
|
39029
39067
|
default: defaultWorkflow && {
|
|
39030
39068
|
...defaultWorkflow,
|
|
39031
39069
|
name: defaultName
|
|
39032
39070
|
},
|
|
39033
39071
|
config: compileConfig(exports["config"], module.filepath),
|
|
39034
|
-
diagnostics: nameRejections(rejections)
|
|
39072
|
+
diagnostics: [...nameRejections(rejections), ...[...derived.clashes].map(([name, builders]) => {
|
|
39073
|
+
const names = [...builders].sort().map((b) => JSON.stringify(b));
|
|
39074
|
+
return {
|
|
39075
|
+
severity: "warning",
|
|
39076
|
+
message: `door "${name}" is built by workflows ${names.slice(0, -1).join(", ")} and ${names.at(-1)} and none is exported under that name — declare it in the "doors" export`,
|
|
39077
|
+
path: ["doors", name],
|
|
39078
|
+
origin: file.origin
|
|
39079
|
+
};
|
|
39080
|
+
})]
|
|
39035
39081
|
};
|
|
39036
39082
|
};
|
|
39037
39083
|
const evaluateFromDisk = (module, shipped) => {
|
|
@@ -39746,22 +39792,14 @@ const runAtTip = (git, def, tip) => gen(function* () {
|
|
|
39746
39792
|
if (closing.length === 0) closing = yield* git.commitHistory(void 0, tip.hash);
|
|
39747
39793
|
return yield* runOf([...before, ...closing], { ...location }, tip.hash);
|
|
39748
39794
|
});
|
|
39749
|
-
|
|
39750
|
-
const summaryRun = gen(function* () {
|
|
39751
|
-
const git = yield* GitService;
|
|
39752
|
-
const def = (yield* (yield* ConfigService).load).workflow;
|
|
39795
|
+
const runAtHead = (git, def) => gen(function* () {
|
|
39753
39796
|
return yield* runAtTip(git, def, (yield* git.subjectHistory(1, 0))[0]);
|
|
39754
39797
|
});
|
|
39755
|
-
/** The process
|
|
39756
|
-
const
|
|
39798
|
+
/** The process HEAD closes or sits inside — `gtd summary`'s run. */
|
|
39799
|
+
const summaryRun = gen(function* () {
|
|
39757
39800
|
const git = yield* GitService;
|
|
39758
39801
|
const def = (yield* (yield* ConfigService).load).workflow;
|
|
39759
|
-
|
|
39760
|
-
const subject = yield* git.lastCommitSubject(hash);
|
|
39761
|
-
return yield* runAtTip(git, def, {
|
|
39762
|
-
hash,
|
|
39763
|
-
subject
|
|
39764
|
-
});
|
|
39802
|
+
return yield* runAtHead(git, def);
|
|
39765
39803
|
});
|
|
39766
39804
|
const commitTree = (workspace, hash) => {
|
|
39767
39805
|
let entries;
|
|
@@ -40090,12 +40128,54 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
40090
40128
|
total: run.costEntries.reduce((sum, entry) => sum + entry.cost, 0),
|
|
40091
40129
|
byModel: costByModel(run.costEntries)
|
|
40092
40130
|
},
|
|
40093
|
-
setup
|
|
40094
|
-
slices: sliceRangesOf(outcome.slices, run)
|
|
40131
|
+
setup
|
|
40095
40132
|
};
|
|
40096
40133
|
});
|
|
40097
40134
|
const currentRest = restAt(void 0);
|
|
40098
40135
|
/**
|
|
40136
|
+
* The slices of the process HEAD closes or sits inside, current episode only,
|
|
40137
|
+
* stack order. Reads like `historyTrail`, never `restAt`: it narrates and
|
|
40138
|
+
* writes nothing, and returns a version warning for a closed run.
|
|
40139
|
+
*/
|
|
40140
|
+
const processSlices = gen(function* () {
|
|
40141
|
+
const git = yield* GitService;
|
|
40142
|
+
const config = yield* (yield* ConfigService).load;
|
|
40143
|
+
const workspace = yield* Workspace;
|
|
40144
|
+
const host = yield* Host;
|
|
40145
|
+
const run = yield* runAtHead(git, config.workflow);
|
|
40146
|
+
if (run.trace.length === 0) return yield* fail$7(/* @__PURE__ */ new Error("gtd slices: refused — HEAD neither closes nor sits inside a process"));
|
|
40147
|
+
const closing = run.closingHash;
|
|
40148
|
+
let outcome;
|
|
40149
|
+
let versionWarning;
|
|
40150
|
+
if (closing !== void 0) {
|
|
40151
|
+
const replayed = yield* replayFinishedProcess(config, run, closing, {
|
|
40152
|
+
command: "gtd slices",
|
|
40153
|
+
configReason: "HEAD's process replays under today's config; revert or stash the edit to list its slices",
|
|
40154
|
+
notEnded: (o) => slicesDivergence(run, o)
|
|
40155
|
+
});
|
|
40156
|
+
outcome = replayed.outcome;
|
|
40157
|
+
versionWarning = replayed.versionWarning;
|
|
40158
|
+
} else {
|
|
40159
|
+
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
40160
|
+
outcome = yield* promise(() => replayFor(setup));
|
|
40161
|
+
if (outcome.kind !== "rest") return yield* fail$7(slicesDivergence(run, outcome));
|
|
40162
|
+
}
|
|
40163
|
+
return {
|
|
40164
|
+
slices: sliceRangesOf(outcome.slices, run).map(({ name, base, tip, open }) => ({
|
|
40165
|
+
name,
|
|
40166
|
+
base,
|
|
40167
|
+
tip,
|
|
40168
|
+
closed: closing !== void 0 || !open
|
|
40169
|
+
})),
|
|
40170
|
+
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
40171
|
+
};
|
|
40172
|
+
});
|
|
40173
|
+
/** Every `gtd slices` refusal reads `gtd slices: refused — <reason>`; replay's own `gtd: ` lead is dropped. */
|
|
40174
|
+
const slicesDivergence = (run, outcome) => {
|
|
40175
|
+
const reason = outcome.kind === "rest" && run.closingHash !== void 0 ? `${run.closingHash.slice(0, 7)} closes a process that does not end under today's config` : replayError(outcome)?.message.replace(/^gtd: /, "") ?? "replay failed";
|
|
40176
|
+
return /* @__PURE__ */ new Error(`gtd slices: refused — ${reason}`);
|
|
40177
|
+
};
|
|
40178
|
+
/**
|
|
40099
40179
|
* Whether `gtd --workflow <name>` can start a process here: the workflow must
|
|
40100
40180
|
* reach a step from the tree the opening commit would capture. That step is
|
|
40101
40181
|
* what the opening commit's subject names.
|
|
@@ -40329,7 +40409,7 @@ const inRepoConfigPaths = (root, files, recorded) => {
|
|
|
40329
40409
|
for (const file of files) paths.add(relative(root, resolve(root, file)).split(sep).join("/"));
|
|
40330
40410
|
return [...paths].filter((p) => p !== "" && !p.startsWith("..") && !isAbsolute(p)).sort();
|
|
40331
40411
|
};
|
|
40332
|
-
const configRefusal = (tip, paths) => /* @__PURE__ */ new Error(
|
|
40412
|
+
const configRefusal = (command, tip, paths, reason = "A history ref replays under today's config, so it is unreadable after the config changes") => /* @__PURE__ */ new Error(`${command}: refused — config differs at ${tip.slice(0, 7)}: ${paths.join(", ")}. ${reason}`);
|
|
40333
40413
|
const differingConfigPaths = (git, workspace, root, config, run, hash) => gen(function* () {
|
|
40334
40414
|
const differing = [];
|
|
40335
40415
|
for (const path of inRepoConfigPaths(root, Object.keys(config.fingerprint), run.pinnedConfig?.files ?? {})) if ((yield* orElseSucceed(git.readFileAtRef(hash, path), () => void 0)) !== workspace.readSync(path)) differing.push(path);
|
|
@@ -40360,22 +40440,19 @@ const versionWarningFor = (recorded) => recorded === GTD_VERSION ? void 0 : `gtd
|
|
|
40360
40440
|
const historyTrail = (tip) => gen(function* () {
|
|
40361
40441
|
const git = yield* GitService;
|
|
40362
40442
|
const config = yield* (yield* ConfigService).load;
|
|
40363
|
-
const workspace = yield* Workspace;
|
|
40364
|
-
const host = yield* Host;
|
|
40365
40443
|
const hash = yield* git.resolveRef(tip);
|
|
40366
|
-
const
|
|
40367
|
-
const
|
|
40368
|
-
|
|
40369
|
-
|
|
40370
|
-
|
|
40371
|
-
const outcome = yield*
|
|
40372
|
-
|
|
40373
|
-
|
|
40374
|
-
|
|
40375
|
-
const versionWarning = versionWarningFor(recorded);
|
|
40444
|
+
const subject = yield* git.lastCommitSubject(hash);
|
|
40445
|
+
const run = yield* runAtTip(git, config.workflow, {
|
|
40446
|
+
hash,
|
|
40447
|
+
subject
|
|
40448
|
+
});
|
|
40449
|
+
const { outcome, recorded, versionWarning } = yield* replayFinishedProcess(config, run, hash, {
|
|
40450
|
+
command: "gtd history",
|
|
40451
|
+
notEnded: (o) => notClosedError(hash, o)
|
|
40452
|
+
});
|
|
40376
40453
|
return {
|
|
40377
40454
|
tip: hash,
|
|
40378
|
-
steps,
|
|
40455
|
+
steps: historySteps(run),
|
|
40379
40456
|
ended: outcome.via,
|
|
40380
40457
|
version: {
|
|
40381
40458
|
recorded,
|
|
@@ -40384,6 +40461,24 @@ const historyTrail = (tip) => gen(function* () {
|
|
|
40384
40461
|
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
40385
40462
|
};
|
|
40386
40463
|
});
|
|
40464
|
+
/** Refuses on config drift before replaying, so a stale config never runs the flow. */
|
|
40465
|
+
const replayFinishedProcess = (config, run, hash, how) => gen(function* () {
|
|
40466
|
+
const git = yield* GitService;
|
|
40467
|
+
const workspace = yield* Workspace;
|
|
40468
|
+
const host = yield* Host;
|
|
40469
|
+
const differing = yield* differingConfigPaths(git, workspace, host.root, config, run, hash);
|
|
40470
|
+
if (differing.length > 0) return yield* fail$7(configRefusal(how.command, hash, differing, how.configReason));
|
|
40471
|
+
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
40472
|
+
const outcome = yield* promise(() => replayFor(setup));
|
|
40473
|
+
if (outcome.kind !== "ended") return yield* fail$7(how.notEnded(outcome));
|
|
40474
|
+
const recorded = parseCommitMessage(run.episode.commits.find((c) => c.hash === hash)?.message ?? "").version;
|
|
40475
|
+
const versionWarning = versionWarningFor(recorded);
|
|
40476
|
+
return {
|
|
40477
|
+
outcome,
|
|
40478
|
+
recorded,
|
|
40479
|
+
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
40480
|
+
};
|
|
40481
|
+
});
|
|
40387
40482
|
//#endregion
|
|
40388
40483
|
//#region src/emit/GitScript.ts
|
|
40389
40484
|
const shellQuote$1 = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
@@ -41256,6 +41351,17 @@ const runDoorsCommand = (json, out) => gen(function* () {
|
|
|
41256
41351
|
if (json.kind === "document") out.write(`${JSON.stringify(document)}\n`);
|
|
41257
41352
|
else yield* writeSelection(out, document, json.path);
|
|
41258
41353
|
});
|
|
41354
|
+
const runSlicesCommand = (json, out) => gen(function* () {
|
|
41355
|
+
const { slices, versionWarning } = yield* processSlices;
|
|
41356
|
+
if (versionWarning !== void 0) yield* (yield* Narrator).warn(versionWarning);
|
|
41357
|
+
if (json.kind === "off") {
|
|
41358
|
+
out.write(slices.map((s) => `${s.name} ${s.base}..${s.tip}\n`).join(""));
|
|
41359
|
+
return;
|
|
41360
|
+
}
|
|
41361
|
+
const document = slicesDocument(slices);
|
|
41362
|
+
if (json.kind === "document") out.write(renderSlicesJson(document));
|
|
41363
|
+
else yield* writeSelection(out, document, json.path);
|
|
41364
|
+
});
|
|
41259
41365
|
/**
|
|
41260
41366
|
* Ends the process underway without completing it. Nothing is discarded: a
|
|
41261
41367
|
* `git reset --mixed` to the process's start commit leaves every turn
|
|
@@ -41591,6 +41697,7 @@ const dispatchVoidCommand = (command, json, out) => {
|
|
|
41591
41697
|
case "start": return runStartCommand(command.actor, command.workflow, command.vars, out, command.label);
|
|
41592
41698
|
case "door": return runDoorCommand(command.name, command.args, out);
|
|
41593
41699
|
case "doors": return runDoorsCommand(json, out);
|
|
41700
|
+
case "slices": return runSlicesCommand(json, out);
|
|
41594
41701
|
case "abandon": return runAbandonCommand(out);
|
|
41595
41702
|
case "restore": return runRestoreCommand(out);
|
|
41596
41703
|
case "next": return runNextCommand(json, out);
|
|
@@ -41649,12 +41756,12 @@ const FLAGS = [
|
|
|
41649
41756
|
name: "--json",
|
|
41650
41757
|
arity: "optional",
|
|
41651
41758
|
repeatable: false,
|
|
41652
|
-
scope: (kind) => kind === "next" || kind === "land" || kind === "judge" || kind === "judgeAnswer" || kind === "doors" || kind === "historyList" || kind === "historyShow" || kind === "strays",
|
|
41759
|
+
scope: (kind) => kind === "next" || kind === "land" || kind === "judge" || kind === "judgeAnswer" || kind === "doors" || kind === "slices" || kind === "historyList" || kind === "historyShow" || kind === "strays",
|
|
41653
41760
|
decode: ([raw]) => right(raw ?? ""),
|
|
41654
|
-
scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd history list`/`gtd history show`/`gtd strays` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
|
|
41761
|
+
scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd slices`/`gtd history list`/`gtd history show`/`gtd strays` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
|
|
41655
41762
|
valueHint: "<path>",
|
|
41656
41763
|
help: [
|
|
41657
|
-
"(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd history",
|
|
41764
|
+
"(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd slices/gtd history",
|
|
41658
41765
|
"list/gtd history show/gtd strays only) output structured JSON. Bare --json prints the whole document;",
|
|
41659
41766
|
"--json=<path> (a dotted key path into that document, e.g.",
|
|
41660
41767
|
"kind, content, session.id) prints just that value: a",
|
|
@@ -42107,10 +42214,10 @@ const COMMAND_ROWS = [
|
|
|
42107
42214
|
"positional args to that workflow's process settings — the",
|
|
42108
42215
|
"same start `gtd --workflow` makes, printed the same way.",
|
|
42109
42216
|
"Each workflow the config exports brings its own doors (gtd's",
|
|
42110
|
-
"`fix
|
|
42111
|
-
"the config's `doors` export. An
|
|
42112
|
-
"too many args, is a usage error
|
|
42113
|
-
"the door's synopsis"
|
|
42217
|
+
"`fix`, `review [base]`, `rollout <flag>` and",
|
|
42218
|
+
"`cleanup <flag>`), plus the config's `doors` export. An",
|
|
42219
|
+
"unknown door, or too few or too many args, is a usage error",
|
|
42220
|
+
"(exit 2) naming the doors or the door's synopsis"
|
|
42114
42221
|
]
|
|
42115
42222
|
},
|
|
42116
42223
|
{
|
|
@@ -42124,6 +42231,25 @@ const COMMAND_ROWS = [
|
|
|
42124
42231
|
"value off it. Writes nothing"
|
|
42125
42232
|
]
|
|
42126
42233
|
},
|
|
42234
|
+
{
|
|
42235
|
+
token: "slices",
|
|
42236
|
+
kind: "slices",
|
|
42237
|
+
arity: "none",
|
|
42238
|
+
details: [
|
|
42239
|
+
"List the slices of the process HEAD closes or sits inside,",
|
|
42240
|
+
"first slice first: one `name base..tip` line each (full",
|
|
42241
|
+
"hashes; plain output carries no promise). --json prints",
|
|
42242
|
+
"{ schema: 1, slices: [{ name, base, tip, closed }] };",
|
|
42243
|
+
"--json=<path> reads one value off it. Writes nothing: no",
|
|
42244
|
+
"git, no state transition, no session identity. When HEAD",
|
|
42245
|
+
"closes a process, every slice is listed as closed; a gtd",
|
|
42246
|
+
"version mismatch only warns on stderr. Refuses (exit 1)",
|
|
42247
|
+
"when HEAD neither closes nor sits inside a process, or when",
|
|
42248
|
+
"today's config disagrees with history about the process:",
|
|
42249
|
+
"HEAD closes it and its config files differ from the working",
|
|
42250
|
+
"tree, or the replay diverged"
|
|
42251
|
+
]
|
|
42252
|
+
},
|
|
42127
42253
|
{
|
|
42128
42254
|
token: "judge",
|
|
42129
42255
|
kind: "judge",
|