@pmelab/gtd 58.2.0 → 59.0.1
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 +120 -395
- package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-07Z_ENch.mjs} +13503 -3460
- package/dist/chunks/{wire-C12dahrH.mjs → wire-BTUhRGXj.mjs} +1 -1
- package/dist/flows.mjs +1 -1
- package/dist/gtd.bundle.mjs +484 -497
- package/dist/testkit/gherkin.mjs +145 -65
- package/dist/testkit.mjs +1 -1
- package/dist/wire.mjs +1 -1
- package/package.json +3 -42
- package/skills/authoring/SKILL.md +34 -27
- package/src/cli/Cli.test.ts +148 -71
- package/src/cli/Cli.ts +384 -463
- package/src/cli/Help.test.ts +47 -0
- package/src/cli/Help.ts +70 -0
- package/src/cli/Program.test.ts +127 -33
- package/src/cli/Program.ts +70 -25
- package/src/edge/Edge.ts +4 -4
- package/src/emit/Emit.test.ts +1 -1
- package/src/emit/Emit.ts +1 -1
- package/src/flags/index.test.ts +0 -13
- package/src/flags/index.ts +1 -3
- package/src/flows/factory.ts +3 -1
- package/src/gherkin/index.ts +135 -71
- package/src/gherkin/messages.test.ts +90 -0
- package/src/gherkin/messages.ts +84 -0
- 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 +47 -2
- package/src/testkit/TestRepo.ts +51 -6
- package/src/twins/EmittedScriptRecognizer.ts +5 -8
- package/src/twins/FlowScripts.ts +1 -1
- package/src/wire/Land.ts +1 -1
- package/src/workflow/index.ts +1 -0
- package/src/workflow/load.test.ts +138 -113
- package/src/workflow/load.ts +7 -7
- package/src/workflow/skills.test.ts +5 -5
- package/src/workflow/skills.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-JDtpNKyi.mjs +0 -10410
- 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/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",
|
|
@@ -72,7 +71,8 @@ describe("parseArgv — unknown options", () => {
|
|
|
72
71
|
expect(plan).toEqual({
|
|
73
72
|
kind: "usage",
|
|
74
73
|
stdout: "",
|
|
75
|
-
message:
|
|
74
|
+
message:
|
|
75
|
+
"gtd: unknown option '--jsn' — did you mean `--json`? Run `gtd help <command>` for its options",
|
|
76
76
|
json: false,
|
|
77
77
|
})
|
|
78
78
|
})
|
|
@@ -370,17 +370,52 @@ describe("parseArgv — bare/unknown command under --json", () => {
|
|
|
370
370
|
}
|
|
371
371
|
})
|
|
372
372
|
|
|
373
|
-
it("bare gtd
|
|
373
|
+
it("bare gtd prints a short usage, not the overview, and points at gtd help and gtd install", () => {
|
|
374
374
|
const plan = parseArgv(["node", "gtd.js"])
|
|
375
375
|
expect(plan.kind).toBe("usage")
|
|
376
376
|
if (plan.kind === "usage") {
|
|
377
377
|
expect(plan.message).toContain("missing command")
|
|
378
|
-
expect(plan.message).toContain("gtd
|
|
378
|
+
expect(plan.message).toContain("`gtd help`")
|
|
379
379
|
expect(plan.message).toContain("gtd install")
|
|
380
|
-
expect(plan.
|
|
380
|
+
expect(plan.stdout).toMatch(/^Usage: gtd <command>/)
|
|
381
|
+
expect(plan.stdout.split("\n").length).toBeLessThan(10)
|
|
381
382
|
}
|
|
382
383
|
})
|
|
383
384
|
|
|
385
|
+
it("an unknown command that looks like a typo names the nearest command", () => {
|
|
386
|
+
expect(parseArgv(["node", "gtd.js", "nxt"])).toEqual({
|
|
387
|
+
kind: "usage",
|
|
388
|
+
stdout: "",
|
|
389
|
+
message:
|
|
390
|
+
"gtd: unknown command 'nxt' — did you mean `gtd next`? Run `gtd help` for every command",
|
|
391
|
+
json: false,
|
|
392
|
+
})
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it("an unknown command nothing resembles points at gtd help only", () => {
|
|
396
|
+
const plan = parseArgv(["node", "gtd.js", "frobnicate"])
|
|
397
|
+
expect(plan.kind === "usage" && plan.message).toBe(
|
|
398
|
+
"gtd: unknown command 'frobnicate' — run `gtd help` for every command",
|
|
399
|
+
)
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
it("a mistyped history subcommand names the nearest one", () => {
|
|
403
|
+
const plan = parseArgv(["node", "gtd.js", "history", "shwo"])
|
|
404
|
+
expect(plan.kind === "usage" && plan.message).toContain("did you mean `gtd history show`?")
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
it("a bare --json followed by a word suggests the = form", () => {
|
|
408
|
+
const plan = parseArgv(["node", "gtd.js", "next", "--json", "kind"])
|
|
409
|
+
expect(plan.kind === "usage" && plan.message).toBe(
|
|
410
|
+
"gtd next: too many arguments — expected none, got: kind — to print one field, write --json=kind",
|
|
411
|
+
)
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
it("an unknown single-dash option is an unknown option, not an extra argument", () => {
|
|
415
|
+
const plan = parseArgv(["node", "gtd.js", "-j", "next"])
|
|
416
|
+
expect(plan.kind === "usage" && plan.message).toContain("unknown option '-j'")
|
|
417
|
+
})
|
|
418
|
+
|
|
384
419
|
it("an unknown command under --json carries json:true, naming --json's own scope error over the subcommand", () => {
|
|
385
420
|
const plan = parseArgv(["node", "gtd.js", "bogus", "--json"])
|
|
386
421
|
expect(plan.kind).toBe("usage")
|
|
@@ -892,10 +927,8 @@ describe("parseArgv — doors", () => {
|
|
|
892
927
|
})
|
|
893
928
|
})
|
|
894
929
|
|
|
895
|
-
it("`gtd door` with no name
|
|
896
|
-
|
|
897
|
-
expect(plan.kind).toBe("usage")
|
|
898
|
-
if (plan.kind === "usage") expect(plan.message).toContain("gtd door: missing name argument")
|
|
930
|
+
it("`gtd door` with no name parses, so the loaded config can list its doors", () => {
|
|
931
|
+
expect(parse("door")).toMatchObject({ kind: "command", command: { kind: "door", args: [] } })
|
|
899
932
|
})
|
|
900
933
|
|
|
901
934
|
it("`gtd doors` parses with every --json shape", () => {
|
|
@@ -1062,7 +1095,8 @@ describe("parseArgv — gtd strays", () => {
|
|
|
1062
1095
|
const plan = parseArgv(["node", "gtd.js", "lsp", "--json"])
|
|
1063
1096
|
if (plan.kind === "usage") expect(plan.message).toContain("`gtd strays`")
|
|
1064
1097
|
expect(renderHelp()).toContain("strays <range> [glob...]")
|
|
1065
|
-
|
|
1098
|
+
const help = parseArgv(["node", "gtd.js", "help", "strays"])
|
|
1099
|
+
expect(help.kind === "output" && help.stdout).toContain("--json[=<path>]")
|
|
1066
1100
|
})
|
|
1067
1101
|
})
|
|
1068
1102
|
|
|
@@ -1101,56 +1135,93 @@ describe("standaloneKinds / needsOf", () => {
|
|
|
1101
1135
|
})
|
|
1102
1136
|
})
|
|
1103
1137
|
|
|
1138
|
+
const helpFor = (...args: string[]): string => {
|
|
1139
|
+
const plan = parseArgv(["node", "gtd.js", ...args])
|
|
1140
|
+
if (plan.kind !== "output") throw new Error(`no help for ${args.join(" ")}`)
|
|
1141
|
+
return plan.stdout
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/** The command names the overview lists — ` name ` lines, as the docs pins read them. */
|
|
1145
|
+
const listedCommands = (): string[] =>
|
|
1146
|
+
[...renderHelp().matchAll(/^ {2}([a-z]+(?:-[a-z]+)?(?: [a-z]+)?)(?= )/gm)].map(([, n]) => n!)
|
|
1147
|
+
|
|
1104
1148
|
describe("renderHelp", () => {
|
|
1105
|
-
it("
|
|
1149
|
+
it("groups the commands and mentions no removed surface", () => {
|
|
1106
1150
|
const help = renderHelp()
|
|
1107
|
-
|
|
1108
|
-
|
|
1151
|
+
for (const heading of ["Core loop:", "Start and stop:", "Inspect:", "Driver plumbing:"]) {
|
|
1152
|
+
expect(help).toContain(`\n${heading}\n`)
|
|
1153
|
+
}
|
|
1109
1154
|
expect(help).toContain("--workflow <name>")
|
|
1110
1155
|
expect(help).toContain("--var")
|
|
1111
|
-
expect(help).toContain("abandon")
|
|
1112
|
-
expect(help).toContain("restore")
|
|
1113
|
-
expect(help).toContain("next")
|
|
1114
|
-
expect(help).toContain("validate")
|
|
1115
|
-
expect(help).toContain("lsp")
|
|
1116
|
-
expect(help).not.toContain("visualize")
|
|
1117
|
-
expect(help).not.toMatch(/^ {2}init\b/m)
|
|
1118
|
-
expect(help).not.toContain(".gtdrc")
|
|
1119
|
-
expect(help).toMatch(/^ {2}ui\b/m)
|
|
1120
1156
|
expect(help).toContain("check <mode> <file>")
|
|
1121
|
-
expect(help).toContain("
|
|
1122
|
-
expect(help).toContain("
|
|
1123
|
-
expect(help).
|
|
1124
|
-
expect(help).toContain("
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
expect(help).
|
|
1136
|
-
expect(help).toContain("--verbose")
|
|
1137
|
-
expect(help).toContain("--version, -V")
|
|
1138
|
-
expect(help).toContain("--help, -h")
|
|
1139
|
-
expect(help).not.toContain("review <commitish>")
|
|
1140
|
-
expect(help).not.toContain("format <file>")
|
|
1141
|
-
expect(help).not.toContain("--debug")
|
|
1142
|
-
expect(help).not.toContain("bin/gtd")
|
|
1143
|
-
expect(help).not.toContain("(no command), loop")
|
|
1144
|
-
expect(help).not.toContain("--dispatch")
|
|
1145
|
-
expect(help).not.toContain("--if-resting")
|
|
1146
|
-
expect(help).not.toMatch(/^ {2}serve\b/m)
|
|
1157
|
+
expect(help).toContain("door <name> [args...]")
|
|
1158
|
+
expect(help).toContain("-V, --version")
|
|
1159
|
+
expect(help).toContain("-h, --help")
|
|
1160
|
+
expect(help).toContain("Examples:")
|
|
1161
|
+
for (const removed of [
|
|
1162
|
+
"visualize",
|
|
1163
|
+
".gtdrc",
|
|
1164
|
+
"--no-open",
|
|
1165
|
+
"--debug",
|
|
1166
|
+
"--dispatch",
|
|
1167
|
+
"bin/gtd",
|
|
1168
|
+
]) {
|
|
1169
|
+
expect(help).not.toContain(removed)
|
|
1170
|
+
}
|
|
1171
|
+
expect(help).not.toMatch(/^ {2}(init|serve)\b/m)
|
|
1147
1172
|
expect(help).toMatch(/\n$/)
|
|
1148
1173
|
})
|
|
1149
1174
|
|
|
1150
|
-
it("every
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1175
|
+
it("keeps every line of every help text within 80 columns", () => {
|
|
1176
|
+
for (const text of [renderHelp(), ...listedCommands().map((name) => helpFor("help", name))]) {
|
|
1177
|
+
for (const line of text.split("\n")) expect(line.length, line).toBeLessThanOrEqual(80)
|
|
1178
|
+
}
|
|
1179
|
+
})
|
|
1180
|
+
|
|
1181
|
+
it("names no internals", () => {
|
|
1182
|
+
const all = [renderHelp(), ...listedCommands().map((name) => helpFor("help", name))].join("")
|
|
1183
|
+
for (const internal of ["it.processCost", "refs/worktree/gtd/history", "Effect Schema"]) {
|
|
1184
|
+
expect(all).not.toContain(internal)
|
|
1185
|
+
}
|
|
1186
|
+
})
|
|
1187
|
+
|
|
1188
|
+
it("every flag is in the help of a command it is valid for", () => {
|
|
1189
|
+
const all = [renderHelp(), ...listedCommands().map((name) => helpFor("help", name))].join("")
|
|
1190
|
+
for (const name of FLAG_NAMES) expect(all).toContain(name)
|
|
1191
|
+
})
|
|
1192
|
+
|
|
1193
|
+
it("gtd <cmd> --help, gtd <cmd> -h and gtd help <cmd> print that command's own help", () => {
|
|
1194
|
+
const help = helpFor("help", "land")
|
|
1195
|
+
expect(help).toMatch(/^Usage: gtd land \[options\]\n/)
|
|
1196
|
+
expect(help).toContain("--cost=<n>")
|
|
1197
|
+
expect(help).not.toContain("--port")
|
|
1198
|
+
expect(helpFor("land", "--help")).toBe(help)
|
|
1199
|
+
expect(helpFor("land", "-h")).toBe(help)
|
|
1200
|
+
})
|
|
1201
|
+
|
|
1202
|
+
it("a two-word command's help is its own", () => {
|
|
1203
|
+
expect(helpFor("judge", "run", "--help")).toMatch(/^Usage: gtd judge run \[options\]/)
|
|
1204
|
+
expect(helpFor("help", "history", "show")).toMatch(/^Usage: gtd history show <ref>/)
|
|
1205
|
+
})
|
|
1206
|
+
|
|
1207
|
+
it("a flag that means something else per command is described per command", () => {
|
|
1208
|
+
expect(helpFor("help", "land")).toContain("With --cost: the model")
|
|
1209
|
+
expect(helpFor("help", "judge", "run")).toContain("The claude model")
|
|
1210
|
+
})
|
|
1211
|
+
|
|
1212
|
+
it("--help after an unknown command still prints the overview", () => {
|
|
1213
|
+
expect(helpFor("bogus", "--help")).toBe(renderHelp())
|
|
1214
|
+
})
|
|
1215
|
+
|
|
1216
|
+
it("gtd help <unknown> is a usage error naming the nearest command", () => {
|
|
1217
|
+
const plan = parseArgv(["node", "gtd.js", "help", "lnad"])
|
|
1218
|
+
expect(plan).toEqual({
|
|
1219
|
+
kind: "usage",
|
|
1220
|
+
stdout: "",
|
|
1221
|
+
message:
|
|
1222
|
+
"gtd: help: no command 'lnad' — did you mean `gtd help land`? Run `gtd help` for every command",
|
|
1223
|
+
json: false,
|
|
1224
|
+
})
|
|
1154
1225
|
})
|
|
1155
1226
|
|
|
1156
1227
|
it("the docs/cli.md Commands fenced block equals renderHelp()", () => {
|
|
@@ -1160,6 +1231,15 @@ describe("renderHelp", () => {
|
|
|
1160
1231
|
expect(match![1] + "\n").toBe(renderHelp())
|
|
1161
1232
|
})
|
|
1162
1233
|
|
|
1234
|
+
it("docs/cli.md's Every command section is every command's help, in the overview's order", () => {
|
|
1235
|
+
const doc = readFileSync(resolve(import.meta.dirname, "../../docs/cli.md"), "utf8")
|
|
1236
|
+
const section = doc.slice(doc.indexOf("\n## Every command\n"))
|
|
1237
|
+
const documented = [...section.matchAll(/^### `gtd ([^`]+)`\n\n```\n([\s\S]*?)```\n/gm)].map(
|
|
1238
|
+
([, name, help]) => [name, help],
|
|
1239
|
+
)
|
|
1240
|
+
expect(documented).toEqual(listedCommands().map((name) => [name, helpFor("help", name)]))
|
|
1241
|
+
})
|
|
1242
|
+
|
|
1163
1243
|
it("README.md and COMPATIBILITY.md list exactly the commands", () => {
|
|
1164
1244
|
const commands = new Set(
|
|
1165
1245
|
[...renderHelp().matchAll(/^ {2}([a-z]+(?:-[a-z]+)?(?: [a-z]+)?)(?= )/gm)].map(
|
|
@@ -1291,8 +1371,9 @@ export const side = async () => {
|
|
|
1291
1371
|
await agent("working", "w")
|
|
1292
1372
|
}
|
|
1293
1373
|
|
|
1294
|
-
export const defaults = {
|
|
1374
|
+
export const defaults = { flag_beta: "0" }
|
|
1295
1375
|
`
|
|
1376
|
+
const BETA = { name: "beta", owner: "pmelab", expiry: "2099-01-01", kind: "release" } as const
|
|
1296
1377
|
const start = async (
|
|
1297
1378
|
flags?: CliIo["flags"],
|
|
1298
1379
|
env: Record<string, string> = {},
|
|
@@ -1307,27 +1388,23 @@ export const defaults = { flag_risk_tier: "0" }
|
|
|
1307
1388
|
return result().stdout
|
|
1308
1389
|
}
|
|
1309
1390
|
|
|
1310
|
-
it("pins
|
|
1311
|
-
expect(await start(
|
|
1391
|
+
it("pins flag_beta=1 with the flag on", async () => {
|
|
1392
|
+
expect(await start([BETA], { GTD_FLAG_BETA: "1" })).toContain("flag_beta=1")
|
|
1312
1393
|
})
|
|
1313
1394
|
|
|
1314
1395
|
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")
|
|
1396
|
+
const out = await start([{ ...BETA, defaultOn: true }], { GTD_FLAG_BETA: "" })
|
|
1397
|
+
expect(out).toContain("flag_beta=1")
|
|
1328
1398
|
})
|
|
1329
1399
|
})
|
|
1330
1400
|
|
|
1401
|
+
const IDLE_CONFIG = `import { human } from "@pmelab/gtd/flows"
|
|
1402
|
+
|
|
1403
|
+
export default async () => {
|
|
1404
|
+
await human("idle", { message: "hi" })
|
|
1405
|
+
}
|
|
1406
|
+
`
|
|
1407
|
+
|
|
1331
1408
|
describe("runCli — exit codes", () => {
|
|
1332
1409
|
it("output plan: exit is never called (success) — --help and --version still exit 0", async () => {
|
|
1333
1410
|
const { io, captured } = capturingIo(throwingLayers)
|
|
@@ -1369,7 +1446,7 @@ describe("runCli — exit codes", () => {
|
|
|
1369
1446
|
it("an unknown --json=<path> selector exits EXIT_USAGE_ERROR, not EXIT_RUNTIME_ERROR — Program.ts's SelectorUsageError mapped by report()", async () => {
|
|
1370
1447
|
const repo = new InMemRepo()
|
|
1371
1448
|
repo.writeFile("NOTE.md", "a note\n")
|
|
1372
|
-
repo.writeFile("gtd.config.ts",
|
|
1449
|
+
repo.writeFile("gtd.config.ts", IDLE_CONFIG)
|
|
1373
1450
|
repo.seedCommit("gtd: init")
|
|
1374
1451
|
const { io, captured } = capturingIo(() => testLayers(repo))
|
|
1375
1452
|
await Effect.runPromise(runCli(["node", "gtd.js", "next", "--json=does.not.exist"], io))
|
|
@@ -1423,7 +1500,7 @@ describe("runCli — stdout stays byte-empty on every failing surface", () => {
|
|
|
1423
1500
|
const { stdout, stderr, exitCode } = captured()
|
|
1424
1501
|
expect(exitCode).toBe(EXIT_RUNTIME_ERROR)
|
|
1425
1502
|
expect(stdout).toBe("")
|
|
1426
|
-
expect(stderr).toContain("gtd
|
|
1503
|
+
expect(stderr).toContain("gtd: the repository has no commits yet")
|
|
1427
1504
|
})
|
|
1428
1505
|
|
|
1429
1506
|
it("runtime error: a handler's own Effect.fail (not the shared repo-root/commit guard) leaves stdout byte-empty", async () => {
|
|
@@ -1534,7 +1611,7 @@ describe("runCli — flush normalizes the artifact's trailing newline", () => {
|
|
|
1534
1611
|
it("a plain-text write command's script (no trailing newline) reaches io.stdout with exactly one", async () => {
|
|
1535
1612
|
const repo = new InMemRepo()
|
|
1536
1613
|
repo.writeFile(".gitignore", "node_modules\n")
|
|
1537
|
-
repo.writeFile("gtd.config.ts",
|
|
1614
|
+
repo.writeFile("gtd.config.ts", IDLE_CONFIG)
|
|
1538
1615
|
repo.seedCommit("chore: initial commit")
|
|
1539
1616
|
const { io, captured } = capturingIo(() => testLayers(repo))
|
|
1540
1617
|
await Effect.runPromise(runCli(["node", "gtd.js", "land"], io))
|