@pmelab/gtd 61.0.0 → 62.1.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 +15 -4
- package/dist/chunks/{testkit-C9sq0x07.mjs → testkit-CPzWnl3P.mjs} +76 -63
- package/dist/gtd.bundle.mjs +282 -80
- package/dist/gtd.core.mjs +121 -118
- package/dist/testkit/gherkin.mjs +1 -1
- package/dist/testkit.mjs +1 -1
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +2 -0
- package/src/cli/Cli.ts +18 -1
- package/src/cli/Launch.test.ts +52 -0
- package/src/cli/Launch.ts +144 -0
- package/src/cli/Program.test.ts +4 -3
- package/src/cli/Program.ts +41 -1
- package/src/cli/index.ts +1 -0
- package/src/edge/Edge.test.ts +3 -3
- package/src/edge/Edge.ts +17 -71
- package/src/emit/GitScript.ts +7 -0
- package/src/emit/index.ts +1 -0
- package/src/platform/PinFile.ts +61 -0
- package/src/platform/index.ts +1 -0
- package/src/replay/Trailers.test.ts +0 -37
- package/src/replay/Trailers.ts +2 -23
- package/src/step/planStart.test.ts +0 -1
- package/src/step/planStart.ts +1 -5
- package/src/twins/EmittedScriptRecognizer.ts +3 -2
- package/src/twins/PinFile.test.ts +107 -0
- package/src/twins/PinFile.ts +12 -0
- package/src/twins/index.ts +1 -0
- package/src/workflow/fingerprint.ts +2 -2
- package/src/workflow/index.ts +2 -2
- package/src/workflow/load.ts +14 -1
- package/src/workflow/pin.ts +15 -7
package/README.md
CHANGED
|
@@ -624,10 +624,10 @@ install for any of these but the testkit's: gtd resolves a `gtd.config.ts`'s
|
|
|
624
624
|
`@pmelab/gtd/*` imports to its own copy.
|
|
625
625
|
|
|
626
626
|
The `gtd` bin's commands — `next`, `land`, `start`, `abandon`, `restore`,
|
|
627
|
-
`validate`, `check`, `uncheck`, `guide`, `bundle`, `summary`, `base`,
|
|
628
|
-
`help`, `history list`, `history show`, `history metrics`,
|
|
629
|
-
`history fetch`, `strays`, the tools `ui`, `lsp`, `judge`
|
|
630
|
-
`judge run`) — are listed in the
|
|
627
|
+
`repin`, `validate`, `check`, `uncheck`, `guide`, `bundle`, `summary`, `base`,
|
|
628
|
+
`version`, `help`, `history list`, `history show`, `history metrics`,
|
|
629
|
+
`history judges`, `history fetch`, `strays`, the tools `ui`, `lsp`, `judge`
|
|
630
|
+
(`judge answer`, `judge run`) — are listed in the
|
|
631
631
|
[CLI reference](https://github.com/pmelab/gtd/blob/main/docs/cli.md). Every
|
|
632
632
|
command that prints a script takes `--json=script`, and `--` ends the options.
|
|
633
633
|
|
|
@@ -643,6 +643,17 @@ Both are ordinary `script` beats a driver runs unattended; see
|
|
|
643
643
|
[Pin and unpin beats](https://github.com/pmelab/gtd/blob/main/docs/driver.md#pin-and-unpin-beats).
|
|
644
644
|
Histories with pin commits are history format 2, which an older gtd refuses.
|
|
645
645
|
|
|
646
|
+
While a process is pinned, the `gtd` on your `PATH` hands every command over to
|
|
647
|
+
the pin, so editing the workflow or upgrading gtd mid-process changes nothing;
|
|
648
|
+
gtd notes on stderr when the live config differs from the pinned one. `ui`,
|
|
649
|
+
`lsp`, `guide`, `bundle`, `repin`, `help`, `version` and the `history` commands
|
|
650
|
+
always run live, and `gtd --live <command>` runs the live engine for any other,
|
|
651
|
+
at the risk of misreading the process. To fix a workflow mid-process, edit it
|
|
652
|
+
and run `gtd repin`: its script commits a fresh pin from the live engine and
|
|
653
|
+
config, and the process continues on it. A pin in a format your gtd does not
|
|
654
|
+
know is refused: upgrade gtd. See
|
|
655
|
+
[Pinned processes](https://github.com/pmelab/gtd/blob/main/docs/cli.md#pinned-processes).
|
|
656
|
+
|
|
646
657
|
## License
|
|
647
658
|
|
|
648
659
|
MIT
|
|
@@ -22873,13 +22873,7 @@ const parseJson = (raw) => {
|
|
|
22873
22873
|
const TRAILER_RE = /^(Gtd-[A-Za-z-]+):[ \t]*(.*?)[ \t]*$/gm;
|
|
22874
22874
|
const COST_RE = /^([0-9]+(?:\.[0-9]+)?)(?:[ \t]+(.+))?$/;
|
|
22875
22875
|
const VAR_RE = /^([^=\s]+)=(.*)$/;
|
|
22876
|
-
const CONFIG_RE = /^([0-9a-f]{16}) (.+)$/;
|
|
22877
22876
|
const CONTROL_CHARACTER$1 = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
|
|
22878
|
-
const encodePath = (path) => path.trim() !== path || path.startsWith("\"") || CONTROL_CHARACTER$1.test(path) ? JSON.stringify(path) : path;
|
|
22879
|
-
const decodePath = (raw) => {
|
|
22880
|
-
const path = raw.startsWith("\"") ? parseJson(raw) : raw;
|
|
22881
|
-
return typeof path === "string" && path !== "" && !CONTROL_CHARACTER$1.test(path) ? path : void 0;
|
|
22882
|
-
};
|
|
22883
22877
|
const decodeApproval = (raw) => {
|
|
22884
22878
|
const parsed = parseJson(raw);
|
|
22885
22879
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
@@ -22931,11 +22925,6 @@ const TRAILER_READERS = {
|
|
|
22931
22925
|
const kv = VAR_RE.exec(value);
|
|
22932
22926
|
if (kv !== null) into.vars[kv[1]] = kv[2];
|
|
22933
22927
|
},
|
|
22934
|
-
"Gtd-Config": (value, into) => {
|
|
22935
|
-
const entry = CONFIG_RE.exec(value);
|
|
22936
|
-
const path = entry === null ? void 0 : decodePath(entry[2]);
|
|
22937
|
-
if (path !== void 0) into.config[path] = entry[1];
|
|
22938
|
-
},
|
|
22939
22928
|
"Gtd-Review-Base": (value, into) => {
|
|
22940
22929
|
into.reviewBase ??= value.split(/\s/)[0];
|
|
22941
22930
|
},
|
|
@@ -22980,7 +22969,6 @@ const parseCommitMessage = (message) => {
|
|
|
22980
22969
|
judge: [],
|
|
22981
22970
|
marks: [],
|
|
22982
22971
|
vars: {},
|
|
22983
|
-
config: {},
|
|
22984
22972
|
cost: [],
|
|
22985
22973
|
format: void 0
|
|
22986
22974
|
};
|
|
@@ -23002,7 +22990,6 @@ const formatCommitMessage = (spec) => {
|
|
|
23002
22990
|
...trailer("Workflow", spec.workflow),
|
|
23003
22991
|
...trailer("Review-Base", spec.reviewBase),
|
|
23004
22992
|
...sortedEntries(spec.vars).map(([name, value]) => `Gtd-Var: ${name}=${value}`),
|
|
23005
|
-
...sortedEntries(spec.config).map(([path, hash]) => `Gtd-Config: ${hash} ${encodePath(path)}`),
|
|
23006
22993
|
...trailer("Cost", costValue(spec.cost)),
|
|
23007
22994
|
...(spec.judge ?? []).map((verdict) => `Gtd-Judge: ${JSON.stringify(verdict)}`),
|
|
23008
22995
|
...(spec.marks ?? []).map((m) => `Gtd-Mark: ${m.name}=${m.value}`),
|
|
@@ -23912,7 +23899,8 @@ const load = gen(function* () {
|
|
|
23912
23899
|
},
|
|
23913
23900
|
...compiled.ui !== void 0 ? { ui: compiled.ui } : {},
|
|
23914
23901
|
warnings: diagnostics.filter((d) => d.severity === "warning"),
|
|
23915
|
-
fingerprint: pinned?.fingerprint ?? configFingerprint(loaded.file.dependencyFiles, host.root, module.read ?? readFromDisk)
|
|
23902
|
+
fingerprint: pinned?.fingerprint ?? configFingerprint(loaded.file.dependencyFiles, host.root, module.read ?? readFromDisk),
|
|
23903
|
+
drift: pinned === void 0 ? [] : changedConfigFiles(pinned.fingerprint, configFingerprint(Object.keys(pinned.fingerprint).map((file) => resolve(host.root, file)), host.root, readFromDisk))
|
|
23916
23904
|
};
|
|
23917
23905
|
});
|
|
23918
23906
|
const readFromDisk = (path) => {
|
|
@@ -24562,10 +24550,6 @@ const resolveScopeAccess = (def, skills, vars) => gen(function* () {
|
|
|
24562
24550
|
//#endregion
|
|
24563
24551
|
//#region src/workflow/Workflow.ts
|
|
24564
24552
|
const knownModes = (def) => Object.keys(def.modes);
|
|
24565
|
-
//#endregion
|
|
24566
|
-
//#region src/workflow/pin.ts
|
|
24567
|
-
/** Bumped when a launcher could no longer run an older pin the same way. */
|
|
24568
|
-
const PIN_FORMAT = 1;
|
|
24569
24553
|
/**
|
|
24570
24554
|
* The engine a pin carries, as ES module source. Live, the `gtd.core.mjs` the
|
|
24571
24555
|
* running gtd ships beside its own bundle.
|
|
@@ -24591,7 +24575,7 @@ const pin = (module, root, version) => gen(function* () {
|
|
|
24591
24575
|
try: () => pinnedWorkflowSource(module, root, version),
|
|
24592
24576
|
catch: (e) => new GtdError(`gtd bundle: ${module.filepath}: ${e instanceof Error ? e.message : String(e)}`)
|
|
24593
24577
|
})) + core.replace(/^#!.*\n/, "");
|
|
24594
|
-
return Buffer.concat([Buffer.from(`gtdpin
|
|
24578
|
+
return Buffer.concat([Buffer.from(`gtdpin 1 gtd ${version}\n`), gzipSync(source, { level: 9 })]);
|
|
24595
24579
|
});
|
|
24596
24580
|
//#endregion
|
|
24597
24581
|
//#region src/emit/GitScript.ts
|
|
@@ -24626,6 +24610,12 @@ const pinScript = (path) => {
|
|
|
24626
24610
|
const slash = path.lastIndexOf("/");
|
|
24627
24611
|
return `${slash === -1 ? "" : `mkdir -p ${shellQuote(path.slice(0, slash))}\n`}gtd bundle >${p} || { rm -f -- ${p}; exit 1; }`;
|
|
24628
24612
|
};
|
|
24613
|
+
/** `gtd repin`'s pin write: a failed `gtd bundle` leaves the committed pin in place. */
|
|
24614
|
+
const repinScript = (path) => {
|
|
24615
|
+
const p = shellQuote(path);
|
|
24616
|
+
const next = shellQuote(`${path}.new`);
|
|
24617
|
+
return `gtd bundle >${next} || { rm -f -- ${next}; exit 1; }\nmv -f -- ${next} ${p}`;
|
|
24618
|
+
};
|
|
24629
24619
|
/** An unpin beat's script, and `gtd abandon`'s removal of the pin. */
|
|
24630
24620
|
const unpinScript = (path) => `rm -f -- ${shellQuote(path)}`;
|
|
24631
24621
|
/**
|
|
@@ -24866,6 +24856,12 @@ const FMT_ABANDONED = "abandoned the process resting at \"%s\" — HEAD is back
|
|
|
24866
24856
|
const FMT_RESTORED = "restored the retained history — HEAD is back at %s (\"%s\"), resting at \"%s\". Resume with the loop, or `git reset` to any earlier turn to restart from there.\n";
|
|
24867
24857
|
/** `no gtd process is underway (resting at "<initial>") — nothing to abandon` — `gtd abandon`'s no-op plain-text line. */
|
|
24868
24858
|
const abandonNoopText = (initial) => renderFormat(FMT_ABANDON_NOOP, initial);
|
|
24859
|
+
/** A landed self-loop/target-change transition. */
|
|
24860
|
+
const transitionOutcome = (from, to) => printfLine("%s %s → %s\n", [
|
|
24861
|
+
"'->'",
|
|
24862
|
+
shellQuote(from),
|
|
24863
|
+
shellQuote(to)
|
|
24864
|
+
]);
|
|
24869
24865
|
/** One already-rendered plain line. */
|
|
24870
24866
|
const noteOutcome = (text) => printfLine("%s\n", [shellQuote(text)]);
|
|
24871
24867
|
/**
|
|
@@ -25231,8 +25227,7 @@ const checkOverrides = (commandLabel, varOverrides, declaredNames, envNames) =>
|
|
|
25231
25227
|
* `gtd start <name>`: start a brand NEW process on a workflow, writing an
|
|
25232
25228
|
* opening commit that names the workflow's first step and carries a
|
|
25233
25229
|
* `Gtd-Workflow:` trailer, every process setting as a `Gtd-Var:` trailer, plus
|
|
25234
|
-
* a `Gtd-Review-Base:` trailer when the workflow fixes the process's diff base
|
|
25235
|
-
* and a `Gtd-Config:` trailer per config file.
|
|
25230
|
+
* and a `Gtd-Review-Base:` trailer when the workflow fixes the process's diff base.
|
|
25236
25231
|
* All validation is a REFUSAL, not an Effect failure.
|
|
25237
25232
|
*/
|
|
25238
25233
|
const planStart = (current, actor, start) => gen(function* () {
|
|
@@ -25270,8 +25265,7 @@ const planStart = (current, actor, start) => gen(function* () {
|
|
|
25270
25265
|
to: firstStep,
|
|
25271
25266
|
workflow: name,
|
|
25272
25267
|
...base !== void 0 ? { reviewBase: base } : {},
|
|
25273
|
-
vars
|
|
25274
|
-
config: start.config
|
|
25268
|
+
vars
|
|
25275
25269
|
})
|
|
25276
25270
|
}
|
|
25277
25271
|
}, {
|
|
@@ -25378,15 +25372,6 @@ const runOf = (history, location, closingHash = void 0) => {
|
|
|
25378
25372
|
const fault = historyFormatFault(history, location);
|
|
25379
25373
|
return fault === void 0 ? succeed$1(buildRun(history, location, closingHash)) : fail$4(new Error(fault));
|
|
25380
25374
|
};
|
|
25381
|
-
const pinnedConfigOf = (history) => {
|
|
25382
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
25383
|
-
const files = parseCommitMessage(history[i].message).config;
|
|
25384
|
-
if (Object.keys(files).length > 0) return {
|
|
25385
|
-
commit: history[i].hash,
|
|
25386
|
-
files
|
|
25387
|
-
};
|
|
25388
|
-
}
|
|
25389
|
-
};
|
|
25390
25375
|
const approvalEntryOf = (hash, message) => message.approved === void 0 ? [] : [{
|
|
25391
25376
|
step: formatStepId(message.approved.step),
|
|
25392
25377
|
name: message.approved.name,
|
|
@@ -25414,7 +25399,6 @@ const buildRun = (history, location, closingHash) => {
|
|
|
25414
25399
|
marks: parsed.flatMap((m) => m.marks),
|
|
25415
25400
|
approvals: processCommits.flatMap((c, i) => approvalEntryOf(c.hash, parsed[i])),
|
|
25416
25401
|
pinnedVars: { ...first?.vars },
|
|
25417
|
-
pinnedConfig: pinnedConfigOf(history.slice(Math.max(location.baseIndex, 0))),
|
|
25418
25402
|
headTurn: headTurnOf(head),
|
|
25419
25403
|
closingHash,
|
|
25420
25404
|
episode: {
|
|
@@ -25504,18 +25488,6 @@ const findBoundaryBase = (git, def, head) => gen(function* () {
|
|
|
25504
25488
|
});
|
|
25505
25489
|
const historyUpTo = (git, def, head) => flatMap(findBoundaryBase(git, def, head), ({ base, head: resolvedHead }) => git.commitHistory(base, resolvedHead));
|
|
25506
25490
|
const computeProcessRun = (git, def, head) => flatMap(historyUpTo(git, def, head), (history) => runOf(history, locateEpisode(def, history)));
|
|
25507
|
-
const changedSinceStart = (pinned, current, workspace) => {
|
|
25508
|
-
if (pinned === void 0) return [];
|
|
25509
|
-
return changedConfigFiles(pinned.files, current).filter((path) => {
|
|
25510
|
-
const committed = workspace.readCommittedSync(path, pinned.commit);
|
|
25511
|
-
return current[path] === void 0 || committed === void 0 || contentHash(committed) !== current[path];
|
|
25512
|
-
});
|
|
25513
|
-
};
|
|
25514
|
-
const warnConfigChanges = (run, current, workspace) => gen(function* () {
|
|
25515
|
-
const changed = changedSinceStart(run.pinnedConfig, current, workspace);
|
|
25516
|
-
if (changed.length === 0) return;
|
|
25517
|
-
yield* (yield* Narrator).warn(`gtd: warning: the config changed since this process started (${changed.join(", ")}) — the rest of the process replays under the changed config`);
|
|
25518
|
-
});
|
|
25519
25491
|
/** The run alone, never replaying — `gtd abandon` must work even when replay would refuse, short of a history it cannot read. */
|
|
25520
25492
|
const currentRun = gen(function* () {
|
|
25521
25493
|
const git = yield* GitService;
|
|
@@ -25831,8 +25803,7 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
25831
25803
|
status: normalizeStatus(e.status),
|
|
25832
25804
|
path: e.path
|
|
25833
25805
|
})) : [];
|
|
25834
|
-
|
|
25835
|
-
if (ref === void 0) yield* warnConfigChanges(run, configNow, workspace);
|
|
25806
|
+
if (ref === void 0 && config.drift.length > 0) yield* (yield* Narrator).warn(`gtd: notice: the live config differs from the pinned one (${config.drift.join(", ")}); \`gtd repin\` to switch`);
|
|
25836
25807
|
return {
|
|
25837
25808
|
def,
|
|
25838
25809
|
workflowName: resolved.name,
|
|
@@ -25846,7 +25817,6 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
25846
25817
|
run,
|
|
25847
25818
|
vars,
|
|
25848
25819
|
env,
|
|
25849
|
-
config: configNow,
|
|
25850
25820
|
changes,
|
|
25851
25821
|
memory: memory.key,
|
|
25852
25822
|
memoryResumed: memory.resumed,
|
|
@@ -26110,10 +26080,7 @@ const pinRefusal = (pin) => ({
|
|
|
26110
26080
|
kind: "refusal",
|
|
26111
26081
|
message: pin === "add" ? `gtd land: ${PIN_PATH} is missing — run the pin beat's script (\`gtd next --json=content\`); if its \`gtd bundle\` fails, fix what it reports, or run \`gtd abandon\`` : `gtd land: ${PIN_PATH} is still there — run the unpin beat's script (\`gtd next --json=content\`), then land`
|
|
26112
26082
|
});
|
|
26113
|
-
const startTrailers = (rest, starts) => starts ? {
|
|
26114
|
-
vars: rest.vars,
|
|
26115
|
-
config: rest.config
|
|
26116
|
-
} : {};
|
|
26083
|
+
const startTrailers = (rest, starts) => starts ? { vars: rest.vars } : {};
|
|
26117
26084
|
const outcomeMarks = (outcome) => outcome.kind === "rest" || outcome.kind === "ended" ? outcome.marks : [];
|
|
26118
26085
|
/**
|
|
26119
26086
|
* What landing the pending turn at `rest` does — a pure decision from a
|
|
@@ -26184,16 +26151,20 @@ const summaryFor = (run) => gen(function* () {
|
|
|
26184
26151
|
if (summary === void 0 || run.trace.length === 0) return { prompt: void 0 };
|
|
26185
26152
|
return { prompt: summary(summaryInput(run, settingsFor(resolved, run.pinnedVars, host.env, {}, {}))) };
|
|
26186
26153
|
});
|
|
26187
|
-
/**
|
|
26188
|
-
|
|
26189
|
-
|
|
26154
|
+
/**
|
|
26155
|
+
* Repository-relative paths of today's config files; one outside the
|
|
26156
|
+
* repository cannot be read at a ref, so it is left out. A file only the old
|
|
26157
|
+
* config read needs no check: whichever file imported it differs too.
|
|
26158
|
+
*/
|
|
26159
|
+
const inRepoConfigPaths = (root, files) => {
|
|
26160
|
+
const paths = /* @__PURE__ */ new Set();
|
|
26190
26161
|
for (const file of files) paths.add(relative(root, resolve(root, file)).split(sep).join("/"));
|
|
26191
26162
|
return [...paths].filter((p) => p !== "" && !p.startsWith("..") && !isAbsolute(p)).sort();
|
|
26192
26163
|
};
|
|
26193
26164
|
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}`);
|
|
26194
|
-
const differingConfigPaths = (git, workspace, root, config,
|
|
26165
|
+
const differingConfigPaths = (git, workspace, root, config, hash) => gen(function* () {
|
|
26195
26166
|
const differing = [];
|
|
26196
|
-
for (const path of inRepoConfigPaths(root, Object.keys(config.fingerprint)
|
|
26167
|
+
for (const path of inRepoConfigPaths(root, Object.keys(config.fingerprint))) if ((yield* orElseSucceed(git.readFileAtRef(hash, path), () => void 0)) !== workspace.readSync(path)) differing.push(path);
|
|
26197
26168
|
return differing;
|
|
26198
26169
|
});
|
|
26199
26170
|
const notClosedError = (hash, outcome) => outcome.kind === "rest" ? /* @__PURE__ */ new Error(`gtd history: ${hash.slice(0, 7)} does not close a process under today's config`) : replayError(outcome) ?? /* @__PURE__ */ new Error("gtd history: replay failed");
|
|
@@ -26250,7 +26221,7 @@ const replayFinishedProcess = (config, run, hash, how) => gen(function* () {
|
|
|
26250
26221
|
const recorded = parseCommitMessage(run.episode.commits.find((c) => c.hash === hash)?.message ?? "").version;
|
|
26251
26222
|
const versionWarning = versionWarningFor(recorded);
|
|
26252
26223
|
const withWarning = (err) => versionWarning === void 0 ? err : /* @__PURE__ */ new Error(`${err.message}\n${versionWarning}`);
|
|
26253
|
-
const differing = yield* differingConfigPaths(git, workspace, host.root, config,
|
|
26224
|
+
const differing = yield* differingConfigPaths(git, workspace, host.root, config, hash);
|
|
26254
26225
|
if (differing.length > 0) return yield* fail$4(withWarning(configRefusal(how.command, hash, differing, how.configReason)));
|
|
26255
26226
|
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
26256
26227
|
const outcome = yield* promise(() => replayFor(setup));
|
|
@@ -27212,8 +27183,7 @@ const startWorkflow = (workflow, varOverrides, json, out, commandLabel) => gen(f
|
|
|
27212
27183
|
workflow: resolved,
|
|
27213
27184
|
first: started,
|
|
27214
27185
|
commandLabel,
|
|
27215
|
-
vars: varOverrides
|
|
27216
|
-
config: rest.config
|
|
27186
|
+
vars: varOverrides
|
|
27217
27187
|
});
|
|
27218
27188
|
if (plan.kind === "refusal") return yield* fail$4(new Error(plan.message));
|
|
27219
27189
|
yield* writeScript(json, out, listingCommitted(landingScript(ScriptSurface.render(plan.steps)), rest.changes));
|
|
@@ -27370,6 +27340,36 @@ const runRestoreCommand = (json, out) => gen(function* () {
|
|
|
27370
27340
|
yield* writeScript(json, out, combinedScript(required, ""));
|
|
27371
27341
|
});
|
|
27372
27342
|
/**
|
|
27343
|
+
* `gtd repin`: the pin beat's script and landing, on demand. It runs live,
|
|
27344
|
+
* so reading the rest first refuses a live workflow that cannot replay the
|
|
27345
|
+
* process before any pin is written.
|
|
27346
|
+
*/
|
|
27347
|
+
const runRepinCommand = (json, out) => gen(function* () {
|
|
27348
|
+
const rest = yield* currentRest;
|
|
27349
|
+
const refusal = rest.pin === "add" ? "the next beat pins the process already — run it (`gtd next --json=content`), then land" : rest.pin === "remove" ? "the flow has ended — its unpin beat is next, so there is nothing to repin" : noProcessUnderway(rest) ? "no gtd process is underway — nothing to repin" : void 0;
|
|
27350
|
+
if (refusal !== void 0) return yield* fail$4(/* @__PURE__ */ new Error(`gtd repin: ${refusal}`));
|
|
27351
|
+
const message = formatCommitMessage({
|
|
27352
|
+
actor: "check",
|
|
27353
|
+
from: PIN_STATES.add,
|
|
27354
|
+
to: rest.step.name,
|
|
27355
|
+
pin: "add"
|
|
27356
|
+
});
|
|
27357
|
+
yield* writeScript(json, out, combinedScript(emitScripts([
|
|
27358
|
+
{
|
|
27359
|
+
kind: "gitWrite",
|
|
27360
|
+
command: repinScript(PIN_PATH)
|
|
27361
|
+
},
|
|
27362
|
+
{
|
|
27363
|
+
kind: "gitWrite",
|
|
27364
|
+
command: commitPath(PIN_PATH, message)
|
|
27365
|
+
},
|
|
27366
|
+
{
|
|
27367
|
+
kind: "outcome",
|
|
27368
|
+
command: transitionOutcome(PIN_STATES.add, rest.step.name)
|
|
27369
|
+
}
|
|
27370
|
+
]).required, ""));
|
|
27371
|
+
});
|
|
27372
|
+
/**
|
|
27373
27373
|
* The mode's own validate command for `file`: a declared `validate:` command
|
|
27374
27374
|
* verbatim; a `"builtin"` validator or no mode at all names the leaf
|
|
27375
27375
|
* `gtd check <mode>` invocation instead, so there's always something concrete
|
|
@@ -27636,6 +27636,7 @@ const dispatchVoidCommand = (command, json, out) => {
|
|
|
27636
27636
|
case "start": return runStartCommand(command, json, out);
|
|
27637
27637
|
case "abandon": return runAbandonCommand(json, out);
|
|
27638
27638
|
case "restore": return runRestoreCommand(json, out);
|
|
27639
|
+
case "repin": return runRepinCommand(json, out);
|
|
27639
27640
|
case "next": return runNextCommand(json, out);
|
|
27640
27641
|
case "validate": return runValidateCommand(json, out);
|
|
27641
27642
|
case "check":
|
|
@@ -27764,6 +27765,7 @@ const SCRIPT_KINDS = /* @__PURE__ */ new Set([
|
|
|
27764
27765
|
"start",
|
|
27765
27766
|
"abandon",
|
|
27766
27767
|
"restore",
|
|
27768
|
+
"repin",
|
|
27767
27769
|
"validate",
|
|
27768
27770
|
"historyFetch"
|
|
27769
27771
|
]);
|
|
@@ -28031,6 +28033,16 @@ With no <name>, list every workflow and door it can start. An unknown <name>, th
|
|
|
28031
28033
|
summary: "Bring back the last abandoned process",
|
|
28032
28034
|
details: `Bring back the process the last \`gtd abandon\` stopped: the printed script resets HEAD to that process's last commit. Refuses on a dirty working tree, when nothing was abandoned, or when commits made since would be lost.`
|
|
28033
28035
|
},
|
|
28036
|
+
{
|
|
28037
|
+
token: "repin",
|
|
28038
|
+
kind: "repin",
|
|
28039
|
+
arity: "none",
|
|
28040
|
+
group: "Start and stop",
|
|
28041
|
+
summary: "Pin the process to the live gtd and workflow",
|
|
28042
|
+
details: `Switch the process underway to the live engine and workflow, the way out when the workflow needs a fix mid-process: the printed script runs the pin beat again, writing \`gtd bundle\`'s output to .gtd/process.gtdpin and committing that file alone. Always runs on the gtd on PATH, never on the pin. Refuses when no process is underway, when the next beat is a pin or unpin beat already, or when the live workflow does not replay the process.
|
|
28043
|
+
|
|
28044
|
+
s=$(gtd repin --json=script) && sh -c "$s"`
|
|
28045
|
+
},
|
|
28034
28046
|
{
|
|
28035
28047
|
token: "summary",
|
|
28036
28048
|
kind: "summary",
|
|
@@ -28215,6 +28227,7 @@ const flagHeader = (row) => {
|
|
|
28215
28227
|
};
|
|
28216
28228
|
const GLOBAL_OPTIONS = [
|
|
28217
28229
|
["-v, --verbose", "Narrate on stderr what gtd resolved"],
|
|
28230
|
+
["--live", "Run this gtd, not the process's pin; it may misread the process"],
|
|
28218
28231
|
["-h, --help", "Print help; gtd <command> --help for one command"],
|
|
28219
28232
|
["-V, --version", "Print the version"],
|
|
28220
28233
|
["--", "End the options: every word after it is an argument"]
|
|
@@ -28226,7 +28239,7 @@ const EXTRA_TOPICS = [[
|
|
|
28226
28239
|
], [
|
|
28227
28240
|
"version",
|
|
28228
28241
|
"Print the version",
|
|
28229
|
-
"Print gtd's version."
|
|
28242
|
+
"Print gtd's version, and the version of a pinned process's gtd."
|
|
28230
28243
|
]];
|
|
28231
28244
|
const EXAMPLES = ` $ gtd start list what can start
|
|
28232
28245
|
$ gtd start <name> start a process
|
|
@@ -29933,9 +29946,9 @@ const recognizeGitBuilders = (repo, block) => {
|
|
|
29933
29946
|
};
|
|
29934
29947
|
/** What the in-memory `gtd bundle` prints: a pin header, and no engine behind it. */
|
|
29935
29948
|
const IN_MEMORY_PIN = "gtdpin 1 gtd in-memory\n";
|
|
29936
|
-
/** A pin or unpin beat's script. */
|
|
29949
|
+
/** A pin or unpin beat's script, or `gtd repin`'s. */
|
|
29937
29950
|
const recognizePinScripts = (repo, block) => {
|
|
29938
|
-
if (block === pinScript(".gtd/process.gtdpin")) {
|
|
29951
|
+
if (block === pinScript(".gtd/process.gtdpin") || block === repinScript(".gtd/process.gtdpin")) {
|
|
29939
29952
|
repo.writeFile(PIN_PATH, IN_MEMORY_PIN);
|
|
29940
29953
|
return { kind: "applied" };
|
|
29941
29954
|
}
|