@pmelab/gtd 27.1.0 → 27.3.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 +16 -9
- package/dist/chunks/registry-CP3YuV9j.mjs +101 -0
- package/dist/chunks/{reserved-BL9krPao.mjs → reserved-BPdVZkgv.mjs} +66 -73
- package/dist/chunks/tail-iLAMoigl.mjs +81 -0
- package/dist/gtd.bundle.mjs +5 -2
- package/dist/testkit.mjs +17 -2
- package/dist/workflows/cleanup.mjs +37 -0
- package/dist/workflows/feature.mjs +119 -5
- package/dist/workflows/fix.mjs +2 -1
- package/dist/workflows/review.mjs +2 -1
- package/dist/workflows/rollout.mjs +33 -0
- package/dist/workflows.mjs +362 -25
- package/package.json +11 -1
- package/src/flags/index.test.ts +28 -0
- package/src/flags/index.ts +9 -4
- package/src/testkit/EmittedScriptRecognizer.ts +21 -0
- package/src/testkit/EmittedScripts.test.ts +9 -0
- package/src/testkit/steps.ts +26 -0
- package/src/testkit/testkit.feature +8 -1
- package/src/workflows/cleanup/cleanup.feature +323 -0
- package/src/workflows/cleanup/cleanup.test.ts +272 -0
- package/src/workflows/cleanup/cleanup.ts +52 -0
- package/src/workflows/cleanup/index.ts +6 -0
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/refactor.test.ts +466 -0
- package/src/workflows/feature/refactor.ts +178 -0
- package/src/workflows/index.ts +4 -1
- package/src/workflows/rollout/index.ts +6 -0
- package/src/workflows/rollout/rollout.feature +214 -0
- package/src/workflows/rollout/rollout.test.ts +167 -0
- package/src/workflows/rollout/rollout.ts +44 -0
- package/src/workflows/shared/access.ts +2 -1
- package/src/workflows/shared/health.test.ts +103 -0
- package/src/workflows/shared/health.ts +32 -7
- package/src/workflows/shared/options.test.ts +30 -0
- package/src/workflows/shared/options.ts +20 -0
- package/src/workflows/shared/registry.test.ts +305 -0
- package/src/workflows/shared/registry.ts +158 -0
- package/src/workflows/shared/skills.test.ts +6 -0
- package/src/workflows/shared/skills.ts +5 -1
- package/src/workflows/shared/tail.test.ts +36 -2
- package/src/workflows/shared/tail.ts +30 -18
- package/src/workflows/shared/text.ts +35 -0
- package/src/workflows/shared/vars.ts +1 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { answered, changes, changesSince, glob, hasThreadFor, head, human, judge, numeric, 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, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, start, vars } from "../flows.mjs";
|
|
2
2
|
import { review as review$1 } from "../workflow-kit.mjs";
|
|
3
|
-
import {
|
|
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 packagesItemFixSuitePrompt, K as idleMessage, L as buildReviewTriageMessage, M as architectureGateAnswerMessage, N as buildReviewAnswerQuestionsPrompt, O as agentWithSkills, P as buildReviewAwaitAfterDeferMessage, Q as reviewEditsCapture, R as builderSystem, T as reviewMissing, V as designSystem, X as refactorFixPrompt, Y as refactorBuildingPrompt, Z as reviewEditNotesCapture, _ as REQUIREMENTS, b as coder, c as envDefaults, d as gate, et as reviewNotesCapture, h as FEEDBACK, i as skills, it as unwindFailure, j as architectureDecomposePrompt, k as architectSystem, m as ARCHITECTURE, n as summary, nt as startGateBlockedMessage, o as shipped, p as healthy, q as packagesItemBuildingPrompt, r as access, rt as unwindFailedMessage, s as defaults, t as steering, tt as reviewerSystem, v as REVIEW, w as planner, x as fixCheck, y as awaitReview, z as deferNotesScript } from "../chunks/reserved-BPdVZkgv.mjs";
|
|
4
|
+
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-iLAMoigl.mjs";
|
|
4
5
|
//#region src/workflows/feature/steps.ts
|
|
5
6
|
/** Turn the sketch since `base` into `.gtd/REQUIREMENTS.md`. */
|
|
6
7
|
const triage = (base) => agentWithSkills("triage", designTriagePrompt(base), {
|
|
@@ -281,7 +282,7 @@ const triage$1 = (severities) => async (notes) => {
|
|
|
281
282
|
return [n.id, severity.tier === "could" ? "defer" : "edit"];
|
|
282
283
|
}));
|
|
283
284
|
};
|
|
284
|
-
const isCode = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
285
|
+
const isCode$1 = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
285
286
|
/**
|
|
286
287
|
* A reviewer writes `.gtd/REVIEW.md` over everything since `base`, a human
|
|
287
288
|
* reviews and signs off or comments, and each note is judged: edits go to
|
|
@@ -294,7 +295,7 @@ const review = (base, escalations = { rounds: 0 }) => {
|
|
|
294
295
|
return review$1(base, {
|
|
295
296
|
file: REVIEW,
|
|
296
297
|
feedback: REQUIREMENTS,
|
|
297
|
-
isCode,
|
|
298
|
+
isCode: isCode$1,
|
|
298
299
|
reviewing: (b, carry) => {
|
|
299
300
|
deferred = [];
|
|
300
301
|
return reviewing(b, carry);
|
|
@@ -404,7 +405,120 @@ const ordinaryStart = async () => {
|
|
|
404
405
|
};
|
|
405
406
|
const feature = shipped(ordinaryStart);
|
|
406
407
|
//#endregion
|
|
408
|
+
//#region src/workflows/feature/refactor.ts
|
|
409
|
+
const REFACTOR_PACKAGE = ".gtd/REFACTOR.md";
|
|
410
|
+
const isCode = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
411
|
+
const requireWithin = (allowed) => {
|
|
412
|
+
const outside = changes().map((c) => c.path).filter((path) => isCode(path) && !allowed.includes(path));
|
|
413
|
+
if (outside.length === 0) return;
|
|
414
|
+
refuse(`gtd land: refactor-scope: the refactor may change only its stray paths — revert these:\n${outside.map((path) => ` - ${path}`).join("\n")}`);
|
|
415
|
+
};
|
|
416
|
+
const flagExpression = (pattern) => {
|
|
417
|
+
if (pattern.trim() === "") throw new Error("gtd: refactorFlagPattern must not be blank");
|
|
418
|
+
let expression;
|
|
419
|
+
try {
|
|
420
|
+
expression = new RegExp(pattern, "g");
|
|
421
|
+
} catch (error) {
|
|
422
|
+
throw new Error(`gtd: refactorFlagPattern is not a valid regular expression: ${String(error)}`);
|
|
423
|
+
}
|
|
424
|
+
if (expression.test("")) throw new Error("gtd: refactorFlagPattern must not match the empty string");
|
|
425
|
+
return expression;
|
|
426
|
+
};
|
|
427
|
+
const countFlags = (text, expression, into) => {
|
|
428
|
+
let zeroLength = false;
|
|
429
|
+
for (const [name] of text.matchAll(expression)) if (name === "") zeroLength = true;
|
|
430
|
+
else into.set(name, (into.get(name) ?? 0) + 1);
|
|
431
|
+
return zeroLength;
|
|
432
|
+
};
|
|
433
|
+
const listPaths = (paths) => paths.map((path) => ` - ${path}`).join("\n");
|
|
434
|
+
const requireNoNewFlags = (pattern, since) => {
|
|
435
|
+
const expression = flagExpression(pattern);
|
|
436
|
+
const before = /* @__PURE__ */ new Map();
|
|
437
|
+
const after = /* @__PURE__ */ new Map();
|
|
438
|
+
const unreadable = [];
|
|
439
|
+
const zeroLength = [];
|
|
440
|
+
for (const { path, status, before: was, after: now } of changesSince(since)) {
|
|
441
|
+
if (!isCode(path)) continue;
|
|
442
|
+
const oldSide = status === "added" ? "" : was;
|
|
443
|
+
const newSide = status === "deleted" ? "" : now;
|
|
444
|
+
if (oldSide === void 0 || newSide === void 0) {
|
|
445
|
+
unreadable.push(path);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
countFlags(oldSide, expression, before);
|
|
449
|
+
if (countFlags(newSide, expression, after)) zeroLength.push(path);
|
|
450
|
+
}
|
|
451
|
+
if (unreadable.length > 0) refuse(`gtd land: refactor-flags: cannot compare flag reads in:\n${listPaths(unreadable)}`);
|
|
452
|
+
if (zeroLength.length > 0) refuse(`gtd land: refactor-flags: refactorFlagPattern (${pattern}) produced a zero-length match in:\n${listPaths(zeroLength)}`);
|
|
453
|
+
const raised = [...after].filter(([name, count]) => count > (before.get(name) ?? 0));
|
|
454
|
+
if (raised.length === 0) return;
|
|
455
|
+
refuse(`gtd land: refactor-flags: the refactor may not introduce or add reads of a flag (${pattern}):\n${raised.map(([name, count]) => ` - ${name}: ${before.get(name) ?? 0} → ${count}`).join("\n")}`);
|
|
456
|
+
};
|
|
457
|
+
const packageLines = (strays, pattern, reference) => [
|
|
458
|
+
"# Behaviour-neutral refactor",
|
|
459
|
+
"",
|
|
460
|
+
"## Scope (read-only)",
|
|
461
|
+
"",
|
|
462
|
+
...strays.map((path) => `- \`${path}\``),
|
|
463
|
+
"",
|
|
464
|
+
"This list is fixed when the package is derived: editing it here widens or narrows nothing.",
|
|
465
|
+
"",
|
|
466
|
+
"## Rules",
|
|
467
|
+
"",
|
|
468
|
+
"- The suite's outcome must not change",
|
|
469
|
+
"- Change only the paths above; a test file only when it is one of them",
|
|
470
|
+
`- Introduce no flag read and add no second read of an existing one (\`${pattern}\`)`,
|
|
471
|
+
"",
|
|
472
|
+
"## Reference",
|
|
473
|
+
"",
|
|
474
|
+
reference === void 0 ? "None." : `Reading material only, never applied: \`git show ${reference} -- ${strays.map(quote).join(" ")}\``,
|
|
475
|
+
""
|
|
476
|
+
];
|
|
477
|
+
const deriveScript = (lines) => `mkdir -p .gtd
|
|
478
|
+
printf '%s\\n' ${lines.map(quote).join(" ")} > ${quote(REFACTOR_PACKAGE)}
|
|
479
|
+
`;
|
|
480
|
+
const refactorFirst = async (shape, options = {}) => {
|
|
481
|
+
if (shape.choice !== "refactor-first" || shape.unreadable.length > 0) return false;
|
|
482
|
+
const pattern = vars.refactorFlagPattern ?? defaults.refactorFlagPattern;
|
|
483
|
+
const expression = flagExpression(pattern);
|
|
484
|
+
const { strays } = shape;
|
|
485
|
+
const zeroLength = strays.filter((path) => countFlags(read(path) ?? "", expression, /* @__PURE__ */ new Map()));
|
|
486
|
+
if (zeroLength.length > 0) throw new Error(`gtd: refactorFlagPattern (${pattern}) produces a zero-length match in:\n${listPaths(zeroLength)}`);
|
|
487
|
+
const reference = options.reference?.trim() ? options.reference : void 0;
|
|
488
|
+
return scope({
|
|
489
|
+
name: "refactor",
|
|
490
|
+
access: { write: [...strays, ".gtd/**"] }
|
|
491
|
+
}, async () => {
|
|
492
|
+
await run("deriving", deriveScript(packageLines(strays, pattern, reference)), {
|
|
493
|
+
label: "Deriving the refactor package",
|
|
494
|
+
file: REFACTOR_PACKAGE
|
|
495
|
+
});
|
|
496
|
+
const from = head();
|
|
497
|
+
const guard = () => {
|
|
498
|
+
requireWithin(strays);
|
|
499
|
+
requireNoNewFlags(pattern, from);
|
|
500
|
+
};
|
|
501
|
+
await agentWithSkills("building", refactorBuildingPrompt(REFACTOR_PACKAGE, strays, pattern, reference), {
|
|
502
|
+
label: "Refactoring",
|
|
503
|
+
model: coder(),
|
|
504
|
+
system: builderSystem()
|
|
505
|
+
});
|
|
506
|
+
guard();
|
|
507
|
+
await healthy(async () => {
|
|
508
|
+
await agentWithSkills("fix.suite.fixing", refactorFixPrompt(strays, pattern), {
|
|
509
|
+
label: "Fixing the check",
|
|
510
|
+
file: FEEDBACK,
|
|
511
|
+
model: coder(),
|
|
512
|
+
system: builderSystem()
|
|
513
|
+
});
|
|
514
|
+
guard();
|
|
515
|
+
}, { describeGuard: guard });
|
|
516
|
+
await run("closing", removeScript([REFACTOR_PACKAGE]), { label: "Closing out the refactor" });
|
|
517
|
+
return true;
|
|
518
|
+
});
|
|
519
|
+
};
|
|
520
|
+
//#endregion
|
|
407
521
|
//#region src/workflows/feature/index.ts
|
|
408
522
|
var feature_default = feature;
|
|
409
523
|
//#endregion
|
|
410
|
-
export { 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, requireDeclaredTests, skills, steering, summary, triage, unwind };
|
|
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 };
|
package/dist/workflows/fix.mjs
CHANGED
|
@@ -1,6 +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 {
|
|
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-BPdVZkgv.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-iLAMoigl.mjs";
|
|
4
5
|
//#region src/workflows/fix/fix.ts
|
|
5
6
|
/**
|
|
6
7
|
* Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { $ as reviewGateBlockedMessage, c as envDefaults, d as gate, i as skills, n as summary, o as shipped, r as access, s as defaults, t as steering } from "../chunks/reserved-BPdVZkgv.mjs";
|
|
3
|
+
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-iLAMoigl.mjs";
|
|
3
4
|
//#region src/workflows/review/review.ts
|
|
4
5
|
/**
|
|
5
6
|
* Pure review of everything since `reviewBase`: a green gate, the quality
|
|
@@ -0,0 +1,33 @@
|
|
|
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-BPdVZkgv.mjs";
|
|
3
|
+
import { i as requireFlag, n as flipProblems } from "../chunks/registry-CP3YuV9j.mjs";
|
|
4
|
+
//#region src/workflows/rollout/rollout.ts
|
|
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.`;
|
|
6
|
+
const suiteBlockedMessage = () => "The suite is red after the rollout. Fix it by hand — a rollout may only touch the flag's registry entry — then land.";
|
|
7
|
+
const rolloutFlow = async () => {
|
|
8
|
+
const registry = vars.flagRegistry ?? "";
|
|
9
|
+
const name = vars.flag ?? "";
|
|
10
|
+
requireFlag("rollout", registry, name, false);
|
|
11
|
+
const base = head();
|
|
12
|
+
const before = read(registry) ?? "";
|
|
13
|
+
const guard = () => {
|
|
14
|
+
const strays = changesSince(base).paths.filter((path) => path !== registry && path !== ".gtd/FEEDBACK.md");
|
|
15
|
+
if (strays.length > 0) refuse(`gtd land: rollout-only: ${strays.join(", ")} changed — rollout may only set defaultOn: true on "${name}" in ${registry}`);
|
|
16
|
+
const problems = flipProblems(before, read(registry) ?? "", name);
|
|
17
|
+
if (problems.length > 0) refuse(`gtd land: rollout-only: other edits in ${registry}: ${problems.join("; ")}`);
|
|
18
|
+
};
|
|
19
|
+
await scope("rollout", async () => {
|
|
20
|
+
await agent("flipping", flippingPrompt(name, registry), {
|
|
21
|
+
label: `Rolling out ${name}`,
|
|
22
|
+
model: coder()
|
|
23
|
+
});
|
|
24
|
+
guard();
|
|
25
|
+
await gate("suite", suiteBlockedMessage(), guard);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
const rollout = flagShipped(rolloutFlow);
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/workflows/rollout/index.ts
|
|
31
|
+
var rollout_default = rollout;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { access, rollout_default as default, defaults, envDefaults, rollout, rolloutFlow, skills, steering, summary };
|