@pmelab/gtd 58.1.0 → 59.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 +115 -390
- package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
- package/dist/gtd.bundle.mjs +76 -59
- package/dist/testkit/gherkin.mjs +30 -2
- package/dist/testkit.mjs +1 -1
- package/package.json +3 -42
- package/skills/authoring/SKILL.md +31 -25
- package/src/cli/Cli.test.ts +15 -19
- package/src/cli/Cli.ts +2 -4
- package/src/cli/Program.test.ts +60 -29
- package/src/cli/Program.ts +5 -7
- package/src/edge/Edge.ts +3 -3
- package/src/flags/index.test.ts +0 -13
- package/src/flags/index.ts +1 -3
- package/src/gherkin/git-control.feature +78 -0
- package/src/gherkin/index.ts +35 -1
- package/src/install/Install.test.ts +14 -11
- package/src/install/Install.ts +38 -15
- package/src/install/index.ts +1 -2
- package/src/replay/Replay.test.ts +1 -1
- package/src/replay/index.ts +1 -1
- package/src/step/planStart.ts +6 -1
- package/src/testkit/TestRepo.test.ts +118 -2
- package/src/testkit/TestRepo.ts +57 -6
- package/src/testkit/index.ts +1 -0
- package/src/twins/EmittedScriptRecognizer.ts +12 -14
- package/src/twins/FlowScripts.ts +10 -9
- package/src/twins/InMemRepo.ts +26 -1
- package/src/twins/Layers.ts +12 -1
- package/src/twins/index.ts +6 -1
- package/src/workflow/index.ts +1 -0
- package/src/workflow/load.test.ts +138 -113
- package/src/workflow/load.ts +1 -1
- package/src/workflow/vars.test.ts +30 -3
- package/src/workflow/vars.ts +34 -2
- package/src/workflow-kit/index.ts +1 -1
- package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
- package/dist/chunks/review-Bfaror38.mjs +0 -427
- package/dist/chunks/tail-Bd7L940p.mjs +0 -355
- package/dist/chunks/testkit-D1-auYau.mjs +0 -10362
- package/dist/workflows/cleanup.mjs +0 -36
- package/dist/workflows/feature.mjs +0 -1018
- package/dist/workflows/fix.mjs +0 -41
- package/dist/workflows/review.mjs +0 -45
- package/dist/workflows/rollout.mjs +0 -48
- package/dist/workflows.mjs +0 -40651
- package/src/install/ConfigScaffold.ts +0 -22
- package/src/workflows/cleanup/cleanup-path.feature +0 -94
- package/src/workflows/cleanup/cleanup.feature +0 -315
- package/src/workflows/cleanup/cleanup.test.ts +0 -276
- package/src/workflows/cleanup/cleanup.ts +0 -52
- package/src/workflows/cleanup/index.ts +0 -6
- package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
- package/src/workflows/feature/acceptance-criteria.feature +0 -389
- package/src/workflows/feature/criteria-coverage.feature +0 -102
- package/src/workflows/feature/criteria.test.ts +0 -147
- package/src/workflows/feature/criteria.ts +0 -267
- package/src/workflows/feature/fast-path.feature +0 -93
- package/src/workflows/feature/feature.test.ts +0 -338
- package/src/workflows/feature/feature.ts +0 -156
- package/src/workflows/feature/green.ts +0 -19
- package/src/workflows/feature/index.ts +0 -18
- package/src/workflows/feature/lens-models.feature +0 -69
- package/src/workflows/feature/lens-review.feature +0 -566
- package/src/workflows/feature/naive-acceptance.feature +0 -147
- package/src/workflows/feature/naive-test.feature +0 -263
- package/src/workflows/feature/one-step-land.feature +0 -93
- package/src/workflows/feature/planning.ts +0 -41
- package/src/workflows/feature/refactor-absorbs.feature +0 -131
- package/src/workflows/feature/refactor-first.feature +0 -158
- package/src/workflows/feature/refactor-lens-models.feature +0 -79
- package/src/workflows/feature/refactor-rebuild.feature +0 -273
- package/src/workflows/feature/refactor-review.feature +0 -1528
- package/src/workflows/feature/refactor.test.ts +0 -1220
- package/src/workflows/feature/refactor.ts +0 -368
- package/src/workflows/feature/release-flag.feature +0 -167
- package/src/workflows/feature/release-flag.test.ts +0 -49
- package/src/workflows/feature/release-flag.ts +0 -46
- package/src/workflows/feature/review-gate.feature +0 -241
- package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
- package/src/workflows/feature/risk-tier.feature +0 -351
- package/src/workflows/feature/seam-rejection.feature +0 -146
- package/src/workflows/feature/seam.feature +0 -297
- package/src/workflows/feature/seam.test.ts +0 -527
- package/src/workflows/feature/seam.ts +0 -426
- package/src/workflows/feature/standards.feature +0 -354
- package/src/workflows/feature/steps.test.ts +0 -65
- package/src/workflows/feature/steps.ts +0 -45
- package/src/workflows/feature/tier-pin.feature +0 -66
- package/src/workflows/feature/tier.test.ts +0 -166
- package/src/workflows/feature/tier.ts +0 -56
- package/src/workflows/fix/fix-lens-origin.feature +0 -114
- package/src/workflows/fix/fix-quality.feature +0 -212
- package/src/workflows/fix/fix.feature +0 -79
- package/src/workflows/fix/fix.test.ts +0 -74
- package/src/workflows/fix/fix.ts +0 -52
- package/src/workflows/fix/index.ts +0 -6
- package/src/workflows/index.ts +0 -47
- package/src/workflows/review/index.ts +0 -6
- package/src/workflows/review/review-door.feature +0 -199
- package/src/workflows/review/review.test.ts +0 -27
- package/src/workflows/review/review.ts +0 -60
- package/src/workflows/rollout/index.ts +0 -6
- package/src/workflows/rollout/rollout.feature +0 -510
- package/src/workflows/rollout/rollout.test.ts +0 -263
- package/src/workflows/rollout/rollout.ts +0 -82
- package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
- package/src/workflows/shared/access.test.ts +0 -62
- package/src/workflows/shared/access.ts +0 -24
- package/src/workflows/shared/config.fixture.ts +0 -12
- package/src/workflows/shared/fences.ts +0 -24
- package/src/workflows/shared/health.test.ts +0 -148
- package/src/workflows/shared/health.ts +0 -191
- package/src/workflows/shared/learn.test.ts +0 -140
- package/src/workflows/shared/learn.ts +0 -201
- package/src/workflows/shared/lenses.test.ts +0 -39
- package/src/workflows/shared/lenses.ts +0 -294
- package/src/workflows/shared/options.test.ts +0 -157
- package/src/workflows/shared/options.ts +0 -92
- package/src/workflows/shared/progress.feature +0 -124
- package/src/workflows/shared/prose.ts +0 -263
- package/src/workflows/shared/recheck.feature +0 -60
- package/src/workflows/shared/registry.test.ts +0 -345
- package/src/workflows/shared/registry.ts +0 -183
- package/src/workflows/shared/reserved.ts +0 -11
- package/src/workflows/shared/review.test.ts +0 -1062
- package/src/workflows/shared/review.ts +0 -446
- package/src/workflows/shared/skills.test.ts +0 -121
- package/src/workflows/shared/skills.ts +0 -49
- package/src/workflows/shared/standards.test.ts +0 -90
- package/src/workflows/shared/standards.ts +0 -156
- package/src/workflows/shared/steps.test.ts +0 -219
- package/src/workflows/shared/steps.ts +0 -201
- package/src/workflows/shared/tail.test.ts +0 -454
- package/src/workflows/shared/tail.ts +0 -264
- package/src/workflows/shared/text.fixture.ts +0 -128
- package/src/workflows/shared/text.test.ts +0 -1430
- package/src/workflows/shared/text.ts +0 -1700
- package/src/workflows/shared/threads.test.ts +0 -129
- package/src/workflows/shared/vars.ts +0 -47
- package/src/workflows/shared/workflows.steps.ts +0 -54
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -20888,12 +20888,7 @@ const restorability = (git, headHash, tipHash) => gen(function* () {
|
|
|
20888
20888
|
});
|
|
20889
20889
|
//#endregion
|
|
20890
20890
|
//#region src/flags/index.ts
|
|
20891
|
-
const FLAGS$1 = [
|
|
20892
|
-
name: "risk_tier",
|
|
20893
|
-
owner: "pmelab",
|
|
20894
|
-
expiry: "2027-01-15",
|
|
20895
|
-
kind: "release"
|
|
20896
|
-
}];
|
|
20891
|
+
const FLAGS$1 = [];
|
|
20897
20892
|
const BOOL = {
|
|
20898
20893
|
"0": false,
|
|
20899
20894
|
false: false,
|
|
@@ -20935,6 +20930,7 @@ const findPackageJson = (fromUrl) => {
|
|
|
20935
20930
|
const GTD_VERSION = _require(findPackageJson(import.meta.url)).version;
|
|
20936
20931
|
//#endregion
|
|
20937
20932
|
//#region src/install/Install.ts
|
|
20933
|
+
const WORKFLOWS_REPO = "https://github.com/pmelab/gtd-workflows";
|
|
20938
20934
|
const MINIMAL_DRIVER = `#!/usr/bin/env sh
|
|
20939
20935
|
set -eu
|
|
20940
20936
|
|
|
@@ -21016,7 +21012,7 @@ while :; do
|
|
|
21016
21012
|
system="$(gtd next --json=system)"
|
|
21017
21013
|
validate="$(gtd next --json=validate)"
|
|
21018
21014
|
log="$(gtd next --json=log)"
|
|
21019
|
-
# \`model\` is a hint, not a model name —
|
|
21015
|
+
# \`model\` is a hint, not a model name — gtd-workflows' default ships
|
|
21020
21016
|
# \`smart\` and \`base\`, which \`claude --model\` rejects. Resolve them
|
|
21021
21017
|
# from your environment; unset, the hint is dropped and the agent runs
|
|
21022
21018
|
# its own default. Any other value is a real model name.
|
|
@@ -21338,14 +21334,37 @@ follow in the same numbered list.
|
|
|
21338
21334
|
existing config, the project's own README/docs) and ask the user what
|
|
21339
21335
|
they're trying to accomplish with gtd here — don't assume a fresh
|
|
21340
21336
|
install or silently start a process on their behalf.
|
|
21341
|
-
2. **Make sure a \`gtd.config.ts\` governs the repository.**
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21337
|
+
2. **Make sure a \`gtd.config.ts\` governs the repository.** gtd ships no
|
|
21338
|
+
workflow: the config imports the workflows it runs. Ask the user which
|
|
21339
|
+
way they want:
|
|
21340
|
+
- **A workflow from ${WORKFLOWS_REPO}.** Its \`default\` is a feature →
|
|
21341
|
+
review → fix workflow. It is used from a checkout pinned on this
|
|
21342
|
+
machine:
|
|
21343
|
+
|
|
21344
|
+
\`\`\`sh
|
|
21345
|
+
git clone ${WORKFLOWS_REPO} ~/.local/share/gtd/workflows
|
|
21346
|
+
~/.local/share/gtd/workflows/bin/gtd-wf pin origin/main # or a tag
|
|
21347
|
+
~/.local/share/gtd/workflows/bin/gtd-wf config default > gtd.config.ts
|
|
21348
|
+
\`\`\`
|
|
21349
|
+
|
|
21350
|
+
\`pin\` checks the ref out and bundles each workflow; \`config\` prints a
|
|
21351
|
+
config importing the bundle by its ABSOLUTE path. Tell the user what
|
|
21352
|
+
that means: the committed config works only where the checkout sits at
|
|
21353
|
+
that same path, so a teammate or CI needs their own checkout there (or
|
|
21354
|
+
their own config). If a checkout exists already, \`gtd-wf status\`
|
|
21355
|
+
shows its pin — reuse it rather than cloning again. Read the
|
|
21356
|
+
repository's README for the workflows it offers and their options.
|
|
21357
|
+
- **Their own workflow.** A workflow is TypeScript the config exports,
|
|
21358
|
+
written against \`@pmelab/gtd/flows\` — see
|
|
21359
|
+
https://github.com/pmelab/gtd/blob/main/docs/configuration.md, and
|
|
21360
|
+
https://github.com/pmelab/gtd/blob/main/docs/testing.md to test it. A
|
|
21361
|
+
copy of a gtd-workflows workflow (\`gtd-wf new <name> --from default\`
|
|
21362
|
+
in a clone of it) is a good start.
|
|
21363
|
+
|
|
21364
|
+
Repository settings — steering-file modes, \`gtd ui\`, per-scope skills
|
|
21365
|
+
and access — go in the config's \`config\` export; workflow settings are
|
|
21366
|
+
options of the workflow factory it exports.
|
|
21367
|
+
3. **Commit the config before the first drive.** An uncommitted config is a
|
|
21349
21368
|
pending change, so starting a process without committing it first turns
|
|
21350
21369
|
the config into the very first thing the process plans against. Commit
|
|
21351
21370
|
it, THEN start driving.
|
|
@@ -21371,7 +21390,8 @@ follow in the same numbered list.
|
|
|
21371
21390
|
from every prompt line and writing no model exports, so every turn runs
|
|
21372
21391
|
on the chosen CLI's own default tier.
|
|
21373
21392
|
6. **If model hints are honored, resolve them to real model identifiers.**
|
|
21374
|
-
|
|
21393
|
+
A workflow may declare model hints — gtd-workflows' \`default\` ships
|
|
21394
|
+
\`plannerModel: smart\` and \`coderModel: base\`.
|
|
21375
21395
|
**Those are opaque hints, not model names** — gtd never interprets them,
|
|
21376
21396
|
and passing \`--model smart\` to a real CLI fails on the first prompt
|
|
21377
21397
|
beat. Derive the chosen CLI's own available models at install time — its
|
|
@@ -21468,11 +21488,8 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
|
|
|
21468
21488
|
rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
|
|
21469
21489
|
straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
|
|
21470
21490
|
\`gtd doors\` lists the doors a repository offers (those of the workflows its
|
|
21471
|
-
config exports —
|
|
21472
|
-
\`
|
|
21473
|
-
(remove it), which a config adds by exporting \`rollout\` from
|
|
21474
|
-
\`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
21475
|
-
\`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
|
|
21491
|
+
config exports — gtd-workflows' \`default\` brings \`fix\` and
|
|
21492
|
+
\`review [base]\` — plus any the config declares). Refuses with
|
|
21476
21493
|
\`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
|
|
21477
21494
|
missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
|
|
21478
21495
|
|
|
@@ -21587,28 +21604,6 @@ Any runtime works: the contract is \`--json\`/\`--json=<path>\` in, subprocesses
|
|
|
21587
21604
|
out.
|
|
21588
21605
|
`;
|
|
21589
21606
|
const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + DRIVER_OBLIGATIONS + RECOVERY + commandSuite() + EDITOR_INTEGRATION + PREREQUISITES;
|
|
21590
|
-
//#endregion
|
|
21591
|
-
//#region src/install/ConfigScaffold.ts
|
|
21592
|
-
const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped feature, review and fix.
|
|
21593
|
-
// Every exported function is startable with \`gtd --workflow <name>\`; the
|
|
21594
|
-
// default export is what a change in the tree starts. Each workflow brings
|
|
21595
|
-
// its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
|
|
21596
|
-
// from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
21597
|
-
// \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
|
|
21598
|
-
// \`gtd door cleanup <flag>\`.
|
|
21599
|
-
export {
|
|
21600
|
-
feature as default,
|
|
21601
|
-
feature,
|
|
21602
|
-
defaults,
|
|
21603
|
-
envDefaults,
|
|
21604
|
-
summary,
|
|
21605
|
-
steering,
|
|
21606
|
-
skills,
|
|
21607
|
-
access,
|
|
21608
|
-
} from "@pmelab/gtd/workflows/feature"
|
|
21609
|
-
export { review } from "@pmelab/gtd/workflows/review"
|
|
21610
|
-
export { fix } from "@pmelab/gtd/workflows/fix"
|
|
21611
|
-
`;
|
|
21612
21607
|
var FlowsProtocolError = class extends Error {
|
|
21613
21608
|
constructor(engine, flows) {
|
|
21614
21609
|
super(`gtd: the workflow speaks flows protocol ${flows}, but the engine installed protocol ${engine} — ${engine < flows ? "upgrade the gtd that runs it" : "upgrade the @pmelab/gtd the workflow imports"}`);
|
|
@@ -38953,13 +38948,35 @@ const resolveVars = (workflowVars, entryVars, env) => {
|
|
|
38953
38948
|
}
|
|
38954
38949
|
return merged;
|
|
38955
38950
|
};
|
|
38951
|
+
const FLAG_VALUES = {
|
|
38952
|
+
"0": "0",
|
|
38953
|
+
false: "0",
|
|
38954
|
+
"1": "1",
|
|
38955
|
+
true: "1"
|
|
38956
|
+
};
|
|
38957
|
+
const flagVariable = (setting) => `GTD_FLAG_${setting.slice(5).toUpperCase()}`;
|
|
38958
|
+
/** The `flag_<name>` settings a workflow declares that gtd's own flags lack: the workflow's own release flags. */
|
|
38959
|
+
const ownFlags = (workflowVars, flags) => Object.keys(workflowVars).filter((name) => name.startsWith("flag_") && !Object.hasOwn(flags, name));
|
|
38960
|
+
/** Why a workflow's own release flag cannot start, if one is set to anything but 1, 0, true or false. */
|
|
38961
|
+
const ownFlagRefusal = (workflowVars, flags, env) => {
|
|
38962
|
+
const bad = ownFlags(workflowVars, flags).find((name) => {
|
|
38963
|
+
const raw = env[flagVariable(name)] ?? "";
|
|
38964
|
+
return raw !== "" && !Object.hasOwn(FLAG_VALUES, raw.toLowerCase());
|
|
38965
|
+
});
|
|
38966
|
+
return bad === void 0 ? void 0 : `${flagVariable(bad)} must be 1, 0, true or false`;
|
|
38967
|
+
};
|
|
38956
38968
|
/**
|
|
38957
38969
|
* `resolveVars` for a process being STARTED: `flags` land between the workflow
|
|
38958
|
-
* defaults and `--var`, for already-declared names only
|
|
38959
|
-
*
|
|
38970
|
+
* defaults and `--var`, for already-declared names only, and so does
|
|
38971
|
+
* `GTD_FLAG_<NAME>` for a `flag_<name>` the workflow declares itself. A running
|
|
38972
|
+
* process never calls this — it replays its pinned record.
|
|
38960
38973
|
*/
|
|
38961
38974
|
const resolveStartVars = (workflowVars, flags, entryVars, env) => {
|
|
38962
38975
|
const declared = Object.fromEntries(Object.entries(flags).filter(([name]) => Object.hasOwn(workflowVars, name)));
|
|
38976
|
+
for (const name of ownFlags(workflowVars, flags)) {
|
|
38977
|
+
const value = FLAG_VALUES[(env[flagVariable(name)] ?? "").toLowerCase()];
|
|
38978
|
+
if (value !== void 0) declared[name] = value;
|
|
38979
|
+
}
|
|
38963
38980
|
return resolveVars({
|
|
38964
38981
|
...workflowVars,
|
|
38965
38982
|
...declared
|
|
@@ -39163,7 +39180,7 @@ const load = gen(function* () {
|
|
|
39163
39180
|
const missing = {
|
|
39164
39181
|
severity: "error",
|
|
39165
39182
|
path: [],
|
|
39166
|
-
message: `no workflows configured: no ${WORKFLOW_MODULE} here or above —
|
|
39183
|
+
message: `no workflows configured: no ${WORKFLOW_MODULE} here or above — gtd ships no workflow; \`gtd install\` says where to get one`,
|
|
39167
39184
|
origin: host.root
|
|
39168
39185
|
};
|
|
39169
39186
|
return yield* fail$7(configError([missing]));
|
|
@@ -39919,7 +39936,11 @@ const planStart = (current, actor, start) => gen(function* () {
|
|
|
39919
39936
|
const overrideRefusal = yield* checkOverrides(commandLabel, varOverrides, Object.keys(workflow.vars), Object.keys(workflow.env));
|
|
39920
39937
|
if (overrideRefusal !== void 0) return overrideRefusal;
|
|
39921
39938
|
const baseOf = workflow.def.base;
|
|
39922
|
-
const
|
|
39939
|
+
const flags = yield* StartFlags;
|
|
39940
|
+
const hostEnv = (yield* Host).env;
|
|
39941
|
+
const badFlag = ownFlagRefusal(workflow.vars, flags, hostEnv);
|
|
39942
|
+
if (badFlag !== void 0) return refusal(`${commandLabel}: ${badFlag}`);
|
|
39943
|
+
const vars = resolveStartVars(workflow.vars, flags, varOverrides, hostEnv);
|
|
39923
39944
|
const multiline = multilineSetting(vars);
|
|
39924
39945
|
if (multiline !== void 0) return refusal(`${commandLabel}: process setting "${multiline}" spans several lines — a process setting is pinned in a commit trailer and must be a single line`);
|
|
39925
39946
|
const template = yield* try_({
|
|
@@ -40782,11 +40803,11 @@ const historySteps = (run) => {
|
|
|
40782
40803
|
};
|
|
40783
40804
|
});
|
|
40784
40805
|
};
|
|
40785
|
-
const versionWarningFor = (recorded) => recorded === GTD_VERSION ? void 0 : `gtd: warning: this history was recorded by ${recorded === void 0 ? "an unknown gtd version" : `gtd ${recorded}`} and is replayed by gtd ${GTD_VERSION} —
|
|
40806
|
+
const versionWarningFor = (recorded) => recorded === GTD_VERSION ? void 0 : `gtd: warning: this history was recorded by ${recorded === void 0 ? "an unknown gtd version" : `gtd ${recorded}`} and is replayed by gtd ${GTD_VERSION} — the engine the workflow runs on may have changed since`;
|
|
40786
40807
|
/**
|
|
40787
40808
|
* Replays the closed run at `tip` under today's config. The workflow is not
|
|
40788
|
-
* fingerprinted for a version change:
|
|
40789
|
-
*
|
|
40809
|
+
* fingerprinted for a version change: the engine under it moves with every
|
|
40810
|
+
* upgrade, and the version warning is the only signal.
|
|
40790
40811
|
*/
|
|
40791
40812
|
const historyTrail = (tip) => gen(function* () {
|
|
40792
40813
|
const git = yield* GitService;
|
|
@@ -42015,9 +42036,8 @@ const selfGtd = () => {
|
|
|
42015
42036
|
const runLspCommand = () => flatMap$4(ToolCommands, (tools) => tools.lsp(selfGtd()));
|
|
42016
42037
|
/**
|
|
42017
42038
|
* Prints the briefing, emitted by the binary itself so it's always current.
|
|
42018
|
-
*
|
|
42019
|
-
*
|
|
42020
|
-
* Anywhere else it writes nothing.
|
|
42039
|
+
* gtd ships no workflow, so where no `gtd.config.ts` governs it says where to
|
|
42040
|
+
* get one; it writes nothing.
|
|
42021
42041
|
*/
|
|
42022
42042
|
const runInstallCommand = (out) => gen(function* () {
|
|
42023
42043
|
const briefing = renderBriefing();
|
|
@@ -42027,8 +42047,7 @@ const runInstallCommand = (out) => gen(function* () {
|
|
|
42027
42047
|
if (topLevel._tag === "Left") return;
|
|
42028
42048
|
if ((yield* host.realPath(topLevel.right)) !== (yield* host.realPath(host.root))) return;
|
|
42029
42049
|
if ((yield* (yield* ConfigDiscovery).workflowModule(host.root, host.home)) !== void 0) return;
|
|
42030
|
-
yield* (yield*
|
|
42031
|
-
yield* (yield* Narrator).warn(`gtd install: wrote ${WORKFLOW_MODULE}, importing gtd's shipped workflows — commit it before your first \`gtd land\``);
|
|
42050
|
+
yield* (yield* Narrator).warn(`gtd install: no ${WORKFLOW_MODULE} governs here, and gtd ships no workflow — get one from ${WORKFLOWS_REPO} or write your own (step 2 of the briefing above)`);
|
|
42032
42051
|
});
|
|
42033
42052
|
/**
|
|
42034
42053
|
* Exactly one trailing newline — duplicated from `Cli.ts`'s own
|
|
@@ -43086,10 +43105,8 @@ const COMMAND_ROWS = [
|
|
|
43086
43105
|
"agent (or a human) to build a gtd driver in any shell or",
|
|
43087
43106
|
"runtime — the self-serve version of",
|
|
43088
43107
|
"https://github.com/pmelab/gtd/blob/main/docs/driver.md's",
|
|
43089
|
-
"'Writing your own driver'
|
|
43090
|
-
"
|
|
43091
|
-
"shipped workflow from @pmelab/gtd/workflows/<name>; anywhere",
|
|
43092
|
-
"else it writes nothing."
|
|
43108
|
+
"'Writing your own driver', and how to get a workflow: gtd",
|
|
43109
|
+
"ships none. It writes nothing."
|
|
43093
43110
|
]
|
|
43094
43111
|
},
|
|
43095
43112
|
{
|
package/dist/testkit/gherkin.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as testRepo, t as runsItsScript } from "../chunks/testkit-
|
|
1
|
+
import { n as testRepo, t as runsItsScript } from "../chunks/testkit-CMBAAyRK.mjs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import assert from "node:assert";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
@@ -10,6 +10,7 @@ var TestkitWorld = class extends QuickPickleWorld {
|
|
|
10
10
|
}
|
|
11
11
|
gtdConfig = void 0;
|
|
12
12
|
env = {};
|
|
13
|
+
commands = [];
|
|
13
14
|
/** Per step, what its scripted actor does, in the order the scenario declared it. */
|
|
14
15
|
actions = /* @__PURE__ */ new Map();
|
|
15
16
|
verdicts = /* @__PURE__ */ new Map();
|
|
@@ -26,6 +27,10 @@ var TestkitWorld = class extends QuickPickleWorld {
|
|
|
26
27
|
if (typeof this.gtdConfig !== "string") throw new Error("a gtd config from a module cannot be extended");
|
|
27
28
|
this.gtdConfig += `\n${tail}`;
|
|
28
29
|
}
|
|
30
|
+
command(command) {
|
|
31
|
+
if (this.created === void 0) this.commands.push(command);
|
|
32
|
+
else this.created.command(command);
|
|
33
|
+
}
|
|
29
34
|
setEnv(name, value) {
|
|
30
35
|
this.assertNotCreated(`the environment variable ${name}`);
|
|
31
36
|
this.env[name] = value;
|
|
@@ -37,7 +42,8 @@ var TestkitWorld = class extends QuickPickleWorld {
|
|
|
37
42
|
if (this.gtdConfig === void 0) throw new Error("no gtd config: add a `gtd config` step first");
|
|
38
43
|
return this.created ??= testRepo({
|
|
39
44
|
workflow: this.gtdConfig,
|
|
40
|
-
env: this.env
|
|
45
|
+
env: this.env,
|
|
46
|
+
commands: this.commands
|
|
41
47
|
});
|
|
42
48
|
}
|
|
43
49
|
act(step, action) {
|
|
@@ -79,6 +85,28 @@ Given("the gtd config ends with:", (world, source) => {
|
|
|
79
85
|
Given("an environment variable {string} set to {string}", (world, name, value) => {
|
|
80
86
|
world.setEnv(name, value);
|
|
81
87
|
});
|
|
88
|
+
Given("the command {string} exits {int}", (world, run, exit) => {
|
|
89
|
+
world.command({
|
|
90
|
+
run,
|
|
91
|
+
exit
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
Given("the command {string} exits {int} with:", (world, run, exit, output) => {
|
|
95
|
+
world.command({
|
|
96
|
+
run,
|
|
97
|
+
exit,
|
|
98
|
+
output: withNewline(output)
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
Given("a branch {string}", (world, name) => {
|
|
102
|
+
world.repo().branch(name);
|
|
103
|
+
});
|
|
104
|
+
Given("{string} is checked out", (world, name) => {
|
|
105
|
+
world.repo().checkout(name);
|
|
106
|
+
});
|
|
107
|
+
Given("the ref {string} points at HEAD", (world, name) => {
|
|
108
|
+
world.repo().ref(name);
|
|
109
|
+
});
|
|
82
110
|
Given("a file {string} with:", (world, path, content) => {
|
|
83
111
|
world.repo().write(path, withNewline(content));
|
|
84
112
|
});
|
package/dist/testkit.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as testRepo, r as InMemRepo, t as runsItsScript } from "./chunks/testkit-
|
|
1
|
+
import { n as testRepo, r as InMemRepo, t as runsItsScript } from "./chunks/testkit-CMBAAyRK.mjs";
|
|
2
2
|
export { InMemRepo, runsItsScript, testRepo };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmelab/gtd",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "59.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
|
|
6
6
|
"bin": {
|
|
@@ -27,41 +27,6 @@
|
|
|
27
27
|
"source": "./src/workflow-kit/index.ts",
|
|
28
28
|
"default": "./dist/workflow-kit.mjs"
|
|
29
29
|
},
|
|
30
|
-
"./workflows": {
|
|
31
|
-
"types": "./src/workflows/index.ts",
|
|
32
|
-
"source": "./src/workflows/index.ts",
|
|
33
|
-
"default": "./dist/workflows.mjs"
|
|
34
|
-
},
|
|
35
|
-
"./workflows/feature": {
|
|
36
|
-
"types": "./src/workflows/feature/index.ts",
|
|
37
|
-
"source": "./src/workflows/feature/index.ts",
|
|
38
|
-
"default": "./dist/workflows/feature.mjs"
|
|
39
|
-
},
|
|
40
|
-
"./workflows/review": {
|
|
41
|
-
"types": "./src/workflows/review/index.ts",
|
|
42
|
-
"source": "./src/workflows/review/index.ts",
|
|
43
|
-
"default": "./dist/workflows/review.mjs"
|
|
44
|
-
},
|
|
45
|
-
"./workflows/fix": {
|
|
46
|
-
"types": "./src/workflows/fix/index.ts",
|
|
47
|
-
"source": "./src/workflows/fix/index.ts",
|
|
48
|
-
"default": "./dist/workflows/fix.mjs"
|
|
49
|
-
},
|
|
50
|
-
"./workflow": {
|
|
51
|
-
"types": "./src/workflows/index.ts",
|
|
52
|
-
"source": "./src/workflows/index.ts",
|
|
53
|
-
"default": "./dist/workflows.mjs"
|
|
54
|
-
},
|
|
55
|
-
"./workflows/rollout": {
|
|
56
|
-
"types": "./src/workflows/rollout/index.ts",
|
|
57
|
-
"source": "./src/workflows/rollout/index.ts",
|
|
58
|
-
"default": "./dist/workflows/rollout.mjs"
|
|
59
|
-
},
|
|
60
|
-
"./workflows/cleanup": {
|
|
61
|
-
"types": "./src/workflows/cleanup/index.ts",
|
|
62
|
-
"source": "./src/workflows/cleanup/index.ts",
|
|
63
|
-
"default": "./dist/workflows/cleanup.mjs"
|
|
64
|
-
},
|
|
65
30
|
"./testkit": {
|
|
66
31
|
"types": "./src/testkit/index.ts",
|
|
67
32
|
"source": "./src/testkit/index.ts",
|
|
@@ -76,7 +41,6 @@
|
|
|
76
41
|
"files": [
|
|
77
42
|
"dist/",
|
|
78
43
|
"src/flows/",
|
|
79
|
-
"src/workflows/",
|
|
80
44
|
"src/workflow-kit/",
|
|
81
45
|
"src/wire/",
|
|
82
46
|
"src/steering/",
|
|
@@ -119,7 +83,7 @@
|
|
|
119
83
|
"scripts": {
|
|
120
84
|
"prepare": "husky",
|
|
121
85
|
"dev": "node dev/run.mjs",
|
|
122
|
-
"build": "tsdown --filter web && npx @tailwindcss/cli -i src/web/styles.css -o dist/web/main.css --minify && node scripts/inline-web-client.mjs && tsdown --filter gtd && tsdown --filter
|
|
86
|
+
"build": "tsdown --filter web && npx @tailwindcss/cli -i src/web/styles.css -o dist/web/main.css --minify && node scripts/inline-web-client.mjs && tsdown --filter gtd && tsdown --filter entrypoints",
|
|
123
87
|
"postbuild": "node scripts/assert-no-test-doubles.mjs",
|
|
124
88
|
"watch": "tsdown --watch",
|
|
125
89
|
"bench:replay": "node scripts/bench-replay.mjs",
|
|
@@ -141,9 +105,7 @@
|
|
|
141
105
|
"analyze": "fallow --summary --quiet",
|
|
142
106
|
"test:web": "vitest run --project storybook",
|
|
143
107
|
"storybook": "storybook dev --host 0.0.0.0",
|
|
144
|
-
"coverage": "vitest run --project unit --coverage"
|
|
145
|
-
"eval": ": \"${GTD_EVALS_URL:?GTD_EVALS_URL is required — the OpenAI-compatible gateway every graded turn and the judge run through}\" && : \"${GTD_EVALS_KEY:?GTD_EVALS_KEY is required — the gateway bearer key}\" && npx turbo run build && node evals/eval.mjs",
|
|
146
|
-
"eval:baseline": "node evals/compare-baseline.mjs --record"
|
|
108
|
+
"coverage": "vitest run --project unit --coverage"
|
|
147
109
|
},
|
|
148
110
|
"lint-staged": {
|
|
149
111
|
"*": "oxfmt --no-error-on-unmatched-pattern --write"
|
|
@@ -184,7 +146,6 @@
|
|
|
184
146
|
"oxlint": "^1.74.0",
|
|
185
147
|
"playwright": "^1.62.1",
|
|
186
148
|
"prettier": "^3.8.1",
|
|
187
|
-
"promptfoo": "0.122.1",
|
|
188
149
|
"quickpickle": "1.11.2",
|
|
189
150
|
"react": "^19.2.0",
|
|
190
151
|
"react-dom": "^19.2.0",
|
|
@@ -29,20 +29,25 @@ what a driver does.
|
|
|
29
29
|
state (`gtd next`, `gtd lsp`, `gtd door` and `gtd doors` included). It is code
|
|
30
30
|
the user's repository runs; write it with the same care as a build script.
|
|
31
31
|
|
|
32
|
-
## Golden rule: start from
|
|
32
|
+
## Golden rule: start from an existing workflow, edit incrementally
|
|
33
33
|
|
|
34
34
|
Do **not** write a workflow from a blank page unless the user wants something
|
|
35
|
-
tiny. gtd ships
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
tiny. gtd ships no workflow; https://github.com/pmelab/gtd-workflows does. Its
|
|
36
|
+
`default` (feature, review, fix, rollout, cleanup) is used from a checkout
|
|
37
|
+
pinned on the machine — `bin/gtd-wf pin` there, then `bin/gtd-wf config default`
|
|
38
|
+
prints a `gtd.config.ts` importing its bundle by absolute path. Every phase and
|
|
39
|
+
single step that bundle is built from is a named export too, so a config can
|
|
40
|
+
import what it keeps and write only what changes. Import phases by name; **never
|
|
40
41
|
`export *` from it** — every exported function of your file would become a
|
|
41
42
|
startable workflow:
|
|
42
43
|
|
|
43
44
|
```ts
|
|
44
45
|
import { start } from "@pmelab/gtd/flows"
|
|
45
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
afterTail,
|
|
48
|
+
buildTail,
|
|
49
|
+
feature,
|
|
50
|
+
} from "/home/me/.local/share/gtd/workflows/dist/default.mjs"
|
|
46
51
|
|
|
47
52
|
export {
|
|
48
53
|
defaults,
|
|
@@ -51,9 +56,9 @@ export {
|
|
|
51
56
|
base,
|
|
52
57
|
steering,
|
|
53
58
|
skills,
|
|
54
|
-
} from "
|
|
59
|
+
} from "/home/me/.local/share/gtd/workflows/dist/default.mjs"
|
|
55
60
|
|
|
56
|
-
export default feature
|
|
61
|
+
export default feature()
|
|
57
62
|
|
|
58
63
|
export async function hotfix() {
|
|
59
64
|
return afterTail(await buildTail(start(), { fixFirst: true }))
|
|
@@ -62,21 +67,22 @@ export async function hotfix() {
|
|
|
62
67
|
export const doors = { hotfix: { workflow: "hotfix" } }
|
|
63
68
|
```
|
|
64
69
|
|
|
65
|
-
`gtd --workflow hotfix` (or `gtd door hotfix`) starts it.
|
|
70
|
+
`gtd --workflow hotfix` (or `gtd door hotfix`) starts it. Use the checkout's
|
|
71
|
+
real path; `gtd-wf status` shows it.
|
|
66
72
|
|
|
67
|
-
To change a phase itself, read its source in the
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
To change a phase itself, read its source in the checkout (`workflows/default/`)
|
|
74
|
+
and either write your own version in `gtd.config.ts`, reusing its single steps,
|
|
75
|
+
or copy the whole workflow in a clone of gtd-workflows
|
|
76
|
+
(`bin/gtd-wf new <name> --from default`) and change it there, with its tests.
|
|
71
77
|
|
|
72
78
|
There is no `extends`/merge: the innermost `gtd.config.ts` walking up from the
|
|
73
79
|
current directory is the whole workflow. If one already exists, read it and edit
|
|
74
80
|
it in place.
|
|
75
81
|
|
|
76
|
-
Prefer the
|
|
82
|
+
Prefer the workflow's own parts over re-implementing them — `healthy`,
|
|
77
83
|
`escalation`, `gate`, `design`, `seamBuild`, `implement`, `qualityLap`,
|
|
78
84
|
`buildTail`, and single steps like `triage` or `fixCheck`. Their full step names
|
|
79
|
-
are versioned API.
|
|
85
|
+
are that workflow's versioned API, not gtd's.
|
|
80
86
|
|
|
81
87
|
Make one small change, **verify it loads** (see "Verify"), then make the next. A
|
|
82
88
|
workflow that fails to load breaks every gtd command in the repository.
|
|
@@ -142,15 +148,15 @@ the pending landing — call it right after the step whose turn you reject.
|
|
|
142
148
|
the last dot). Rename a step and every process resting on it diverges.
|
|
143
149
|
- An episode ends when the flow returns or calls `restart()`; the next starts at
|
|
144
150
|
the flow's first step on an ordinary start — that step is where a finished
|
|
145
|
-
process waits (
|
|
151
|
+
process waits (gtd-workflows' default uses `human("idle", …)`).
|
|
146
152
|
- `gtd --workflow <name>` starts a process on the exported workflow `<name>` (an
|
|
147
153
|
unknown name is a usage error); `gtd door <name> [args]` starts one through a
|
|
148
154
|
`doors` entry `{ workflow, args?, vars? }` (`gtd doors` lists them). A
|
|
149
|
-
factory's build may return `doors` without `workflow`; exporting
|
|
150
|
-
`fix`, `review`, `rollout` and `cleanup` brings `fix`,
|
|
151
|
-
`rollout <flag>` and `cleanup <flag>`, and yours merge over
|
|
152
|
-
`export const base = (workflow, vars) => commitish | undefined` fixes
|
|
153
|
-
started process's diff base (blank = default-branch merge-base).
|
|
155
|
+
factory's build may return `doors` without `workflow`; exporting
|
|
156
|
+
gtd-workflows' `fix`, `review`, `rollout` and `cleanup` brings `fix`,
|
|
157
|
+
`review [base]`, `rollout <flag>` and `cleanup <flag>`, and yours merge over
|
|
158
|
+
them. An `export const base = (workflow, vars) => commitish | undefined` fixes
|
|
159
|
+
a started process's diff base (blank = default-branch merge-base).
|
|
154
160
|
`--var <name>=<value>` only pins process settings: names the workflow's
|
|
155
161
|
`defaults` declare — never an environment setting.
|
|
156
162
|
|
|
@@ -222,7 +228,7 @@ workflow.
|
|
|
222
228
|
|
|
223
229
|
## Worked example: add an approval gate before building
|
|
224
230
|
|
|
225
|
-
In
|
|
231
|
+
In gtd-workflows' default, `planAndBuild` runs the design phase, then
|
|
226
232
|
`buildFeature`: the naive pass and its seam judge, then the implementation (the
|
|
227
233
|
pass itself on `land`), the quality lap and the human review (a review feedback
|
|
228
234
|
lap re-runs design inside `buildFeature` and does not pass it again). Add a
|
|
@@ -260,5 +266,5 @@ process before switching to the edited workflow.
|
|
|
260
266
|
- This skill is versioned in the gtd repository, not auto-installed. When gtd is
|
|
261
267
|
upgraded, re-copy it from the new version's `skills/authoring/SKILL.md`.
|
|
262
268
|
- Where this file and the code disagree, the code wins: the step API's own doc
|
|
263
|
-
comments in `@pmelab/gtd/flows`, and the
|
|
264
|
-
`
|
|
269
|
+
comments in `@pmelab/gtd/flows`, and the workflow's source in its own
|
|
270
|
+
repository (gtd-workflows' `workflows/default/`).
|
package/src/cli/Cli.test.ts
CHANGED
|
@@ -26,7 +26,6 @@ import { EXIT_CODES, EXIT_RUNTIME_ERROR, EXIT_USAGE_ERROR } from "../wire/index.
|
|
|
26
26
|
import { Host } from "../platform/index.js"
|
|
27
27
|
import { type CommandRequirements } from "./Cli.js"
|
|
28
28
|
import { InMemRepo, makeCapturingCliIo, testLayers } from "../twins/index.js"
|
|
29
|
-
import { CONFIG_SCAFFOLD } from "../install/index.js"
|
|
30
29
|
|
|
31
30
|
const FLAG_NAMES = [
|
|
32
31
|
"--json",
|
|
@@ -1291,8 +1290,9 @@ export const side = async () => {
|
|
|
1291
1290
|
await agent("working", "w")
|
|
1292
1291
|
}
|
|
1293
1292
|
|
|
1294
|
-
export const defaults = {
|
|
1293
|
+
export const defaults = { flag_beta: "0" }
|
|
1295
1294
|
`
|
|
1295
|
+
const BETA = { name: "beta", owner: "pmelab", expiry: "2099-01-01", kind: "release" } as const
|
|
1296
1296
|
const start = async (
|
|
1297
1297
|
flags?: CliIo["flags"],
|
|
1298
1298
|
env: Record<string, string> = {},
|
|
@@ -1307,27 +1307,23 @@ export const defaults = { flag_risk_tier: "0" }
|
|
|
1307
1307
|
return result().stdout
|
|
1308
1308
|
}
|
|
1309
1309
|
|
|
1310
|
-
it("pins
|
|
1311
|
-
expect(await start(
|
|
1310
|
+
it("pins flag_beta=1 with the flag on", async () => {
|
|
1311
|
+
expect(await start([BETA], { GTD_FLAG_BETA: "1" })).toContain("flag_beta=1")
|
|
1312
1312
|
})
|
|
1313
1313
|
|
|
1314
1314
|
it("honours defaultOn with GTD_FLAG_* unset", async () => {
|
|
1315
|
-
const out = await start(
|
|
1316
|
-
|
|
1317
|
-
{
|
|
1318
|
-
name: "risk_tier",
|
|
1319
|
-
owner: "pmelab",
|
|
1320
|
-
expiry: "2099-01-01",
|
|
1321
|
-
kind: "release",
|
|
1322
|
-
defaultOn: true,
|
|
1323
|
-
},
|
|
1324
|
-
],
|
|
1325
|
-
{ GTD_FLAG_RISK_TIER: "" },
|
|
1326
|
-
)
|
|
1327
|
-
expect(out).toContain("flag_risk_tier=1")
|
|
1315
|
+
const out = await start([{ ...BETA, defaultOn: true }], { GTD_FLAG_BETA: "" })
|
|
1316
|
+
expect(out).toContain("flag_beta=1")
|
|
1328
1317
|
})
|
|
1329
1318
|
})
|
|
1330
1319
|
|
|
1320
|
+
const IDLE_CONFIG = `import { human } from "@pmelab/gtd/flows"
|
|
1321
|
+
|
|
1322
|
+
export default async () => {
|
|
1323
|
+
await human("idle", { message: "hi" })
|
|
1324
|
+
}
|
|
1325
|
+
`
|
|
1326
|
+
|
|
1331
1327
|
describe("runCli — exit codes", () => {
|
|
1332
1328
|
it("output plan: exit is never called (success) — --help and --version still exit 0", async () => {
|
|
1333
1329
|
const { io, captured } = capturingIo(throwingLayers)
|
|
@@ -1369,7 +1365,7 @@ describe("runCli — exit codes", () => {
|
|
|
1369
1365
|
it("an unknown --json=<path> selector exits EXIT_USAGE_ERROR, not EXIT_RUNTIME_ERROR — Program.ts's SelectorUsageError mapped by report()", async () => {
|
|
1370
1366
|
const repo = new InMemRepo()
|
|
1371
1367
|
repo.writeFile("NOTE.md", "a note\n")
|
|
1372
|
-
repo.writeFile("gtd.config.ts",
|
|
1368
|
+
repo.writeFile("gtd.config.ts", IDLE_CONFIG)
|
|
1373
1369
|
repo.seedCommit("gtd: init")
|
|
1374
1370
|
const { io, captured } = capturingIo(() => testLayers(repo))
|
|
1375
1371
|
await Effect.runPromise(runCli(["node", "gtd.js", "next", "--json=does.not.exist"], io))
|
|
@@ -1534,7 +1530,7 @@ describe("runCli — flush normalizes the artifact's trailing newline", () => {
|
|
|
1534
1530
|
it("a plain-text write command's script (no trailing newline) reaches io.stdout with exactly one", async () => {
|
|
1535
1531
|
const repo = new InMemRepo()
|
|
1536
1532
|
repo.writeFile(".gitignore", "node_modules\n")
|
|
1537
|
-
repo.writeFile("gtd.config.ts",
|
|
1533
|
+
repo.writeFile("gtd.config.ts", IDLE_CONFIG)
|
|
1538
1534
|
repo.seedCommit("chore: initial commit")
|
|
1539
1535
|
const { io, captured } = capturingIo(() => testLayers(repo))
|
|
1540
1536
|
await Effect.runPromise(runCli(["node", "gtd.js", "land"], io))
|
package/src/cli/Cli.ts
CHANGED
|
@@ -578,10 +578,8 @@ const COMMAND_ROWS: readonly CommandRow[] = [
|
|
|
578
578
|
"agent (or a human) to build a gtd driver in any shell or",
|
|
579
579
|
"runtime — the self-serve version of",
|
|
580
580
|
"https://github.com/pmelab/gtd/blob/main/docs/driver.md's",
|
|
581
|
-
"'Writing your own driver'
|
|
582
|
-
"
|
|
583
|
-
"shipped workflow from @pmelab/gtd/workflows/<name>; anywhere",
|
|
584
|
-
"else it writes nothing.",
|
|
581
|
+
"'Writing your own driver', and how to get a workflow: gtd",
|
|
582
|
+
"ships none. It writes nothing.",
|
|
585
583
|
],
|
|
586
584
|
},
|
|
587
585
|
{
|