@pmelab/gtd 12.5.0 → 14.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/README.md +49 -38
- package/dist/gtd.bundle.mjs +3572 -11508
- package/package.json +8 -3
- package/schema.json +5 -287
- package/src/flows/helpers.ts +93 -0
- package/src/flows/index.ts +3 -0
- package/src/flows/runtime.ts +295 -0
- package/src/flows/scripts.ts +116 -0
- package/src/workflows/health.ts +126 -0
- package/src/workflows/index.ts +1 -0
- package/src/workflows/packages.ts +93 -0
- package/src/workflows/planning.ts +99 -0
- package/src/workflows/prose.ts +200 -0
- package/src/workflows/review.ts +169 -0
- package/src/workflows/steps.ts +200 -0
- package/src/workflows/text.fixture.ts +38 -0
- package/src/workflows/text.test.ts +37 -0
- package/src/workflows/text.ts +682 -0
- package/src/workflows/unified.ts +128 -0
- package/src/workflows/vars.ts +23 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
changes,
|
|
3
|
+
head,
|
|
4
|
+
human,
|
|
5
|
+
refuse,
|
|
6
|
+
requireRevert,
|
|
7
|
+
restoreScript,
|
|
8
|
+
revertScript,
|
|
9
|
+
run,
|
|
10
|
+
start,
|
|
11
|
+
type EntryBase,
|
|
12
|
+
type FlowArgs,
|
|
13
|
+
type Summary,
|
|
14
|
+
} from "../flows/index.js"
|
|
15
|
+
import { baseline, gate } from "./health.js"
|
|
16
|
+
import { packages } from "./packages.js"
|
|
17
|
+
import { architecturePass, design } from "./planning.js"
|
|
18
|
+
import { buildTail, type ReviewOutcome } from "./review.js"
|
|
19
|
+
import { ARCHITECTURE, FEEDBACK, REQUIREMENTS, REVIEW } from "./steps.js"
|
|
20
|
+
import * as t from "./text.js"
|
|
21
|
+
|
|
22
|
+
// gtd's built-in default workflow. Any change to the tree starts a process:
|
|
23
|
+
// `idle` → `unwind` reverts the sketch (its intent survives in history) → a
|
|
24
|
+
// green-baseline gate → design, architecture and one package per concern →
|
|
25
|
+
// the quality lap → human review, which signs off (the episode ends back at
|
|
26
|
+
// `idle`) or sends a full re-plan lap. `--entry fix-precheck`, `--entry
|
|
27
|
+
// review-gate.check --var reviewBase=<commitish>` and `--entry
|
|
28
|
+
// start-gate.check` enter the same flow further in.
|
|
29
|
+
//
|
|
30
|
+
// Every part is exported for other workflows to compose; see the modules
|
|
31
|
+
// re-exported below.
|
|
32
|
+
|
|
33
|
+
export { defaults } from "./vars.js"
|
|
34
|
+
export * from "./steps.js"
|
|
35
|
+
export * from "./health.js"
|
|
36
|
+
export * from "./planning.js"
|
|
37
|
+
export * from "./packages.js"
|
|
38
|
+
export * from "./review.js"
|
|
39
|
+
|
|
40
|
+
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
41
|
+
export const unwind = (): Promise<void> => {
|
|
42
|
+
const commit = head()
|
|
43
|
+
return run("unwind", revertScript(commit, FEEDBACK, t.unwindFailure(commit)), {
|
|
44
|
+
label: "Unwinding your input",
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Undo the human's review-round code edits. A path changed since the human
|
|
50
|
+
* left it is not overwritten; requireRevert names it instead.
|
|
51
|
+
*/
|
|
52
|
+
export const reUnwind = async (
|
|
53
|
+
feedback: Extract<ReviewOutcome, { verdict: "feedback" }>,
|
|
54
|
+
): Promise<void> => {
|
|
55
|
+
const { base, edited } = feedback
|
|
56
|
+
const restore = edited.filter((c) => c.status !== "added").map((c) => c.path)
|
|
57
|
+
const remove = edited.filter((c) => c.status === "added").map((c) => c.path)
|
|
58
|
+
await run("re-unwind", restoreScript(base, { restore, remove }), {
|
|
59
|
+
label: "Re-unwinding your review edit",
|
|
60
|
+
file: REVIEW,
|
|
61
|
+
base,
|
|
62
|
+
})
|
|
63
|
+
requireRevert(edited, base)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Plan, build and review until a review round signs off; feedback re-plans from scratch. */
|
|
67
|
+
export const planAndBuild = async (firstBase: string): Promise<void> => {
|
|
68
|
+
let base = firstBase
|
|
69
|
+
for (;;) {
|
|
70
|
+
await design(base)
|
|
71
|
+
await architecturePass()
|
|
72
|
+
await packages()
|
|
73
|
+
const outcome = await buildTail(false, base)
|
|
74
|
+
if (outcome.verdict === "signoff") return
|
|
75
|
+
await reUnwind(outcome)
|
|
76
|
+
base = outcome.base
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. */
|
|
81
|
+
export const afterTail = async (outcome: ReviewOutcome): Promise<void> => {
|
|
82
|
+
if (outcome.verdict === "signoff") return
|
|
83
|
+
await reUnwind(outcome)
|
|
84
|
+
await planAndBuild(outcome.base)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
88
|
+
export const ordinaryStart = async (): Promise<void> => {
|
|
89
|
+
await human("idle", { message: t.idleMessage(), label: "Idle", file: ".gtd/TODO.md" })
|
|
90
|
+
await unwind()
|
|
91
|
+
if (changes(FEEDBACK).some((c) => c.status !== "deleted")) {
|
|
92
|
+
await human("unwind-failed", {
|
|
93
|
+
message: t.unwindFailedMessage(),
|
|
94
|
+
label: "Could not unwind your input",
|
|
95
|
+
file: FEEDBACK,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
await gate("start-gate", t.startGateBlockedMessage())
|
|
99
|
+
await planAndBuild(start())
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const ENTRIES = ["fix-precheck", "review-gate.check", "start-gate.check"]
|
|
103
|
+
|
|
104
|
+
export default async function unified({ entry }: FlowArgs): Promise<void> {
|
|
105
|
+
if (entry === undefined) return ordinaryStart()
|
|
106
|
+
if (entry === "fix-precheck") {
|
|
107
|
+
if (await baseline("fix-precheck")) return
|
|
108
|
+
return afterTail(await buildTail(true, start()))
|
|
109
|
+
}
|
|
110
|
+
if (entry === "review-gate.check") {
|
|
111
|
+
await gate("review-gate", t.reviewGateBlockedMessage())
|
|
112
|
+
return afterTail(await buildTail(false, start()))
|
|
113
|
+
}
|
|
114
|
+
if (entry === "start-gate.check") {
|
|
115
|
+
await gate("start-gate", t.startGateBlockedMessage())
|
|
116
|
+
return planAndBuild(start())
|
|
117
|
+
}
|
|
118
|
+
refuse(
|
|
119
|
+
`"${entry}" is not an enterable state — enterable states:\n${ENTRIES.map((name) => ` ${name}`).join("\n")}`,
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const summary: Summary = t.summaryPrompt
|
|
124
|
+
|
|
125
|
+
export const steering = { [REQUIREMENTS]: "qa", [ARCHITECTURE]: "qa", [REVIEW]: "review" }
|
|
126
|
+
|
|
127
|
+
export const base: EntryBase = (entry, vars) =>
|
|
128
|
+
entry === "review-gate.check" ? (vars.reviewBase ?? "") : undefined
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The bundled workflow's variable defaults. `.gtdrc` `vars:`, `--var` and
|
|
2
|
+
// `GTD_<NAME>` override any of them.
|
|
3
|
+
export const defaults: Readonly<Record<string, string>> = {
|
|
4
|
+
testCommand: "npm test",
|
|
5
|
+
plannerModel: "smart",
|
|
6
|
+
coderModel: "base",
|
|
7
|
+
reviewBase: "",
|
|
8
|
+
judgeBudgetBytes: "32768",
|
|
9
|
+
judgeIdenticalMinP: "0.7",
|
|
10
|
+
specPreJudge: "0.9",
|
|
11
|
+
reviewNoteActionable: "0.7",
|
|
12
|
+
architectureSkipMinP: "0.85",
|
|
13
|
+
triageSkills: "spec-driven-development, planning-and-task-breakdown",
|
|
14
|
+
architectureSkills: "api-and-interface-design, documentation-and-adrs",
|
|
15
|
+
decomposeSkills: "incremental-implementation, planning-and-task-breakdown",
|
|
16
|
+
buildSkills: "test-driven-development, incremental-implementation",
|
|
17
|
+
fixSkills: "debugging-and-error-recovery",
|
|
18
|
+
reviewFixSkills: "incremental-implementation, code-simplification",
|
|
19
|
+
reviewSkills: "code-review-and-quality",
|
|
20
|
+
specReviewSkills: "code-review-and-quality, spec-driven-development",
|
|
21
|
+
escalateSkills: "debugging-and-error-recovery",
|
|
22
|
+
qualityReviews: "owasp-security, code-simplification",
|
|
23
|
+
}
|