@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/workflow/load.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface ConfigOperations {
|
|
|
50
50
|
// prints `message` verbatim and would print a duplicated `detail` twice.
|
|
51
51
|
const configError = (diagnostics: readonly Diagnostic[]): GtdError =>
|
|
52
52
|
new GtdError(
|
|
53
|
-
`gtd config:\n${diagnostics
|
|
53
|
+
`gtd: config:\n${diagnostics
|
|
54
54
|
.filter((d) => d.severity === "error")
|
|
55
55
|
.map((d) => ` - ${formatDiagnostic(d)}`)
|
|
56
56
|
.join("\n")}`,
|
|
@@ -84,7 +84,7 @@ export const refuseLegacyRc: Effect.Effect<void, GtdError, Host | ConfigDiscover
|
|
|
84
84
|
return yield* Effect.fail(
|
|
85
85
|
new GtdError(
|
|
86
86
|
[
|
|
87
|
-
"gtd config:",
|
|
87
|
+
"gtd: config:",
|
|
88
88
|
...found.map((file) => ` - ${file}: no longer read — delete it`),
|
|
89
89
|
" gtd 23 removed the .gtdrc files; move each key they set:",
|
|
90
90
|
" vars → an option of the workflow factory in gtd.config.ts, or its `defaults` export",
|
|
@@ -115,7 +115,7 @@ export const load: Effect.Effect<
|
|
|
115
115
|
const missing: Diagnostic = {
|
|
116
116
|
severity: "error",
|
|
117
117
|
path: [],
|
|
118
|
-
message: `no workflows configured: no ${WORKFLOW_MODULE} here or above —
|
|
118
|
+
message: `no workflows configured: no ${WORKFLOW_MODULE} here or above — gtd ships no workflow; \`gtd install\` says where to get one`,
|
|
119
119
|
origin: host.root,
|
|
120
120
|
}
|
|
121
121
|
return yield* Effect.fail(configError([missing]))
|
|
@@ -129,7 +129,7 @@ export const load: Effect.Effect<
|
|
|
129
129
|
try: () => loadWorkflows(module, shipped),
|
|
130
130
|
catch: (e) =>
|
|
131
131
|
new GtdError(
|
|
132
|
-
`gtd config:\n - ${module.filepath}: ${e instanceof Error ? e.message : String(e)}`,
|
|
132
|
+
`gtd: config:\n - ${module.filepath}: ${e instanceof Error ? e.message : String(e)}`,
|
|
133
133
|
),
|
|
134
134
|
})
|
|
135
135
|
const compiled = loaded.config
|
|
@@ -885,7 +885,7 @@ const replayToFirstStep = (
|
|
|
885
885
|
outcome.kind === "failed" || outcome.kind === "refused"
|
|
886
886
|
? outcome.message.replace(/^gtd: /, "")
|
|
887
887
|
: "the default entry must begin at a step — that step is where a finished process waits"
|
|
888
|
-
return Effect.fail(new GtdError(`gtd config:\n - ${loaded.file.origin}: ${problem}`))
|
|
888
|
+
return Effect.fail(new GtdError(`gtd: config:\n - ${loaded.file.origin}: ${problem}`))
|
|
889
889
|
},
|
|
890
890
|
)
|
|
891
891
|
|
|
@@ -912,7 +912,7 @@ const firstStep = (
|
|
|
912
912
|
if (reads.length > 0) {
|
|
913
913
|
return yield* Effect.fail(
|
|
914
914
|
new GtdError(
|
|
915
|
-
`gtd config:\n - ${loaded.file.origin}: the flow's first step on an ordinary start reads ${reads.join(" and ")} — that step is where a finished process waits, so it must not depend on a setting`,
|
|
915
|
+
`gtd: config:\n - ${loaded.file.origin}: the flow's first step on an ordinary start reads ${reads.join(" and ")} — that step is where a finished process waits, so it must not depend on a setting`,
|
|
916
916
|
),
|
|
917
917
|
)
|
|
918
918
|
}
|
|
@@ -923,7 +923,7 @@ const firstStep = (
|
|
|
923
923
|
if (atHead === name) return name
|
|
924
924
|
return yield* Effect.fail(
|
|
925
925
|
new GtdError(
|
|
926
|
-
`gtd config:\n - ${loaded.file.origin}: the flow's first step on an ordinary start depends on the repository's files ("${name}" without them, "${atHead}" at HEAD) — that step is where a finished process waits, so it must not change as files do`,
|
|
926
|
+
`gtd: config:\n - ${loaded.file.origin}: the flow's first step on an ordinary start depends on the repository's files ("${name}" without them, "${atHead}" at HEAD) — that step is where a finished process waits, so it must not change as files do`,
|
|
927
927
|
),
|
|
928
928
|
)
|
|
929
929
|
})
|
|
@@ -17,24 +17,24 @@ describe("resolveScopeSkills", () => {
|
|
|
17
17
|
const message = await failure(() => {
|
|
18
18
|
throw new Error("boom")
|
|
19
19
|
})
|
|
20
|
-
expect(message).toContain(`gtd config:\n - ${FILE}: boom`)
|
|
20
|
+
expect(message).toContain(`gtd: config:\n - ${FILE}: boom`)
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
it("rejects a non-object return", async () => {
|
|
24
24
|
expect(await failure(() => ["a"])).toContain(
|
|
25
|
-
`gtd config:\n - ${FILE}: skills export must return an object of scope -> skill names`,
|
|
25
|
+
`gtd: config:\n - ${FILE}: skills export must return an object of scope -> skill names`,
|
|
26
26
|
)
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
it("rejects a non-array value", async () => {
|
|
30
30
|
expect(await failure(() => ({ a: "x" }))).toContain(
|
|
31
|
-
`gtd config:\n - ${FILE}: skills export: "a" must be an array of skill names`,
|
|
31
|
+
`gtd: config:\n - ${FILE}: skills export: "a" must be an array of skill names`,
|
|
32
32
|
)
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
it("rejects a non-string entry", async () => {
|
|
36
36
|
expect(await failure(() => ({ a: ["x", 1] }))).toContain(
|
|
37
|
-
`gtd config:\n - ${FILE}: skills export: "a" must be an array of skill names`,
|
|
37
|
+
`gtd: config:\n - ${FILE}: skills export: "a" must be an array of skill names`,
|
|
38
38
|
)
|
|
39
39
|
})
|
|
40
40
|
|
|
@@ -80,7 +80,7 @@ describe("resolveScopeAccess", () => {
|
|
|
80
80
|
|
|
81
81
|
it("rejects a malformed export with the file", async () => {
|
|
82
82
|
expect(message(await run({ access: () => ["a"] }))).toContain(
|
|
83
|
-
`gtd config:\n - ${FILE}: access export must return an object`,
|
|
83
|
+
`gtd: config:\n - ${FILE}: access export must return an object`,
|
|
84
84
|
)
|
|
85
85
|
expect(message(await run({ access: () => ({ a: { write: "x" } }) }))).toContain(
|
|
86
86
|
`${FILE}: access export: "a": access.write must be an array`,
|
package/src/workflow/skills.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { Diagnostic } from "./Diagnostic.js"
|
|
|
7
7
|
import { failOnErrors } from "./load.js"
|
|
8
8
|
|
|
9
9
|
const fail = (origin: string, message: string): GtdError =>
|
|
10
|
-
new GtdError(`gtd config:\n - ${origin}: ${message}`)
|
|
10
|
+
new GtdError(`gtd: config:\n - ${origin}: ${message}`)
|
|
11
11
|
|
|
12
12
|
const checkShape = (value: unknown): Record<string, readonly string[]> => {
|
|
13
13
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
|
-
import { resolveStartVars, resolveVars } from "./vars.js"
|
|
2
|
+
import { ownFlagRefusal, resolveStartVars, resolveVars } from "./vars.js"
|
|
3
3
|
|
|
4
4
|
describe("resolveVars — GTD_FLAG_* belongs to feature flags", () => {
|
|
5
5
|
it.each(["flag_x", "FLAG_X", "Flag_x"])("%s keeps its default with GTD_FLAG_X set", (name) => {
|
|
@@ -30,9 +30,36 @@ describe("resolveStartVars", () => {
|
|
|
30
30
|
})
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
it("GTD_FLAG_X never overrides, while GTD_<NAME> still does for other names", () => {
|
|
33
|
+
it("GTD_FLAG_X never overrides one of gtd's own flags, while GTD_<NAME> still does for other names", () => {
|
|
34
34
|
expect(
|
|
35
|
-
resolveStartVars(
|
|
35
|
+
resolveStartVars(
|
|
36
|
+
{ flag_x: "0", a: "1" },
|
|
37
|
+
{ flag_x: "0" },
|
|
38
|
+
{},
|
|
39
|
+
{ GTD_FLAG_X: "1", GTD_A: "2" },
|
|
40
|
+
),
|
|
36
41
|
).toEqual({ flag_x: "0", a: "2" })
|
|
37
42
|
})
|
|
43
|
+
|
|
44
|
+
it("pins a workflow's own flag from GTD_FLAG_<NAME>, below --var", () => {
|
|
45
|
+
expect(resolveStartVars({ flag_own: "0" }, {}, {}, { GTD_FLAG_OWN: "true" })).toEqual({
|
|
46
|
+
flag_own: "1",
|
|
47
|
+
})
|
|
48
|
+
expect(
|
|
49
|
+
resolveStartVars({ flag_own: "0" }, {}, { flag_own: "0" }, { GTD_FLAG_OWN: "1" }),
|
|
50
|
+
).toEqual({
|
|
51
|
+
flag_own: "0",
|
|
52
|
+
})
|
|
53
|
+
expect(resolveStartVars({ flag_own: "1" }, {}, {}, {})).toEqual({ flag_own: "1" })
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe("ownFlagRefusal", () => {
|
|
58
|
+
it("refuses a workflow's own flag set to anything but 1, 0, true or false", () => {
|
|
59
|
+
expect(ownFlagRefusal({ flag_own: "0" }, {}, { GTD_FLAG_OWN: "yes" })).toBe(
|
|
60
|
+
"GTD_FLAG_OWN must be 1, 0, true or false",
|
|
61
|
+
)
|
|
62
|
+
expect(ownFlagRefusal({ flag_own: "0" }, {}, { GTD_FLAG_OWN: "TRUE" })).toBeUndefined()
|
|
63
|
+
expect(ownFlagRefusal({ flag_x: "0" }, { flag_x: "1" }, { GTD_FLAG_X: "yes" })).toBeUndefined()
|
|
64
|
+
})
|
|
38
65
|
})
|
package/src/workflow/vars.ts
CHANGED
|
@@ -29,10 +29,38 @@ export const resolveVars = (
|
|
|
29
29
|
return merged
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const FLAG_VALUES: Readonly<Record<string, string>> = { "0": "0", false: "0", "1": "1", true: "1" }
|
|
33
|
+
|
|
34
|
+
const flagVariable = (setting: string): string =>
|
|
35
|
+
`GTD_FLAG_${setting.slice("flag_".length).toUpperCase()}`
|
|
36
|
+
|
|
37
|
+
/** The `flag_<name>` settings a workflow declares that gtd's own flags lack: the workflow's own release flags. */
|
|
38
|
+
const ownFlags = (
|
|
39
|
+
workflowVars: Readonly<Record<string, string>>,
|
|
40
|
+
flags: Readonly<Record<string, string>>,
|
|
41
|
+
): string[] =>
|
|
42
|
+
Object.keys(workflowVars).filter(
|
|
43
|
+
(name) => name.startsWith("flag_") && !Object.hasOwn(flags, name),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
/** Why a workflow's own release flag cannot start, if one is set to anything but 1, 0, true or false. */
|
|
47
|
+
export const ownFlagRefusal = (
|
|
48
|
+
workflowVars: Readonly<Record<string, string>>,
|
|
49
|
+
flags: Readonly<Record<string, string>>,
|
|
50
|
+
env: Readonly<Record<string, string | undefined>>,
|
|
51
|
+
): string | undefined => {
|
|
52
|
+
const bad = ownFlags(workflowVars, flags).find((name) => {
|
|
53
|
+
const raw = env[flagVariable(name)] ?? ""
|
|
54
|
+
return raw !== "" && !Object.hasOwn(FLAG_VALUES, raw.toLowerCase())
|
|
55
|
+
})
|
|
56
|
+
return bad === undefined ? undefined : `${flagVariable(bad)} must be 1, 0, true or false`
|
|
57
|
+
}
|
|
58
|
+
|
|
32
59
|
/**
|
|
33
60
|
* `resolveVars` for a process being STARTED: `flags` land between the workflow
|
|
34
|
-
* defaults and `--var`, for already-declared names only
|
|
35
|
-
*
|
|
61
|
+
* defaults and `--var`, for already-declared names only, and so does
|
|
62
|
+
* `GTD_FLAG_<NAME>` for a `flag_<name>` the workflow declares itself. A running
|
|
63
|
+
* process never calls this — it replays its pinned record.
|
|
36
64
|
*/
|
|
37
65
|
export const resolveStartVars = (
|
|
38
66
|
workflowVars: Readonly<Record<string, string>>,
|
|
@@ -43,6 +71,10 @@ export const resolveStartVars = (
|
|
|
43
71
|
const declared = Object.fromEntries(
|
|
44
72
|
Object.entries(flags).filter(([name]) => Object.hasOwn(workflowVars, name)),
|
|
45
73
|
)
|
|
74
|
+
for (const name of ownFlags(workflowVars, flags)) {
|
|
75
|
+
const value = FLAG_VALUES[(env[flagVariable(name)] ?? "").toLowerCase()]
|
|
76
|
+
if (value !== undefined) declared[name] = value
|
|
77
|
+
}
|
|
46
78
|
return resolveVars({ ...workflowVars, ...declared }, entryVars, env)
|
|
47
79
|
}
|
|
48
80
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Building blocks
|
|
1
|
+
// Building blocks workflows share. Policy-free: every skill list,
|
|
2
2
|
// lens, prompt, step, path and threshold arrives as a parameter, so a new
|
|
3
3
|
// workflow composes these without inheriting the shipped one's choices.
|
|
4
4
|
export * from "./health.js"
|