@pmelab/gtd 20.4.0 → 21.1.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 +21 -16
- package/claude/hooks/drive.ts +3 -1
- package/claude/hooks/judge.ts +18 -4
- package/claude/hooks/register.tsx +10 -7
- package/dist/gtd.bundle.mjs +247 -2150
- package/dist/web/main.css +2 -0
- package/dist/web/main.js +35086 -0
- package/dist/workflows.mjs +18155 -0
- package/package.json +12 -3
- package/schema.json +3 -3
- package/skills/authoring/SKILL.md +6 -6
- package/src/workflows/access.test.ts +1 -1
- package/src/workflows/index.ts +4 -1
- package/src/workflows/review.test.ts +1 -1
- package/src/workflows/skills.test.ts +1 -1
package/README.md
CHANGED
|
@@ -56,7 +56,9 @@ enforces it at the OS level. See
|
|
|
56
56
|
|
|
57
57
|
## Quick start
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Run `gtd install` at your repository's root. It writes a `gtd.config.ts` that
|
|
60
|
+
runs gtd's shipped workflows — commit it; gtd refuses to run without one — and
|
|
61
|
+
prints a briefing. Pipe that briefing into your coding agent and answer its
|
|
60
62
|
questions. It should build you a script that will autonomously run the process
|
|
61
63
|
for you. Ask your agent if you have any questions.
|
|
62
64
|
|
|
@@ -92,9 +94,9 @@ Two commands make up the whole cycle. **`gtd next` says what to do now.
|
|
|
92
94
|
that.
|
|
93
95
|
|
|
94
96
|
The fastest way to understand it is to drive a few beats by hand. Any git
|
|
95
|
-
repository with at least one commit will do
|
|
96
|
-
|
|
97
|
-
command
|
|
97
|
+
repository with at least one commit will do once `gtd install` has written its
|
|
98
|
+
`gtd.config.ts` and you have committed it — the first two beats need nothing
|
|
99
|
+
else. Later beats run your test command, set under `env:` in `.gtdrc`.
|
|
98
100
|
|
|
99
101
|
### Beat 1 — `gtd next` says whose turn it is
|
|
100
102
|
|
|
@@ -412,20 +414,22 @@ noted in steps 3 and 4 below.
|
|
|
412
414
|
users only; model `haiku`, `--model <name>` overrides) and reuses its login.
|
|
413
415
|
With no `--provider`, `gtd judge run` picks jev when `TYPESAFE_API_KEY` is
|
|
414
416
|
set and non-empty and no `--model` is given, else llm — never falling back
|
|
415
|
-
across them;
|
|
416
|
-
|
|
417
|
-
judge without editing the driver (flags
|
|
418
|
-
|
|
419
|
-
stdout when they cannot answer
|
|
417
|
+
across them; the `GTD_JUDGE_PROVIDER` / `GTD_JUDGE_MODEL` environment
|
|
418
|
+
variables, a user-level `~/.config/gtd/judge.json` `{ provider, model }`, and
|
|
419
|
+
a `.gtdrc` `judge:` key choose the judge without editing the driver (flags
|
|
420
|
+
win over all three, in that order); `--model` with `--provider fixed`/`jev`
|
|
421
|
+
is refused. jev and llm exit 1 with nothing on stdout when they cannot answer
|
|
422
|
+
every question):
|
|
420
423
|
- Every red round after the first: was the failure identical, new, or
|
|
421
424
|
progress?
|
|
422
425
|
- After you review: is each of your notes an edit, a question, a nit or
|
|
423
426
|
praise?
|
|
424
427
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
428
|
+
gtd itself never calls a judge: `gtd loop`, the Claude Code mod and the
|
|
429
|
+
reference driver answer these through `gtd judge run` (auto selection: jev
|
|
430
|
+
when `TYPESAFE_API_KEY` is set, else `llm` via `claude`, default model haiku,
|
|
431
|
+
`--model <name>` overrides); if that fails it shows you the message and
|
|
432
|
+
stops. **The `llm` provider's `p` is self-reported by the model, not a
|
|
429
433
|
measured probability, so a confidently wrong verdict can clear the 0.7 floor
|
|
430
434
|
and skip a gate unattended.**
|
|
431
435
|
|
|
@@ -505,9 +509,10 @@ starts a pure review of everything from `<commitish>` to HEAD — straight to st
|
|
|
505
509
|
4, no planning and no building. `gtd doors` lists every door; a `gtd.config.ts`
|
|
506
510
|
can add its own. `gtd --workflow <name>` starts any workflow by name.
|
|
507
511
|
|
|
508
|
-
The workflow itself is a plain async TypeScript function
|
|
509
|
-
the repository root
|
|
510
|
-
|
|
512
|
+
The workflow itself is a plain async TypeScript function, and the
|
|
513
|
+
`gtd.config.ts` at the repository root names it: the one `gtd install` writes
|
|
514
|
+
re-exports the shipped workflows from `@pmelab/gtd/workflows`, and the pieces
|
|
515
|
+
they are built from are exported for yours to reuse. See
|
|
511
516
|
[Configuration](https://github.com/pmelab/gtd/blob/main/docs/configuration.md) —
|
|
512
517
|
and remember gtd evaluates that file on every command. A shared workflow package
|
|
513
518
|
bundling its own `@pmelab/gtd` is checked against the running gtd and refused,
|
package/claude/hooks/drive.ts
CHANGED
|
@@ -119,7 +119,9 @@ async function act(
|
|
|
119
119
|
|
|
120
120
|
async function message(io: Io, b: Beat, beat: number, judged: string | undefined): Promise<Acted> {
|
|
121
121
|
const verdict = b.judge && judged !== b.state ? await io.judge() : undefined
|
|
122
|
-
|
|
122
|
+
// An unanswered judge gate never lands on its own, opening beat included:
|
|
123
|
+
// that would take the conservative default unseen.
|
|
124
|
+
if (verdict || (beat === 1 && !b.judge)) return { verdict }
|
|
123
125
|
// Any later beat is a gate this run produced and the human has not read yet.
|
|
124
126
|
const stop: Stop = { kind: "gate", text: b.content ?? "", ...where(b) }
|
|
125
127
|
if (b.judge) stop.isJudge = true
|
package/claude/hooks/judge.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// A judge gate is answered by `gtd judge run`, except where its own
|
|
2
|
+
// configuration would pick the `llm` provider: that one is answered by a
|
|
3
|
+
// subagent instead of `claude -p`. A port of src/judges/providers/llm.ts
|
|
4
|
+
// (which needs Node, which a mod has not): claude/hooks/judge.test.ts pins the
|
|
5
|
+
// prompt to gtd's own, so the two cannot drift apart unnoticed.
|
|
5
6
|
|
|
6
7
|
type Question = {
|
|
7
8
|
id: string
|
|
@@ -14,6 +15,19 @@ export type Judgment = { state?: Record<string, string>; questions: Question[] }
|
|
|
14
15
|
|
|
15
16
|
type Verdict = { id: string; answer: string | number; p: number }
|
|
16
17
|
|
|
18
|
+
// The env a `gtd judge run` spawn adds to the inherited one, or `undefined` to
|
|
19
|
+
// put the gate to the in-session subagent. A provider configured only in a
|
|
20
|
+
// file is invisible here, so no variable and no key means the llm, as `gtd
|
|
21
|
+
// judge run`'s own automatic pick does.
|
|
22
|
+
export function judgeRunEnv(env: {
|
|
23
|
+
provider?: string | undefined
|
|
24
|
+
key?: string | undefined
|
|
25
|
+
}): Record<string, string> | undefined {
|
|
26
|
+
const provider = env.provider || undefined
|
|
27
|
+
if (provider === "llm" || (provider === undefined && !env.key)) return undefined
|
|
28
|
+
return env.key ? { TYPESAFE_API_KEY: env.key } : {}
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
export const JUDGE_SYSTEM =
|
|
18
32
|
"You are a strict, impartial judge. Answer each question from the supplied evidence alone.\n" +
|
|
19
33
|
"Reply only with the structured answer; never use tools or ask questions."
|
|
@@ -9,7 +9,7 @@ import type { Io, Turn, TurnEnd } from "./drive"
|
|
|
9
9
|
import { doors, enter } from "./doors"
|
|
10
10
|
import { subagentModel } from "./models"
|
|
11
11
|
import { personaSpec, resumable } from "./persona"
|
|
12
|
-
import { JUDGE_SYSTEM, judgePrompt, toVerdicts } from "./judge"
|
|
12
|
+
import { JUDGE_SYSTEM, judgePrompt, judgeRunEnv, toVerdicts } from "./judge"
|
|
13
13
|
import type { Judgment } from "./judge"
|
|
14
14
|
import { catchFrom, throwTo } from "./handoff"
|
|
15
15
|
import { documents } from "./schema"
|
|
@@ -153,13 +153,16 @@ function io($: $): Io {
|
|
|
153
153
|
},
|
|
154
154
|
judge: () =>
|
|
155
155
|
docs($).judge(async (doc, j) => {
|
|
156
|
-
const
|
|
157
|
-
|
|
156
|
+
const env = judgeRunEnv({
|
|
157
|
+
provider: await $.env.get("GTD_JUDGE_PROVIDER"),
|
|
158
|
+
key: (await $.env.get("TYPESAFE_API_KEY")) || (await $.env.get("TYPESAFE_AI_KEY")),
|
|
159
|
+
})
|
|
160
|
+
if (env === undefined) return judgeInSession($, j)
|
|
158
161
|
try {
|
|
159
|
-
const r = await $.process.run(["gtd", "judge", "run"
|
|
162
|
+
const r = await $.process.run(["gtd", "judge", "run"], {
|
|
160
163
|
cwd: root,
|
|
161
164
|
stdin: doc,
|
|
162
|
-
env
|
|
165
|
+
env,
|
|
163
166
|
timeoutMs: TEN_MINUTES,
|
|
164
167
|
})
|
|
165
168
|
return r.exitCode === 0 && r.stdout.trim() ? r.stdout : undefined
|
|
@@ -511,8 +514,8 @@ async function writer($: $, prompt: string) {
|
|
|
511
514
|
return delegate($, SHIPPER, prompt, model, "Your final answer, exactly as you would print it.")
|
|
512
515
|
}
|
|
513
516
|
|
|
514
|
-
//
|
|
515
|
-
//
|
|
517
|
+
// Where `gtd judge run` would pick its llm, a judge gate is put to a small
|
|
518
|
+
// model as that provider does, but as a subagent of this session. Any doubt leaves the
|
|
516
519
|
// gate to the person, which routes the workflow its cautious way.
|
|
517
520
|
async function judgeInSession($: $, j: Judgment) {
|
|
518
521
|
try {
|