@pmelab/gtd 59.0.1 → 60.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 +37 -33
- package/claude/hooks/doors.ts +8 -4
- package/dist/chunks/flows-DENt0uWV.mjs +707 -0
- package/dist/chunks/{steering-CCNvngSH.mjs → steering-DF_BMadH.mjs} +292 -289
- package/dist/chunks/{testkit-07Z_ENch.mjs → testkit-BvecVjQ0.mjs} +570 -390
- package/dist/chunks/{wire-BTUhRGXj.mjs → wire-DZ8R1RU9.mjs} +8 -1
- package/dist/flows.mjs +2 -676
- package/dist/gtd.bundle.mjs +751 -645
- package/dist/steering.mjs +2 -2
- package/dist/testkit/gherkin.mjs +245 -36
- package/dist/testkit.mjs +3 -2
- package/dist/web/main.js +234 -232
- package/dist/wire.mjs +2 -2
- package/dist/workflow-kit.mjs +3 -3
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +66 -60
- package/src/cli/Cli.test.ts +183 -182
- package/src/cli/Cli.ts +164 -186
- package/src/cli/History.ts +9 -4
- package/src/cli/Judges.ts +1 -1
- package/src/cli/Output.test.ts +35 -0
- package/src/cli/Output.ts +31 -1
- package/src/cli/Program.test.ts +65 -79
- package/src/cli/Program.ts +157 -118
- package/src/edge/Edge.test.ts +64 -56
- package/src/edge/Edge.ts +3 -3
- package/src/flows/factory.test.ts +14 -0
- package/src/flows/factory.ts +22 -5
- package/src/flows/helpers.test.ts +35 -0
- package/src/flows/helpers.ts +19 -10
- package/src/flows/index.test.ts +66 -0
- package/src/flows/index.ts +109 -5
- package/src/flows/internal.ts +7 -0
- package/src/flows/runtime.test.ts +192 -1
- package/src/flows/runtime.ts +188 -54
- package/src/gherkin/git-control.feature +6 -6
- package/src/gherkin/index.test.ts +127 -0
- package/src/gherkin/index.ts +370 -51
- package/src/gherkin/messages.test.ts +38 -4
- package/src/gherkin/messages.ts +21 -4
- package/src/gherkin/more-steps.feature +170 -0
- package/src/gherkin/scenario-end.feature +101 -0
- package/src/gherkin/testkit.feature +83 -7
- package/src/install/Install.test.ts +7 -7
- package/src/install/Install.ts +8 -8
- package/src/replay/Replay.test.ts +13 -2
- package/src/replay/Replay.ts +19 -30
- package/src/replay/judgeDiffShape.test.ts +1 -1
- package/src/steering/Footnotes.test.ts +1 -1
- package/src/steering/SteeringFormats.test.ts +1 -1
- package/src/steering/freeform.test.ts +1 -1
- package/src/steering/index.test.ts +27 -1
- package/src/steering/index.ts +6 -104
- package/src/steering/internal.ts +11 -0
- package/src/steering/qa.test.ts +7 -2
- package/src/steering/reads.test.ts +42 -0
- package/src/steering/reads.ts +43 -0
- package/src/steering/registry.ts +93 -0
- package/src/steering/review.test.ts +2 -2
- package/src/step/planStart.test.ts +18 -16
- package/src/step/planStart.ts +9 -9
- package/src/testkit/TestRepo.test.ts +245 -7
- package/src/testkit/TestRepo.ts +271 -40
- package/src/testkit/imports.test.ts +58 -0
- package/src/testkit/imports.ts +102 -0
- package/src/testkit/index.test.ts +28 -0
- package/src/testkit/index.ts +14 -2
- package/src/twins/EmittedScriptRecognizer.ts +2 -6
- package/src/twins/EmittedScripts.test.ts +4 -2
- package/src/twins/InMemRepo.ts +1 -0
- package/src/wire/Script.ts +11 -0
- package/src/wire/WireSchema.test.ts +3 -0
- package/src/wire/golden/1/script.json +4 -0
- package/src/wire/index.ts +1 -0
- package/src/workflow/SteeringFormats.test.ts +1 -1
- package/src/workflow/compile.ts +2 -2
- package/src/workflow/discovery.test.ts +3 -3
- package/src/workflow/load.test.ts +256 -116
- package/src/workflow/load.ts +169 -146
- package/src/workflow-kit/index.test.ts +25 -0
- package/src/workflow-kit/index.ts +37 -4
- package/src/workflow-kit/review.ts +2 -2
|
@@ -11,23 +11,27 @@ description: >-
|
|
|
11
11
|
# Authoring a gtd workflow
|
|
12
12
|
|
|
13
13
|
A gtd workflow is **plain async TypeScript**: a `gtd.config.ts` at the
|
|
14
|
-
repository root exports **workflows** —
|
|
15
|
-
|
|
16
|
-
`default` export is the
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
repository root exports **workflows** — each an async flow wrapped in
|
|
15
|
+
`workflow(flow, options?)` (or a `defineWorkflow` factory), named by its export,
|
|
16
|
+
awaiting **steps** built from `@pmelab/gtd/flows`; the `default` export is the
|
|
17
|
+
ordinary start and may be a plain flow. Any other exported function is a plain
|
|
18
|
+
helper. A workflow's options — `defaults` (process settings), `envDefaults`
|
|
19
|
+
(environment settings), `summary`, `base`, `steering`, `skills`, `access`,
|
|
20
|
+
`doors` — go in `workflow(flow, { … })` or a factory build, never in file-level
|
|
21
|
+
exports: exporting one of those names fails the load. `workflow()` returns a new
|
|
22
|
+
function and leaves `flow` untouched, so an already-wrapped flow (one a library
|
|
23
|
+
exports) can be wrapped again: `workflow(libraryFlow, { … })` — its options
|
|
24
|
+
replace the library's, nothing is merged. Every step is a commit; gtd finds
|
|
25
|
+
where a process rests by **replaying** the flow over the episode's commits, so
|
|
26
|
+
the git history IS the state and nothing is stored anywhere else.
|
|
23
27
|
|
|
24
28
|
Your job is to produce or edit that module so it loads cleanly and does what the
|
|
25
29
|
user wants. Driving a workflow once it exists is a separate concern — that is
|
|
26
30
|
what a driver does.
|
|
27
31
|
|
|
28
32
|
**Trust:** gtd evaluates `gtd.config.ts` on every command that resolves workflow
|
|
29
|
-
state (`gtd next`, `gtd lsp
|
|
30
|
-
|
|
33
|
+
state (`gtd next`, `gtd lsp` and `gtd start` included). It is code the user's
|
|
34
|
+
repository runs; write it with the same care as a build script.
|
|
31
35
|
|
|
32
36
|
## Golden rule: start from an existing workflow, edit incrementally
|
|
33
37
|
|
|
@@ -38,36 +42,28 @@ pinned on the machine — `bin/gtd-wf pin` there, then `bin/gtd-wf config defaul
|
|
|
38
42
|
prints a `gtd.config.ts` importing its bundle by absolute path. Every phase and
|
|
39
43
|
single step that bundle is built from is a named export too, so a config can
|
|
40
44
|
import what it keeps and write only what changes. Import phases by name; **never
|
|
41
|
-
`export *` from it** —
|
|
42
|
-
startable workflow:
|
|
45
|
+
`export *` from it** — re-exported option names fail the load:
|
|
43
46
|
|
|
44
47
|
```ts
|
|
45
|
-
import { start } from "@pmelab/gtd/flows"
|
|
48
|
+
import { start, workflow } from "@pmelab/gtd/flows"
|
|
46
49
|
import {
|
|
47
50
|
afterTail,
|
|
48
51
|
buildTail,
|
|
49
|
-
feature,
|
|
50
|
-
} from "/home/me/.local/share/gtd/workflows/dist/default.mjs"
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
52
|
defaults,
|
|
54
53
|
envDefaults,
|
|
55
|
-
|
|
56
|
-
base,
|
|
57
|
-
steering,
|
|
54
|
+
feature,
|
|
58
55
|
skills,
|
|
59
56
|
} from "/home/me/.local/share/gtd/workflows/dist/default.mjs"
|
|
60
57
|
|
|
61
58
|
export default feature()
|
|
62
59
|
|
|
63
|
-
export
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const doors = { hotfix: { workflow: "hotfix" } }
|
|
60
|
+
export const hotfix = workflow(
|
|
61
|
+
async () => afterTail(await buildTail(start(), { fixFirst: true })),
|
|
62
|
+
{ defaults, envDefaults, skills, doors: { hotfix: {} } },
|
|
63
|
+
)
|
|
68
64
|
```
|
|
69
65
|
|
|
70
|
-
`gtd
|
|
66
|
+
`gtd start hotfix` starts it (the door of the same name too). Use the checkout's
|
|
71
67
|
real path; `gtd-wf status` shows it.
|
|
72
68
|
|
|
73
69
|
To change a phase itself, read its source in the checkout (`workflows/default/`)
|
|
@@ -89,47 +85,53 @@ workflow that fails to load breaks every gtd command in the repository.
|
|
|
89
85
|
|
|
90
86
|
## The step API
|
|
91
87
|
|
|
92
|
-
| Call
|
|
93
|
-
|
|
|
94
|
-
| `agent(name, prompt, opts?)`
|
|
95
|
-
| `human(name, opts?)`
|
|
96
|
-
| `run(name, body, opts?)`
|
|
97
|
-
| `judge(name, spec)`
|
|
98
|
-
| `restart()`
|
|
88
|
+
| Call | Actor | Rest content | Resolves to |
|
|
89
|
+
| ------------------------------ | ------- | ------------ | -------------------------------------- |
|
|
90
|
+
| `agent(name, prompt, opts?)` | `agent` | `prompt` | `void`, once an agent turn landed |
|
|
91
|
+
| `human(name, message?, opts?)` | `human` | `message` | `void`, once a person landed |
|
|
92
|
+
| `run(name, body, opts?)` | `check` | `script` | `void`, once the run's tree landed |
|
|
93
|
+
| `judge(name, spec)` | `judge` | `message` | `{ answers, truncated }` |
|
|
94
|
+
| `restart()` | — | — | never: ends the episode from any depth |
|
|
99
95
|
|
|
100
96
|
- `run` body: a POSIX `sh` string the driver runs verbatim. Decide in flow code,
|
|
101
97
|
then render the script from those values — `check(name, command, …)` and the
|
|
102
98
|
exported `checkScript`, `revertScript`, `restoreScript`, `removeScript`,
|
|
103
99
|
`moveScript` and `quote` do that for the common cases. **A run's outcome is
|
|
104
100
|
what it leaves in the tree** — read it back with `changes()` and `read()`.
|
|
105
|
-
- `judge` takes
|
|
106
|
-
`{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
- `judge` takes
|
|
102
|
+
`{ questions, evidence, message?, label?, file?, mode?, base? }`. Questions
|
|
103
|
+
are
|
|
104
|
+
`{ id, primitive: "noul" | "yesno" | "choice" | "score", instructions, criteria }`
|
|
105
|
+
(`yesno` is `noul`); `evidence` is an object of strings, the
|
|
106
|
+
`judgeBudgetBytes` var split evenly across its keys. It resolves to `answers`
|
|
107
|
+
— one `{ answer, p }` per question id (a `noul` reads back as `"yes"`/`"no"`),
|
|
108
|
+
`undefined` when the verdict left it out — and `truncated`, the evidence keys
|
|
109
|
+
the budget cut. Compare answers with plain `if`s; landing with no verdict
|
|
110
|
+
leaves every answer `undefined`, so make `undefined` take the conservative
|
|
111
|
+
branch.
|
|
113
112
|
- A person or a driver sees a rest as one of the five content kinds `capture` (a
|
|
114
113
|
dirty tree at a human step), `message`, `script`, `prompt`, `stalled`. Every
|
|
115
114
|
step you add must fit one of them; there is no sixth.
|
|
116
115
|
|
|
117
116
|
Options (all optional): `label`, `file` (a `.gtd/` path), `mode` (needs `file`;
|
|
118
117
|
`qa`, `review`, `findings`, or a `config.modes` name), `message` (human/judge),
|
|
119
|
-
`model`, `system` (agent), `allowEmpty` (agent), `
|
|
120
|
-
|
|
118
|
+
`model`, `system` (agent), `allowEmpty` (agent), `keep` (agent, not with
|
|
119
|
+
`allowEmpty`), `acceptClean` (human), `approve` (human, not with `file`/`mode`),
|
|
120
|
+
`base` (the commit the step reviews since — what `gtd base` prints). The types
|
|
121
|
+
reject the combinations replay refuses.
|
|
121
122
|
|
|
122
123
|
Helpers — pure reads of the commit replay stands on (the tree the last step
|
|
123
124
|
left, never the live working tree): `read(path)`, `glob(pattern)`,
|
|
124
125
|
`changes(glob?)` (what the last step changed: `{ path, status, before, after }`
|
|
125
126
|
per path, `status` one of `"added"`/`"modified"`/`"deleted"`, plus `paths` and
|
|
126
127
|
`get(path)`), `sections(text)` (`## ` headings), `openQuestions(text)` (a `qa`
|
|
127
|
-
document's unanswered questions), `
|
|
128
|
-
start — safe to branch on), `
|
|
129
|
-
prompt text, step options and `run()` bodies, never a branch)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
document's unanswered questions), `settings` (process settings, pinned at
|
|
129
|
+
process start — safe to branch on), `envSettings` (environment settings, read
|
|
130
|
+
live — only for prompt text, step options and `run()` bodies, never a branch) —
|
|
131
|
+
`settingsOf(defaults)` types them by the declared names — `head()` (the commit
|
|
132
|
+
the flow stands on) and `start()` (the process's diff base). State a flow needs
|
|
133
|
+
across steps — a counter, the previous report, a review round's base — lives in
|
|
134
|
+
local variables; replay rebuilds them.
|
|
133
135
|
|
|
134
136
|
Composition: `scope(name, fn)` prefixes step names (`build.fix`) and sets their
|
|
135
137
|
**memory scope** (one scope = one agent conversation = one model/system/skills —
|
|
@@ -139,7 +141,12 @@ inside. Skills are declared per scope, never on `agent()`, and a driver
|
|
|
139
141
|
restricts each turn to its scope's list where it can. `refuse(message)` refuses
|
|
140
142
|
the pending landing — call it right after the step whose turn you reject.
|
|
141
143
|
`@pmelab/gtd/flows` exports `requireProgress(file)`, `requireAnswers(file)` and
|
|
142
|
-
`requireRevert(edited, base)` among its ready-made
|
|
144
|
+
`requireRevert(edited, base)` among its ready-made guards, and
|
|
145
|
+
`check(name, command, report)` runs a command as a `run` step.
|
|
146
|
+
`@pmelab/gtd/workflow-kit` adds larger building blocks (`healthy`, `qualityLap`,
|
|
147
|
+
`review`, …; see its reference, `docs/workflow-kit.md`). `sections`, `threads`
|
|
148
|
+
and `openQuestions` throw outside a replay; `@pmelab/gtd/steering` exports pure
|
|
149
|
+
copies under the same names.
|
|
143
150
|
|
|
144
151
|
## Names, commits and history
|
|
145
152
|
|
|
@@ -149,14 +156,13 @@ the pending landing — call it right after the step whose turn you reject.
|
|
|
149
156
|
- An episode ends when the flow returns or calls `restart()`; the next starts at
|
|
150
157
|
the flow's first step on an ordinary start — that step is where a finished
|
|
151
158
|
process waits (gtd-workflows' default uses `human("idle", …)`).
|
|
152
|
-
- `gtd
|
|
153
|
-
unknown name is a usage error)
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
`
|
|
158
|
-
|
|
159
|
-
a started process's diff base (blank = default-branch merge-base).
|
|
159
|
+
- `gtd start <name>` starts a process on the exported workflow `<name>` (an
|
|
160
|
+
unknown name is a usage error), or through a door `<name> [args]`; bare
|
|
161
|
+
`gtd start` lists both. A workflow's `doors` option maps a door name to
|
|
162
|
+
`{ args?, vars? }`; exporting gtd-workflows' `fix`, `review`, `rollout` and
|
|
163
|
+
`cleanup` brings `fix`, `review [base]`, `rollout <flag>` and
|
|
164
|
+
`cleanup <flag>`. A `base: (workflow, vars) => commitish | undefined` option
|
|
165
|
+
fixes a started process's diff base (blank = default-branch merge-base).
|
|
160
166
|
`--var <name>=<value>` only pins process settings: names the workflow's
|
|
161
167
|
`defaults` declare — never an environment setting.
|
|
162
168
|
|
|
@@ -178,7 +184,7 @@ Branch on what the step left, not on who acted:
|
|
|
178
184
|
```ts
|
|
179
185
|
await run(
|
|
180
186
|
"check",
|
|
181
|
-
`${
|
|
187
|
+
`${envSettings.testCommand} > .gtd/FEEDBACK.md 2>&1 && rm -f .gtd/FEEDBACK.md`,
|
|
182
188
|
)
|
|
183
189
|
if (read(".gtd/FEEDBACK.md") !== undefined) {
|
|
184
190
|
await agent("fix", "Fix what .gtd/FEEDBACK.md reports, then delete it.", {
|