@planu/cli 3.9.11 → 3.9.13

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/config/criteria-injection-rules.json +1 -1
  3. package/dist/config/dor-dod-items.json +3 -3
  4. package/dist/config/hook-templates/planu-spec-sanctity.sh +14 -5
  5. package/dist/config/server-instructions.js +1 -1
  6. package/dist/config/server-instructions.ts +1 -1
  7. package/dist/config/skill-templates/planu-new-spec.md +1 -1
  8. package/dist/config/skill-templates/planu-resume-work.md +1 -1
  9. package/dist/config/subagent-templates/planu-readiness-auditor.md +3 -3
  10. package/dist/config/subagent-templates/planu-spec-implementer.md +1 -1
  11. package/dist/config/workflow-conventions-catalog.json +3 -3
  12. package/dist/core/spec-api.js +1 -1
  13. package/dist/engine/agent-generator/builders.js +1 -1
  14. package/dist/engine/autopilot/bootstrap.js +0 -138
  15. package/dist/engine/autopilot/handlers-b2.d.ts +2 -2
  16. package/dist/engine/autopilot/handlers-b2.js +15 -19
  17. package/dist/engine/ci-generator/local-script.js +4 -4
  18. package/dist/engine/ci-generator/planu-steps.js +4 -5
  19. package/dist/engine/compliance/auto-remediator.js +0 -1
  20. package/dist/engine/drift/violation-resolver.js +0 -1
  21. package/dist/engine/health/auto-fixer.js +1 -33
  22. package/dist/engine/housekeeping/ephemeral-artifacts-cleaner.d.ts +2 -2
  23. package/dist/engine/housekeeping/ephemeral-artifacts-cleaner.js +7 -28
  24. package/dist/engine/living-spec-analyzer.js +3 -34
  25. package/dist/engine/living-specs/index.d.ts +2 -2
  26. package/dist/engine/living-specs/index.js +10 -35
  27. package/dist/engine/planu-config-writer.js +1 -1
  28. package/dist/engine/progress-writer.d.ts +2 -2
  29. package/dist/engine/progress-writer.js +2 -2
  30. package/dist/engine/scan-project/index.js +2 -2
  31. package/dist/engine/skill-generator/skills-content.js +1 -1
  32. package/dist/engine/spec-format/lean-technical-generator.d.ts +1 -1
  33. package/dist/engine/spec-format/lean-technical-generator.js +2 -2
  34. package/dist/engine/spec-format/technical-md-populator.d.ts +1 -1
  35. package/dist/engine/spec-format/technical-md-populator.js +2 -2
  36. package/dist/engine/spec-migrator/drift-detector.js +1 -1
  37. package/dist/engine/spec-migrator/lean-migration.js +5 -4
  38. package/dist/engine/spec-migrator/planu-root-cleaner.js +1 -1
  39. package/dist/engine/spec-registry/packager.d.ts +1 -1
  40. package/dist/engine/spec-registry/packager.js +2 -2
  41. package/dist/engine/spec-registry/validator.js +1 -2
  42. package/dist/engine/spec-splitter.js +2 -2
  43. package/dist/engine/spec-summary-html/report-renderer.d.ts +3 -4
  44. package/dist/engine/spec-summary-html/report-renderer.js +6 -135
  45. package/dist/engine/spec-summary-html.js +1 -1
  46. package/dist/engine/universal-rules/rules/planu-english-specs.js +4 -6
  47. package/dist/server/routes/specs.js +1 -1
  48. package/dist/tools/create-spec/autopilot-analyzer.js +1 -1
  49. package/dist/tools/create-spec/spec-builder.js +1 -1
  50. package/dist/tools/decompose-spec.js +1 -1
  51. package/dist/tools/git/pr-ops.js +2 -2
  52. package/dist/tools/git/sync-ops.js +1 -1
  53. package/dist/tools/reconcile-spec-living-handler.js +1 -1
  54. package/dist/tools/reconcile-spec.js +1 -1
  55. package/dist/tools/registry/install.js +27 -29
  56. package/dist/tools/reverse-engineer/handler.js +1 -1
  57. package/dist/tools/schemas/registry.js +1 -1
  58. package/dist/tools/spec-coverage.js +2 -2
  59. package/dist/tools/spec-templates.d.ts +1 -1
  60. package/dist/tools/spec-templates.js +17 -9
  61. package/dist/tools/tool-registry/group-infra.js +1 -1
  62. package/dist/tools/tool-registry/group-platform.js +1 -1
  63. package/dist/tools/update-status/index.js +1 -1
  64. package/dist/types/impact-detection.d.ts +6 -0
  65. package/dist/types/index.d.ts +0 -1
  66. package/dist/types/index.js +0 -1
  67. package/dist/types/spec-templates.d.ts +3 -5
  68. package/package.json +7 -7
  69. package/src/i18n/messages/en.json +3 -3
  70. package/src/i18n/messages/es.json +3 -3
  71. package/src/i18n/messages/pt.json +3 -3
  72. package/dist/engine/implementation-brief/convention-extractor.d.ts +0 -5
  73. package/dist/engine/implementation-brief/convention-extractor.js +0 -75
  74. package/dist/engine/implementation-brief/extension-points.d.ts +0 -2
  75. package/dist/engine/implementation-brief/extension-points.js +0 -32
  76. package/dist/engine/implementation-brief/generator.d.ts +0 -3
  77. package/dist/engine/implementation-brief/generator.js +0 -139
  78. package/dist/engine/implementation-brief/helpers-scanner.d.ts +0 -3
  79. package/dist/engine/implementation-brief/helpers-scanner.js +0 -163
  80. package/dist/engine/implementation-brief/test-pattern-matcher.d.ts +0 -3
  81. package/dist/engine/implementation-brief/test-pattern-matcher.js +0 -90
  82. package/dist/engine/risk-analyzer/risk-generator.d.ts +0 -6
  83. package/dist/engine/risk-analyzer/risk-generator.js +0 -94
  84. package/dist/types/implementation-brief.d.ts +0 -41
  85. package/dist/types/implementation-brief.js +0 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [3.9.12] - 2026-05-19
2
+
3
+ **Tarball SHA-256:** `cd07a22fdfc0c982726a918c1e47f147ca300ecad710e8377f1751ef993fea60`
4
+
5
+ ### Bug Fixes
6
+ - fix(specs): purge legacy spec artifact writers
7
+
8
+ ### Chores
9
+ - chore(claude): reconcile typescript skill asset
10
+ - chore(claude): remove unavailable skill artifact
11
+
12
+
1
13
  ## [3.9.11] - 2026-05-17
2
14
 
3
15
  **Tarball SHA-256:** `a201430a93ae5f87af8322409c328266c29b340e890eb2fa49c7181da32886d3`
@@ -3,7 +3,7 @@
3
3
  "rules": [
4
4
  {
5
5
  "id": "eu-ai-act-53-1a",
6
- "criterion": "GIVEN the feature uses a foundation model WHEN the spec is implemented THEN technical.md documents: model name, provider, and access date — EU AI Act Article 53(1)(a)",
6
+ "criterion": "GIVEN the feature uses a foundation model WHEN the spec is implemented THEN spec.md documents in its inline Technical section: model name, provider, and access date — EU AI Act Article 53(1)(a)",
7
7
  "requiresTags": ["llm", "foundation-model", "ai-feature"],
8
8
  "requiresTargets": [],
9
9
  "requiresScopes": []
@@ -42,7 +42,7 @@
42
42
  {
43
43
  "id": "dor-5",
44
44
  "kind": "dor",
45
- "description": "HU.md file exists and is complete",
45
+ "description": "spec.md file exists and is complete",
46
46
  "category": "spec",
47
47
  "required": true,
48
48
  "autoCheck": true,
@@ -52,7 +52,7 @@
52
52
  {
53
53
  "id": "dor-6",
54
54
  "kind": "dor",
55
- "description": "FICHA-TECNICA.md file exists and is complete",
55
+ "description": "spec.md inline Technical section is complete",
56
56
  "category": "design",
57
57
  "required": true,
58
58
  "autoCheck": true,
@@ -194,7 +194,7 @@
194
194
  {
195
195
  "id": "dod-7",
196
196
  "kind": "dod",
197
- "description": "Documentation updated (HU.md, FICHA-TECNICA.md, PLAN.md)",
197
+ "description": "Documentation updated in spec.md",
198
198
  "category": "docs",
199
199
  "required": false,
200
200
  "autoCheck": false,
@@ -20,9 +20,7 @@ set -euo pipefail
20
20
  # ---------------------------------------------------------------------------
21
21
  ALLOWED_FILES=(
22
22
  "spec.md"
23
- "technical.md"
24
23
  "progress.json"
25
- "implementation-brief.md"
26
24
  )
27
25
 
28
26
  # ---------------------------------------------------------------------------
@@ -79,6 +77,18 @@ NORMALISED="${NORMALISED#/*/}" # strip any leading absolute segment
79
77
  if [[ "$NORMALISED" =~ ^(.*/)?planu/specs/SPEC-[^/]+/([^/]+)$ ]]; then
80
78
  BASENAME="${BASH_REMATCH[2]}"
81
79
 
80
+ case "$BASENAME" in
81
+ technical.md|progress.md|HU.md|FICHA-TECNICA.md|PROGRESS.md)
82
+ cat >&2 <<EOF
83
+ [planu-spec-sanctity] BLOCKED: "$FILE_PATH" is a legacy spec artifact.
84
+
85
+ Planu specs are unified in spec.md. Put technical planning, file ownership,
86
+ and progress into inline sections: ## Technical, ## Files, and ## Progress.
87
+ EOF
88
+ exit 2
89
+ ;;
90
+ esac
91
+
82
92
  # Check against whitelist
83
93
  for allowed in "${ALLOWED_FILES[@]}"; do
84
94
  if [[ "$BASENAME" == "$allowed" ]]; then
@@ -92,11 +102,10 @@ if [[ "$NORMALISED" =~ ^(.*/)?planu/specs/SPEC-[^/]+/([^/]+)$ ]]; then
92
102
 
93
103
  Allowed files inside planu/specs/SPEC-*/ are:
94
104
  - spec.md
95
- - technical.md
96
105
  - progress.json
97
- - implementation-brief.md
98
106
 
99
- Do NOT create PLAN.md, NOTES.md, ADRs, or other files here.
107
+ Do NOT create technical.md, progress.md, implementation-brief.md, prompt.md,
108
+ risk-register.md, PLAN.md, NOTES.md, ADRs, or other files here.
100
109
  Keep implementation notes in conversation context or in src/.
101
110
  EOF
102
111
  exit 2
@@ -53,7 +53,7 @@ export const SERVER_INSTRUCTIONS = [
53
53
  '4. When presenting lists or results, use plain language headers — not jargon.',
54
54
  '5. If the user seems confused, offer to explain further. Never assume knowledge.',
55
55
  "6. Adapt your language to the user's locale (English, Spanish, or Portuguese).",
56
- ' EXCEPTION: spec.md and technical.md files must ALWAYS be written in English regardless of locale.',
56
+ ' EXCEPTION: spec.md files must ALWAYS be written in English regardless of locale.',
57
57
  ' The user-facing narrative (tool response text) is translated; spec docs are always English.',
58
58
  '',
59
59
  'INTERACTIVE QUESTIONS — HARD GATE enforcement (SPEC-584):',
@@ -54,7 +54,7 @@ export const SERVER_INSTRUCTIONS = [
54
54
  '4. When presenting lists or results, use plain language headers — not jargon.',
55
55
  '5. If the user seems confused, offer to explain further. Never assume knowledge.',
56
56
  "6. Adapt your language to the user's locale (English, Spanish, or Portuguese).",
57
- ' EXCEPTION: spec.md and technical.md files must ALWAYS be written in English regardless of locale.',
57
+ ' EXCEPTION: spec.md files must ALWAYS be written in English regardless of locale.',
58
58
  ' The user-facing narrative (tool response text) is translated; spec docs are always English.',
59
59
  '',
60
60
  'INTERACTIVE QUESTIONS — HARD GATE enforcement (SPEC-584):',
@@ -45,7 +45,7 @@ User: "I want to add Stripe payment support"
45
45
  ## What you get
46
46
 
47
47
  - A complete `spec.md` with GIVEN/WHEN/THEN acceptance criteria
48
- - A `technical.md` with file-level implementation plan
48
+ - Inline `## Technical` and `## Files` sections with file-level implementation plan
49
49
  - Challenge report identifying any gaps found (auto-addressed where possible)
50
50
  - Readiness score — must be ≥ 80 before implementation begins
51
51
 
@@ -25,7 +25,7 @@ Use this skill at the start of a session, or when returning to a project after a
25
25
 
26
26
  1. **Check status** — calls `planu_status` to see all active specs
27
27
  2. **Find in-progress** — identifies specs with status `implementing` or `review`
28
- 3. **Load context** — reads the spec's `spec.md`, `technical.md`, and `progress.json`
28
+ 3. **Load context** — reads the spec's `spec.md` and any inline `## Technical`, `## Files`, and `## Progress` sections
29
29
  4. **Restore session** — calls `restore_session` or `session_checkpoint` to recover the previous session state
30
30
  5. **Report** — summarizes exactly where to resume: which files were being modified, which criteria are still open
31
31
 
@@ -23,12 +23,12 @@ You are a readiness gatekeeper. Your job is to verify that a spec is actionable
23
23
 
24
24
  ## Workflow
25
25
 
26
- 1. **Load spec** — call `list_specs` to find the spec. Read both `spec.md` and `technical.md`.
26
+ 1. **Load spec** — call `list_specs` to find the spec. Read `spec.md`, including inline `## Technical` and `## Files` sections.
27
27
  2. **Check readiness** — call `check_readiness({ specId })`. Record the score and blockers.
28
28
  3. **Quality score** — call `spec_quality_score({ specId })` for a structured breakdown.
29
29
  4. **Size analysis** — call `analyze_spec_size({ specId })` to detect over/under-specified sections.
30
30
  5. **Suggest missing criteria** — call `suggest_criteria({ specId })` for any dimension below threshold.
31
- 6. **Heal docs if needed** — call `heal_spec_docs({ specId })` if `technical.md` has placeholder content.
31
+ 6. **Heal docs if needed** — call `heal_spec_docs({ specId })` if the inline `## Technical` section has placeholder content.
32
32
  7. **Verdict**:
33
33
  - Score ≥ 80 and no blockers → ✅ Ready for approval
34
34
  - Score 60–79 → 🟡 Conditional — list specific improvements required
@@ -54,5 +54,5 @@ Score: XX/100
54
54
 
55
55
  - Never approve a spec with undefined terms in criteria
56
56
  - Every criterion must be independently verifiable by an automated test
57
- - The `technical.md` file must list concrete file paths, not placeholders
57
+ - The inline `## Technical` / `## Files` sections must list concrete file paths, not placeholders
58
58
  - Missing error-path criteria always block readiness
@@ -26,7 +26,7 @@ You are an autonomous implementation agent for Spec Driven Development. Your job
26
26
 
27
27
  ## Workflow
28
28
 
29
- 1. **Read the spec** — call `list_specs` and find the target spec. Read `spec.md` and `technical.md` for full context.
29
+ 1. **Read the spec** — call `list_specs` and find the target spec. Read `spec.md`, including inline `## Technical`, `## Files`, and `## Progress` sections.
30
30
  2. **Verify approval** — confirm spec status is `approved`. If not, call `check_readiness` and stop with a note.
31
31
  3. **Update status to `implementing`** — call `update_status({ specId, status: "implementing" })`.
32
32
  4. **Create a feature branch** — `git checkout -b feat/SPEC-NNN-slug`.
@@ -25,10 +25,10 @@
25
25
  },
26
26
  "propose-spec-split.sh": {
27
27
  "name": "propose-spec-split.sh",
28
- "purpose": "Analyze a spec's HU.md and propose a split when it exceeds the 35-criteria threshold for a single implementation context.",
29
- "triggerCondition": "When a spec has > 35 acceptance criteria in its HU.md. Must run BEFORE starting implementation — never implement XL specs directly.",
28
+ "purpose": "Analyze a spec.md and propose a split when it exceeds the 35-criteria threshold for a single implementation context.",
29
+ "triggerCondition": "When a spec has > 35 acceptance criteria in spec.md. Must run BEFORE starting implementation — never implement XL specs directly.",
30
30
  "expectedOutput": "Proposed sub-spec split with suggested groupings, estimated size per sub-spec, and recommended implementation order.",
31
- "preConditions": ["SPEC-XXX directory exists with HU.md", "HU.md has acceptance criteria section"],
31
+ "preConditions": ["SPEC-XXX directory exists with spec.md", "spec.md has acceptance criteria section"],
32
32
  "whenToUse": "Proactively at spec planning time. The /implement-spec skill should call this automatically for specs > 35 criteria."
33
33
  }
34
34
  },
@@ -45,7 +45,7 @@ export async function createSpec(input) {
45
45
  const specLocation = 'planu/specs';
46
46
  const specDir = resolve(join(projectPath, specLocation, `${specId}-${slug}`));
47
47
  const specPath = join(specDir, 'spec.md');
48
- const technicalPath = join(specDir, 'technical.md');
48
+ const technicalPath = specPath;
49
49
  const now = new Date().toISOString();
50
50
  const spec = {
51
51
  id: specId,
@@ -33,7 +33,7 @@ export function buildSystemPrompt(agentName, role, capabilities, knowledge) {
33
33
  lines.push('');
34
34
  lines.push('## Scope Restrictions');
35
35
  lines.push('- NEVER git commit or git push changes — that is handled by the parent skill');
36
- lines.push('- NEVER update PROGRESS.md or spec tracking files — delegate to parent skill');
36
+ lines.push('- NEVER create or update standalone progress.md/PROGRESS.md tracking files — delegate to parent skill');
37
37
  lines.push('- NEVER run the full CI/CD pipeline — parent skill manages pipeline execution');
38
38
  lines.push('- Focus exclusively on your assigned subtask and report results clearly');
39
39
  return lines.join('\n');
@@ -24,140 +24,6 @@ function registerSessionContextFreshnessListener() {
24
24
  })();
25
25
  });
26
26
  }
27
- /**
28
- * SPEC-586: Fire-and-forget implementation-brief generation + impact detector
29
- * on spec:status:approved. Errors are silently logged and never block the parent tool.
30
- */
31
- function registerImplementationReadyListener() {
32
- onAutopilotEvent('spec:status:approved', (event) => {
33
- if (!event.specId) {
34
- return;
35
- }
36
- const { specId, projectPath, projectId } = event;
37
- void (async () => {
38
- try {
39
- const { glob } = await import('glob');
40
- const [specFiles, techFiles] = await Promise.all([
41
- glob(`planu/specs/${specId}-*/spec.md`, { cwd: projectPath, absolute: true }),
42
- glob(`planu/specs/${specId}-*/technical.md`, { cwd: projectPath, absolute: true }),
43
- ]);
44
- const specPath = specFiles[0];
45
- const technicalPath = techFiles[0];
46
- if (!specPath) {
47
- return;
48
- }
49
- const { readFile } = await import('node:fs/promises');
50
- const specBody = await readFile(specPath, 'utf-8').catch(() => '');
51
- const tagsMatch = /^tags:\s*\[([^\]]*)\]/m.exec(specBody);
52
- const targetMatch = /^target:\s*(.+)$/m.exec(specBody);
53
- const scopeMatch = /^scope:\s*(.+)$/m.exec(specBody);
54
- const tags = tagsMatch?.[1] !== undefined
55
- ? tagsMatch[1].split(',').map((t) => t.trim().replace(/^["']|["']$/g, ''))
56
- : [];
57
- const target = targetMatch?.[1]?.trim() ?? 'backend';
58
- const scope = scopeMatch?.[1]?.trim() ?? 'cross-module';
59
- void projectId;
60
- const { generateImplementationBrief } = await import('../implementation-brief/generator.js');
61
- generateImplementationBrief({
62
- specId,
63
- specPath,
64
- projectPath,
65
- description: specBody,
66
- tags,
67
- target,
68
- scope,
69
- }).catch((err) => {
70
- const msg = err instanceof Error ? err.message : String(err);
71
- console.error(`[autopilot] implementation-brief failed for ${specId}: ${msg}`);
72
- });
73
- if (technicalPath) {
74
- const { runImpactDetector } = await import('../impact-detector/index.js');
75
- runImpactDetector({
76
- specId,
77
- specPath,
78
- technicalPath,
79
- projectPath,
80
- }).catch((err) => {
81
- const msg = err instanceof Error ? err.message : String(err);
82
- console.error(`[autopilot] impact-detector failed for ${specId}: ${msg}`);
83
- });
84
- }
85
- }
86
- catch {
87
- /* best-effort — never surface to user */
88
- }
89
- })();
90
- });
91
- }
92
- /**
93
- * SPEC-627: Fire-and-forget risk-register generation on spec:status:approved.
94
- * Writes planu/specs/SPEC-XXX/risk-register.md from spec metadata.
95
- */
96
- function registerRiskAnalyzerListener() {
97
- onAutopilotEvent('spec:status:approved', (event) => {
98
- if (!event.specId) {
99
- return;
100
- }
101
- const { specId, projectPath } = event;
102
- void (async () => {
103
- try {
104
- const { glob } = await import('glob');
105
- const specFiles = await glob(`planu/specs/${specId}-*/spec.md`, { cwd: projectPath, absolute: true });
106
- const specPath = specFiles[0];
107
- if (!specPath) {
108
- return;
109
- }
110
- const { readFile, writeFile } = await import('node:fs/promises');
111
- const specBody = await readFile(specPath, 'utf-8');
112
- const typeMatch = /^type:\s*(.+)$/m.exec(specBody);
113
- const scopeMatch = /^scope:\s*(.+)$/m.exec(specBody);
114
- const difficultyMatch = /^difficulty:\s*(\d+)/m.exec(specBody);
115
- const riskMatch = /^risk:\s*(.+)$/m.exec(specBody);
116
- const tagsMatch = /^tags:\s*\[([^\]]*)\]/m.exec(specBody);
117
- const tags = tagsMatch?.[1] !== undefined
118
- ? tagsMatch[1].split(',').map((t) => t.trim().replace(/^["']|["']$/g, ''))
119
- : [];
120
- const { generateRiskRegister } = await import('../risk-analyzer/risk-generator.js');
121
- const rawDifficulty = parseInt(difficultyMatch?.[1] ?? '2', 10);
122
- const difficulty = (rawDifficulty >= 1 && rawDifficulty <= 5 ? rawDifficulty : 2);
123
- const rawRisk = riskMatch?.[1]?.trim() ?? 'low';
124
- const riskLevel = (['low', 'medium', 'high', 'critical'].includes(rawRisk) ? rawRisk : 'low');
125
- const ctx = {
126
- specId,
127
- title: specId,
128
- type: (typeMatch?.[1]?.trim() ?? 'feature'),
129
- scope: (scopeMatch?.[1]?.trim() ?? 'feature'),
130
- difficulty,
131
- risk: riskLevel,
132
- tags,
133
- language: 'TypeScript',
134
- framework: null,
135
- dependencyCount: 0,
136
- fileCount: 0,
137
- };
138
- const register = generateRiskRegister(ctx);
139
- const lines = [
140
- `# Risk Register — ${specId}`,
141
- `_Generated ${new Date().toISOString()}_`,
142
- '',
143
- `**Aggregate score:** ${register.aggregateRiskScore}/100`,
144
- '',
145
- '## Risks',
146
- ];
147
- for (const r of register.risks) {
148
- lines.push(`- **${r.category}** (${r.probability}% probability, ${r.impactDays}d impact): ${r.description}`);
149
- lines.push(` Mitigation: ${r.mitigationPlan}`);
150
- }
151
- const { dirname, join } = await import('node:path');
152
- const registerPath = join(dirname(specPath), 'risk-register.md');
153
- await writeFile(registerPath, lines.join('\n'), 'utf-8');
154
- }
155
- catch {
156
- /* best-effort — never surface to user */
157
- }
158
- })();
159
- });
160
- }
161
27
  /**
162
28
  * SPEC-600: Fire-and-forget cascade on approved→implementing.
163
29
  * Auto-recommends model + generates orchestration plan for complex specs.
@@ -260,10 +126,6 @@ export function bootstrapAutopilotHandlers() {
260
126
  }
261
127
  // SPEC-585: Register session-context freshness listener
262
128
  registerSessionContextFreshnessListener();
263
- // SPEC-586: Register implementation-brief + impact-detector on spec:status:approved
264
- registerImplementationReadyListener();
265
- // SPEC-627: Register risk-register generator on spec:status:approved
266
- registerRiskAnalyzerListener();
267
129
  // SPEC-600: Register cascade listener for implementing transition
268
130
  // SPEC-649: registerDoneCascadeListener removed — optimize_context no longer auto-runs on done
269
131
  registerImplementingCascadeListener();
@@ -11,11 +11,11 @@ export declare function injectCriteria(ctx: ActionContext): Promise<ActionResult
11
11
  export declare function rewriteCriteriaEars(ctx: ActionContext): Promise<ActionResult>;
12
12
  /**
13
13
  * verify_spec_compliance: Checks that a spec has spec.md with criteria,
14
- * technical.md with file references, and that those files exist on disk.
14
+ * inline file references in spec.md, and that those files exist on disk.
15
15
  */
16
16
  export declare function verifySpecCompliance(ctx: ActionContext): Promise<ActionResult>;
17
17
  /**
18
- * analyze_code_impact: Counts and categorizes files listed in technical.md.
18
+ * analyze_code_impact: Counts and categorizes files listed in spec.md.
19
19
  * Flags specs with >10 affected files as HIGH IMPACT.
20
20
  */
21
21
  export declare function analyzeCodeImpact(ctx: ActionContext): Promise<ActionResult>;
@@ -21,7 +21,7 @@ async function findSpecDir(projectPath, specId) {
21
21
  const dir = matches[0];
22
22
  return dir !== undefined ? join(specDir, dir) : null;
23
23
  }
24
- /** Extract file path references from technical.md content (backtick-quoted paths with slashes). */
24
+ /** Extract file path references from spec content (backtick-quoted paths with slashes). */
25
25
  function extractFileRefs(content) {
26
26
  const regex = /`([^`]*\/[^`]+\.[a-zA-Z]+)`/g;
27
27
  const seen = new Set();
@@ -149,7 +149,7 @@ export async function rewriteCriteriaEars(ctx) {
149
149
  // ---------------------------------------------------------------------------
150
150
  /**
151
151
  * verify_spec_compliance: Checks that a spec has spec.md with criteria,
152
- * technical.md with file references, and that those files exist on disk.
152
+ * inline file references in spec.md, and that those files exist on disk.
153
153
  */
154
154
  export async function verifySpecCompliance(ctx) {
155
155
  if (!ctx.specId) {
@@ -168,29 +168,25 @@ export async function verifySpecCompliance(ctx) {
168
168
  }
169
169
  const findings = [];
170
170
  const specMdPath = join(specDirPath, 'spec.md');
171
- const technicalMdPath = join(specDirPath, 'technical.md');
171
+ let specContent = '';
172
172
  // Check 1: spec.md has acceptance criteria
173
173
  if (!existsSync(specMdPath)) {
174
174
  findings.push('spec.md is missing');
175
175
  }
176
176
  else {
177
- const specContent = await readFile(specMdPath, 'utf-8');
177
+ specContent = await readFile(specMdPath, 'utf-8');
178
178
  const criteriaCount = (specContent.match(/^\s*-\s+\[[ xX]\]\s+.+$/gm) ?? []).length;
179
179
  if (criteriaCount === 0) {
180
180
  findings.push('spec.md has no acceptance criteria');
181
181
  }
182
182
  }
183
- // Check 2: technical.md with file references
183
+ // Check 2: spec.md inline file references
184
184
  const implementedFiles = [];
185
- if (!existsSync(technicalMdPath)) {
186
- findings.push('technical.md is missing');
187
- }
188
- else {
189
- const techContent = await readFile(technicalMdPath, 'utf-8');
190
- const refs = extractFileRefs(techContent);
185
+ if (specContent.length > 0) {
186
+ const refs = extractFileRefs(specContent);
191
187
  implementedFiles.push(...refs);
192
188
  if (refs.length === 0) {
193
- findings.push('technical.md has no file references');
189
+ findings.push('spec.md inline Technical/Files sections have no file references');
194
190
  }
195
191
  }
196
192
  // Check 3: referenced files exist on disk
@@ -203,7 +199,7 @@ export async function verifySpecCompliance(ctx) {
203
199
  }
204
200
  if (missingFiles.length > 0) {
205
201
  const listed = missingFiles.slice(0, 3).join(', ');
206
- findings.push(`${String(missingFiles.length)} file(s) listed in technical.md not found: ${listed}`);
202
+ findings.push(`${String(missingFiles.length)} file(s) listed in spec.md not found: ${listed}`);
207
203
  }
208
204
  const passed = findings.length === 0;
209
205
  const summary = passed
@@ -215,7 +211,7 @@ export async function verifySpecCompliance(ctx) {
215
211
  // analyze_code_impact
216
212
  // ---------------------------------------------------------------------------
217
213
  /**
218
- * analyze_code_impact: Counts and categorizes files listed in technical.md.
214
+ * analyze_code_impact: Counts and categorizes files listed in spec.md.
219
215
  * Flags specs with >10 affected files as HIGH IMPACT.
220
216
  */
221
217
  export async function analyzeCodeImpact(ctx) {
@@ -233,16 +229,16 @@ export async function analyzeCodeImpact(ctx) {
233
229
  durationMs: 0,
234
230
  };
235
231
  }
236
- const technicalMdPath = join(specDirPath, 'technical.md');
237
- if (!existsSync(technicalMdPath)) {
232
+ const specMdPath = join(specDirPath, 'spec.md');
233
+ if (!existsSync(specMdPath)) {
238
234
  return {
239
235
  success: false,
240
- summary: `analyze_code_impact: technical.md not found for ${ctx.specId}`,
236
+ summary: `analyze_code_impact: spec.md not found for ${ctx.specId}`,
241
237
  durationMs: 0,
242
238
  };
243
239
  }
244
- const techContent = await readFile(technicalMdPath, 'utf-8');
245
- const allFiles = extractFileRefs(techContent);
240
+ const specContent = await readFile(specMdPath, 'utf-8');
241
+ const allFiles = extractFileRefs(specContent);
246
242
  // Breakdown by extension — normalize .test.ts/.test.js as separate categories
247
243
  const breakdown = {};
248
244
  for (const filePath of allFiles) {
@@ -60,8 +60,8 @@ function buildValidateSection() {
60
60
  ' SPEC_NAME=$(basename "$spec_dir")',
61
61
  ' TOTAL=$((TOTAL + 1))',
62
62
  '',
63
- ' SPEC_FILE=$(ls "$spec_dir"spec.md "$spec_dir"HU.md 2>/dev/null | head -1 || echo "")',
64
- ' if [ -z "$SPEC_FILE" ]; then',
63
+ ' SPEC_FILE="$spec_dir/spec.md"',
64
+ ' if [ ! -f "$SPEC_FILE" ]; then',
65
65
  ' echo -e " ${RED}x${RESET} $SPEC_NAME - missing spec.md"',
66
66
  ' ISSUES=$((ISSUES + 1))',
67
67
  ' continue',
@@ -90,8 +90,8 @@ function buildDriftSection() {
90
90
  ' echo -e "${BOLD}Drift Detection${RESET}"',
91
91
  ' CHANGED_FILES=$(git diff HEAD --name-only 2>/dev/null || echo "")',
92
92
  ' for spec_dir in "$SPECS_DIR"/*/; do',
93
- ' [ -f "$spec_dir/technical.md" ] || continue',
94
- ' SPEC_FILES=$(grep -oE \'src/[a-zA-Z0-9_./-]+\' "$spec_dir/technical.md" 2>/dev/null || echo "")',
93
+ ' [ -f "$spec_dir/spec.md" ] || continue',
94
+ ' SPEC_FILES=$(grep -oE \'src/[a-zA-Z0-9_./-]+\' "$spec_dir/spec.md" 2>/dev/null || echo "")',
95
95
  ' for sf in $SPEC_FILES; do',
96
96
  ' if echo "$CHANGED_FILES" | grep -q "$sf"; then',
97
97
  ' SPEC_NAME=$(basename "$spec_dir")',
@@ -113,9 +113,8 @@ function buildDriftStepLines(indent, specsDir) {
113
113
  `${indent} echo "$CHANGED_FILES"`,
114
114
  `${indent} DRIFT_FOUND="false"`,
115
115
  `${indent} for spec_dir in ${specsDir}/*/; do`,
116
- `${indent} if [ -f "$spec_dir/technical.md" ] || [ -f "$spec_dir/FICHA-TECNICA.md" ]; then`,
117
- `${indent} TECH_FILE=$(ls "$spec_dir"technical.md "$spec_dir"FICHA-TECNICA.md 2>/dev/null | head -1)`,
118
- `${indent} SPEC_FILES=$(grep -oE 'src/[a-zA-Z0-9_./-]+' "$TECH_FILE" 2>/dev/null || echo "")`,
116
+ `${indent} if [ -f "$spec_dir/spec.md" ]; then`,
117
+ `${indent} SPEC_FILES=$(grep -oE 'src/[a-zA-Z0-9_./-]+' "$spec_dir/spec.md" 2>/dev/null || echo "")`,
119
118
  `${indent} for sf in $SPEC_FILES; do`,
120
119
  `${indent} if echo "$CHANGED_FILES" | grep -q "$sf"; then`,
121
120
  `${indent} SPEC_NAME=$(basename "$spec_dir")`,
@@ -145,8 +144,8 @@ function buildValidateStepLines(indent, specsDir) {
145
144
  `${indent} [ -d "$spec_dir" ] || continue`,
146
145
  `${indent} SPEC_NAME=$(basename "$spec_dir")`,
147
146
  `${indent} TOTAL=$((TOTAL + 1))`,
148
- `${indent} SPEC_FILE=$(ls "$spec_dir"spec.md "$spec_dir"HU.md 2>/dev/null | head -1)`,
149
- `${indent} if [ -z "$SPEC_FILE" ]; then`,
147
+ `${indent} SPEC_FILE="$spec_dir/spec.md"`,
148
+ `${indent} if [ ! -f "$SPEC_FILE" ]; then`,
150
149
  `${indent} RESULTS+="$SPEC_NAME FAIL missing-spec\\n"`,
151
150
  `${indent} COMMENT+="| $SPEC_NAME | FAIL | missing spec.md |\\n"`,
152
151
  `${indent} ISSUES=$((ISSUES + 1))`,
@@ -48,7 +48,6 @@ async function writeStubSpec(projectPath, specId, title, framework, criteria, dr
48
48
  '',
49
49
  ].join('\n');
50
50
  await writeFile(join(specDir, 'spec.md'), specContent, 'utf-8');
51
- await writeFile(join(specDir, 'progress.md'), `# ${specId} — Progress\n\nstatus: draft\n`, 'utf-8');
52
51
  }
53
52
  // ---------------------------------------------------------------------------
54
53
  // Manual action builders
@@ -43,7 +43,6 @@ async function createFollowUpSpec(projectPath, parentSpecId, reason, dryRun) {
43
43
  '',
44
44
  ].join('\n');
45
45
  await writeFile(join(specDir, 'spec.md'), specContent, 'utf-8');
46
- await writeFile(join(specDir, 'progress.md'), `# ${specId} — Progress\n\nstatus: draft\n`, 'utf-8');
47
46
  }
48
47
  return specId;
49
48
  }
@@ -1,6 +1,4 @@
1
1
  // engine/health/auto-fixer.ts — Auto-fix for health check issues (SPEC-408)
2
- import { mkdir, writeFile, access } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
2
  import { specStore } from '../../storage/index.js';
5
3
  import { computeSpecHealth } from '../validation-loop.js';
6
4
  async function collectHealthIssues(projectPath, projectId) {
@@ -31,30 +29,6 @@ async function collectHealthIssues(projectPath, projectId) {
31
29
  // ---------------------------------------------------------------------------
32
30
  // Fix handlers
33
31
  // ---------------------------------------------------------------------------
34
- async function fixMissingProgress(issue, dryRun) {
35
- const specId = issue.specId;
36
- const progressPath = join(issue.projectPath, 'planu', 'specs', specId, 'progress.md');
37
- try {
38
- await access(progressPath);
39
- return null; // Already exists
40
- }
41
- catch {
42
- // Does not exist — create it
43
- if (!dryRun) {
44
- await mkdir(join(issue.projectPath, 'planu', 'specs', specId), { recursive: true });
45
- const content = `# ${specId} — Progress\n\nstatus: draft\n\n## Tasks\n\n- [ ] Define acceptance criteria\n`;
46
- await writeFile(progressPath, content, 'utf-8');
47
- }
48
- return {
49
- issue: 'missing-progress.md',
50
- action: dryRun
51
- ? 'would create progress.md from template'
52
- : 'created progress.md from template',
53
- affectedItem: specId,
54
- success: true,
55
- };
56
- }
57
- }
58
32
  function buildSkipForIncompleteSpec(specId) {
59
33
  return {
60
34
  issue: `incomplete-spec: ${specId}`,
@@ -96,18 +70,12 @@ function buildSkipForDrift(specId) {
96
70
  * When dryRun=true, reports what would be fixed without writing any files.
97
71
  */
98
72
  export async function autoFixHealth(projectPath, projectId, dryRun = false) {
73
+ void dryRun;
99
74
  const issues = await collectHealthIssues(projectPath, projectId);
100
75
  const fixes = [];
101
76
  const skipped = [];
102
77
  for (const issue of issues) {
103
78
  switch (issue.type) {
104
- case 'missing-progress': {
105
- const fix = await fixMissingProgress(issue, dryRun);
106
- if (fix) {
107
- fixes.push(fix);
108
- }
109
- break;
110
- }
111
79
  case 'incomplete-spec':
112
80
  skipped.push(buildSkipForIncompleteSpec(issue.specId));
113
81
  break;
@@ -1,7 +1,7 @@
1
1
  import type { EphemeralArtifactsResult, CleanEphemeralArtifactsInput } from '../../types/housekeeping.js';
2
2
  /**
3
- * Scan all planu/specs/SPEC-* directories. For each spec whose status is
4
- * `done` or `discarded`, delete (or report) the ephemeral artifact files.
3
+ * Scan all planu/specs/SPEC-* directories and delete (or report) obsolete
4
+ * sidecar artifact files.
5
5
  */
6
6
  export declare function cleanEphemeralArtifacts(input: CleanEphemeralArtifactsInput): Promise<EphemeralArtifactsResult>;
7
7
  //# sourceMappingURL=ephemeral-artifacts-cleaner.d.ts.map