@pmelab/gtd 38.0.0 → 38.2.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 +36 -3
- package/dist/chunks/{Schema-DO5ttJaI.mjs → Schema-C6fLlQzx.mjs} +1 -1
- package/dist/chunks/{reserved-sfMdy7v7.mjs → reserved-C3PgP7XB.mjs} +56 -12
- package/dist/chunks/{tail-Cgca5Gp-.mjs → tail-Bkxki2nK.mjs} +1 -1
- package/dist/chunks/{wire-CSsa3Wxd.mjs → wire-BJuZWR3C.mjs} +1 -1
- package/dist/gtd.bundle.mjs +292 -53
- package/dist/testkit.mjs +257 -21
- package/dist/wire.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +158 -42
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +230 -70
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +15 -3
- package/src/cli/Cli.ts +61 -28
- package/src/cli/History.ts +1 -19
- package/src/cli/Metrics.test.ts +224 -0
- package/src/cli/Metrics.ts +310 -0
- package/src/cli/Output.ts +13 -0
- package/src/cli/Program.test.ts +3 -1
- package/src/cli/Program.ts +4 -0
- package/src/testkit/steps.ts +4 -0
- package/src/wire/ContextLimit.ts +1 -1
- package/src/workflows/feature/continuation-cap.feature +65 -0
- package/src/workflows/feature/continuation-rebuild.feature +88 -0
- package/src/workflows/feature/continuation-slices.feature +64 -0
- package/src/workflows/feature/continuation.feature +205 -0
- package/src/workflows/feature/criteria.ts +11 -0
- package/src/workflows/feature/refactor.ts +64 -30
- package/src/workflows/feature/seam.ts +102 -30
- package/src/workflows/shared/continuation.ts +92 -0
- package/src/workflows/shared/options.ts +2 -0
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.test.ts +6 -2
- package/src/workflows/shared/text.ts +104 -8
- package/src/workflows/shared/vars.ts +5 -0
|
@@ -375,6 +375,7 @@ export const refactorBuildingPrompt = ({
|
|
|
375
375
|
reference,
|
|
376
376
|
feedback = false,
|
|
377
377
|
note = false,
|
|
378
|
+
from,
|
|
378
379
|
}: {
|
|
379
380
|
pkg: string
|
|
380
381
|
strays: readonly string[]
|
|
@@ -382,6 +383,8 @@ export const refactorBuildingPrompt = ({
|
|
|
382
383
|
reference?: string | undefined
|
|
383
384
|
feedback?: boolean | undefined
|
|
384
385
|
note?: boolean | undefined
|
|
386
|
+
/** A continuation of a refactor turn that stopped at its context limit; `open` are its untouched paths. */
|
|
387
|
+
from?: Handoff | undefined
|
|
385
388
|
}): string =>
|
|
386
389
|
`${stateFileRules}
|
|
387
390
|
- The package to implement is \`${pkg}\`; never delete it
|
|
@@ -410,8 +413,9 @@ ${
|
|
|
410
413
|
refactor that touches none of it is wrong, however clean — never
|
|
411
414
|
swap in an unrelated tidy-up
|
|
412
415
|
`
|
|
413
|
-
}
|
|
414
|
-
|
|
416
|
+
}${handoffRule()}
|
|
417
|
+
- Leave everything uncommitted and finish your turn — do not commit
|
|
418
|
+
${from === undefined ? "" : continuationLines(from)}`
|
|
415
419
|
|
|
416
420
|
export const refactorFixPrompt = (strays: readonly string[], pattern: string): string =>
|
|
417
421
|
`${stateFileRules}
|
|
@@ -1198,8 +1202,8 @@ export type NaiveRetry = SeamRejection | NaiveRed | NaiveFault
|
|
|
1198
1202
|
const isRed = (retry: NaiveRetry): retry is NaiveRed => "red" in retry
|
|
1199
1203
|
const isFault = (retry: NaiveRetry): retry is NaiveFault => "fault" in retry
|
|
1200
1204
|
|
|
1201
|
-
const pickUp = (hash: string): string =>
|
|
1202
|
-
`Pick it up — \`git diff --binary ${hash}^ ${hash} | git apply\``
|
|
1205
|
+
const pickUp = (hash: string, exclude?: string): string =>
|
|
1206
|
+
`Pick it up — \`git diff --binary --no-color --no-ext-diff --no-textconv ${hash}^ ${hash}${exclude === undefined ? "" : ` -- ${quote(":(top)")} ${quote(`:(top,exclude)${exclude}`)}`} | git apply\``
|
|
1203
1207
|
|
|
1204
1208
|
const criteriaBinding = (refused: boolean): string =>
|
|
1205
1209
|
`- Its \`## Acceptance Criteria\` (\`AC-<n>\`) bind this turn: re-read them from
|
|
@@ -1227,7 +1231,72 @@ ${listed(retry.strays)}
|
|
|
1227
1231
|
Start over and choose a different approach; do not rebuild the same diff.
|
|
1228
1232
|
`
|
|
1229
1233
|
|
|
1230
|
-
|
|
1234
|
+
/** A turn that stopped at its context limit: the snapshot it left, its handoff note, what it left open. */
|
|
1235
|
+
export interface Handoff {
|
|
1236
|
+
/** The commit holding the stopped turn's work: its kept tip, or the turn's own commit. */
|
|
1237
|
+
readonly snapshot: string
|
|
1238
|
+
readonly note: string
|
|
1239
|
+
readonly open: readonly string[]
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
const quoted = (text: string): string =>
|
|
1243
|
+
text
|
|
1244
|
+
.trimEnd()
|
|
1245
|
+
.split("\n")
|
|
1246
|
+
.map((line) => (line === "" ? "" : ` > ${line}`))
|
|
1247
|
+
.join("\n")
|
|
1248
|
+
|
|
1249
|
+
/** Every turn a continuation may pick up tells its agent where the note goes. */
|
|
1250
|
+
const handoffRule = (): string =>
|
|
1251
|
+
`- Should you be told to stop at a context limit, write the handoff note to
|
|
1252
|
+
\`.gtd/HANDOFF.md\` — what you touched, what was next — and finish your turn:
|
|
1253
|
+
a fresh turn picks up from what you leave, broken or not. That file is the one
|
|
1254
|
+
exception to any path rule here; write it at no other time`
|
|
1255
|
+
|
|
1256
|
+
/** `pickUp`: how the snapshot gets into the tree — absent when it already is. */
|
|
1257
|
+
const continuationLines = (from: Handoff, pickUp?: string): string =>
|
|
1258
|
+
`
|
|
1259
|
+
Your previous turn stopped at its context limit; this fresh turn picks up from
|
|
1260
|
+
its snapshot (\`${from.snapshot}\`). ${pickUp === undefined ? "Its work is already in the tree." : `${pickUp}.`} Its handoff note:
|
|
1261
|
+
|
|
1262
|
+
${quoted(from.note === "" ? "(empty)" : from.note)}
|
|
1263
|
+
|
|
1264
|
+
Still open: ${from.open.length === 0 ? "nothing the workflow can count — finish what the note says is next" : from.open.join(", ")}.
|
|
1265
|
+
${pickUp === undefined ? "Delete `.gtd/HANDOFF.md` once you are done; rewrite it should you be stopped again.\n" : ""}`
|
|
1266
|
+
|
|
1267
|
+
export const oversizedMessage = (
|
|
1268
|
+
phase: string,
|
|
1269
|
+
from: Handoff,
|
|
1270
|
+
why: { readonly cap: number } | { readonly stall: number },
|
|
1271
|
+
narrowable = true,
|
|
1272
|
+
): string =>
|
|
1273
|
+
`The ${phase} keeps outgrowing its context: ${
|
|
1274
|
+
"cap" in why
|
|
1275
|
+
? why.cap === 0
|
|
1276
|
+
? "continuations are off (`continuations` is 0) and the turn stopped at its context limit"
|
|
1277
|
+
: `${why.cap} continuation${why.cap === 1 ? "" : "s"} ran and the last still stopped at its context limit`
|
|
1278
|
+
: `the last ${why.stall} continuation${why.stall === 1 ? "" : "s"} closed nothing still open`
|
|
1279
|
+
}. The last snapshot is \`${from.snapshot}\`; still open: ${from.open.length === 0 ? "nothing countable" : from.open.join(", ")}.
|
|
1280
|
+
|
|
1281
|
+
Its handoff note:
|
|
1282
|
+
|
|
1283
|
+
${quoted(from.note === "" ? "(empty)" : from.note)}
|
|
1284
|
+
|
|
1285
|
+
Land to go on: a fresh round of continuations picks up from that snapshot${
|
|
1286
|
+
narrowable
|
|
1287
|
+
? ` — narrow
|
|
1288
|
+
\`.gtd/REQUIREMENTS.md\` first if the change is too big, or raise the context limit`
|
|
1289
|
+
: ` — raise
|
|
1290
|
+
the context limit first if the refactor is too big`
|
|
1291
|
+
}. \`gtd abandon\` gives the change up.
|
|
1292
|
+
`
|
|
1293
|
+
|
|
1294
|
+
/** `from`: a continuation of a pass that stopped at its context limit, in place of `retry`'s pick-up. */
|
|
1295
|
+
export const naivePassPrompt = (
|
|
1296
|
+
retry?: NaiveRetry,
|
|
1297
|
+
slice?: readonly string[],
|
|
1298
|
+
from?: Handoff,
|
|
1299
|
+
): string =>
|
|
1231
1300
|
`${stateFileRules}
|
|
1232
1301
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
1233
1302
|
${sliceLine(slice)}${criteriaBinding(false)}
|
|
@@ -1245,9 +1314,34 @@ ${sliceLine(slice)}${criteriaBinding(false)}
|
|
|
1245
1314
|
- Run \`${env.testCommand}\` and finish only once it is green with every
|
|
1246
1315
|
criterion's test passing: the next step runs it on your diff and sends a red
|
|
1247
1316
|
pass back
|
|
1248
|
-
- Leave \`.gtd/\` untouched
|
|
1317
|
+
- Leave \`.gtd/\` untouched, a handoff note excepted
|
|
1318
|
+
${handoffRule()}
|
|
1249
1319
|
- Leave everything uncommitted and finish your turn — do not commit
|
|
1250
|
-
${
|
|
1320
|
+
${
|
|
1321
|
+
from !== undefined
|
|
1322
|
+
? `${continuationLines(from, pickUp(from.snapshot, ".gtd/HANDOFF.md"))}${
|
|
1323
|
+
retry === undefined
|
|
1324
|
+
? ""
|
|
1325
|
+
: isFault(retry)
|
|
1326
|
+
? `
|
|
1327
|
+
The pass this continues was a retry: its tests fell short, and still must not:
|
|
1328
|
+
${retry.fault}
|
|
1329
|
+
`
|
|
1330
|
+
: isRed(retry)
|
|
1331
|
+
? `
|
|
1332
|
+
The pass this continues was a retry after a red suite (\`.gtd/FEEDBACK.md\`
|
|
1333
|
+
holds the output): finish only once the suite is green.
|
|
1334
|
+
`
|
|
1335
|
+
: `
|
|
1336
|
+
The pass this continues was a retry after a \`reject\`: keep away from the
|
|
1337
|
+
approach that changed these files outside the expected set:
|
|
1338
|
+
${listed(retry.strays)}
|
|
1339
|
+
`
|
|
1340
|
+
}`
|
|
1341
|
+
: retry === undefined
|
|
1342
|
+
? ""
|
|
1343
|
+
: naiveRetry(retry)
|
|
1344
|
+
}`
|
|
1251
1345
|
|
|
1252
1346
|
/** The naive pass's kept tip, its acceptance test files and the criterion IDs they name. */
|
|
1253
1347
|
export interface Acceptance {
|
|
@@ -1340,6 +1434,7 @@ export const implementBuildingPrompt = (
|
|
|
1340
1434
|
flagMode = true,
|
|
1341
1435
|
rebuild: Rebuild = {},
|
|
1342
1436
|
slice?: readonly string[],
|
|
1437
|
+
from?: Handoff,
|
|
1343
1438
|
): string =>
|
|
1344
1439
|
`${stateFileRules}
|
|
1345
1440
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
@@ -1366,9 +1461,10 @@ ${afterRefactor === true ? afterRefactorLine(rebuild) : ""}${
|
|
|
1366
1461
|
- Write the tests first, then the code that makes them pass. Name each
|
|
1367
1462
|
criterion's ID in the test that covers it (\`[AC-3] …\`): once the suite is
|
|
1368
1463
|
green a judge checks every ID has a passing test, and sends uncovered ones back
|
|
1464
|
+
${handoffRule()}
|
|
1369
1465
|
- Leave \`.gtd/REQUIREMENTS.md\` in place and everything uncommitted, then
|
|
1370
1466
|
finish your turn
|
|
1371
|
-
`
|
|
1467
|
+
${from === undefined ? "" : continuationLines(from)}`
|
|
1372
1468
|
|
|
1373
1469
|
export const implementFixPrompt = (): string =>
|
|
1374
1470
|
`${stateFileRules}
|
|
@@ -34,6 +34,11 @@ export const defaults: Readonly<Record<string, string>> = {
|
|
|
34
34
|
qualityReviews:
|
|
35
35
|
"correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
|
|
36
36
|
refactorQualityReviews: "correctness, ponytail-review, conventions",
|
|
37
|
+
// Per phase (naive pass, refactor, build): continuations of a turn that
|
|
38
|
+
// stopped at its context limit before a person is asked, and how many in a
|
|
39
|
+
// row may close nothing still open.
|
|
40
|
+
continuations: "3",
|
|
41
|
+
continuationStall: "2",
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/** Environment settings. */
|