@dzhechkov/p-replicator 1.5.8 → 1.5.12

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 (27) hide show
  1. package/README/ru/html/build.js +11 -3
  2. package/package.json +1 -1
  3. package/src/commands/remove.js +16 -2
  4. package/src/commands/update.js +7 -0
  5. package/templates/.claude/skills/brutal-honesty-review/SKILL.md +18 -1
  6. package/templates/.claude/skills/brutal-honesty-review/evals/brutal-honesty-review.yaml +61 -0
  7. package/templates/.claude/skills/brutal-honesty-review/schemas/output.json +291 -0
  8. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-tests.sh +2 -2
  9. package/templates/.claude/skills/brutal-honesty-review/scripts/validate-config.json +34 -0
  10. package/templates/.claude/skills/cc-toolkit-generator-enhanced/SKILL.md +1 -1
  11. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/06-package-deliver.md +1 -1
  12. package/templates/.claude/skills/goap-research-ed25519/SKILL.md +78 -371
  13. package/templates/.claude/skills/goap-research-ed25519/references/ed25519-verification.md +58 -624
  14. package/templates/.claude/skills/goap-research-ed25519/references/research-actions.md +61 -506
  15. package/templates/.claude/skills/goap-research-ed25519/references/source-evaluation.md +42 -543
  16. package/templates/.claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py +427 -448
  17. package/templates/.claude/skills/goap-research-ed25519/scripts/goap_planner.py +4 -4
  18. package/templates/.claude/skills/goap-research-ed25519/scripts/test_ed25519_verifier.py +112 -0
  19. package/templates/.claude/skills/knowledge-extractor/SKILL.md +1 -1
  20. package/templates/.claude/skills/pipeline-forge/SKILL.md +5 -1
  21. package/templates/.claude/skills/requirements-validator/SKILL.md +2 -2
  22. package/templates/.claude/skills/reverse-engineering-unicorn/SKILL.md +4 -2
  23. package/templates/.claude/skills/reverse-engineering-unicorn/modules/01-intelligence.md +15 -29
  24. package/templates/.claude/skills/reverse-engineering-unicorn/modules/02-product-customers.md +4 -4
  25. package/templates/.claude/skills/reverse-engineering-unicorn/modules/03-market-competition.md +9 -16
  26. package/templates/.claude/skills/reverse-engineering-unicorn/modules/04-business-finance.md +4 -3
  27. package/templates/.claude/skills/reverse-engineering-unicorn/modules/05-growth-engine.md +3 -2
@@ -186,7 +186,7 @@ function renderCode(code, lang) {
186
186
  }
187
187
 
188
188
  // ─── Main parser ──────────────────────────────────────────────────────────
189
- function parseMarkdown(md, idPrefix = '') {
189
+ function parseMarkdown(md, idPrefix = '', usedIds = new Set()) {
190
190
  // Normalize line endings
191
191
  const lines = md.replace(/\r\n/g, '\n').split('\n');
192
192
  const out = [];
@@ -228,7 +228,15 @@ function parseMarkdown(md, idPrefix = '') {
228
228
  const level = h[1].length;
229
229
  const text = h[2].trim();
230
230
  const baseId = slugify(text);
231
- const id = idPrefix && level > 1 ? `${idPrefix}-${baseId}` : baseId || idPrefix || 'section';
231
+ let id = idPrefix && level > 1 ? `${idPrefix}-${baseId}` : baseId || idPrefix || 'section';
232
+ // P3: de-duplicate heading slugs (GitHub-style -2/-3 suffix) so duplicate headings never emit
233
+ // duplicate `id` attributes (invalid HTML + hash-nav resolving only to the first occurrence).
234
+ if (usedIds.has(id)) {
235
+ let n = 2;
236
+ while (usedIds.has(`${id}-${n}`)) n++;
237
+ id = `${id}-${n}`;
238
+ }
239
+ usedIds.add(id);
232
240
  out.push(`<h${level} id="${escapeAttr(id)}">${processInline(text)}</h${level}>`);
233
241
  i++;
234
242
  continue;
@@ -248,7 +256,7 @@ function parseMarkdown(md, idPrefix = '') {
248
256
  qLines.push(lines[i].replace(/^>\s?/, ''));
249
257
  i++;
250
258
  }
251
- out.push(`<blockquote>${parseMarkdown(qLines.join('\n'), idPrefix)}</blockquote>`);
259
+ out.push(`<blockquote>${parseMarkdown(qLines.join('\n'), idPrefix, usedIds)}</blockquote>`);
252
260
  continue;
253
261
  }
254
262
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/p-replicator",
3
- "version": "1.5.8",
3
+ "version": "1.5.12",
4
4
  "description": "P-Replicator — Claude Code toolkit for AI-assisted product development (Vibe Coding). Full /replicate pipeline, 10 modular skills (194K+ chars), /harvest knowledge extraction, swarm agents, quality gates, security patterns, cross-project learning.",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -37,7 +37,12 @@ function run(options) {
37
37
 
38
38
  if (existingFiles.length === 0) {
39
39
  warn('No files found to remove (already cleaned up?).');
40
- // Still remove manifest
40
+ // Dry run is a strict no-op — must NOT unregister the install (the P2 bug: this ran BEFORE the
41
+ // dryRun guard below, so `remove --dry-run` deleted the manifest on the already-cleaned path).
42
+ if (dryRun) {
43
+ warn(`Dry run — would remove ${MANIFEST_FILE} to unregister the (already-empty) install. Nothing written.`);
44
+ process.exit(0);
45
+ }
41
46
  const manifestPath = path.join(targetDir, MANIFEST_FILE);
42
47
  if (fileExists(manifestPath)) {
43
48
  fs.unlinkSync(manifestPath);
@@ -53,6 +58,7 @@ function run(options) {
53
58
 
54
59
  // ── c) Remove files ────────────────────────────────────────────────────
55
60
  let removed = 0;
61
+ const failed = [];
56
62
  for (const f of existingFiles) {
57
63
  const fullPath = path.join(targetDir, f);
58
64
  try {
@@ -60,6 +66,7 @@ function run(options) {
60
66
  removed++;
61
67
  } catch (err) {
62
68
  warn(`Could not remove: ${f} (${err.message})`);
69
+ failed.push(f);
63
70
  }
64
71
  }
65
72
 
@@ -88,9 +95,16 @@ function run(options) {
88
95
  }
89
96
 
90
97
  // ── e) Remove manifest ─────────────────────────────────────────────────
98
+ // P3: only unregister the install when EVERY tracked file was actually removed. If some failed
99
+ // (permissions, locked, read-only mount), keep the manifest so the survivors stay tracked and a
100
+ // later `remove`/`doctor` can still see + finish them — deleting it would strand them as orphans.
91
101
  const manifestPath = path.join(targetDir, MANIFEST_FILE);
92
102
  if (fileExists(manifestPath)) {
93
- fs.unlinkSync(manifestPath);
103
+ if (failed.length > 0) {
104
+ warn(`${failed.length} file(s) could not be removed — keeping ${MANIFEST_FILE} so they stay tracked (re-run remove after fixing permissions).`);
105
+ } else {
106
+ fs.unlinkSync(manifestPath);
107
+ }
94
108
  }
95
109
 
96
110
  console.log('');
@@ -50,6 +50,13 @@ function run(options) {
50
50
  console.log('');
51
51
 
52
52
  if (added.length === 0 && modified.length === 0) {
53
+ // P3: refresh the recorded version even when templates are byte-identical, so the manifest is not
54
+ // a STALE version record after a metadata-only release (doctor/update kept reporting the old
55
+ // installed version). Only the version field changes — no files touched.
56
+ if (manifest.version !== newVersion) {
57
+ writeManifest(targetDir, { ...manifest, version: newVersion });
58
+ info(`Version record refreshed to ${bold(newVersion)} (templates unchanged).`);
59
+ }
53
60
  success('Already up to date!');
54
61
  process.exit(0);
55
62
  }
@@ -1,6 +1,20 @@
1
1
  ---
2
2
  name: brutal-honesty-review
3
- description: Unvarnished technical criticism combining Linus Torvalds precision, Gordon Ramsay standards, and James Bach BS-detection. Use when code or tests need harsh reality checks, certification schemes smell fishy, or technical decisions lack rigor. No sugar-coating, just surgical truth about what is broken and why.
3
+ description: "Unvarnished technical criticism combining Linus Torvalds' precision, Gordon Ramsay's standards, and James Bach's BS-detection. Use when code/tests need harsh reality checks, certification schemes smell fishy, or technical decisions lack rigor. No sugar-coating, just surgical truth about what's broken and why."
4
+ category: quality-review
5
+ priority: high
6
+ tokenEstimate: 1200
7
+ agents: [qe-code-reviewer, qe-quality-gate, qe-security-auditor]
8
+ implementation_status: optimized
9
+ optimization_version: 1.0
10
+ last_optimized: 2025-12-03
11
+ dependencies: []
12
+ quick_reference_card: true
13
+ tags: [code-review, honesty, critical-thinking, technical-criticism, quality]
14
+ trust_tier: 2
15
+ validation:
16
+ schema_path: schemas/output.json
17
+ validator_path: scripts/validate-config.json
4
18
  ---
5
19
 
6
20
  # Brutal Honesty Review
@@ -24,6 +38,9 @@ When brutal honesty is needed:
24
38
  - Level 3 (Brutal): "This is negligent. You're exposing user data because..."
25
39
 
26
40
  **DO NOT USE FOR:** Junior devs' first PRs, demoralized teams, public forums, low psychological safety
41
+
42
+ ## Minimum Findings Enforcement
43
+ All brutal honesty reviews enforce a minimum of 3 weighted findings (CRITICAL=3, HIGH=2, MEDIUM=1, LOW=0.5). If the initial review finds fewer, escalate to deeper analysis. Brutally honest reviewers should ALWAYS find something -- if you can't, explain exactly why with evidence.
27
44
  </default_to_action>
28
45
 
29
46
  ## Quick Reference Card
@@ -0,0 +1,61 @@
1
+ skill: brutal-honesty-review
2
+ version: 1.0.0
3
+ description: >
4
+ Evaluation suite for the brutal-honesty-review skill. Tests that the three
5
+ reviewer modes (Linus precision, Ramsay standards, Bach BS-detection) produce
6
+ unvarnished, specific, evidence-backed criticism — and that they do NOT
7
+ sugar-coat genuine defects or fabricate findings on clean code.
8
+
9
+ models_to_test:
10
+ - claude-3.5-sonnet
11
+ - claude-3-haiku
12
+
13
+ mcp_integration:
14
+ enabled: true
15
+ namespace: skill-validation
16
+ query_patterns: true
17
+ track_outcomes: true
18
+ store_patterns: true
19
+ target_agents:
20
+ - qe-learning-coordinator
21
+ - qe-code-reviewer
22
+
23
+ learning:
24
+ store_success_patterns: true
25
+ store_failure_patterns: true
26
+ pattern_ttl_days: 90
27
+ min_confidence_to_store: 0.7
28
+
29
+ result_format:
30
+ json_output: true
31
+ include_timing: true
32
+ include_token_usage: true
33
+
34
+ scenarios:
35
+ - id: BHR-1
36
+ name: Real defect — must be called out specifically
37
+ input: >
38
+ Code with a genuine bug (e.g. an unhandled null path, a SQL string
39
+ concatenation, a swallowed exception).
40
+ expect:
41
+ - The review names the exact defect with file/line, not vague "could be better".
42
+ - At least one mode (Linus/Ramsay/Bach) flags it as a real problem.
43
+ - A concrete fix or direction is given, not just criticism.
44
+
45
+ - id: BHR-2
46
+ name: Clean code — no fabricated findings
47
+ input: A small, correct, well-tested function with no real issues.
48
+ expect:
49
+ - The review does NOT invent severe findings to appear thorough.
50
+ - If it nitpicks, it labels them as nits, not blockers.
51
+
52
+ - id: BHR-3
53
+ name: Certification/claim smell test
54
+ input: A README claiming "100% coverage" or "fully secure" without evidence.
55
+ expect:
56
+ - Bach BS-detection mode challenges the unsubstantiated claim.
57
+ - Asks for the evidence behind the claim.
58
+
59
+ pass_criteria:
60
+ - Findings are specific (cite location) and evidence-backed.
61
+ - No sugar-coating of real defects; no fabrication on clean code.
@@ -0,0 +1,291 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://agentic-qe.dev/schemas/skills/brutal-honesty-review/output.json",
4
+ "title": "Brutal Honesty Review Skill Output Schema",
5
+ "description": "Schema for brutal-honesty-review skill output. Validates unvarnished technical criticism with actionable recommendations.",
6
+ "type": "object",
7
+ "required": ["skillName", "version", "timestamp", "status", "trustTier", "output"],
8
+ "properties": {
9
+ "skillName": {
10
+ "type": "string",
11
+ "const": "brutal-honesty-review"
12
+ },
13
+ "version": {
14
+ "type": "string",
15
+ "pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9]+)?$"
16
+ },
17
+ "timestamp": {
18
+ "type": "string",
19
+ "format": "date-time"
20
+ },
21
+ "status": {
22
+ "type": "string",
23
+ "enum": ["success", "partial", "failed", "skipped"]
24
+ },
25
+ "trustTier": {
26
+ "type": "integer",
27
+ "const": 3
28
+ },
29
+ "output": {
30
+ "type": "object",
31
+ "required": ["summary", "reviewFindings", "codeQuality"],
32
+ "properties": {
33
+ "summary": {
34
+ "type": "string",
35
+ "minLength": 50,
36
+ "maxLength": 3000,
37
+ "description": "Brutally honest executive summary"
38
+ },
39
+ "reviewFindings": {
40
+ "type": "array",
41
+ "items": {
42
+ "$ref": "#/$defs/reviewFinding"
43
+ },
44
+ "minItems": 1,
45
+ "maxItems": 200,
46
+ "description": "Detailed findings with no sugar-coating"
47
+ },
48
+ "codeQuality": {
49
+ "$ref": "#/$defs/codeQuality"
50
+ },
51
+ "severity": {
52
+ "$ref": "#/$defs/severityBreakdown"
53
+ },
54
+ "recommendations": {
55
+ "type": "array",
56
+ "items": {
57
+ "$ref": "#/$defs/recommendation"
58
+ },
59
+ "maxItems": 100,
60
+ "description": "Actionable improvement recommendations"
61
+ },
62
+ "technicalDebt": {
63
+ "$ref": "#/$defs/technicalDebt"
64
+ },
65
+ "readabilityScore": {
66
+ "type": "number",
67
+ "minimum": 0,
68
+ "maximum": 100
69
+ },
70
+ "maintainabilityScore": {
71
+ "type": "number",
72
+ "minimum": 0,
73
+ "maximum": 100
74
+ },
75
+ "verdict": {
76
+ "type": "string",
77
+ "enum": ["ship-it", "needs-work", "major-issues", "do-not-merge", "rewrite-needed"],
78
+ "description": "Final honest verdict"
79
+ },
80
+ "metrics": {
81
+ "$ref": "#/$defs/metrics"
82
+ }
83
+ }
84
+ },
85
+ "metadata": {
86
+ "$ref": "#/$defs/metadata"
87
+ },
88
+ "validation": {
89
+ "$ref": "#/$defs/validationResult"
90
+ },
91
+ "learning": {
92
+ "$ref": "#/$defs/learningData"
93
+ }
94
+ },
95
+ "$defs": {
96
+ "reviewFinding": {
97
+ "type": "object",
98
+ "required": ["id", "title", "severity", "category", "honestAssessment"],
99
+ "properties": {
100
+ "id": {
101
+ "type": "string",
102
+ "pattern": "^REV-\\d{3,6}$"
103
+ },
104
+ "title": {
105
+ "type": "string",
106
+ "minLength": 10,
107
+ "maxLength": 200
108
+ },
109
+ "description": {
110
+ "type": "string",
111
+ "maxLength": 2000
112
+ },
113
+ "severity": {
114
+ "type": "string",
115
+ "enum": ["critical", "high", "medium", "low", "nitpick"]
116
+ },
117
+ "category": {
118
+ "type": "string",
119
+ "enum": ["architecture", "design", "implementation", "performance", "security", "maintainability", "readability", "testing", "documentation", "naming"]
120
+ },
121
+ "honestAssessment": {
122
+ "type": "string",
123
+ "minLength": 20,
124
+ "maxLength": 2000,
125
+ "description": "Brutally honest assessment with no softening"
126
+ },
127
+ "location": {
128
+ "type": "object",
129
+ "properties": {
130
+ "file": { "type": "string" },
131
+ "line": { "type": "integer", "minimum": 1 },
132
+ "endLine": { "type": "integer", "minimum": 1 },
133
+ "function": { "type": "string" }
134
+ }
135
+ },
136
+ "codeSnippet": {
137
+ "type": "string",
138
+ "maxLength": 5000
139
+ },
140
+ "suggestedFix": {
141
+ "type": "string",
142
+ "maxLength": 5000
143
+ },
144
+ "effort": {
145
+ "type": "string",
146
+ "enum": ["trivial", "low", "medium", "high", "major"]
147
+ },
148
+ "blocking": {
149
+ "type": "boolean",
150
+ "description": "Whether this blocks merging"
151
+ }
152
+ }
153
+ },
154
+ "codeQuality": {
155
+ "type": "object",
156
+ "required": ["overallScore", "grade"],
157
+ "properties": {
158
+ "overallScore": {
159
+ "type": "number",
160
+ "minimum": 0,
161
+ "maximum": 100
162
+ },
163
+ "grade": {
164
+ "type": "string",
165
+ "pattern": "^[A-F][+-]?$"
166
+ },
167
+ "dimensions": {
168
+ "type": "object",
169
+ "properties": {
170
+ "correctness": { "type": "number", "minimum": 0, "maximum": 100 },
171
+ "readability": { "type": "number", "minimum": 0, "maximum": 100 },
172
+ "maintainability": { "type": "number", "minimum": 0, "maximum": 100 },
173
+ "performance": { "type": "number", "minimum": 0, "maximum": 100 },
174
+ "security": { "type": "number", "minimum": 0, "maximum": 100 },
175
+ "testability": { "type": "number", "minimum": 0, "maximum": 100 }
176
+ }
177
+ },
178
+ "strengths": {
179
+ "type": "array",
180
+ "items": { "type": "string" }
181
+ },
182
+ "weaknesses": {
183
+ "type": "array",
184
+ "items": { "type": "string" }
185
+ }
186
+ }
187
+ },
188
+ "severityBreakdown": {
189
+ "type": "object",
190
+ "properties": {
191
+ "critical": { "type": "integer", "minimum": 0 },
192
+ "high": { "type": "integer", "minimum": 0 },
193
+ "medium": { "type": "integer", "minimum": 0 },
194
+ "low": { "type": "integer", "minimum": 0 },
195
+ "nitpick": { "type": "integer", "minimum": 0 },
196
+ "blocking": { "type": "integer", "minimum": 0 }
197
+ }
198
+ },
199
+ "recommendation": {
200
+ "type": "object",
201
+ "required": ["id", "title", "priority", "impact"],
202
+ "properties": {
203
+ "id": {
204
+ "type": "string",
205
+ "pattern": "^REC-\\d{3,6}$"
206
+ },
207
+ "title": {
208
+ "type": "string",
209
+ "maxLength": 200
210
+ },
211
+ "description": {
212
+ "type": "string",
213
+ "maxLength": 2000
214
+ },
215
+ "priority": {
216
+ "type": "string",
217
+ "enum": ["critical", "high", "medium", "low"]
218
+ },
219
+ "impact": {
220
+ "type": "string",
221
+ "enum": ["transformative", "significant", "moderate", "minor"]
222
+ },
223
+ "relatedFindings": {
224
+ "type": "array",
225
+ "items": { "type": "string" }
226
+ },
227
+ "effort": {
228
+ "type": "string",
229
+ "enum": ["trivial", "low", "medium", "high", "major"]
230
+ },
231
+ "codeExample": {
232
+ "type": "string",
233
+ "maxLength": 5000
234
+ }
235
+ }
236
+ },
237
+ "technicalDebt": {
238
+ "type": "object",
239
+ "properties": {
240
+ "totalDebtHours": { "type": "number", "minimum": 0 },
241
+ "debtRatio": { "type": "number", "minimum": 0, "maximum": 100 },
242
+ "categories": {
243
+ "type": "array",
244
+ "items": {
245
+ "type": "object",
246
+ "properties": {
247
+ "category": { "type": "string" },
248
+ "hours": { "type": "number" },
249
+ "priority": { "type": "string" }
250
+ }
251
+ }
252
+ }
253
+ }
254
+ },
255
+ "metrics": {
256
+ "type": "object",
257
+ "properties": {
258
+ "filesReviewed": { "type": "integer", "minimum": 0 },
259
+ "linesReviewed": { "type": "integer", "minimum": 0 },
260
+ "totalFindings": { "type": "integer", "minimum": 0 },
261
+ "blockingIssues": { "type": "integer", "minimum": 0 },
262
+ "suggestedFixes": { "type": "integer", "minimum": 0 }
263
+ }
264
+ },
265
+ "metadata": {
266
+ "type": "object",
267
+ "properties": {
268
+ "executionTimeMs": { "type": "integer", "minimum": 0 },
269
+ "toolsUsed": { "type": "array", "items": { "type": "string" } },
270
+ "agentId": { "type": "string", "pattern": "^qe-[a-z][a-z0-9-]*$" },
271
+ "reviewScope": { "type": "string" },
272
+ "reviewType": { "type": "string", "enum": ["full", "incremental", "focused"] }
273
+ }
274
+ },
275
+ "validationResult": {
276
+ "type": "object",
277
+ "properties": {
278
+ "schemaValid": { "type": "boolean" },
279
+ "contentValid": { "type": "boolean" },
280
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 }
281
+ }
282
+ },
283
+ "learningData": {
284
+ "type": "object",
285
+ "properties": {
286
+ "patternsDetected": { "type": "array", "items": { "type": "string" } },
287
+ "reward": { "type": "number", "minimum": 0, "maximum": 1 }
288
+ }
289
+ }
290
+ }
291
+ }
@@ -78,7 +78,7 @@ assess_edge_cases() {
78
78
  found_count=0
79
79
  for pattern in "${edge_case_patterns[@]}"; do
80
80
  if grep -ri "$pattern" "$TEST_DIR" > /dev/null 2>&1; then
81
- ((found_count++))
81
+ found_count=$((found_count+1))
82
82
  fi
83
83
  done
84
84
 
@@ -161,7 +161,7 @@ assess_stability() {
161
161
  failures=0
162
162
  for i in {1..3}; do
163
163
  if ! npm test > /dev/null 2>&1; then
164
- ((failures++))
164
+ failures=$((failures+1))
165
165
  fi
166
166
  done
167
167
 
@@ -0,0 +1,34 @@
1
+ {
2
+ "skillName": "brutal-honesty-review",
3
+ "skillVersion": "1.0.0",
4
+ "requiredTools": [
5
+ "jq"
6
+ ],
7
+ "optionalTools": [],
8
+ "schemaPath": "schemas/output.json",
9
+ "requiredFields": [
10
+ "skillName",
11
+ "status",
12
+ "output",
13
+ "output.summary",
14
+ "output.reviewFindings",
15
+ "output.codeQuality"
16
+ ],
17
+ "requiredNonEmptyFields": [],
18
+ "mustContainTerms": [
19
+ "review",
20
+ "code"
21
+ ],
22
+ "mustNotContainTerms": [
23
+ "TODO",
24
+ "FIXME"
25
+ ],
26
+ "enumValidations": {
27
+ ".status": [
28
+ "success",
29
+ "partial",
30
+ "failed",
31
+ "skipped"
32
+ ]
33
+ }
34
+ }
@@ -419,7 +419,7 @@ Run in Phase 6 before delivery.
419
419
  | brutal-honesty-review | .claude/skills/ | P0 | Always |
420
420
  | idea2prd-manual | .claude/skills/ | P1 | IF DDD detected |
421
421
  | goap-research-ed25519 | .claude/skills/ | P1 | IF DDD detected |
422
- | feature-navigator | .claude/skills/ | P1 | Always (recommended) |
422
+ | feature-navigator | .claude/skills/ (GENERATED per-project by module 04 — NOT a module-08 copy-dependency) | P1 | Always (recommended) |
423
423
 
424
424
  ## Reusability
425
425
 
@@ -275,7 +275,7 @@ Scan targets:
275
275
 
276
276
  Scan patterns (all are CRITICAL if found):
277
277
 
278
- Pattern: {{[A-Z_]+}}
278
+ Pattern: {{[^}]+}} # ANY placeholder token — uppercase, lowercase, hyphenated, or spaced (was {{[A-Z_]+}}, which silently passed {{feature-id}}, {{Feature Name}})
279
279
  Examples: {{PROJECT_NAME}}, {{LANGUAGE}}, {{MAX_ENTITIES}}, {{TECH_KEYWORDS}}
280
280
  Action: Look up value in IPM and substitute
281
281