@pmelab/gtd 27.4.0 → 27.5.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 +8 -0
- package/dist/chunks/{Schema-CyImoHTj.mjs → Schema-DO5ttJaI.mjs} +1 -1
- package/dist/chunks/{reserved-BPdVZkgv.mjs → reserved-DM5W_LAt.mjs} +64 -3
- package/dist/chunks/{tail-iLAMoigl.mjs → tail-6gvLXXcg.mjs} +1 -1
- package/dist/flows.mjs +4 -2
- package/dist/gtd.bundle.mjs +44 -9
- package/dist/testkit.mjs +43 -10
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +140 -64
- 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 +346 -107
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +49 -1
- package/src/cli/Cli.ts +20 -3
- package/src/edge/Edge.test.ts +45 -1
- package/src/edge/Edge.ts +15 -4
- package/src/flags/index.test.ts +14 -1
- package/src/flags/index.ts +14 -3
- package/src/flows/helpers.ts +7 -3
- package/src/replay/judgeDiffShape.test.ts +43 -0
- package/src/step/planStart.ts +3 -2
- package/src/testkit/TestRepo.ts +3 -0
- package/src/testkit/steps.ts +18 -0
- package/src/workflow/index.ts +8 -1
- package/src/workflow/vars.test.ts +23 -1
- package/src/workflow/vars.ts +24 -0
- package/src/workflows/feature/feature.ts +9 -2
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/seam-pin.feature +52 -0
- package/src/workflows/feature/seam.feature +387 -0
- package/src/workflows/feature/seam.test.ts +88 -0
- package/src/workflows/feature/seam.ts +110 -0
- package/src/workflows/index.ts +1 -0
- package/src/workflows/shared/options.test.ts +12 -1
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/skills.test.ts +20 -2
- package/src/workflows/shared/skills.ts +6 -0
- package/src/workflows/shared/text.test.ts +59 -0
- package/src/workflows/shared/text.ts +86 -0
- package/src/workflows/shared/vars.ts +4 -0
- package/src/workflows/shared/workflows.steps.ts +39 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { answered, changes, changesSince, glob, hasThreadFor, head, human, judge, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, start, vars } from "../flows.mjs";
|
|
1
|
+
import { answered, changes, changesSince, glob, hasThreadFor, head, human, judge, judgeDiffShape, kept, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, seamGate, start, vars } from "../flows.mjs";
|
|
2
2
|
import { review as review$1 } from "../workflow-kit.mjs";
|
|
3
|
-
import { A as architectureAuthorPrompt, B as designGateAnswerMessage, C as fixRisks, D as reviewing, F as buildReviewCollectingPrompt, H as designTriagePrompt, I as buildReviewFixNitsPrompt, J as
|
|
4
|
-
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-
|
|
3
|
+
import { $ as refactorFixPrompt, A as architectureAuthorPrompt, B as designGateAnswerMessage, C as fixRisks, D as reviewing, F as buildReviewCollectingPrompt, H as designTriagePrompt, I as buildReviewFixNitsPrompt, J as implementFixPrompt, K as idleMessage, L as buildReviewTriageMessage, M as architectureGateAnswerMessage, N as buildReviewAnswerQuestionsPrompt, O as agentWithSkills, P as buildReviewAwaitAfterDeferMessage, Q as refactorBuildingPrompt, R as builderSystem, T as reviewMissing, V as designSystem, X as packagesItemBuildingPrompt, Y as naivePassPrompt, Z as packagesItemFixSuitePrompt, _ as REQUIREMENTS, at as seamByHandMessage, b as coder, c as envDefaults, ct as unwindFailedMessage, d as gate, et as reviewEditNotesCapture, h as FEEDBACK, i as skills, it as reviewerSystem, j as architectureDecomposePrompt, k as architectSystem, lt as unwindFailure, m as ARCHITECTURE, n as summary, o as shipped, ot as seamSteerMessage, p as healthy, q as implementBuildingPrompt, r as access, rt as reviewNotesCapture, s as defaults, st as startGateBlockedMessage, t as steering, tt as reviewEditsCapture, v as REVIEW, w as planner, x as fixCheck, y as awaitReview, z as deferNotesScript } from "../chunks/reserved-DM5W_LAt.mjs";
|
|
4
|
+
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-6gvLXXcg.mjs";
|
|
5
5
|
//#region src/workflows/feature/steps.ts
|
|
6
6
|
/** Turn the sketch since `base` into `.gtd/REQUIREMENTS.md`. */
|
|
7
7
|
const triage = (base) => agentWithSkills("triage", designTriagePrompt(base), {
|
|
@@ -345,66 +345,6 @@ const review = (base, escalations = { rounds: 0 }) => {
|
|
|
345
345
|
/** The build tail: fix (when entered red), keep green unless `green`, the quality lap, then human review since `base`. */
|
|
346
346
|
const buildTail = (fixFirst, base, green = false) => tail(fixFirst, (escalations) => review(base, escalations), green);
|
|
347
347
|
//#endregion
|
|
348
|
-
//#region src/workflows/feature/feature.ts
|
|
349
|
-
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
350
|
-
const unwind = () => {
|
|
351
|
-
const commit = head();
|
|
352
|
-
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
353
|
-
};
|
|
354
|
-
/**
|
|
355
|
-
* Undo the human's review-round code edits. A path changed since the human
|
|
356
|
-
* left it is not overwritten; requireRevert names it instead.
|
|
357
|
-
*/
|
|
358
|
-
const reUnwind = async (feedback) => {
|
|
359
|
-
const { base, restoreFrom, edited } = feedback;
|
|
360
|
-
await run("re-unwind", restoreScript(base, {
|
|
361
|
-
restore: edited.filter((c) => c.status !== "added").map((c) => c.path),
|
|
362
|
-
remove: edited.filter((c) => c.status === "added").map((c) => c.path)
|
|
363
|
-
}, restoreFrom), {
|
|
364
|
-
label: "Re-unwinding your review edit",
|
|
365
|
-
file: REVIEW,
|
|
366
|
-
base
|
|
367
|
-
});
|
|
368
|
-
requireRevert(edited, base);
|
|
369
|
-
};
|
|
370
|
-
/** Plan, build and review until a review round signs off; feedback re-plans from scratch. */
|
|
371
|
-
const planAndBuild = async (firstBase) => {
|
|
372
|
-
let base = firstBase;
|
|
373
|
-
for (;;) {
|
|
374
|
-
await design(base);
|
|
375
|
-
await architecture();
|
|
376
|
-
await packages();
|
|
377
|
-
const outcome = await buildTail(false, base, true);
|
|
378
|
-
if (outcome.verdict === "signoff") return;
|
|
379
|
-
await reUnwind(outcome);
|
|
380
|
-
base = outcome.base;
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. */
|
|
384
|
-
const afterTail = async (outcome) => {
|
|
385
|
-
if (outcome.verdict === "signoff") return;
|
|
386
|
-
await reUnwind(outcome);
|
|
387
|
-
await planAndBuild(outcome.base);
|
|
388
|
-
};
|
|
389
|
-
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
390
|
-
const ordinaryStart = async () => {
|
|
391
|
-
await human("idle", {
|
|
392
|
-
message: idleMessage(),
|
|
393
|
-
label: "Idle",
|
|
394
|
-
file: ".gtd/TODO.md"
|
|
395
|
-
});
|
|
396
|
-
refusePendingHandOff();
|
|
397
|
-
await unwind();
|
|
398
|
-
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
399
|
-
message: unwindFailedMessage(),
|
|
400
|
-
label: "Could not unwind your input",
|
|
401
|
-
file: FEEDBACK
|
|
402
|
-
});
|
|
403
|
-
await gate("start-gate", startGateBlockedMessage());
|
|
404
|
-
await planAndBuild(start());
|
|
405
|
-
};
|
|
406
|
-
const feature = shipped(ordinaryStart);
|
|
407
|
-
//#endregion
|
|
408
348
|
//#region src/workflows/feature/refactor.ts
|
|
409
349
|
const REFACTOR_PACKAGE = ".gtd/REFACTOR.md";
|
|
410
350
|
const isCode = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
@@ -518,7 +458,143 @@ const refactorFirst = async (shape, options = {}) => {
|
|
|
518
458
|
});
|
|
519
459
|
};
|
|
520
460
|
//#endregion
|
|
461
|
+
//#region src/workflows/feature/seam.ts
|
|
462
|
+
/** Naive-pass retries after a `reject`; the next `reject` rests at `seam.steer`. */
|
|
463
|
+
const NAIVE_RETRIES = 2;
|
|
464
|
+
/** A change to an existing file outside the allowlist is a stray even when the task needs it: the judge decides. */
|
|
465
|
+
const judgeSeam = (tip) => judgeDiffShape("judge", {
|
|
466
|
+
changes: tip.changes,
|
|
467
|
+
allow: (vars.seamAllow ?? defaults.seamAllow).split(",").map((glob) => glob.trim()).filter((glob) => glob !== ""),
|
|
468
|
+
exempt: tip.changes.filter((c) => c.status === "added").map((c) => c.path)
|
|
469
|
+
});
|
|
470
|
+
const naivePass = (rejection) => agentWithSkills("naive", naivePassPrompt(rejection), {
|
|
471
|
+
label: rejection === void 0 ? "Writing the naive pass" : "Rewriting the naive pass",
|
|
472
|
+
keep: true,
|
|
473
|
+
model: coder(),
|
|
474
|
+
system: builderSystem()
|
|
475
|
+
});
|
|
476
|
+
const implement = (reference) => scope("implement", async () => {
|
|
477
|
+
await agentWithSkills("building", implementBuildingPrompt(reference), {
|
|
478
|
+
label: "Implementing",
|
|
479
|
+
model: coder(),
|
|
480
|
+
system: builderSystem()
|
|
481
|
+
});
|
|
482
|
+
await healthy(() => agentWithSkills("fix.suite.fixing", implementFixPrompt(), {
|
|
483
|
+
label: "Fixing the check",
|
|
484
|
+
file: FEEDBACK,
|
|
485
|
+
model: coder(),
|
|
486
|
+
system: builderSystem()
|
|
487
|
+
}));
|
|
488
|
+
await run("closing", removeScript([REQUIREMENTS]), { label: "Closing out the implementation" });
|
|
489
|
+
});
|
|
490
|
+
const decide = () => scope("seam", async () => {
|
|
491
|
+
let rejection;
|
|
492
|
+
let retries = 0;
|
|
493
|
+
for (;;) {
|
|
494
|
+
await naivePass(rejection);
|
|
495
|
+
const tip = kept("naive");
|
|
496
|
+
const shape = await judgeSeam(tip);
|
|
497
|
+
if (shape.choice === "land") return { reference: tip.hash };
|
|
498
|
+
if (shape.choice === "refactor-first") {
|
|
499
|
+
await seamGate("approve", shape);
|
|
500
|
+
return {
|
|
501
|
+
reference: tip.hash,
|
|
502
|
+
shape
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
rejection = {
|
|
506
|
+
strays: shape.strays,
|
|
507
|
+
p: shape.judgment?.answers.shape?.p ?? 0
|
|
508
|
+
};
|
|
509
|
+
if (retries < NAIVE_RETRIES) {
|
|
510
|
+
retries++;
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
await human("steer", {
|
|
514
|
+
message: seamSteerMessage(rejection),
|
|
515
|
+
label: "Steer or overrule the rejected naive pass",
|
|
516
|
+
file: REQUIREMENTS,
|
|
517
|
+
acceptClean: true
|
|
518
|
+
});
|
|
519
|
+
if (changes().length === 0) return { reference: tip.hash };
|
|
520
|
+
rejection = void 0;
|
|
521
|
+
retries = 0;
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
const seamBuild = async () => {
|
|
525
|
+
const { reference, shape } = await decide();
|
|
526
|
+
if (shape !== void 0 && !await refactorFirst(shape, { reference })) await scope("seam", () => human("by-hand", {
|
|
527
|
+
message: seamByHandMessage(shape.strays, shape.unreadable, reference),
|
|
528
|
+
label: "Reviewing unreadable files by hand",
|
|
529
|
+
acceptClean: true
|
|
530
|
+
}));
|
|
531
|
+
await implement(reference);
|
|
532
|
+
};
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/workflows/feature/feature.ts
|
|
535
|
+
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
536
|
+
const unwind = () => {
|
|
537
|
+
const commit = head();
|
|
538
|
+
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* Undo the human's review-round code edits. A path changed since the human
|
|
542
|
+
* left it is not overwritten; requireRevert names it instead.
|
|
543
|
+
*/
|
|
544
|
+
const reUnwind = async (feedback) => {
|
|
545
|
+
const { base, restoreFrom, edited } = feedback;
|
|
546
|
+
await run("re-unwind", restoreScript(base, {
|
|
547
|
+
restore: edited.filter((c) => c.status !== "added").map((c) => c.path),
|
|
548
|
+
remove: edited.filter((c) => c.status === "added").map((c) => c.path)
|
|
549
|
+
}, restoreFrom), {
|
|
550
|
+
label: "Re-unwinding your review edit",
|
|
551
|
+
file: REVIEW,
|
|
552
|
+
base
|
|
553
|
+
});
|
|
554
|
+
requireRevert(edited, base);
|
|
555
|
+
};
|
|
556
|
+
/** Plan, build and review until a review round signs off; feedback re-plans from scratch. */
|
|
557
|
+
const planAndBuild = async (firstBase) => {
|
|
558
|
+
let base = firstBase;
|
|
559
|
+
for (;;) {
|
|
560
|
+
await design(base);
|
|
561
|
+
if (vars.flag_naive_pass === "1") await seamBuild();
|
|
562
|
+
else {
|
|
563
|
+
await architecture();
|
|
564
|
+
await packages();
|
|
565
|
+
}
|
|
566
|
+
const outcome = await buildTail(false, base, true);
|
|
567
|
+
if (outcome.verdict === "signoff") return;
|
|
568
|
+
await reUnwind(outcome);
|
|
569
|
+
base = outcome.base;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. */
|
|
573
|
+
const afterTail = async (outcome) => {
|
|
574
|
+
if (outcome.verdict === "signoff") return;
|
|
575
|
+
await reUnwind(outcome);
|
|
576
|
+
await planAndBuild(outcome.base);
|
|
577
|
+
};
|
|
578
|
+
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
579
|
+
const ordinaryStart = async () => {
|
|
580
|
+
await human("idle", {
|
|
581
|
+
message: idleMessage(),
|
|
582
|
+
label: "Idle",
|
|
583
|
+
file: ".gtd/TODO.md"
|
|
584
|
+
});
|
|
585
|
+
refusePendingHandOff();
|
|
586
|
+
await unwind();
|
|
587
|
+
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
588
|
+
message: unwindFailedMessage(),
|
|
589
|
+
label: "Could not unwind your input",
|
|
590
|
+
file: FEEDBACK
|
|
591
|
+
});
|
|
592
|
+
await gate("start-gate", startGateBlockedMessage());
|
|
593
|
+
await planAndBuild(start());
|
|
594
|
+
};
|
|
595
|
+
const feature = shipped(ordinaryStart);
|
|
596
|
+
//#endregion
|
|
521
597
|
//#region src/workflows/feature/index.ts
|
|
522
598
|
var feature_default = feature;
|
|
523
599
|
//#endregion
|
|
524
|
-
export { REFACTOR_PACKAGE, access, afterTail, answerProductQuestions, answerReviewQuestions, answerTechnicalQuestions, architecture, author, build, buildTail, collecting, declaredTests, decompose, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, fixSuite, nextPackage, ordinaryStart, packageItem, packages, planAndBuild, reUnwind, refactorFirst, requireDeclaredTests, requireNoNewFlags, requireWithin, skills, steering, summary, triage, unwind };
|
|
600
|
+
export { REFACTOR_PACKAGE, access, afterTail, answerProductQuestions, answerReviewQuestions, answerTechnicalQuestions, architecture, author, build, buildTail, collecting, declaredTests, decompose, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, fixSuite, implement, judgeSeam, naivePass, nextPackage, ordinaryStart, packageItem, packages, planAndBuild, reUnwind, refactorFirst, requireDeclaredTests, requireNoNewFlags, requireWithin, seamBuild, skills, steering, summary, triage, unwind };
|
package/dist/workflows/fix.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, start } from "../flows.mjs";
|
|
2
2
|
import { d as reviewNotes, f as reviewRisks } from "../chunks/steering-DVILanVf.mjs";
|
|
3
|
-
import { O as agentWithSkills, U as finisherSystem, W as fixReviewNotesPrompt, b as coder, c as envDefaults, i as skills, l as baseline, n as summary, o as shipped, r as access, s as defaults, t as steering, v as REVIEW } from "../chunks/reserved-
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { O as agentWithSkills, U as finisherSystem, W as fixReviewNotesPrompt, b as coder, c as envDefaults, i as skills, l as baseline, n as summary, o as shipped, r as access, s as defaults, t as steering, v as REVIEW } from "../chunks/reserved-DM5W_LAt.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-6gvLXXcg.mjs";
|
|
5
5
|
//#region src/workflows/fix/fix.ts
|
|
6
6
|
/**
|
|
7
7
|
* Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { c as envDefaults, d as gate, i as skills, n as summary, nt as reviewGateBlockedMessage, o as shipped, r as access, s as defaults, t as steering } from "../chunks/reserved-DM5W_LAt.mjs";
|
|
3
|
+
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-6gvLXXcg.mjs";
|
|
4
4
|
//#region src/workflows/review/review.ts
|
|
5
5
|
/**
|
|
6
6
|
* Pure review of everything since `reviewBase`: a green gate, the quality
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agent, changesSince, head, read, refuse, scope, vars } from "../flows.mjs";
|
|
2
|
-
import { a as flagShipped, b as coder, c as envDefaults, d as gate, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-
|
|
2
|
+
import { a as flagShipped, b as coder, c as envDefaults, d as gate, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-DM5W_LAt.mjs";
|
|
3
3
|
import { i as requireFlag, n as flipProblems } from "../chunks/registry-CP3YuV9j.mjs";
|
|
4
4
|
//#region src/workflows/rollout/rollout.ts
|
|
5
5
|
const flippingPrompt = (name, registry) => `Roll out the release flag "${name}": in ${registry}, set \`defaultOn: true\` on its entry. Change nothing else — no other entry, no other field, no other file, no comments.`;
|