strikethroo 3.7.0 → 3.8.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/README.md +12 -12
- package/dist/index.js +1 -1
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.js +1 -1
- package/dist/serve/derivation.d.ts +1 -0
- package/dist/serve/derivation.d.ts.map +1 -1
- package/dist/serve/derivation.js +1 -0
- package/dist/serve/derivation.js.map +1 -1
- package/dist/serve/markdown.d.ts +1 -0
- package/dist/serve/markdown.d.ts.map +1 -1
- package/dist/serve/markdown.js +9 -0
- package/dist/serve/markdown.js.map +1 -1
- package/dist/skill-scripts/shared/root.js +1 -1
- package/dist-web/assets/{arc-xFaTbwSI.js → arc-ClZ3judi.js} +1 -1
- package/dist-web/assets/{architectureDiagram-3BPJPVTR-Xz0i2dF0.js → architectureDiagram-3BPJPVTR-BRD031Y3.js} +1 -1
- package/dist-web/assets/{blockDiagram-GPEHLZMM-BdKR7Rki.js → blockDiagram-GPEHLZMM-CfaXqzcG.js} +1 -1
- package/dist-web/assets/{c4Diagram-AAUBKEIU-DAayjcH7.js → c4Diagram-AAUBKEIU-D9Os_mjw.js} +1 -1
- package/dist-web/assets/channel-CJXJUs_u.js +1 -0
- package/dist-web/assets/{chunk-2J33WTMH-D4OfjzqG.js → chunk-2J33WTMH-CScaCetR.js} +1 -1
- package/dist-web/assets/{chunk-4BX2VUAB-Ds6twHLE.js → chunk-4BX2VUAB-C_0wTF2D.js} +1 -1
- package/dist-web/assets/{chunk-55IACEB6-CoVGj0A6.js → chunk-55IACEB6-BU0yxIXn.js} +1 -1
- package/dist-web/assets/{chunk-727SXJPM--kGZcEL_.js → chunk-727SXJPM-CEYPnfpF.js} +1 -1
- package/dist-web/assets/{chunk-AQP2D5EJ-dK8X6Knv.js → chunk-AQP2D5EJ-3Wp97XA5.js} +1 -1
- package/dist-web/assets/{chunk-FMBD7UC4-CIkH8fwk.js → chunk-FMBD7UC4-B_iff0-_.js} +1 -1
- package/dist-web/assets/{chunk-ND2GUHAM-BqrY0fV2.js → chunk-ND2GUHAM-C9bo0MIV.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-Bx8TPE1r.js → chunk-QZHKN3VN-DuWFBza0.js} +1 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-DSnAL-3H.js +1 -0
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-DSnAL-3H.js +1 -0
- package/dist-web/assets/{cose-bilkent-S5V4N54A-PyZxi1yI.js → cose-bilkent-S5V4N54A-BvbHJ-Zg.js} +1 -1
- package/dist-web/assets/{dagre-BM42HDAG-yXPAIOqC.js → dagre-BM42HDAG-D6Et-JgF.js} +1 -1
- package/dist-web/assets/{diagram-2AECGRRQ-DDkzzAIo.js → diagram-2AECGRRQ-CNCYsbbA.js} +1 -1
- package/dist-web/assets/{diagram-5GNKFQAL-BD_aXIRb.js → diagram-5GNKFQAL-CxRozyyO.js} +1 -1
- package/dist-web/assets/{diagram-KO2AKTUF-rntOw_IG.js → diagram-KO2AKTUF-IZlLB90E.js} +1 -1
- package/dist-web/assets/{diagram-LMA3HP47-BAB3HQQs.js → diagram-LMA3HP47-CrSB8ihh.js} +1 -1
- package/dist-web/assets/{diagram-OG6HWLK6-CwTzozdw.js → diagram-OG6HWLK6-tCjUSZwa.js} +1 -1
- package/dist-web/assets/{erDiagram-TEJ5UH35-BW-Ha54s.js → erDiagram-TEJ5UH35-JLv81E72.js} +1 -1
- package/dist-web/assets/{flowDiagram-I6XJVG4X-AGkxH_mx.js → flowDiagram-I6XJVG4X-Det1_WLC.js} +1 -1
- package/dist-web/assets/{ganttDiagram-6RSMTGT7--ZS9LkZ2.js → ganttDiagram-6RSMTGT7-Mi2wMw1v.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-D4KfIxPI.js → gitGraphDiagram-PVQCEYII-Ddy55D8B.js} +1 -1
- package/dist-web/assets/{index-uk0kSvAs.js → index-BwABcoi9.js} +4 -4
- package/dist-web/assets/{index-BN30VZfQ.js → index-CEoxzYhV.js} +1 -1
- package/dist-web/assets/{index-IHSMZO2U.js → index-Dw0N6U6b.js} +1 -1
- package/dist-web/assets/{infoDiagram-5YYISTIA-D_Z40zdo.js → infoDiagram-5YYISTIA-vrktGk5U.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-CGzxJ2vS.js → ishikawaDiagram-YF4QCWOH-CATnDGrp.js} +1 -1
- package/dist-web/assets/{journeyDiagram-JHISSGLW-DLtpX1Ni.js → journeyDiagram-JHISSGLW-CEvYFLj2.js} +1 -1
- package/dist-web/assets/{kanban-definition-UN3LZRKU-C4_O7GEC.js → kanban-definition-UN3LZRKU-_6lp0n4D.js} +1 -1
- package/dist-web/assets/{linear-BoMijEDN.js → linear-C1oEZdKm.js} +1 -1
- package/dist-web/assets/{mermaid.core-B7AM5BmV.js → mermaid.core-bqVxqCbb.js} +4 -4
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-b8o1AjHb.js → mindmap-definition-RKZ34NQL-DxAus_u1.js} +1 -1
- package/dist-web/assets/{pieDiagram-4H26LBE5-D7BGsvW4.js → pieDiagram-4H26LBE5-Bqjkt8dx.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-BOsVmBQ_.js → quadrantDiagram-W4KKPZXB-BoZHhSII.js} +1 -1
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-BwuELhlD.js → requirementDiagram-4Y6WPE33-D0rcLxmM.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-5OEKKPKP-DDoQnSJz.js → sankeyDiagram-5OEKKPKP-CWShHxDe.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-3UESZ5HK-Dto9SgGd.js → sequenceDiagram-3UESZ5HK-T8LE2GTr.js} +1 -1
- package/dist-web/assets/{stateDiagram-AJRCARHV-Dx0qn_5x.js → stateDiagram-AJRCARHV-a8e0ie70.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BmYjmWHJ.js +1 -0
- package/dist-web/assets/{timeline-definition-PNZ67QCA-Dbhzc3Xh.js → timeline-definition-PNZ67QCA-Fn2xKeuG.js} +1 -1
- package/dist-web/assets/{vennDiagram-CIIHVFJN-BEUtD6fT.js → vennDiagram-CIIHVFJN-CdlvIB8X.js} +1 -1
- package/dist-web/assets/{wardley-L42UT6IY-CqNfcWnP.js → wardley-L42UT6IY-9pciD2XX.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-Co_bCOQZ.js → wardleyDiagram-YWT4CUSO-BK6qNOxa.js} +1 -1
- package/dist-web/assets/{xychartDiagram-2RQKCTM6-CguzZIIB.js → xychartDiagram-2RQKCTM6-QjMTFvse.js} +1 -1
- package/dist-web/index.html +1 -1
- package/package.json +1 -1
- package/templates/harness/skills/st-create-plan/scripts/find-strikethroo-root.cjs +1 -1
- package/templates/harness/skills/st-create-plan/scripts/get-next-plan-id.cjs +1 -1
- package/templates/harness/skills/st-execute-blueprint/SKILL.md +1 -1
- package/templates/harness/skills/st-execute-blueprint/scripts/check-phase-readiness.cjs +1 -1
- package/templates/harness/skills/st-execute-blueprint/scripts/create-feature-branch.cjs +1 -1
- package/templates/harness/skills/st-execute-blueprint/scripts/find-strikethroo-root.cjs +1 -1
- package/templates/harness/skills/st-execute-blueprint/scripts/validate-plan-blueprint.cjs +51 -3
- package/templates/harness/skills/st-execute-task/scripts/check-task-dependencies.cjs +1 -1
- package/templates/harness/skills/st-execute-task/scripts/find-strikethroo-root.cjs +1 -1
- package/templates/harness/skills/st-execute-task/scripts/validate-plan-blueprint.cjs +51 -3
- package/templates/harness/skills/st-full-workflow/SKILL.md +56 -10
- package/templates/harness/skills/st-full-workflow/scripts/check-phase-readiness.cjs +1 -1
- package/templates/harness/skills/st-full-workflow/scripts/create-feature-branch.cjs +1 -1
- package/templates/harness/skills/st-full-workflow/scripts/find-strikethroo-root.cjs +1 -1
- package/templates/harness/skills/st-full-workflow/scripts/get-next-plan-id.cjs +1 -1
- package/templates/harness/skills/st-full-workflow/scripts/get-next-task-id.cjs +1 -1
- package/templates/harness/skills/st-full-workflow/scripts/validate-plan-blueprint.cjs +51 -3
- package/templates/harness/skills/st-generate-tasks/SKILL.md +56 -11
- package/templates/harness/skills/st-generate-tasks/scripts/find-strikethroo-root.cjs +1 -1
- package/templates/harness/skills/st-generate-tasks/scripts/get-next-task-id.cjs +1 -1
- package/templates/harness/skills/st-generate-tasks/scripts/validate-plan-blueprint.cjs +51 -3
- package/templates/harness/skills/st-refine-plan/scripts/find-strikethroo-root.cjs +1 -1
- package/templates/harness/skills/st-refine-plan/scripts/validate-plan-blueprint.cjs +51 -3
- package/templates/strikethroo/README.md +1 -1
- package/templates/strikethroo/config/hooks/POST_TASK_GENERATION_ALL.md +3 -13
- package/templates/strikethroo/config/templates/TASK_TEMPLATE.md +1 -0
- package/dist-web/assets/channel-Cmb5ngmL.js +0 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-Dh2pEfOy.js +0 -1
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-Dh2pEfOy.js +0 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-qo6que2v.js +0 -1
|
@@ -39,7 +39,7 @@ var path4 = __toESM(require("path"));
|
|
|
39
39
|
// src/skill-scripts/shared/root.ts
|
|
40
40
|
var fs = __toESM(require("fs"));
|
|
41
41
|
var path = __toESM(require("path"));
|
|
42
|
-
var EXPECTED_SCHEMA = true ?
|
|
42
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
43
43
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
44
44
|
try {
|
|
45
45
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -242,6 +242,12 @@ var resolvePlan = (input, startPath = process.cwd()) => {
|
|
|
242
242
|
return resolveByIdInAncestry(planId, startPath);
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
// src/skill-scripts/shared/task-file.ts
|
|
246
|
+
var extractFrontmatter = (content) => {
|
|
247
|
+
const match = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
248
|
+
return match && match[1] ? match[1] : null;
|
|
249
|
+
};
|
|
250
|
+
|
|
245
251
|
// src/skill-scripts/validate-plan-blueprint.ts
|
|
246
252
|
var VALID_FIELDS = [
|
|
247
253
|
"planFile",
|
|
@@ -249,8 +255,47 @@ var VALID_FIELDS = [
|
|
|
249
255
|
"taskCount",
|
|
250
256
|
"blueprintExists",
|
|
251
257
|
"strikethrooRoot",
|
|
252
|
-
"planId"
|
|
258
|
+
"planId",
|
|
259
|
+
"complexityScoresValid",
|
|
260
|
+
"invalidComplexityTasks"
|
|
253
261
|
];
|
|
262
|
+
var validateComplexityScores = (planDir) => {
|
|
263
|
+
const tasksDir = path4.join(planDir, "tasks");
|
|
264
|
+
if (!fs4.existsSync(tasksDir)) return [];
|
|
265
|
+
let files;
|
|
266
|
+
try {
|
|
267
|
+
if (!fs4.lstatSync(tasksDir).isDirectory()) return [];
|
|
268
|
+
files = fs4.readdirSync(tasksDir).filter((f) => f.endsWith(".md")).sort();
|
|
269
|
+
} catch (_err) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
const invalid = [];
|
|
273
|
+
for (const file of files) {
|
|
274
|
+
let content;
|
|
275
|
+
try {
|
|
276
|
+
content = fs4.readFileSync(path4.join(tasksDir, file), "utf8");
|
|
277
|
+
} catch (_err) {
|
|
278
|
+
invalid.push(`${file}: unreadable`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const frontmatter = extractFrontmatter(content);
|
|
282
|
+
const match = frontmatter ? frontmatter.match(/^\s*complexity_score\s*:\s*(.*?)\s*(?:#.*)?$/im) : null;
|
|
283
|
+
if (!match || match[1] === void 0) {
|
|
284
|
+
invalid.push(`${file}: missing complexity_score`);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const raw = match[1].trim();
|
|
288
|
+
if (!/^\d+$/.test(raw)) {
|
|
289
|
+
invalid.push(`${file}: non-integer complexity_score "${raw}"`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const parsed = Number.parseInt(raw, 10);
|
|
293
|
+
if (parsed < 1 || parsed > 10) {
|
|
294
|
+
invalid.push(`${file}: complexity_score ${parsed} out of range 1-10`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return invalid;
|
|
298
|
+
};
|
|
254
299
|
var countTasks = (planDir) => {
|
|
255
300
|
const tasksDir = path4.join(planDir, "tasks");
|
|
256
301
|
if (!fs4.existsSync(tasksDir)) return 0;
|
|
@@ -324,13 +369,16 @@ var main = () => {
|
|
|
324
369
|
}
|
|
325
370
|
process.exit(1);
|
|
326
371
|
}
|
|
372
|
+
const invalidComplexity = validateComplexityScores(resolved.planDir);
|
|
327
373
|
const result = {
|
|
328
374
|
planFile: resolved.planFile,
|
|
329
375
|
planDir: resolved.planDir,
|
|
330
376
|
strikethrooRoot: resolved.strikethrooRoot,
|
|
331
377
|
planId: resolved.planId,
|
|
332
378
|
taskCount: countTasks(resolved.planDir),
|
|
333
|
-
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no"
|
|
379
|
+
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no",
|
|
380
|
+
complexityScoresValid: invalidComplexity.length === 0 ? "yes" : "no",
|
|
381
|
+
invalidComplexityTasks: invalidComplexity.join("; ")
|
|
334
382
|
};
|
|
335
383
|
if (fieldName) {
|
|
336
384
|
if (!VALID_FIELDS.includes(fieldName)) {
|
|
@@ -222,13 +222,48 @@ For each task, identify:
|
|
|
222
222
|
|
|
223
223
|
A task B depends on A if B requires A's output or artifacts, modifies code created by A, or tests functionality implemented by A. Validate that the final dependency graph is acyclic.
|
|
224
224
|
|
|
225
|
-
#### 7.
|
|
225
|
+
#### 7. Complexity analysis
|
|
226
|
+
|
|
227
|
+
For every candidate task, assign a `complexity_score` (integer 1–10) before
|
|
228
|
+
writing any file. Base the score on these four dimensions:
|
|
229
|
+
|
|
230
|
+
| Score | Skill breadth | Acceptance-criteria clarity | Integration surface | Decomposition depth |
|
|
231
|
+
| --- | --- | --- | --- | --- |
|
|
232
|
+
| 1–3 | One well-known skill | Criteria are concrete and observable | None or a single file/module | No further split possible |
|
|
233
|
+
| 4–5 | One primary skill plus a familiar adjacent skill | Criteria are clear with few edge cases | One component or API boundary | Already atomic |
|
|
234
|
+
| 6–7 | Two distinct skills, or one skill with ambiguous requirements | Criteria need clarification or have multiple edge cases | Multiple components or contracts | Could still be split |
|
|
235
|
+
| 8–10 | Three or more skills, or cross-cutting design decisions | Criteria are vague, unknown, or depend on unresolved choices | Wide integration surface or external systems | Must be decomposed further |
|
|
236
|
+
|
|
237
|
+
**Pre-emission sanity rules** — apply these before any task is written:
|
|
238
|
+
|
|
239
|
+
- 3+ skills assigned → split the task into smaller tasks, each with 1–2 skills.
|
|
240
|
+
- Vague acceptance criteria → sharpen them until they include at least one
|
|
241
|
+
concrete, runnable verification step.
|
|
242
|
+
- Trivially small adjacent tasks → merge them into a single task.
|
|
243
|
+
- Score ≥ 8 → decompose further; do not emit as-is.
|
|
244
|
+
- Score 6–7 → either sharpen criteria or split; do not emit without an
|
|
245
|
+
explicit reason.
|
|
246
|
+
|
|
247
|
+
**Required frontmatter:**
|
|
248
|
+
|
|
249
|
+
- Every emitted task MUST include `complexity_score` (integer 1–10).
|
|
250
|
+
- Optionally include `complexity_notes` when the score needs justification,
|
|
251
|
+
such as "Ambiguous API contract" or "Decomposed from a higher-score parent".
|
|
252
|
+
|
|
253
|
+
**Loop-back rule:**
|
|
254
|
+
|
|
255
|
+
After applying split, sharpen, or merge, re-run dependency analysis and
|
|
256
|
+
re-score the adjusted tasks. Repeat this loop no more than three times. If
|
|
257
|
+
complexity is still unresolved after three passes, stop and surface the
|
|
258
|
+
blocker to the user.
|
|
259
|
+
|
|
260
|
+
#### 8. Allocate task IDs
|
|
226
261
|
|
|
227
262
|
Run `scripts/get-next-task-id.cjs <plan-id>` to obtain the first available task ID. Allocate subsequent IDs by incrementing in-process. Use the unpadded integer in the task frontmatter `id` field and the zero-padded form (`{padded-id}--{slug}`) for the filename.
|
|
228
263
|
|
|
229
264
|
The slug derives from a short task title: lowercase, alphanumeric and hyphens only, collapsed, trimmed.
|
|
230
265
|
|
|
231
|
-
####
|
|
266
|
+
#### 9. Emit the task files
|
|
232
267
|
|
|
233
268
|
Write each task to:
|
|
234
269
|
|
|
@@ -246,11 +281,15 @@ including required frontmatter fields:
|
|
|
246
281
|
- `created` (YYYY-MM-DD)
|
|
247
282
|
- `skills` (array of 1–2 kebab-case skills)
|
|
248
283
|
|
|
249
|
-
|
|
284
|
+
Required additional frontmatter:
|
|
285
|
+
|
|
286
|
+
- `complexity_score` (integer 1–10, required on every emitted task)
|
|
287
|
+
|
|
288
|
+
Optional frontmatter:
|
|
250
289
|
|
|
251
|
-
- `
|
|
252
|
-
|
|
253
|
-
|
|
290
|
+
- `complexity_notes` (string) — include when the score needs justification,
|
|
291
|
+
such as "Decomposed from a cross-cutting parent task" or "Ambiguous API
|
|
292
|
+
contract".
|
|
254
293
|
|
|
255
294
|
The body sections (Objective, Skills Required, Acceptance Criteria, Technical
|
|
256
295
|
Requirements, Input Dependencies, Output Artifacts, Implementation Notes)
|
|
@@ -258,7 +297,7 @@ must be filled with task-specific content. Place detailed implementation
|
|
|
258
297
|
guidance inside a `<details>` block under "Implementation Notes" — write it
|
|
259
298
|
so a non-thinking LLM could execute the task from that section alone.
|
|
260
299
|
|
|
261
|
-
####
|
|
300
|
+
#### 10. Validation checklist
|
|
262
301
|
|
|
263
302
|
Before declaring task generation complete, verify:
|
|
264
303
|
|
|
@@ -276,16 +315,23 @@ Before declaring task generation complete, verify:
|
|
|
276
315
|
- Minimization applied (20–30% reduction target).
|
|
277
316
|
- Test tasks focus on business logic, not framework functionality.
|
|
278
317
|
- No gold-plating: only plan requirements are addressed.
|
|
318
|
+
- After writing the task files, run
|
|
319
|
+
`scripts/validate-plan-blueprint.cjs <plan-id> complexityScoresValid`. Stop
|
|
320
|
+
unless it prints `yes`; if it prints `no`, run
|
|
321
|
+
`scripts/validate-plan-blueprint.cjs <plan-id> invalidComplexityTasks` to see
|
|
322
|
+
which files are missing, non-integer, or out-of-range, fix them, and re-run.
|
|
323
|
+
Every generated task must carry an integer `complexity_score` from 1 to 10.
|
|
324
|
+
- Add `complexity_notes` only when a score needs explanation (typically atomic
|
|
325
|
+
tasks scoring greater than 4).
|
|
279
326
|
|
|
280
|
-
####
|
|
327
|
+
#### 11. Run the POST_TASK_GENERATION_ALL hook
|
|
281
328
|
|
|
282
329
|
Read `<root>/config/hooks/POST_TASK_GENERATION_ALL.md` and follow its instructions. This typically requires:
|
|
283
330
|
|
|
284
|
-
- Sanity-checking complexity.
|
|
285
331
|
- Appending an Execution Blueprint section to the plan document, including a Mermaid dependency diagram and explicit phase groupings.
|
|
286
332
|
- Use `<root>/config/templates/BLUEPRINT_TEMPLATE.md` for structure.
|
|
287
333
|
|
|
288
|
-
####
|
|
334
|
+
#### 12. Emit the Phase 2 structured summary
|
|
289
335
|
|
|
290
336
|
Conclude Phase 2 with exactly this block:
|
|
291
337
|
|
|
@@ -86,7 +86,7 @@ var path3 = __toESM(require("path"));
|
|
|
86
86
|
// src/skill-scripts/shared/root.ts
|
|
87
87
|
var fs = __toESM(require("fs"));
|
|
88
88
|
var path = __toESM(require("path"));
|
|
89
|
-
var EXPECTED_SCHEMA = true ?
|
|
89
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
90
90
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
91
91
|
try {
|
|
92
92
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -54,7 +54,7 @@ var path3 = __toESM(require("path"));
|
|
|
54
54
|
// src/skill-scripts/shared/root.ts
|
|
55
55
|
var fs = __toESM(require("fs"));
|
|
56
56
|
var path = __toESM(require("path"));
|
|
57
|
-
var EXPECTED_SCHEMA = true ?
|
|
57
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
58
58
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
59
59
|
try {
|
|
60
60
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(find_strikethroo_root_exports);
|
|
|
37
37
|
// src/skill-scripts/shared/root.ts
|
|
38
38
|
var fs = __toESM(require("fs"));
|
|
39
39
|
var path = __toESM(require("path"));
|
|
40
|
-
var EXPECTED_SCHEMA = true ?
|
|
40
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
41
41
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
42
42
|
try {
|
|
43
43
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(get_next_plan_id_exports);
|
|
|
37
37
|
// src/skill-scripts/shared/root.ts
|
|
38
38
|
var fs = __toESM(require("fs"));
|
|
39
39
|
var path = __toESM(require("path"));
|
|
40
|
-
var EXPECTED_SCHEMA = true ?
|
|
40
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
41
41
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
42
42
|
try {
|
|
43
43
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -41,7 +41,7 @@ var path3 = __toESM(require("path"));
|
|
|
41
41
|
// src/skill-scripts/shared/root.ts
|
|
42
42
|
var fs = __toESM(require("fs"));
|
|
43
43
|
var path = __toESM(require("path"));
|
|
44
|
-
var EXPECTED_SCHEMA = true ?
|
|
44
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
45
45
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
46
46
|
try {
|
|
47
47
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -39,7 +39,7 @@ var path4 = __toESM(require("path"));
|
|
|
39
39
|
// src/skill-scripts/shared/root.ts
|
|
40
40
|
var fs = __toESM(require("fs"));
|
|
41
41
|
var path = __toESM(require("path"));
|
|
42
|
-
var EXPECTED_SCHEMA = true ?
|
|
42
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
43
43
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
44
44
|
try {
|
|
45
45
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -242,6 +242,12 @@ var resolvePlan = (input, startPath = process.cwd()) => {
|
|
|
242
242
|
return resolveByIdInAncestry(planId, startPath);
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
// src/skill-scripts/shared/task-file.ts
|
|
246
|
+
var extractFrontmatter = (content) => {
|
|
247
|
+
const match = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
248
|
+
return match && match[1] ? match[1] : null;
|
|
249
|
+
};
|
|
250
|
+
|
|
245
251
|
// src/skill-scripts/validate-plan-blueprint.ts
|
|
246
252
|
var VALID_FIELDS = [
|
|
247
253
|
"planFile",
|
|
@@ -249,8 +255,47 @@ var VALID_FIELDS = [
|
|
|
249
255
|
"taskCount",
|
|
250
256
|
"blueprintExists",
|
|
251
257
|
"strikethrooRoot",
|
|
252
|
-
"planId"
|
|
258
|
+
"planId",
|
|
259
|
+
"complexityScoresValid",
|
|
260
|
+
"invalidComplexityTasks"
|
|
253
261
|
];
|
|
262
|
+
var validateComplexityScores = (planDir) => {
|
|
263
|
+
const tasksDir = path4.join(planDir, "tasks");
|
|
264
|
+
if (!fs4.existsSync(tasksDir)) return [];
|
|
265
|
+
let files;
|
|
266
|
+
try {
|
|
267
|
+
if (!fs4.lstatSync(tasksDir).isDirectory()) return [];
|
|
268
|
+
files = fs4.readdirSync(tasksDir).filter((f) => f.endsWith(".md")).sort();
|
|
269
|
+
} catch (_err) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
const invalid = [];
|
|
273
|
+
for (const file of files) {
|
|
274
|
+
let content;
|
|
275
|
+
try {
|
|
276
|
+
content = fs4.readFileSync(path4.join(tasksDir, file), "utf8");
|
|
277
|
+
} catch (_err) {
|
|
278
|
+
invalid.push(`${file}: unreadable`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const frontmatter = extractFrontmatter(content);
|
|
282
|
+
const match = frontmatter ? frontmatter.match(/^\s*complexity_score\s*:\s*(.*?)\s*(?:#.*)?$/im) : null;
|
|
283
|
+
if (!match || match[1] === void 0) {
|
|
284
|
+
invalid.push(`${file}: missing complexity_score`);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const raw = match[1].trim();
|
|
288
|
+
if (!/^\d+$/.test(raw)) {
|
|
289
|
+
invalid.push(`${file}: non-integer complexity_score "${raw}"`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const parsed = Number.parseInt(raw, 10);
|
|
293
|
+
if (parsed < 1 || parsed > 10) {
|
|
294
|
+
invalid.push(`${file}: complexity_score ${parsed} out of range 1-10`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return invalid;
|
|
298
|
+
};
|
|
254
299
|
var countTasks = (planDir) => {
|
|
255
300
|
const tasksDir = path4.join(planDir, "tasks");
|
|
256
301
|
if (!fs4.existsSync(tasksDir)) return 0;
|
|
@@ -324,13 +369,16 @@ var main = () => {
|
|
|
324
369
|
}
|
|
325
370
|
process.exit(1);
|
|
326
371
|
}
|
|
372
|
+
const invalidComplexity = validateComplexityScores(resolved.planDir);
|
|
327
373
|
const result = {
|
|
328
374
|
planFile: resolved.planFile,
|
|
329
375
|
planDir: resolved.planDir,
|
|
330
376
|
strikethrooRoot: resolved.strikethrooRoot,
|
|
331
377
|
planId: resolved.planId,
|
|
332
378
|
taskCount: countTasks(resolved.planDir),
|
|
333
|
-
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no"
|
|
379
|
+
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no",
|
|
380
|
+
complexityScoresValid: invalidComplexity.length === 0 ? "yes" : "no",
|
|
381
|
+
invalidComplexityTasks: invalidComplexity.join("; ")
|
|
334
382
|
};
|
|
335
383
|
if (fieldName) {
|
|
336
384
|
if (!VALID_FIELDS.includes(fieldName)) {
|
|
@@ -160,7 +160,42 @@ A task B depends on A if B requires A's output or artifacts, modifies code
|
|
|
160
160
|
created by A, or tests functionality implemented by A. Validate that the
|
|
161
161
|
final dependency graph is acyclic.
|
|
162
162
|
|
|
163
|
-
### 8.
|
|
163
|
+
### 8. Complexity analysis
|
|
164
|
+
|
|
165
|
+
For every candidate task, assign a `complexity_score` (integer 1–10) before
|
|
166
|
+
writing any file. Base the score on these four dimensions:
|
|
167
|
+
|
|
168
|
+
| Score | Skill breadth | Acceptance-criteria clarity | Integration surface | Decomposition depth |
|
|
169
|
+
| --- | --- | --- | --- | --- |
|
|
170
|
+
| 1–3 | One well-known skill | Criteria are concrete and observable | None or a single file/module | No further split possible |
|
|
171
|
+
| 4–5 | One primary skill plus a familiar adjacent skill | Criteria are clear with few edge cases | One component or API boundary | Already atomic |
|
|
172
|
+
| 6–7 | Two distinct skills, or one skill with ambiguous requirements | Criteria need clarification or have multiple edge cases | Multiple components or contracts | Could still be split |
|
|
173
|
+
| 8–10 | Three or more skills, or cross-cutting design decisions | Criteria are vague, unknown, or depend on unresolved choices | Wide integration surface or external systems | Must be decomposed further |
|
|
174
|
+
|
|
175
|
+
**Pre-emission sanity rules** — apply these before any task is written:
|
|
176
|
+
|
|
177
|
+
- 3+ skills assigned → split the task into smaller tasks, each with 1–2 skills.
|
|
178
|
+
- Vague acceptance criteria → sharpen them until they include at least one
|
|
179
|
+
concrete, runnable verification step.
|
|
180
|
+
- Trivially small adjacent tasks → merge them into a single task.
|
|
181
|
+
- Score ≥ 8 → decompose further; do not emit as-is.
|
|
182
|
+
- Score 6–7 → either sharpen criteria or split; do not emit without an
|
|
183
|
+
explicit reason.
|
|
184
|
+
|
|
185
|
+
**Required frontmatter:**
|
|
186
|
+
|
|
187
|
+
- Every emitted task MUST include `complexity_score` (integer 1–10).
|
|
188
|
+
- Optionally include `complexity_notes` when the score needs justification,
|
|
189
|
+
such as "Ambiguous API contract" or "Decomposed from a higher-score parent".
|
|
190
|
+
|
|
191
|
+
**Loop-back rule:**
|
|
192
|
+
|
|
193
|
+
After applying split, sharpen, or merge, re-run dependency analysis and
|
|
194
|
+
re-score the adjusted tasks. Repeat this loop no more than three times. If
|
|
195
|
+
complexity is still unresolved after three passes, stop and surface the
|
|
196
|
+
blocker to the user.
|
|
197
|
+
|
|
198
|
+
### 9. Allocate task IDs
|
|
164
199
|
|
|
165
200
|
Run `scripts/get-next-task-id.cjs <plan-id>` to obtain the first available
|
|
166
201
|
task ID. Allocate subsequent IDs by incrementing in-process; do not invoke
|
|
@@ -170,7 +205,7 @@ field and the zero-padded form (`{padded-id}--{slug}`) for the filename.
|
|
|
170
205
|
The slug derives from a short task title: lowercase, alphanumeric and
|
|
171
206
|
hyphens only, collapsed, trimmed.
|
|
172
207
|
|
|
173
|
-
###
|
|
208
|
+
### 10. Emit the task files
|
|
174
209
|
|
|
175
210
|
Write each task to:
|
|
176
211
|
|
|
@@ -188,11 +223,15 @@ including required frontmatter fields:
|
|
|
188
223
|
- `created` (YYYY-MM-DD)
|
|
189
224
|
- `skills` (array of 1–2 kebab-case skills)
|
|
190
225
|
|
|
191
|
-
|
|
226
|
+
Required additional frontmatter:
|
|
227
|
+
|
|
228
|
+
- `complexity_score` (integer 1–10, required on every emitted task)
|
|
229
|
+
|
|
230
|
+
Optional frontmatter:
|
|
192
231
|
|
|
193
|
-
- `
|
|
194
|
-
|
|
195
|
-
|
|
232
|
+
- `complexity_notes` (string) — include when the score needs justification,
|
|
233
|
+
such as "Decomposed from a cross-cutting parent task" or "Ambiguous API
|
|
234
|
+
contract".
|
|
196
235
|
|
|
197
236
|
The body sections (Objective, Skills Required, Acceptance Criteria, Technical
|
|
198
237
|
Requirements, Input Dependencies, Output Artifacts, Implementation Notes)
|
|
@@ -200,7 +239,7 @@ must be filled with task-specific content. Place detailed implementation
|
|
|
200
239
|
guidance inside a `<details>` block under "Implementation Notes" — write it
|
|
201
240
|
so a non-thinking LLM could execute the task from that section alone.
|
|
202
241
|
|
|
203
|
-
###
|
|
242
|
+
### 11. Validation checklist
|
|
204
243
|
|
|
205
244
|
Before declaring task generation complete, verify:
|
|
206
245
|
|
|
@@ -218,21 +257,27 @@ Before declaring task generation complete, verify:
|
|
|
218
257
|
- Minimization applied (20–30% reduction target).
|
|
219
258
|
- Test tasks focus on business logic, not framework functionality.
|
|
220
259
|
- No gold-plating: only plan requirements are addressed.
|
|
260
|
+
- After writing the task files, run
|
|
261
|
+
`scripts/validate-plan-blueprint.cjs <plan-id> complexityScoresValid`. Stop
|
|
262
|
+
unless it prints `yes`; if it prints `no`, run
|
|
263
|
+
`scripts/validate-plan-blueprint.cjs <plan-id> invalidComplexityTasks` to see
|
|
264
|
+
which files are missing, non-integer, or out-of-range, fix them, and re-run.
|
|
265
|
+
Every generated task must carry an integer `complexity_score` from 1 to 10.
|
|
266
|
+
- Add `complexity_notes` only when a score needs explanation (typically atomic
|
|
267
|
+
tasks scoring greater than 4).
|
|
221
268
|
|
|
222
|
-
###
|
|
269
|
+
### 12. Run the POST_TASK_GENERATION_ALL hook
|
|
223
270
|
|
|
224
271
|
Read `<root>/config/hooks/POST_TASK_GENERATION_ALL.md` and follow its
|
|
225
272
|
instructions. This typically requires:
|
|
226
273
|
|
|
227
|
-
- Sanity-checking complexity (3+ technologies/skills → split; vague criteria
|
|
228
|
-
→ sharpen; trivially small → merge).
|
|
229
274
|
- Appending an Execution Blueprint section to the plan document, including a
|
|
230
275
|
Mermaid dependency diagram and explicit phase groupings (Phase 1 contains
|
|
231
276
|
zero-dependency tasks; each subsequent phase contains tasks whose
|
|
232
277
|
dependencies all live in earlier phases). Use
|
|
233
278
|
`<root>/config/templates/BLUEPRINT_TEMPLATE.md` for structure.
|
|
234
279
|
|
|
235
|
-
###
|
|
280
|
+
### 13. Emit the structured summary
|
|
236
281
|
|
|
237
282
|
Conclude with exactly this block as the final output:
|
|
238
283
|
|
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(find_strikethroo_root_exports);
|
|
|
37
37
|
// src/skill-scripts/shared/root.ts
|
|
38
38
|
var fs = __toESM(require("fs"));
|
|
39
39
|
var path = __toESM(require("path"));
|
|
40
|
-
var EXPECTED_SCHEMA = true ?
|
|
40
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
41
41
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
42
42
|
try {
|
|
43
43
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -41,7 +41,7 @@ var path3 = __toESM(require("path"));
|
|
|
41
41
|
// src/skill-scripts/shared/root.ts
|
|
42
42
|
var fs = __toESM(require("fs"));
|
|
43
43
|
var path = __toESM(require("path"));
|
|
44
|
-
var EXPECTED_SCHEMA = true ?
|
|
44
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
45
45
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
46
46
|
try {
|
|
47
47
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -39,7 +39,7 @@ var path4 = __toESM(require("path"));
|
|
|
39
39
|
// src/skill-scripts/shared/root.ts
|
|
40
40
|
var fs = __toESM(require("fs"));
|
|
41
41
|
var path = __toESM(require("path"));
|
|
42
|
-
var EXPECTED_SCHEMA = true ?
|
|
42
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
43
43
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
44
44
|
try {
|
|
45
45
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -242,6 +242,12 @@ var resolvePlan = (input, startPath = process.cwd()) => {
|
|
|
242
242
|
return resolveByIdInAncestry(planId, startPath);
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
// src/skill-scripts/shared/task-file.ts
|
|
246
|
+
var extractFrontmatter = (content) => {
|
|
247
|
+
const match = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
248
|
+
return match && match[1] ? match[1] : null;
|
|
249
|
+
};
|
|
250
|
+
|
|
245
251
|
// src/skill-scripts/validate-plan-blueprint.ts
|
|
246
252
|
var VALID_FIELDS = [
|
|
247
253
|
"planFile",
|
|
@@ -249,8 +255,47 @@ var VALID_FIELDS = [
|
|
|
249
255
|
"taskCount",
|
|
250
256
|
"blueprintExists",
|
|
251
257
|
"strikethrooRoot",
|
|
252
|
-
"planId"
|
|
258
|
+
"planId",
|
|
259
|
+
"complexityScoresValid",
|
|
260
|
+
"invalidComplexityTasks"
|
|
253
261
|
];
|
|
262
|
+
var validateComplexityScores = (planDir) => {
|
|
263
|
+
const tasksDir = path4.join(planDir, "tasks");
|
|
264
|
+
if (!fs4.existsSync(tasksDir)) return [];
|
|
265
|
+
let files;
|
|
266
|
+
try {
|
|
267
|
+
if (!fs4.lstatSync(tasksDir).isDirectory()) return [];
|
|
268
|
+
files = fs4.readdirSync(tasksDir).filter((f) => f.endsWith(".md")).sort();
|
|
269
|
+
} catch (_err) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
const invalid = [];
|
|
273
|
+
for (const file of files) {
|
|
274
|
+
let content;
|
|
275
|
+
try {
|
|
276
|
+
content = fs4.readFileSync(path4.join(tasksDir, file), "utf8");
|
|
277
|
+
} catch (_err) {
|
|
278
|
+
invalid.push(`${file}: unreadable`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const frontmatter = extractFrontmatter(content);
|
|
282
|
+
const match = frontmatter ? frontmatter.match(/^\s*complexity_score\s*:\s*(.*?)\s*(?:#.*)?$/im) : null;
|
|
283
|
+
if (!match || match[1] === void 0) {
|
|
284
|
+
invalid.push(`${file}: missing complexity_score`);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const raw = match[1].trim();
|
|
288
|
+
if (!/^\d+$/.test(raw)) {
|
|
289
|
+
invalid.push(`${file}: non-integer complexity_score "${raw}"`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const parsed = Number.parseInt(raw, 10);
|
|
293
|
+
if (parsed < 1 || parsed > 10) {
|
|
294
|
+
invalid.push(`${file}: complexity_score ${parsed} out of range 1-10`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return invalid;
|
|
298
|
+
};
|
|
254
299
|
var countTasks = (planDir) => {
|
|
255
300
|
const tasksDir = path4.join(planDir, "tasks");
|
|
256
301
|
if (!fs4.existsSync(tasksDir)) return 0;
|
|
@@ -324,13 +369,16 @@ var main = () => {
|
|
|
324
369
|
}
|
|
325
370
|
process.exit(1);
|
|
326
371
|
}
|
|
372
|
+
const invalidComplexity = validateComplexityScores(resolved.planDir);
|
|
327
373
|
const result = {
|
|
328
374
|
planFile: resolved.planFile,
|
|
329
375
|
planDir: resolved.planDir,
|
|
330
376
|
strikethrooRoot: resolved.strikethrooRoot,
|
|
331
377
|
planId: resolved.planId,
|
|
332
378
|
taskCount: countTasks(resolved.planDir),
|
|
333
|
-
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no"
|
|
379
|
+
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no",
|
|
380
|
+
complexityScoresValid: invalidComplexity.length === 0 ? "yes" : "no",
|
|
381
|
+
invalidComplexityTasks: invalidComplexity.join("; ")
|
|
334
382
|
};
|
|
335
383
|
if (fieldName) {
|
|
336
384
|
if (!VALID_FIELDS.includes(fieldName)) {
|
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(find_strikethroo_root_exports);
|
|
|
37
37
|
// src/skill-scripts/shared/root.ts
|
|
38
38
|
var fs = __toESM(require("fs"));
|
|
39
39
|
var path = __toESM(require("path"));
|
|
40
|
-
var EXPECTED_SCHEMA = true ?
|
|
40
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
41
41
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
42
42
|
try {
|
|
43
43
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -39,7 +39,7 @@ var path4 = __toESM(require("path"));
|
|
|
39
39
|
// src/skill-scripts/shared/root.ts
|
|
40
40
|
var fs = __toESM(require("fs"));
|
|
41
41
|
var path = __toESM(require("path"));
|
|
42
|
-
var EXPECTED_SCHEMA = true ?
|
|
42
|
+
var EXPECTED_SCHEMA = true ? 3 : 3;
|
|
43
43
|
var isValidStrikethrooRoot = (strikethrooPath) => {
|
|
44
44
|
try {
|
|
45
45
|
if (!fs.existsSync(strikethrooPath)) return false;
|
|
@@ -242,6 +242,12 @@ var resolvePlan = (input, startPath = process.cwd()) => {
|
|
|
242
242
|
return resolveByIdInAncestry(planId, startPath);
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
// src/skill-scripts/shared/task-file.ts
|
|
246
|
+
var extractFrontmatter = (content) => {
|
|
247
|
+
const match = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
248
|
+
return match && match[1] ? match[1] : null;
|
|
249
|
+
};
|
|
250
|
+
|
|
245
251
|
// src/skill-scripts/validate-plan-blueprint.ts
|
|
246
252
|
var VALID_FIELDS = [
|
|
247
253
|
"planFile",
|
|
@@ -249,8 +255,47 @@ var VALID_FIELDS = [
|
|
|
249
255
|
"taskCount",
|
|
250
256
|
"blueprintExists",
|
|
251
257
|
"strikethrooRoot",
|
|
252
|
-
"planId"
|
|
258
|
+
"planId",
|
|
259
|
+
"complexityScoresValid",
|
|
260
|
+
"invalidComplexityTasks"
|
|
253
261
|
];
|
|
262
|
+
var validateComplexityScores = (planDir) => {
|
|
263
|
+
const tasksDir = path4.join(planDir, "tasks");
|
|
264
|
+
if (!fs4.existsSync(tasksDir)) return [];
|
|
265
|
+
let files;
|
|
266
|
+
try {
|
|
267
|
+
if (!fs4.lstatSync(tasksDir).isDirectory()) return [];
|
|
268
|
+
files = fs4.readdirSync(tasksDir).filter((f) => f.endsWith(".md")).sort();
|
|
269
|
+
} catch (_err) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
const invalid = [];
|
|
273
|
+
for (const file of files) {
|
|
274
|
+
let content;
|
|
275
|
+
try {
|
|
276
|
+
content = fs4.readFileSync(path4.join(tasksDir, file), "utf8");
|
|
277
|
+
} catch (_err) {
|
|
278
|
+
invalid.push(`${file}: unreadable`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const frontmatter = extractFrontmatter(content);
|
|
282
|
+
const match = frontmatter ? frontmatter.match(/^\s*complexity_score\s*:\s*(.*?)\s*(?:#.*)?$/im) : null;
|
|
283
|
+
if (!match || match[1] === void 0) {
|
|
284
|
+
invalid.push(`${file}: missing complexity_score`);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const raw = match[1].trim();
|
|
288
|
+
if (!/^\d+$/.test(raw)) {
|
|
289
|
+
invalid.push(`${file}: non-integer complexity_score "${raw}"`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const parsed = Number.parseInt(raw, 10);
|
|
293
|
+
if (parsed < 1 || parsed > 10) {
|
|
294
|
+
invalid.push(`${file}: complexity_score ${parsed} out of range 1-10`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return invalid;
|
|
298
|
+
};
|
|
254
299
|
var countTasks = (planDir) => {
|
|
255
300
|
const tasksDir = path4.join(planDir, "tasks");
|
|
256
301
|
if (!fs4.existsSync(tasksDir)) return 0;
|
|
@@ -324,13 +369,16 @@ var main = () => {
|
|
|
324
369
|
}
|
|
325
370
|
process.exit(1);
|
|
326
371
|
}
|
|
372
|
+
const invalidComplexity = validateComplexityScores(resolved.planDir);
|
|
327
373
|
const result = {
|
|
328
374
|
planFile: resolved.planFile,
|
|
329
375
|
planDir: resolved.planDir,
|
|
330
376
|
strikethrooRoot: resolved.strikethrooRoot,
|
|
331
377
|
planId: resolved.planId,
|
|
332
378
|
taskCount: countTasks(resolved.planDir),
|
|
333
|
-
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no"
|
|
379
|
+
blueprintExists: checkBlueprintExists(resolved.planFile) ? "yes" : "no",
|
|
380
|
+
complexityScoresValid: invalidComplexity.length === 0 ? "yes" : "no",
|
|
381
|
+
invalidComplexityTasks: invalidComplexity.join("; ")
|
|
334
382
|
};
|
|
335
383
|
if (fieldName) {
|
|
336
384
|
if (!VALID_FIELDS.includes(fieldName)) {
|
|
@@ -4,4 +4,4 @@ This directory contains AI-assisted task management files for this project.
|
|
|
4
4
|
|
|
5
5
|
Managed by the [Strikethroo](https://www.github.com/e0ipso/strikethroo) project.
|
|
6
6
|
|
|
7
|
-
**Documentation**: https://
|
|
7
|
+
**Documentation**: https://strikethroo.canpicasoft.com
|