@pmelab/gtd 21.0.0 → 22.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 +15 -10
- 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 +344 -75
- package/dist/workflows.mjs +19415 -47
- package/package.json +2 -1
- package/schema.json +5 -5
- package/src/flows/factory.test.ts +94 -0
- package/src/flows/factory.ts +116 -0
- package/src/flows/index.ts +1 -0
- package/src/workflows/bundled.ts +9 -4
- package/src/workflows/options.test.ts +52 -0
- package/src/workflows/options.ts +38 -0
package/README.md
CHANGED
|
@@ -414,20 +414,22 @@ noted in steps 3 and 4 below.
|
|
|
414
414
|
users only; model `haiku`, `--model <name>` overrides) and reuses its login.
|
|
415
415
|
With no `--provider`, `gtd judge run` picks jev when `TYPESAFE_API_KEY` is
|
|
416
416
|
set and non-empty and no `--model` is given, else llm — never falling back
|
|
417
|
-
across them;
|
|
418
|
-
|
|
419
|
-
judge without editing the driver (flags
|
|
420
|
-
|
|
421
|
-
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):
|
|
422
423
|
- Every red round after the first: was the failure identical, new, or
|
|
423
424
|
progress?
|
|
424
425
|
- After you review: is each of your notes an edit, a question, a nit or
|
|
425
426
|
praise?
|
|
426
427
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
|
431
433
|
measured probability, so a confidently wrong verdict can clear the 0.7 floor
|
|
432
434
|
and skip a gate unattended.**
|
|
433
435
|
|
|
@@ -510,7 +512,10 @@ can add its own. `gtd --workflow <name>` starts any workflow by name.
|
|
|
510
512
|
The workflow itself is a plain async TypeScript function, and the
|
|
511
513
|
`gtd.config.ts` at the repository root names it: the one `gtd install` writes
|
|
512
514
|
re-exports the shipped workflows from `@pmelab/gtd/workflows`, and the pieces
|
|
513
|
-
they are built from are exported for yours to reuse.
|
|
515
|
+
they are built from are exported for yours to reuse. Each shipped workflow is a
|
|
516
|
+
factory — `feature({ testCommand: "npm run ci" })` — whose options gtd validates
|
|
517
|
+
when it loads the config: a misspelled or mistyped one fails every command,
|
|
518
|
+
naming the workflow, the key and what was expected. See
|
|
514
519
|
[Configuration](https://github.com/pmelab/gtd/blob/main/docs/configuration.md) —
|
|
515
520
|
and remember gtd evaluates that file on every command. A shared workflow package
|
|
516
521
|
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 {
|