@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/dist/gtd.bundle.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __createRequire } from 'module';
|
|
3
3
|
const require = __createRequire(import.meta.url);
|
|
4
|
-
import { createRequire, isBuiltin } from "node:module";
|
|
4
|
+
import { createRequire, isBuiltin, registerHooks } from "node:module";
|
|
5
5
|
import * as OS from "node:os";
|
|
6
6
|
import { homedir, networkInterfaces, tmpdir } from "node:os";
|
|
7
7
|
import * as NodeChildProcess from "node:child_process";
|
|
@@ -14,7 +14,7 @@ import * as NFS from "node:fs";
|
|
|
14
14
|
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
15
15
|
import * as Path from "node:path";
|
|
16
16
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
17
|
-
import { gzipSync } from "node:zlib";
|
|
17
|
+
import { gunzipSync, gzipSync } from "node:zlib";
|
|
18
18
|
import * as NodeUrl from "node:url";
|
|
19
19
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
20
|
import { stripVTControlCharacters } from "node:util";
|
|
@@ -12054,6 +12054,57 @@ const restorability = (git, headHash, tipHash) => gen(function* () {
|
|
|
12054
12054
|
};
|
|
12055
12055
|
});
|
|
12056
12056
|
//#endregion
|
|
12057
|
+
//#region src/platform/PinFile.ts
|
|
12058
|
+
const livePinFile = (cwd, path) => () => {
|
|
12059
|
+
const file = join(cwd, path);
|
|
12060
|
+
if (existsSync(file)) try {
|
|
12061
|
+
return {
|
|
12062
|
+
path: file,
|
|
12063
|
+
bytes: readFileSync(file)
|
|
12064
|
+
};
|
|
12065
|
+
} catch {
|
|
12066
|
+
return {
|
|
12067
|
+
path: file,
|
|
12068
|
+
bytes: /* @__PURE__ */ new Uint8Array()
|
|
12069
|
+
};
|
|
12070
|
+
}
|
|
12071
|
+
try {
|
|
12072
|
+
return {
|
|
12073
|
+
path: file,
|
|
12074
|
+
bytes: execFileSync("git", ["show", `HEAD:${path}`], {
|
|
12075
|
+
cwd,
|
|
12076
|
+
stdio: [
|
|
12077
|
+
"ignore",
|
|
12078
|
+
"pipe",
|
|
12079
|
+
"ignore"
|
|
12080
|
+
]
|
|
12081
|
+
})
|
|
12082
|
+
};
|
|
12083
|
+
} catch {
|
|
12084
|
+
return;
|
|
12085
|
+
}
|
|
12086
|
+
};
|
|
12087
|
+
/**
|
|
12088
|
+
* Runs a pin's module as this process's main program. Node refuses an unknown
|
|
12089
|
+
* extension, so a load hook serves the unpacked source under the pin's own
|
|
12090
|
+
* URL — `import.meta.url` stays a file in the repository.
|
|
12091
|
+
*/
|
|
12092
|
+
const runPinModule = async (path, source) => {
|
|
12093
|
+
const url = pathToFileURL(path).href;
|
|
12094
|
+
registerHooks({
|
|
12095
|
+
resolve: (specifier, context, nextResolve) => specifier === url ? {
|
|
12096
|
+
url,
|
|
12097
|
+
shortCircuit: true
|
|
12098
|
+
} : nextResolve(specifier, context),
|
|
12099
|
+
load: (requested, context, nextLoad) => requested === url ? {
|
|
12100
|
+
format: "module",
|
|
12101
|
+
source,
|
|
12102
|
+
shortCircuit: true
|
|
12103
|
+
} : nextLoad(requested, context)
|
|
12104
|
+
});
|
|
12105
|
+
await import(url);
|
|
12106
|
+
};
|
|
12107
|
+
//#endregion
|
|
12057
12108
|
//#region src/flags/index.ts
|
|
12058
12109
|
const FLAGS$1 = [];
|
|
12059
12110
|
const BOOL = {
|
|
@@ -29513,13 +29564,7 @@ const parseJson = (raw) => {
|
|
|
29513
29564
|
const TRAILER_RE = /^(Gtd-[A-Za-z-]+):[ \t]*(.*?)[ \t]*$/gm;
|
|
29514
29565
|
const COST_RE = /^([0-9]+(?:\.[0-9]+)?)(?:[ \t]+(.+))?$/;
|
|
29515
29566
|
const VAR_RE = /^([^=\s]+)=(.*)$/;
|
|
29516
|
-
const CONFIG_RE = /^([0-9a-f]{16}) (.+)$/;
|
|
29517
29567
|
const CONTROL_CHARACTER$1 = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
|
|
29518
|
-
const encodePath = (path) => path.trim() !== path || path.startsWith("\"") || CONTROL_CHARACTER$1.test(path) ? JSON.stringify(path) : path;
|
|
29519
|
-
const decodePath = (raw) => {
|
|
29520
|
-
const path = raw.startsWith("\"") ? parseJson(raw) : raw;
|
|
29521
|
-
return typeof path === "string" && path !== "" && !CONTROL_CHARACTER$1.test(path) ? path : void 0;
|
|
29522
|
-
};
|
|
29523
29568
|
const decodeApproval = (raw) => {
|
|
29524
29569
|
const parsed = parseJson(raw);
|
|
29525
29570
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
@@ -29571,11 +29616,6 @@ const TRAILER_READERS = {
|
|
|
29571
29616
|
const kv = VAR_RE.exec(value);
|
|
29572
29617
|
if (kv !== null) into.vars[kv[1]] = kv[2];
|
|
29573
29618
|
},
|
|
29574
|
-
"Gtd-Config": (value, into) => {
|
|
29575
|
-
const entry = CONFIG_RE.exec(value);
|
|
29576
|
-
const path = entry === null ? void 0 : decodePath(entry[2]);
|
|
29577
|
-
if (path !== void 0) into.config[path] = entry[1];
|
|
29578
|
-
},
|
|
29579
29619
|
"Gtd-Review-Base": (value, into) => {
|
|
29580
29620
|
into.reviewBase ??= value.split(/\s/)[0];
|
|
29581
29621
|
},
|
|
@@ -29620,7 +29660,6 @@ const parseCommitMessage = (message) => {
|
|
|
29620
29660
|
judge: [],
|
|
29621
29661
|
marks: [],
|
|
29622
29662
|
vars: {},
|
|
29623
|
-
config: {},
|
|
29624
29663
|
cost: [],
|
|
29625
29664
|
format: void 0
|
|
29626
29665
|
};
|
|
@@ -29642,7 +29681,6 @@ const formatCommitMessage = (spec) => {
|
|
|
29642
29681
|
...trailer("Workflow", spec.workflow),
|
|
29643
29682
|
...trailer("Review-Base", spec.reviewBase),
|
|
29644
29683
|
...sortedEntries(spec.vars).map(([name, value]) => `Gtd-Var: ${name}=${value}`),
|
|
29645
|
-
...sortedEntries(spec.config).map(([path, hash]) => `Gtd-Config: ${hash} ${encodePath(path)}`),
|
|
29646
29684
|
...trailer("Cost", costValue(spec.cost)),
|
|
29647
29685
|
...(spec.judge ?? []).map((verdict) => `Gtd-Judge: ${JSON.stringify(verdict)}`),
|
|
29648
29686
|
...(spec.marks ?? []).map((m) => `Gtd-Mark: ${m.name}=${m.value}`),
|
|
@@ -30564,7 +30602,8 @@ const load = gen(function* () {
|
|
|
30564
30602
|
},
|
|
30565
30603
|
...compiled.ui !== void 0 ? { ui: compiled.ui } : {},
|
|
30566
30604
|
warnings: diagnostics.filter((d) => d.severity === "warning"),
|
|
30567
|
-
fingerprint: pinned?.fingerprint ?? configFingerprint(loaded.file.dependencyFiles, host.root, module.read ?? readFromDisk)
|
|
30605
|
+
fingerprint: pinned?.fingerprint ?? configFingerprint(loaded.file.dependencyFiles, host.root, module.read ?? readFromDisk),
|
|
30606
|
+
drift: pinned === void 0 ? [] : changedConfigFiles(pinned.fingerprint, configFingerprint(Object.keys(pinned.fingerprint).map((file) => resolve(host.root, file)), host.root, readFromDisk))
|
|
30568
30607
|
};
|
|
30569
30608
|
});
|
|
30570
30609
|
const readFromDisk = (path) => {
|
|
@@ -31218,8 +31257,6 @@ const knownModes = (def) => Object.keys(def.modes);
|
|
|
31218
31257
|
//#endregion
|
|
31219
31258
|
//#region src/workflow/pin.ts
|
|
31220
31259
|
init_dist$1();
|
|
31221
|
-
/** Bumped when a launcher could no longer run an older pin the same way. */
|
|
31222
|
-
const PIN_FORMAT = 1;
|
|
31223
31260
|
/**
|
|
31224
31261
|
* The engine a pin carries, as ES module source. Live, the `gtd.core.mjs` the
|
|
31225
31262
|
* running gtd ships beside its own bundle.
|
|
@@ -31245,8 +31282,26 @@ const pin = (module, root, version) => gen(function* () {
|
|
|
31245
31282
|
try: () => pinnedWorkflowSource(module, root, version),
|
|
31246
31283
|
catch: (e) => new GtdError(`gtd bundle: ${module.filepath}: ${e instanceof Error ? e.message : String(e)}`)
|
|
31247
31284
|
})) + core.replace(/^#!.*\n/, "");
|
|
31248
|
-
return Buffer.concat([Buffer.from(`gtdpin
|
|
31285
|
+
return Buffer.concat([Buffer.from(`gtdpin 1 gtd ${version}\n`), gzipSync(source, { level: 9 })]);
|
|
31249
31286
|
});
|
|
31287
|
+
/** A pin's header line, read without unpacking it; undefined when the bytes are no pin. */
|
|
31288
|
+
const pinHeader = (bytes) => {
|
|
31289
|
+
const end = bytes.indexOf(10);
|
|
31290
|
+
const header = end < 0 ? null : /^gtdpin (\d+) gtd (\S+)$/.exec(Buffer.from(bytes.subarray(0, end)).toString());
|
|
31291
|
+
return header === null ? void 0 : {
|
|
31292
|
+
format: Number(header[1]),
|
|
31293
|
+
version: header[2]
|
|
31294
|
+
};
|
|
31295
|
+
};
|
|
31296
|
+
/** A pin's header and module source. */
|
|
31297
|
+
const readPin = (bytes) => {
|
|
31298
|
+
const header = pinHeader(bytes);
|
|
31299
|
+
if (header === void 0) throw new Error("not a gtd pin");
|
|
31300
|
+
return {
|
|
31301
|
+
...header,
|
|
31302
|
+
source: gunzipSync(bytes.subarray(bytes.indexOf(10) + 1)).toString("utf8")
|
|
31303
|
+
};
|
|
31304
|
+
};
|
|
31250
31305
|
//#endregion
|
|
31251
31306
|
//#region src/emit/GitScript.ts
|
|
31252
31307
|
const shellQuote$1 = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
@@ -31280,6 +31335,12 @@ const pinScript = (path) => {
|
|
|
31280
31335
|
const slash = path.lastIndexOf("/");
|
|
31281
31336
|
return `${slash === -1 ? "" : `mkdir -p ${shellQuote$1(path.slice(0, slash))}\n`}gtd bundle >${p} || { rm -f -- ${p}; exit 1; }`;
|
|
31282
31337
|
};
|
|
31338
|
+
/** `gtd repin`'s pin write: a failed `gtd bundle` leaves the committed pin in place. */
|
|
31339
|
+
const repinScript = (path) => {
|
|
31340
|
+
const p = shellQuote$1(path);
|
|
31341
|
+
const next = shellQuote$1(`${path}.new`);
|
|
31342
|
+
return `gtd bundle >${next} || { rm -f -- ${next}; exit 1; }\nmv -f -- ${next} ${p}`;
|
|
31343
|
+
};
|
|
31283
31344
|
/** An unpin beat's script, and `gtd abandon`'s removal of the pin. */
|
|
31284
31345
|
const unpinScript = (path) => `rm -f -- ${shellQuote$1(path)}`;
|
|
31285
31346
|
/**
|
|
@@ -31508,6 +31569,12 @@ const FMT_ABANDONED = "abandoned the process resting at \"%s\" — HEAD is back
|
|
|
31508
31569
|
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";
|
|
31509
31570
|
/** `no gtd process is underway (resting at "<initial>") — nothing to abandon` — `gtd abandon`'s no-op plain-text line. */
|
|
31510
31571
|
const abandonNoopText = (initial) => renderFormat(FMT_ABANDON_NOOP, initial);
|
|
31572
|
+
/** A landed self-loop/target-change transition. */
|
|
31573
|
+
const transitionOutcome = (from, to) => printfLine("%s %s → %s\n", [
|
|
31574
|
+
"'->'",
|
|
31575
|
+
shellQuote$1(from),
|
|
31576
|
+
shellQuote$1(to)
|
|
31577
|
+
]);
|
|
31511
31578
|
/** One already-rendered plain line. */
|
|
31512
31579
|
const noteOutcome = (text) => printfLine("%s\n", [shellQuote$1(text)]);
|
|
31513
31580
|
/**
|
|
@@ -31876,8 +31943,7 @@ const checkOverrides = (commandLabel, varOverrides, declaredNames, envNames) =>
|
|
|
31876
31943
|
* `gtd start <name>`: start a brand NEW process on a workflow, writing an
|
|
31877
31944
|
* opening commit that names the workflow's first step and carries a
|
|
31878
31945
|
* `Gtd-Workflow:` trailer, every process setting as a `Gtd-Var:` trailer, plus
|
|
31879
|
-
* a `Gtd-Review-Base:` trailer when the workflow fixes the process's diff base
|
|
31880
|
-
* and a `Gtd-Config:` trailer per config file.
|
|
31946
|
+
* and a `Gtd-Review-Base:` trailer when the workflow fixes the process's diff base.
|
|
31881
31947
|
* All validation is a REFUSAL, not an Effect failure.
|
|
31882
31948
|
*/
|
|
31883
31949
|
const planStart = (current, actor, start) => gen(function* () {
|
|
@@ -31915,8 +31981,7 @@ const planStart = (current, actor, start) => gen(function* () {
|
|
|
31915
31981
|
to: firstStep,
|
|
31916
31982
|
workflow: name,
|
|
31917
31983
|
...base !== void 0 ? { reviewBase: base } : {},
|
|
31918
|
-
vars
|
|
31919
|
-
config: start.config
|
|
31984
|
+
vars
|
|
31920
31985
|
})
|
|
31921
31986
|
}
|
|
31922
31987
|
}, {
|
|
@@ -32025,15 +32090,6 @@ const runOf = (history, location, closingHash = void 0) => {
|
|
|
32025
32090
|
const fault = historyFormatFault(history, location);
|
|
32026
32091
|
return fault === void 0 ? succeed$1(buildRun(history, location, closingHash)) : fail$8(new Error(fault));
|
|
32027
32092
|
};
|
|
32028
|
-
const pinnedConfigOf = (history) => {
|
|
32029
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
32030
|
-
const files = parseCommitMessage(history[i].message).config;
|
|
32031
|
-
if (Object.keys(files).length > 0) return {
|
|
32032
|
-
commit: history[i].hash,
|
|
32033
|
-
files
|
|
32034
|
-
};
|
|
32035
|
-
}
|
|
32036
|
-
};
|
|
32037
32093
|
const approvalEntryOf = (hash, message) => message.approved === void 0 ? [] : [{
|
|
32038
32094
|
step: formatStepId(message.approved.step),
|
|
32039
32095
|
name: message.approved.name,
|
|
@@ -32061,7 +32117,6 @@ const buildRun = (history, location, closingHash) => {
|
|
|
32061
32117
|
marks: parsed.flatMap((m) => m.marks),
|
|
32062
32118
|
approvals: processCommits.flatMap((c, i) => approvalEntryOf(c.hash, parsed[i])),
|
|
32063
32119
|
pinnedVars: { ...first?.vars },
|
|
32064
|
-
pinnedConfig: pinnedConfigOf(history.slice(Math.max(location.baseIndex, 0))),
|
|
32065
32120
|
headTurn: headTurnOf(head),
|
|
32066
32121
|
closingHash,
|
|
32067
32122
|
episode: {
|
|
@@ -32151,18 +32206,6 @@ const findBoundaryBase = (git, def, head) => gen(function* () {
|
|
|
32151
32206
|
});
|
|
32152
32207
|
const historyUpTo = (git, def, head) => flatMap(findBoundaryBase(git, def, head), ({ base, head: resolvedHead }) => git.commitHistory(base, resolvedHead));
|
|
32153
32208
|
const computeProcessRun = (git, def, head) => flatMap(historyUpTo(git, def, head), (history) => runOf(history, locateEpisode(def, history)));
|
|
32154
|
-
const changedSinceStart = (pinned, current, workspace) => {
|
|
32155
|
-
if (pinned === void 0) return [];
|
|
32156
|
-
return changedConfigFiles(pinned.files, current).filter((path) => {
|
|
32157
|
-
const committed = workspace.readCommittedSync(path, pinned.commit);
|
|
32158
|
-
return current[path] === void 0 || committed === void 0 || contentHash(committed) !== current[path];
|
|
32159
|
-
});
|
|
32160
|
-
};
|
|
32161
|
-
const warnConfigChanges = (run, current, workspace) => gen(function* () {
|
|
32162
|
-
const changed = changedSinceStart(run.pinnedConfig, current, workspace);
|
|
32163
|
-
if (changed.length === 0) return;
|
|
32164
|
-
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`);
|
|
32165
|
-
});
|
|
32166
32209
|
/** The run alone, never replaying — `gtd abandon` must work even when replay would refuse, short of a history it cannot read. */
|
|
32167
32210
|
const currentRun = gen(function* () {
|
|
32168
32211
|
const git = yield* GitService;
|
|
@@ -32478,8 +32521,7 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
32478
32521
|
status: normalizeStatus(e.status),
|
|
32479
32522
|
path: e.path
|
|
32480
32523
|
})) : [];
|
|
32481
|
-
|
|
32482
|
-
if (ref === void 0) yield* warnConfigChanges(run, configNow, workspace);
|
|
32524
|
+
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`);
|
|
32483
32525
|
return {
|
|
32484
32526
|
def,
|
|
32485
32527
|
workflowName: resolved.name,
|
|
@@ -32493,7 +32535,6 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
32493
32535
|
run,
|
|
32494
32536
|
vars,
|
|
32495
32537
|
env,
|
|
32496
|
-
config: configNow,
|
|
32497
32538
|
changes,
|
|
32498
32539
|
memory: memory.key,
|
|
32499
32540
|
memoryResumed: memory.resumed,
|
|
@@ -32757,10 +32798,7 @@ const pinRefusal = (pin) => ({
|
|
|
32757
32798
|
kind: "refusal",
|
|
32758
32799
|
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`
|
|
32759
32800
|
});
|
|
32760
|
-
const startTrailers = (rest, starts) => starts ? {
|
|
32761
|
-
vars: rest.vars,
|
|
32762
|
-
config: rest.config
|
|
32763
|
-
} : {};
|
|
32801
|
+
const startTrailers = (rest, starts) => starts ? { vars: rest.vars } : {};
|
|
32764
32802
|
const outcomeMarks = (outcome) => outcome.kind === "rest" || outcome.kind === "ended" ? outcome.marks : [];
|
|
32765
32803
|
/**
|
|
32766
32804
|
* What landing the pending turn at `rest` does — a pure decision from a
|
|
@@ -32831,16 +32869,20 @@ const summaryFor = (run) => gen(function* () {
|
|
|
32831
32869
|
if (summary === void 0 || run.trace.length === 0) return { prompt: void 0 };
|
|
32832
32870
|
return { prompt: summary(summaryInput(run, settingsFor(resolved, run.pinnedVars, host.env, {}, {}))) };
|
|
32833
32871
|
});
|
|
32834
|
-
/**
|
|
32835
|
-
|
|
32836
|
-
|
|
32872
|
+
/**
|
|
32873
|
+
* Repository-relative paths of today's config files; one outside the
|
|
32874
|
+
* repository cannot be read at a ref, so it is left out. A file only the old
|
|
32875
|
+
* config read needs no check: whichever file imported it differs too.
|
|
32876
|
+
*/
|
|
32877
|
+
const inRepoConfigPaths = (root, files) => {
|
|
32878
|
+
const paths = /* @__PURE__ */ new Set();
|
|
32837
32879
|
for (const file of files) paths.add(relative(root, resolve(root, file)).split(sep).join("/"));
|
|
32838
32880
|
return [...paths].filter((p) => p !== "" && !p.startsWith("..") && !isAbsolute(p)).sort();
|
|
32839
32881
|
};
|
|
32840
32882
|
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}`);
|
|
32841
|
-
const differingConfigPaths = (git, workspace, root, config,
|
|
32883
|
+
const differingConfigPaths = (git, workspace, root, config, hash) => gen(function* () {
|
|
32842
32884
|
const differing = [];
|
|
32843
|
-
for (const path of inRepoConfigPaths(root, Object.keys(config.fingerprint)
|
|
32885
|
+
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);
|
|
32844
32886
|
return differing;
|
|
32845
32887
|
});
|
|
32846
32888
|
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");
|
|
@@ -32897,7 +32939,7 @@ const replayFinishedProcess = (config, run, hash, how) => gen(function* () {
|
|
|
32897
32939
|
const recorded = parseCommitMessage(run.episode.commits.find((c) => c.hash === hash)?.message ?? "").version;
|
|
32898
32940
|
const versionWarning = versionWarningFor(recorded);
|
|
32899
32941
|
const withWarning = (err) => versionWarning === void 0 ? err : /* @__PURE__ */ new Error(`${err.message}\n${versionWarning}`);
|
|
32900
|
-
const differing = yield* differingConfigPaths(git, workspace, host.root, config,
|
|
32942
|
+
const differing = yield* differingConfigPaths(git, workspace, host.root, config, hash);
|
|
32901
32943
|
if (differing.length > 0) return yield* fail$8(withWarning(configRefusal(how.command, hash, differing, how.configReason)));
|
|
32902
32944
|
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
32903
32945
|
const outcome = yield* promise(() => replayFor(setup));
|
|
@@ -33868,8 +33910,7 @@ const startWorkflow = (workflow, varOverrides, json, out, commandLabel) => gen(f
|
|
|
33868
33910
|
workflow: resolved,
|
|
33869
33911
|
first: started,
|
|
33870
33912
|
commandLabel,
|
|
33871
|
-
vars: varOverrides
|
|
33872
|
-
config: rest.config
|
|
33913
|
+
vars: varOverrides
|
|
33873
33914
|
});
|
|
33874
33915
|
if (plan.kind === "refusal") return yield* fail$8(new Error(plan.message));
|
|
33875
33916
|
yield* writeScript(json, out, listingCommitted(landingScript(ScriptSurface.render(plan.steps)), rest.changes));
|
|
@@ -34026,6 +34067,36 @@ const runRestoreCommand = (json, out) => gen(function* () {
|
|
|
34026
34067
|
yield* writeScript(json, out, combinedScript(required, ""));
|
|
34027
34068
|
});
|
|
34028
34069
|
/**
|
|
34070
|
+
* `gtd repin`: the pin beat's script and landing, on demand. It runs live,
|
|
34071
|
+
* so reading the rest first refuses a live workflow that cannot replay the
|
|
34072
|
+
* process before any pin is written.
|
|
34073
|
+
*/
|
|
34074
|
+
const runRepinCommand = (json, out) => gen(function* () {
|
|
34075
|
+
const rest = yield* currentRest;
|
|
34076
|
+
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;
|
|
34077
|
+
if (refusal !== void 0) return yield* fail$8(/* @__PURE__ */ new Error(`gtd repin: ${refusal}`));
|
|
34078
|
+
const message = formatCommitMessage({
|
|
34079
|
+
actor: "check",
|
|
34080
|
+
from: PIN_STATES.add,
|
|
34081
|
+
to: rest.step.name,
|
|
34082
|
+
pin: "add"
|
|
34083
|
+
});
|
|
34084
|
+
yield* writeScript(json, out, combinedScript(emitScripts([
|
|
34085
|
+
{
|
|
34086
|
+
kind: "gitWrite",
|
|
34087
|
+
command: repinScript(PIN_PATH)
|
|
34088
|
+
},
|
|
34089
|
+
{
|
|
34090
|
+
kind: "gitWrite",
|
|
34091
|
+
command: commitPath(PIN_PATH, message)
|
|
34092
|
+
},
|
|
34093
|
+
{
|
|
34094
|
+
kind: "outcome",
|
|
34095
|
+
command: transitionOutcome(PIN_STATES.add, rest.step.name)
|
|
34096
|
+
}
|
|
34097
|
+
]).required, ""));
|
|
34098
|
+
});
|
|
34099
|
+
/**
|
|
34029
34100
|
* The mode's own validate command for `file`: a declared `validate:` command
|
|
34030
34101
|
* verbatim; a `"builtin"` validator or no mode at all names the leaf
|
|
34031
34102
|
* `gtd check <mode>` invocation instead, so there's always something concrete
|
|
@@ -34292,6 +34363,7 @@ const dispatchVoidCommand = (command, json, out) => {
|
|
|
34292
34363
|
case "start": return runStartCommand(command, json, out);
|
|
34293
34364
|
case "abandon": return runAbandonCommand(json, out);
|
|
34294
34365
|
case "restore": return runRestoreCommand(json, out);
|
|
34366
|
+
case "repin": return runRepinCommand(json, out);
|
|
34295
34367
|
case "next": return runNextCommand(json, out);
|
|
34296
34368
|
case "validate": return runValidateCommand(json, out);
|
|
34297
34369
|
case "check":
|
|
@@ -34421,6 +34493,7 @@ const SCRIPT_KINDS = /* @__PURE__ */ new Set([
|
|
|
34421
34493
|
"start",
|
|
34422
34494
|
"abandon",
|
|
34423
34495
|
"restore",
|
|
34496
|
+
"repin",
|
|
34424
34497
|
"validate",
|
|
34425
34498
|
"historyFetch"
|
|
34426
34499
|
]);
|
|
@@ -34688,6 +34761,16 @@ With no <name>, list every workflow and door it can start. An unknown <name>, th
|
|
|
34688
34761
|
summary: "Bring back the last abandoned process",
|
|
34689
34762
|
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.`
|
|
34690
34763
|
},
|
|
34764
|
+
{
|
|
34765
|
+
token: "repin",
|
|
34766
|
+
kind: "repin",
|
|
34767
|
+
arity: "none",
|
|
34768
|
+
group: "Start and stop",
|
|
34769
|
+
summary: "Pin the process to the live gtd and workflow",
|
|
34770
|
+
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.
|
|
34771
|
+
|
|
34772
|
+
s=$(gtd repin --json=script) && sh -c "$s"`
|
|
34773
|
+
},
|
|
34691
34774
|
{
|
|
34692
34775
|
token: "summary",
|
|
34693
34776
|
kind: "summary",
|
|
@@ -34872,6 +34955,7 @@ const flagHeader = (row) => {
|
|
|
34872
34955
|
};
|
|
34873
34956
|
const GLOBAL_OPTIONS = [
|
|
34874
34957
|
["-v, --verbose", "Narrate on stderr what gtd resolved"],
|
|
34958
|
+
["--live", "Run this gtd, not the process's pin; it may misread the process"],
|
|
34875
34959
|
["-h, --help", "Print help; gtd <command> --help for one command"],
|
|
34876
34960
|
["-V, --version", "Print the version"],
|
|
34877
34961
|
["--", "End the options: every word after it is an argument"]
|
|
@@ -34883,7 +34967,7 @@ const EXTRA_TOPICS = [[
|
|
|
34883
34967
|
], [
|
|
34884
34968
|
"version",
|
|
34885
34969
|
"Print the version",
|
|
34886
|
-
"Print gtd's version."
|
|
34970
|
+
"Print gtd's version, and the version of a pinned process's gtd."
|
|
34887
34971
|
]];
|
|
34888
34972
|
const EXAMPLES = ` $ gtd start list what can start
|
|
34889
34973
|
$ gtd start <name> start a process
|
|
@@ -35381,6 +35465,109 @@ const runCli = (argv, io) => {
|
|
|
35381
35465
|
}), provide(io.layers(plan.verbose)), sandbox, catch_((cause) => hasInterruptsOnly(cause) ? failCause$1(cause) : report(io, plan.json.kind !== "off")(cause)));
|
|
35382
35466
|
};
|
|
35383
35467
|
//#endregion
|
|
35468
|
+
//#region src/cli/Launch.ts
|
|
35469
|
+
/** What the core a pin carries lacks, or what must read the live state: these never hand over. */
|
|
35470
|
+
const LIVE_KINDS = /* @__PURE__ */ new Set([
|
|
35471
|
+
"ui",
|
|
35472
|
+
"lsp",
|
|
35473
|
+
"guide",
|
|
35474
|
+
"bundle",
|
|
35475
|
+
"repin",
|
|
35476
|
+
"historyList",
|
|
35477
|
+
"historyShow",
|
|
35478
|
+
"historyMetrics",
|
|
35479
|
+
"historyJudges",
|
|
35480
|
+
"historyFetch"
|
|
35481
|
+
]);
|
|
35482
|
+
const LIVE_FLAG = "--live";
|
|
35483
|
+
const optionWords = (argv) => {
|
|
35484
|
+
const end = argv.indexOf("--", 2);
|
|
35485
|
+
return argv.slice(2, end < 0 ? void 0 : end);
|
|
35486
|
+
};
|
|
35487
|
+
const withoutLiveFlag = (argv) => {
|
|
35488
|
+
const end = argv.indexOf("--", 2);
|
|
35489
|
+
return argv.filter((word, i) => word !== LIVE_FLAG || i < 2 || end >= 0 && i > end);
|
|
35490
|
+
};
|
|
35491
|
+
/**
|
|
35492
|
+
* Whether this gtd runs the command or hands it to the process's pin. A pin
|
|
35493
|
+
* this gtd cannot run is refused, never guessed at; a usage error the live
|
|
35494
|
+
* parser finds is still the pin's to judge, since its engine may read
|
|
35495
|
+
* another CLI.
|
|
35496
|
+
*/
|
|
35497
|
+
const launchFor = (argv, pinFile) => {
|
|
35498
|
+
if (globalThis[PIN_KEY] !== void 0) return {
|
|
35499
|
+
kind: "live",
|
|
35500
|
+
argv
|
|
35501
|
+
};
|
|
35502
|
+
const live = optionWords(argv).includes(LIVE_FLAG);
|
|
35503
|
+
const rest = withoutLiveFlag(argv);
|
|
35504
|
+
if (live) return {
|
|
35505
|
+
kind: "live",
|
|
35506
|
+
argv: rest
|
|
35507
|
+
};
|
|
35508
|
+
const plan = parseArgv(rest);
|
|
35509
|
+
const version = plan.kind === "output" && plan.stdout === `${GTD_VERSION}\n`;
|
|
35510
|
+
if (plan.kind === "output" && !version) return {
|
|
35511
|
+
kind: "live",
|
|
35512
|
+
argv: rest
|
|
35513
|
+
};
|
|
35514
|
+
if (plan.kind === "command" && LIVE_KINDS.has(plan.command.kind)) return {
|
|
35515
|
+
kind: "live",
|
|
35516
|
+
argv: rest
|
|
35517
|
+
};
|
|
35518
|
+
const found = pinFile();
|
|
35519
|
+
if (found === void 0) return {
|
|
35520
|
+
kind: "live",
|
|
35521
|
+
argv: rest
|
|
35522
|
+
};
|
|
35523
|
+
const header = pinHeader(found.bytes);
|
|
35524
|
+
if (version) return {
|
|
35525
|
+
kind: "output",
|
|
35526
|
+
stdout: header === void 0 ? plan.stdout : `${GTD_VERSION} (this process is pinned to gtd ${header.version})\n`
|
|
35527
|
+
};
|
|
35528
|
+
if (header === void 0 || header.format !== 1) {
|
|
35529
|
+
const what = header === void 0 ? "is no pin this gtd can read" : `is pin format ${header.format}, made by gtd ${header.version}`;
|
|
35530
|
+
return {
|
|
35531
|
+
kind: "refuse",
|
|
35532
|
+
message: `gtd: ${found.path} ${what}; gtd ${GTD_VERSION} runs pin format 1 — upgrade gtd (\`gtd --live\` runs this gtd instead, and may misread the process)`
|
|
35533
|
+
};
|
|
35534
|
+
}
|
|
35535
|
+
return {
|
|
35536
|
+
kind: "pin",
|
|
35537
|
+
argv: rest,
|
|
35538
|
+
path: found.path,
|
|
35539
|
+
version: header.version,
|
|
35540
|
+
source: () => readPin(found.bytes).source
|
|
35541
|
+
};
|
|
35542
|
+
};
|
|
35543
|
+
const isJsonFlag = (word) => word === "--json" || word.startsWith("--json=");
|
|
35544
|
+
const refusalText = (argv, message) => (optionWords(argv).some(isJsonFlag) ? `${JSON.stringify({
|
|
35545
|
+
state: "error",
|
|
35546
|
+
prompt: message
|
|
35547
|
+
})}\n` : "") + `${message}\n`;
|
|
35548
|
+
const runPin = async (launch, launcher) => {
|
|
35549
|
+
const words = optionWords(launch.argv);
|
|
35550
|
+
if (words.includes("--verbose") || words.includes("-v")) launcher.stderr(`launcher: running the pin ${launch.path} (gtd ${launch.version})\n`);
|
|
35551
|
+
try {
|
|
35552
|
+
await launcher.pin(launch);
|
|
35553
|
+
} catch (error) {
|
|
35554
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
35555
|
+
launcher.stderr(`gtd: could not run the pin ${launch.path}: ${reason}\n`);
|
|
35556
|
+
launcher.exit(1);
|
|
35557
|
+
}
|
|
35558
|
+
};
|
|
35559
|
+
const runLauncher = async (argv, pinFile, launcher) => {
|
|
35560
|
+
const launch = launchFor(argv, pinFile);
|
|
35561
|
+
switch (launch.kind) {
|
|
35562
|
+
case "live": return launcher.live(launch.argv);
|
|
35563
|
+
case "output": return launcher.stdout(launch.stdout);
|
|
35564
|
+
case "refuse":
|
|
35565
|
+
launcher.stderr(refusalText(argv, launch.message));
|
|
35566
|
+
return launcher.exit(1);
|
|
35567
|
+
case "pin": return runPin(launch, launcher);
|
|
35568
|
+
}
|
|
35569
|
+
};
|
|
35570
|
+
//#endregion
|
|
35384
35571
|
//#region node_modules/@trpc/server/dist/codes-DagpWZLc.mjs
|
|
35385
35572
|
/**
|
|
35386
35573
|
* Ensures there are no duplicate keys when building a procedure.
|
|
@@ -51407,25 +51594,40 @@ const liveToolCommands = effect(ToolCommands, gen(function* () {
|
|
|
51407
51594
|
//#endregion
|
|
51408
51595
|
//#region src/main.ts
|
|
51409
51596
|
init_dist$1();
|
|
51410
|
-
|
|
51411
|
-
|
|
51412
|
-
|
|
51413
|
-
|
|
51414
|
-
|
|
51415
|
-
|
|
51416
|
-
|
|
51417
|
-
|
|
51418
|
-
|
|
51419
|
-
|
|
51597
|
+
const runLive = (argv) => {
|
|
51598
|
+
let deathSignal;
|
|
51599
|
+
process.once("SIGINT", () => {
|
|
51600
|
+
deathSignal = "SIGINT";
|
|
51601
|
+
});
|
|
51602
|
+
process.once("SIGTERM", () => {
|
|
51603
|
+
deathSignal ??= "SIGTERM";
|
|
51604
|
+
});
|
|
51605
|
+
process.once("SIGHUP", () => {
|
|
51606
|
+
deathSignal = "SIGHUP";
|
|
51607
|
+
process.kill(process.pid, "SIGTERM");
|
|
51608
|
+
});
|
|
51609
|
+
runMain(runCli(argv, nodeCliIo(liveToolCommands)), { teardown: (exit, onExit) => {
|
|
51610
|
+
if (deathSignal === void 0) {
|
|
51611
|
+
defaultTeardown(exit, onExit);
|
|
51612
|
+
return;
|
|
51613
|
+
}
|
|
51614
|
+
const signal = deathSignal;
|
|
51615
|
+
process.removeAllListeners(signal);
|
|
51616
|
+
process.kill(process.pid, signal);
|
|
51617
|
+
} });
|
|
51618
|
+
};
|
|
51619
|
+
await runLauncher(process.argv, livePinFile(process.cwd(), PIN_PATH), {
|
|
51620
|
+
stdout: (chunk) => {
|
|
51621
|
+
process.stdout.write(chunk);
|
|
51622
|
+
},
|
|
51623
|
+
stderr: (chunk) => {
|
|
51624
|
+
process.stderr.write(chunk);
|
|
51625
|
+
},
|
|
51626
|
+
exit: (code) => {
|
|
51627
|
+
process.exitCode = code;
|
|
51628
|
+
},
|
|
51629
|
+
live: runLive,
|
|
51630
|
+
pin: (launch) => runPinModule(launch.path, launch.source())
|
|
51420
51631
|
});
|
|
51421
|
-
runMain(runCli(process.argv, nodeCliIo(liveToolCommands)), { teardown: (exit, onExit) => {
|
|
51422
|
-
if (deathSignal === void 0) {
|
|
51423
|
-
defaultTeardown(exit, onExit);
|
|
51424
|
-
return;
|
|
51425
|
-
}
|
|
51426
|
-
const signal = deathSignal;
|
|
51427
|
-
process.removeAllListeners(signal);
|
|
51428
|
-
process.kill(process.pid, signal);
|
|
51429
|
-
} });
|
|
51430
51632
|
//#endregion
|
|
51431
51633
|
export {};
|