bmad-method 6.10.1-next.2 → 6.10.1-next.21

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 (156) hide show
  1. package/.claude-plugin/marketplace.json +52 -27
  2. package/package.json +4 -3
  3. package/removals.txt +5 -0
  4. package/src/bmm-skills/1-analysis/bmad-agent-analyst/customize.toml +19 -4
  5. package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +1 -1
  6. package/src/bmm-skills/1-analysis/bmad-product-brief/customize.toml +5 -3
  7. package/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md +2 -0
  8. package/src/bmm-skills/2-plan-workflows/bmad-prd/customize.toml +5 -3
  9. package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/SKILL.md +18 -3
  10. package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/assets/spec-template.md +1 -1
  11. package/src/bmm-skills/2-plan-workflows/bmad-spec/assets/stories-schema.md +44 -0
  12. package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/customize.toml +3 -4
  13. package/src/bmm-skills/2-plan-workflows/bmad-ux/customize.toml +4 -2
  14. package/src/bmm-skills/3-solutioning/bmad-architecture/SKILL.md +2 -2
  15. package/src/bmm-skills/3-solutioning/bmad-architecture/customize.toml +4 -2
  16. package/src/bmm-skills/4-implementation/bmad-agent-dev/customize.toml +3 -0
  17. package/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md +2 -4
  18. package/src/bmm-skills/4-implementation/bmad-code-review/customize.toml +52 -0
  19. package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-01-gather-context.md +0 -1
  20. package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-02-review.md +10 -24
  21. package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-03-triage.md +3 -4
  22. package/src/bmm-skills/4-implementation/bmad-dev-auto/SKILL.md +33 -12
  23. package/src/bmm-skills/4-implementation/bmad-dev-auto/customize.toml +75 -0
  24. package/src/bmm-skills/4-implementation/bmad-dev-auto/spec-template.md +4 -4
  25. package/src/bmm-skills/4-implementation/bmad-dev-auto/step-01-clarify-and-route.md +22 -4
  26. package/src/bmm-skills/4-implementation/bmad-dev-auto/step-02-plan.md +5 -6
  27. package/src/bmm-skills/4-implementation/bmad-dev-auto/step-03-implement.md +8 -4
  28. package/src/bmm-skills/4-implementation/bmad-dev-auto/step-04-review.md +13 -19
  29. package/src/bmm-skills/4-implementation/bmad-dev-story/customize.toml +3 -0
  30. package/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +6 -108
  31. package/src/bmm-skills/4-implementation/bmad-quick-dev/customize.toml +73 -0
  32. package/src/bmm-skills/4-implementation/bmad-quick-dev/render.py +381 -0
  33. package/src/bmm-skills/4-implementation/bmad-quick-dev/spec-template.md +2 -2
  34. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +11 -13
  35. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +4 -9
  36. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-03-implement.md +12 -4
  37. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-04-review.md +8 -20
  38. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-05-present.md +4 -4
  39. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +12 -13
  40. package/src/bmm-skills/4-implementation/bmad-quick-dev/sync-sprint-status.md +6 -6
  41. package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +85 -0
  42. package/src/bmm-skills/module-help.csv +2 -4
  43. package/src/bmm-skills/module.yaml +2 -5
  44. package/src/bmm-skills/v6-shims/README.md +24 -0
  45. package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/SKILL.md +1 -1
  46. package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/SKILL.md +3 -3
  47. package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +14 -0
  48. package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/SKILL.md +3 -3
  49. package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +14 -0
  50. package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +14 -0
  51. package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/SKILL.md +3 -3
  52. package/src/core-skills/bmad-advanced-elicitation/SKILL.md +26 -103
  53. package/src/core-skills/bmad-advanced-elicitation/customize.toml +54 -0
  54. package/src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
  55. package/src/core-skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
  56. package/src/core-skills/bmad-brainstorming/SKILL.md +3 -3
  57. package/src/core-skills/bmad-brainstorming/assets/brain-selector.html +2 -0
  58. package/src/core-skills/bmad-brainstorming/references/mode-autonomous.md +1 -1
  59. package/src/core-skills/bmad-brainstorming/scripts/brain.py +36 -6
  60. package/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +22 -0
  61. package/src/core-skills/bmad-customize/SKILL.md +2 -2
  62. package/src/core-skills/bmad-deep-recon/SKILL.md +82 -0
  63. package/src/core-skills/bmad-deep-recon/assets/research.template.md +18 -0
  64. package/src/core-skills/bmad-deep-recon/customize.toml +212 -0
  65. package/src/core-skills/bmad-deep-recon/references/draft.md +8 -0
  66. package/src/core-skills/bmad-deep-recon/references/finalize.md +11 -0
  67. package/src/core-skills/bmad-deep-recon/references/html-briefing.md +16 -0
  68. package/src/core-skills/bmad-deep-recon/references/lifecycle.md +11 -0
  69. package/src/core-skills/bmad-deep-recon/references/process.md +10 -0
  70. package/src/core-skills/bmad-deep-recon/references/run.md +73 -0
  71. package/src/core-skills/bmad-deep-recon/references/selection.md +13 -0
  72. package/src/core-skills/bmad-deep-recon/references/synthesis.md +16 -0
  73. package/src/core-skills/bmad-deep-recon/references/verification.md +29 -0
  74. package/src/core-skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
  75. package/src/core-skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
  76. package/src/core-skills/bmad-deep-recon/types/academic-lit.md +19 -0
  77. package/src/core-skills/bmad-deep-recon/types/competitive.md +19 -0
  78. package/src/core-skills/bmad-deep-recon/types/domain.md +19 -0
  79. package/src/core-skills/bmad-deep-recon/types/market.md +19 -0
  80. package/src/core-skills/bmad-deep-recon/types/technical.md +19 -0
  81. package/src/core-skills/bmad-deep-recon/types/user-voice.md +19 -0
  82. package/src/core-skills/bmad-forge-idea/SKILL.md +2 -2
  83. package/src/core-skills/bmad-forge-idea/scripts/resolve_personas.py +4 -1
  84. package/src/core-skills/bmad-help/SKILL.md +1 -1
  85. package/src/core-skills/bmad-party-mode/SKILL.md +2 -2
  86. package/src/core-skills/bmad-party-mode/scripts/resolve_party.py +11 -3
  87. package/src/core-skills/bmad-review/SKILL.md +49 -0
  88. package/src/core-skills/bmad-review/customize.toml +141 -0
  89. package/src/core-skills/bmad-review/references/editorial-common.md +56 -0
  90. package/src/core-skills/bmad-review/references/lens-adversarial.md +18 -0
  91. package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +54 -0
  92. package/src/core-skills/bmad-review/references/lens-prose.md +7 -0
  93. package/src/core-skills/bmad-review/references/lens-structure.md +9 -0
  94. package/src/core-skills/{bmad-review-verification-gap/SKILL.md → bmad-review/references/lens-verification-gap.md} +15 -39
  95. package/src/core-skills/bmad-review/references/structure-models.md +44 -0
  96. package/src/core-skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
  97. package/src/core-skills/bmad-review/scripts/word_metrics.py +102 -0
  98. package/src/core-skills/module-help.csv +3 -7
  99. package/src/core-skills/module.yaml +5 -0
  100. package/src/core-skills/v6-shims/README.md +25 -0
  101. package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +6 -0
  102. package/src/core-skills/v6-shims/bmad-editorial-review/customize.toml +31 -0
  103. package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +6 -0
  104. package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +6 -0
  105. package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +6 -0
  106. package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +6 -0
  107. package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +6 -0
  108. package/tools/installer/ide/platform-codes.yaml +7 -0
  109. package/tools/installer/modules/custom-module-manager.js +12 -6
  110. package/tools/installer/modules/external-manager.js +12 -8
  111. package/tools/installer/modules/git-env.js +47 -0
  112. package/tools/skill-validator.md +11 -1
  113. package/tools/validate-skills.js +33 -0
  114. package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +0 -96
  115. package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +0 -41
  116. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-01-init.md +0 -137
  117. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-02-domain-analysis.md +0 -229
  118. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-03-competitive-landscape.md +0 -238
  119. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-04-regulatory-focus.md +0 -206
  120. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-05-technical-trends.md +0 -234
  121. package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +0 -450
  122. package/src/bmm-skills/1-analysis/research/bmad-domain-research/research.template.md +0 -29
  123. package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +0 -96
  124. package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +0 -41
  125. package/src/bmm-skills/1-analysis/research/bmad-market-research/research.template.md +0 -29
  126. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-01-init.md +0 -184
  127. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-02-customer-behavior.md +0 -239
  128. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-03-customer-pain-points.md +0 -251
  129. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-04-customer-decisions.md +0 -261
  130. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-05-competitive-analysis.md +0 -173
  131. package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +0 -484
  132. package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +0 -96
  133. package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +0 -41
  134. package/src/bmm-skills/1-analysis/research/bmad-technical-research/research.template.md +0 -29
  135. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-01-init.md +0 -137
  136. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-02-technical-overview.md +0 -239
  137. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-03-integration-patterns.md +0 -248
  138. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-04-architectural-patterns.md +0 -202
  139. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-05-implementation-research.md +0 -233
  140. package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +0 -493
  141. package/src/core-skills/bmad-brainstorming/analysis/catalog-analysis.md +0 -239
  142. package/src/core-skills/bmad-brainstorming/analysis/method-matrix.csv +0 -109
  143. package/src/core-skills/bmad-editorial-review-prose/SKILL.md +0 -86
  144. package/src/core-skills/bmad-editorial-review-structure/SKILL.md +0 -179
  145. package/src/core-skills/bmad-index-docs/SKILL.md +0 -66
  146. package/src/core-skills/bmad-review-adversarial-general/SKILL.md +0 -37
  147. package/src/core-skills/bmad-review-edge-case-hunter/SKILL.md +0 -73
  148. package/src/core-skills/bmad-review-edge-case-hunter/references/deletion-check.md +0 -14
  149. package/src/core-skills/bmad-shard-doc/SKILL.md +0 -105
  150. /package/src/{core-skills → bmm-skills/2-plan-workflows}/bmad-spec/assets/headless-schemas.md +0 -0
  151. /package/src/bmm-skills/{3-solutioning → v6-shims}/bmad-create-architecture/customize.toml +0 -0
  152. /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-create-prd/customize.toml +0 -0
  153. /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-edit-prd/customize.toml +0 -0
  154. /package/src/bmm-skills/{2-plan-workflows → v6-shims}/bmad-validate-prd/customize.toml +0 -0
  155. /package/src/core-skills/bmad-advanced-elicitation/{methods.csv → assets/methods.csv} +0 -0
  156. /package/src/core-skills/bmad-party-mode/scripts/tests/{test-resolve_party.py → test_resolve_party.py} +0 -0
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.10"
4
+ # ///
5
+ """Exact word counts for a document, as JSON.
6
+
7
+ Emits the document's total word count and a per-heading-section breakdown so
8
+ an editorial review can ground word-impact estimates and reduction
9
+ percentages in real numbers instead of guessing. Sections are delimited by
10
+ markdown headings (# through ######); heading markers inside fenced code
11
+ blocks are ignored (fences pair CommonMark-style: a fence closes only on a
12
+ run of the same character at least as long, so ```` fences may embed ```
13
+ examples). A word is any whitespace-separated token, plus one word per CJK
14
+ character since those scripts do not space-delimit words. For non-markdown
15
+ input the result is a single section holding the full text.
16
+ """
17
+
18
+ import argparse
19
+ import json
20
+ import re
21
+ import sys
22
+ from pathlib import Path
23
+
24
+ HEADING = re.compile(r"^(#{1,6})\s+(\S.*)$")
25
+ FENCE = re.compile(r"^ {0,3}(`{3,}|~{3,})")
26
+ CJK = re.compile(r"[぀-ヿ㐀-䶿一-鿿豈-﫿가-힯ヲ-゚]")
27
+
28
+
29
+ def word_count(text: str) -> int:
30
+ cjk = len(CJK.findall(text))
31
+ return cjk + len(CJK.sub(" ", text).split())
32
+
33
+
34
+ def section_metrics(text: str) -> list[dict]:
35
+ sections = []
36
+ current = {"heading": "(preamble)", "level": 0, "body": []}
37
+ open_fence = None # (char, length) while inside a fenced block
38
+ for line in text.splitlines():
39
+ fence = FENCE.match(line)
40
+ if fence:
41
+ marker = fence.group(1)
42
+ if open_fence is None:
43
+ open_fence = (marker[0], len(marker))
44
+ elif marker[0] == open_fence[0] and len(marker) >= open_fence[1] and line.strip() == marker:
45
+ open_fence = None
46
+ current["body"].append(line)
47
+ continue
48
+ match = None if open_fence else HEADING.match(line)
49
+ if match:
50
+ sections.append(current)
51
+ current = {
52
+ "heading": match.group(2).strip(),
53
+ "level": len(match.group(1)),
54
+ "body": [],
55
+ }
56
+ else:
57
+ current["body"].append(line)
58
+ sections.append(current)
59
+
60
+ out = []
61
+ for section in sections:
62
+ words = word_count("\n".join(section["body"]))
63
+ if section["heading"] == "(preamble)" and words == 0:
64
+ continue
65
+ out.append(
66
+ {"heading": section["heading"], "level": section["level"], "words": words}
67
+ )
68
+ return out
69
+
70
+
71
+ def metrics(path: Path) -> dict:
72
+ text = path.read_text(encoding="utf-8", errors="replace")
73
+ return {
74
+ "file": str(path),
75
+ "total_words": word_count(text),
76
+ "sections": section_metrics(text),
77
+ }
78
+
79
+
80
+ def main() -> int:
81
+ if hasattr(sys.stdout, "reconfigure"):
82
+ sys.stdout.reconfigure(encoding="utf-8") # JSON is UTF-8 regardless of locale code page
83
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
84
+ parser.add_argument("path", help="document to measure")
85
+ parser.add_argument("-o", "--output", help="write JSON here (default: stdout)")
86
+ args = parser.parse_args()
87
+
88
+ path = Path(args.path)
89
+ if not path.is_file():
90
+ print(f"error: not a readable file: {path}", file=sys.stderr)
91
+ return 2
92
+
93
+ result = json.dumps(metrics(path), indent=2, ensure_ascii=False)
94
+ if args.output:
95
+ Path(args.output).write_text(result + "\n", encoding="utf-8")
96
+ else:
97
+ print(result)
98
+ return 0
99
+
100
+
101
+ if __name__ == "__main__":
102
+ sys.exit(main())
@@ -3,12 +3,8 @@ Core,_meta,,,,,,,,,false,https://docs.bmad-method.org/llms.txt,
3
3
  Core,bmad-brainstorming,Brainstorming,BSP,Use early in ideation or when stuck generating ideas.,,,anytime,,,false,{output_folder}/brainstorming,brainstorming session
4
4
  Core,bmad-party-mode,Party Mode,PM,Orchestrate multi-agent discussions when you need multiple perspectives or want agents to collaborate.,,,anytime,,,false,,
5
5
  Core,bmad-help,BMad Help,BH,,,,anytime,,,false,,
6
- Core,bmad-index-docs,Index Docs,ID,Use when LLM needs to understand available docs without loading everything.,,,anytime,,,false,,
7
- Core,bmad-shard-doc,Shard Document,SD,Use when doc becomes too large (>500 lines) to manage effectively.,,[path],anytime,,,false,,
8
- Core,bmad-editorial-review-prose,Editorial Review - Prose,EP,Use after drafting to polish written content.,,[path],anytime,,,false,report located with target document,three-column markdown table with suggested fixes
9
- Core,bmad-editorial-review-structure,Editorial Review - Structure,ES,Use when doc produced from multiple subprocesses or needs structural improvement.,,[path],anytime,,,false,report located with target document,
10
- Core,bmad-review-adversarial-general,Adversarial Review,AR,"Use for quality assurance or before finalizing deliverables. Code Review in other modules runs this automatically, but also useful for document reviews.",,[path],anytime,,,false,,
11
- Core,bmad-review-edge-case-hunter,Edge Case Hunter Review,ECH,Use alongside adversarial review for orthogonal coverage — method-driven not attitude-driven.,,[path],anytime,,,false,,
12
- Core,bmad-spec,Spec,SP,"Use to distill any intent input (brief, PRD, transcript, brain dump, design folder, mixed multi-source) into a succinct, no-fluff SPEC.md contract + companions that downstream work derives from. Locks the WHAT before the HOW. Works for software, game design, research, editorial, policy, business, anything intent-bearing. Validation mode also available.",,[path],anytime,,,false,{output_folder}/specs/spec-{slug},SPEC.md + companion files
13
6
  Core,bmad-customize,BMad Customize,BC,"Use when you want to change how an agent or workflow behaves — add persistent facts, swap templates, insert activation hooks, or customize menus. Scans what's customizable, picks the right scope (agent vs workflow), writes the override to _bmad/custom/, and verifies the merge. No TOML hand-authoring required.",,,anytime,,,false,{project-root}/_bmad/custom,TOML override files
7
+ Core,bmad-advanced-elicitation,Advanced Elicitation,AE,"Use at any checkpoint to push a just-produced draft, section, or plan past its first version — pick from a menu of elicitation methods (pre-mortem, first principles, red team, socratic) and apply the improvements.",,,anytime,,,false,,
8
+ Core,bmad-review,Review,RV,"Use to review anything before it ships — a diff, branch, or uncommitted changes; a PRD, spec, story, or architecture doc; any prose document. Runs whichever installed lenses fit the content and reports findings in one shape: shipped lenses cover adversarial critique, edge cases, verification gaps, document structure (cuts, merges, moves), and prose copy-edit, and installs may add more. Reach for it to tighten a bloated or LLM-slop draft, pressure-test a plan before building, or check a change is adequately tested. Run every applicable lens or name the ones you want. Code Review in other modules calls it automatically.",,[path],anytime,,,false,,findings JSON array + markdown report
14
9
  Core,bmad-forge-idea,Forge Idea,FI,"Use to pressure-test and harden an idea — software, business, creative, research, or life — until it proves out, hardens into something buildable, or dies cheaply. Persona-driven interrogation; optional handoff to bmad-spec or bmad-quick-dev.",,,anytime,,,false,{output_folder}/forge,refined-idea brief (optional)
10
+ Core,bmad-deep-recon,Deep Recon,RS,Decision-grade research three ways: draft a deep-research prompt for your own AI tool then process the returned report into a downstream-ready cited summary — or run the research directly via web fan-out. Types: market domain technical competitive user-voice academic-lit plus a select shape for choose-between decisions; claim verification and refresh lifecycle. Custom types via overrides.,,[type],anytime,,,false,{planning_artifacts}/research,research report/summary + optional html briefing
@@ -31,3 +31,8 @@ output_folder:
31
31
  prompt: "Where should output files be saved?"
32
32
  default: "_bmad-output"
33
33
  result: "{project-root}/{value}"
34
+
35
+ # The one directory created at install time. Everything else (module artifact
36
+ # folders, project knowledge) is created lazily by the first skill that writes there.
37
+ directories:
38
+ - "{output_folder}"
@@ -0,0 +1,25 @@
1
+ # v6 Deprecation Shims
2
+
3
+ Skills in this folder are forwarders kept for backward compatibility with v6 skill IDs.
4
+ Each one holds no logic of its own — it forwards to the skill that replaced it, pinning
5
+ the legacy output contract so existing callers keep working.
6
+
7
+ | Shim | Forwards to |
8
+ | --------------------------------- | ---------------------------------------- |
9
+ | `bmad-editorial-review` | `bmad-review` (structure + prose lenses) |
10
+ | `bmad-editorial-review-prose` | `bmad-review` (prose lens) |
11
+ | `bmad-editorial-review-structure` | `bmad-review` (structure lens) |
12
+ | `bmad-review-adversarial-general` | `bmad-review` (adversarial lens) |
13
+ | `bmad-review-edge-case-hunter` | `bmad-review` (edge-case lens) |
14
+ | `bmad-review-verification-gap` | `bmad-review` (verification-gap lens) |
15
+
16
+ `bmad-editorial-review` keeps its `customize.toml` so existing team and user
17
+ overrides still resolve; the shim forwards those resolved values to `bmad-review`.
18
+
19
+ External module repos (gds, loop, tea, bmb, os-utils) still invoke these IDs, so they
20
+ ship by default. Removal rides the v7 cut — never a 6.x minor.
21
+
22
+ The folder is grouping only: the installer discovers skills recursively and installs each
23
+ one under its own `name`, so nesting here does not change any installed path or skill ID.
24
+ A future install option will let users include or exclude this folder before it is removed
25
+ outright.
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-editorial-review
3
+ description: 'Deprecated — forwards to bmad-review.'
4
+ ---
5
+
6
+ Merged into `bmad-review`. Invoke the `bmad-review` skill on the same content with the `structure` and `prose` lenses — both, structure first, so prose runs on top of the structure findings — unless the caller asked for a structure-only or prose-only review, in which case pass only that lens. Pass through any `also_consider` areas, and forward this skill's resolved `[workflow]` fields as pre-resolved values — but only those that resolved to something, since an empty value here means no legacy override exists and bmad-review's own default should stand: `reader_type`, `style_guide`, `review_guidance`, `output_preferences`, `persistent_facts`, `activation_steps_prepend`, `activation_steps_append`, `on_complete`, and `review_output_path` as the report path. Present the findings in the legacy shape: the two-pass findings table `| Pass | Original Text | Revised Text | Changes |` with the purpose/audience read above it and, when the structure pass ran, the reduction summary below it — and no other lens's output.
@@ -0,0 +1,31 @@
1
+ # DO NOT EDIT -- overwritten on every update.
2
+ #
3
+ # Legacy override surface for bmad-editorial-review, which is deprecated and
4
+ # forwards to bmad-review's `structure` and `prose` lenses. These keys exist
5
+ # only so overrides written against the old skill keep resolving: the shim
6
+ # resolves them here and passes them to bmad-review as pre-resolved values,
7
+ # which bmad-review honors verbatim over its own.
8
+ #
9
+ # Every field is documented in bmad-review's customize.toml, which is where
10
+ # new customization belongs. Defaults are empty here so the shim contributes
11
+ # nothing of its own — an unset key falls through to bmad-review's default.
12
+ #
13
+ # Override files (not edited here):
14
+ # {project-root}/_bmad/custom/bmad-editorial-review.toml (team)
15
+ # {project-root}/_bmad/custom/bmad-editorial-review.user.toml (personal)
16
+ #
17
+ # Overrides merge per BMad structural rules: scalars win, arrays append.
18
+
19
+ [workflow]
20
+
21
+ activation_steps_prepend = []
22
+ activation_steps_append = []
23
+ persistent_facts = []
24
+ review_guidance = []
25
+ on_complete = ""
26
+ output_preferences = ""
27
+ reader_type = ""
28
+ style_guide = ""
29
+
30
+ # Where the findings table lands; forwarded as bmad-review's report path.
31
+ review_output_path = ""
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-editorial-review-prose
3
+ description: 'Deprecated — forwards to bmad-review.'
4
+ ---
5
+
6
+ Merged into `bmad-review`. Invoke the `bmad-review` skill on the same content with only the `prose` lens, passing through the same inputs and any `also_consider` areas. Present the findings in the legacy shape: a three-column markdown table `| Original Text | Revised Text | Changes |` — no Pass column, no preamble above the table. If no issues are found, output exactly: `No editorial issues identified`.
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-editorial-review-structure
3
+ description: 'Deprecated — forwards to bmad-review.'
4
+ ---
5
+
6
+ Merged into `bmad-review`. Invoke the `bmad-review` skill on the same content with only the `structure` lens, passing through the same inputs and any `also_consider` areas. Present the findings in the legacy report shape: a `## Document Summary` block (purpose, audience, reader type, structure model, current length), a `## Recommendations` list of numbered `[CUT/MERGE/MOVE/CONDENSE/QUESTION/PRESERVE]` entries each with rationale and word impact, and a closing `## Summary` (total recommendations, estimated reduction) — not the findings table. If no structural issues are found, output exactly: `No substantive changes recommended`.
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-review-adversarial-general
3
+ description: 'Deprecated — forwards to bmad-review.'
4
+ ---
5
+
6
+ Merged into `bmad-review`. Invoke the `bmad-review` skill on the same content with only the `adversarial` lens, passing through any `also_consider` areas. Present the findings as a Markdown list — descriptions only, no severity, priority, or ranking; no JSON block.
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-review-edge-case-hunter
3
+ description: 'Deprecated — forwards to bmad-review.'
4
+ ---
5
+
6
+ Merged into `bmad-review`. Invoke the `bmad-review` skill on the same content with only the `edge-case-hunter` lens, passing through any `also_consider` areas. Output ONLY the raw findings JSON array in the legacy shape: the four standard fields (plus `kind`/`confidence` on deletion findings), no `lens` field, no markdown wrapping, no extra text. `[]` is valid when nothing is found.
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: bmad-review-verification-gap
3
+ description: 'Deprecated — forwards to bmad-review.'
4
+ ---
5
+
6
+ Merged into `bmad-review`. Invoke the `bmad-review` skill on the same content with only the `verification-gap` lens. Present the markdown rendering only (no JSON block), listing any `gap_shape: "other"` findings under an `## Other findings` heading. When there are no findings at all, output exactly this single line: `No verification gaps found.`
@@ -35,6 +35,13 @@ platforms:
35
35
  target_dir: .agent/skills
36
36
  global_target_dir: ~/.gemini/antigravity/skills
37
37
 
38
+ antigravity-cli:
39
+ name: "Antigravity CLI (AGY)"
40
+ preferred: false
41
+ installer:
42
+ target_dir: .agents/skills
43
+ global_target_dir: ~/.gemini/antigravity-cli/skills
44
+
38
45
  auggie:
39
46
  name: "Auggie"
40
47
  preferred: false
@@ -3,6 +3,7 @@ const os = require('node:os');
3
3
  const path = require('node:path');
4
4
  const { execSync } = require('node:child_process');
5
5
  const prompts = require('../prompts');
6
+ const { gitEnv } = require('./git-env');
6
7
 
7
8
  function quoteCustomRef(ref) {
8
9
  if (typeof ref !== 'string' || !/^[\w.\-+/]+$/.test(ref)) {
@@ -418,7 +419,7 @@ class CustomModuleManager {
418
419
  execSync('git fetch origin --depth 1', {
419
420
  cwd: repoCacheDir,
420
421
  stdio: ['ignore', 'pipe', 'pipe'],
421
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
422
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
422
423
  });
423
424
  if (effectiveVersion) {
424
425
  // Fetch the ref as either a tag or a branch — `origin <ref>` works
@@ -427,11 +428,12 @@ class CustomModuleManager {
427
428
  execSync(`git fetch --depth 1 origin ${quoteCustomRef(effectiveVersion)} --no-tags`, {
428
429
  cwd: repoCacheDir,
429
430
  stdio: ['ignore', 'pipe', 'pipe'],
430
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
431
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
431
432
  });
432
433
  execSync(`git checkout --quiet FETCH_HEAD`, {
433
434
  cwd: repoCacheDir,
434
435
  stdio: ['ignore', 'pipe', 'pipe'],
436
+ env: gitEnv(),
435
437
  });
436
438
  } else {
437
439
  // Resolve the default branch (origin/HEAD) and fetch it explicitly.
@@ -442,6 +444,7 @@ class CustomModuleManager {
442
444
  defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD --short', {
443
445
  cwd: repoCacheDir,
444
446
  stdio: 'pipe',
447
+ env: gitEnv(),
445
448
  })
446
449
  .toString()
447
450
  .trim()
@@ -452,11 +455,12 @@ class CustomModuleManager {
452
455
  execSync(`git fetch --depth 1 origin ${quoteCustomRef(defaultBranch)}`, {
453
456
  cwd: repoCacheDir,
454
457
  stdio: ['ignore', 'pipe', 'pipe'],
455
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
458
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
456
459
  });
457
460
  execSync(`git reset --hard origin/${quoteCustomRef(defaultBranch)}`, {
458
461
  cwd: repoCacheDir,
459
462
  stdio: ['ignore', 'pipe', 'pipe'],
463
+ env: gitEnv(),
460
464
  });
461
465
  }
462
466
  fetchSpinner.stop(`Updated ${displayName}`);
@@ -477,12 +481,12 @@ class CustomModuleManager {
477
481
  if (effectiveVersion) {
478
482
  execSync(`git clone --depth 1 --branch ${quoteCustomRef(effectiveVersion)} "${parsed.cloneUrl}" "${repoCacheDir}"`, {
479
483
  stdio: ['ignore', 'pipe', 'pipe'],
480
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
484
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
481
485
  });
482
486
  } else {
483
487
  execSync(`git clone --depth 1 "${parsed.cloneUrl}" "${repoCacheDir}"`, {
484
488
  stdio: ['ignore', 'pipe', 'pipe'],
485
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
489
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
486
490
  });
487
491
  }
488
492
  fetchSpinner.stop(`Cloned ${displayName}`);
@@ -496,7 +500,7 @@ class CustomModuleManager {
496
500
  // Record the resolved SHA for the manifest writer.
497
501
  let resolvedSha = null;
498
502
  try {
499
- resolvedSha = execSync('git rev-parse HEAD', { cwd: repoCacheDir, stdio: 'pipe' }).toString().trim();
503
+ resolvedSha = execSync('git rev-parse HEAD', { cwd: repoCacheDir, stdio: 'pipe', env: gitEnv() }).toString().trim();
500
504
  } catch {
501
505
  // swallow — a non-git repo (local path) wouldn't reach here anyway
502
506
  }
@@ -508,6 +512,7 @@ class CustomModuleManager {
508
512
  const symbolic = execSync('git symbolic-ref --short refs/remotes/origin/HEAD', {
509
513
  cwd: repoCacheDir,
510
514
  stdio: 'pipe',
515
+ env: gitEnv(),
511
516
  })
512
517
  .toString()
513
518
  .trim();
@@ -558,6 +563,7 @@ class CustomModuleManager {
558
563
  cwd: repoCacheDir,
559
564
  stdio: ['ignore', 'pipe', 'pipe'],
560
565
  timeout: 120_000,
566
+ env: gitEnv(), // npm shells out to git for git-URL deps; keep hook GIT_* vars away from it
561
567
  });
562
568
  installSpinner.stop(`Installed dependencies for ${displayName}`);
563
569
  } catch (error_) {
@@ -5,6 +5,7 @@ const { execSync } = require('node:child_process');
5
5
  const yaml = require('yaml');
6
6
  const prompts = require('../prompts');
7
7
  const { resolveChannel, tagExists, parseGitHubRepo } = require('./channel-resolver');
8
+ const { gitEnv } = require('./git-env');
8
9
  const { decideChannelForModule } = require('./channel-plan');
9
10
  const { getProjectRoot } = require('../project-root');
10
11
 
@@ -370,33 +371,34 @@ class ExternalModuleManager {
370
371
  const fetchSpinner = await createSpinner();
371
372
  fetchSpinner.start(`Fetching ${moduleInfo.name}...`);
372
373
  try {
373
- const currentSha = execSync('git rev-parse HEAD', { cwd: moduleCacheDir, stdio: 'pipe' }).toString().trim();
374
+ const currentSha = execSync('git rev-parse HEAD', { cwd: moduleCacheDir, stdio: 'pipe', env: gitEnv() }).toString().trim();
374
375
 
375
376
  if (resolved.channel === 'next') {
376
377
  execSync('git fetch origin --depth 1', {
377
378
  cwd: moduleCacheDir,
378
379
  stdio: ['ignore', 'pipe', 'pipe'],
379
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
380
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
380
381
  });
381
382
  execSync('git reset --hard origin/HEAD', {
382
383
  cwd: moduleCacheDir,
383
384
  stdio: ['ignore', 'pipe', 'pipe'],
384
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
385
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
385
386
  });
386
387
  } else {
387
388
  // stable or pinned — fetch the specific tag and check it out.
388
389
  execSync(`git fetch --depth 1 origin tag ${quoteShell(resolved.ref)} --no-tags`, {
389
390
  cwd: moduleCacheDir,
390
391
  stdio: ['ignore', 'pipe', 'pipe'],
391
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
392
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
392
393
  });
393
394
  execSync(`git checkout --quiet FETCH_HEAD`, {
394
395
  cwd: moduleCacheDir,
395
396
  stdio: ['ignore', 'pipe', 'pipe'],
397
+ env: gitEnv(),
396
398
  });
397
399
  }
398
400
 
399
- const newSha = execSync('git rev-parse HEAD', { cwd: moduleCacheDir, stdio: 'pipe' }).toString().trim();
401
+ const newSha = execSync('git rev-parse HEAD', { cwd: moduleCacheDir, stdio: 'pipe', env: gitEnv() }).toString().trim();
400
402
  fetchSpinner.stop(`Fetched ${moduleInfo.name}`);
401
403
  if (currentSha !== newSha) needsDependencyInstall = true;
402
404
  } catch {
@@ -415,12 +417,12 @@ class ExternalModuleManager {
415
417
  if (resolved.channel === 'next') {
416
418
  execSync(`git clone --depth 1 "${moduleInfo.url}" "${moduleCacheDir}"`, {
417
419
  stdio: ['ignore', 'pipe', 'pipe'],
418
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
420
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
419
421
  });
420
422
  } else {
421
423
  execSync(`git clone --depth 1 --branch ${quoteShell(resolved.ref)} "${moduleInfo.url}" "${moduleCacheDir}"`, {
422
424
  stdio: ['ignore', 'pipe', 'pipe'],
423
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
425
+ env: gitEnv({ GIT_TERMINAL_PROMPT: '0' }),
424
426
  });
425
427
  }
426
428
  fetchSpinner.stop(`Fetched ${moduleInfo.name}`);
@@ -431,7 +433,7 @@ class ExternalModuleManager {
431
433
  }
432
434
 
433
435
  // Record resolution (channel + tag + SHA) for the manifest writer to pick up.
434
- const sha = execSync('git rev-parse HEAD', { cwd: moduleCacheDir, stdio: 'pipe' }).toString().trim();
436
+ const sha = execSync('git rev-parse HEAD', { cwd: moduleCacheDir, stdio: 'pipe', env: gitEnv() }).toString().trim();
435
437
  ExternalModuleManager._resolutions.set(moduleCode, {
436
438
  channel: resolved.channel,
437
439
  version: resolved.version,
@@ -459,6 +461,7 @@ class ExternalModuleManager {
459
461
  cwd: moduleCacheDir,
460
462
  stdio: ['ignore', 'pipe', 'pipe'],
461
463
  timeout: 120_000, // 2 minute timeout
464
+ env: gitEnv(), // npm shells out to git for git-URL deps; keep hook GIT_* vars away from it
462
465
  });
463
466
  installSpinner.stop(`Installed dependencies for ${moduleInfo.name}`);
464
467
  } catch (error) {
@@ -485,6 +488,7 @@ class ExternalModuleManager {
485
488
  cwd: moduleCacheDir,
486
489
  stdio: ['ignore', 'pipe', 'pipe'],
487
490
  timeout: 120_000, // 2 minute timeout
491
+ env: gitEnv(), // npm shells out to git for git-URL deps; keep hook GIT_* vars away from it
488
492
  });
489
493
  installSpinner.stop(`Installed dependencies for ${moduleInfo.name}`);
490
494
  } catch (error) {
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Sanitized environment for spawning git subprocesses.
3
+ *
4
+ * When the installer runs inside a git hook (e.g. `npm test` from a
5
+ * pre-commit hook), git exports repo-targeting variables — GIT_DIR,
6
+ * GIT_INDEX_FILE, GIT_WORK_TREE, etc. — into the hook's environment. A
7
+ * spawned `git` inherits them and silently operates on the hook's repository
8
+ * instead of the cwd passed to execSync (GIT_DIR overrides directory
9
+ * discovery, and the cwd then becomes its work tree). In a worktree checkout
10
+ * this let a cache refresh shallow-fetch and hard-reset the developer's own
11
+ * repo. Strip these variables so cache-directory git commands can only ever
12
+ * act on the repository at their cwd.
13
+ */
14
+
15
+ const REPO_TARGETING_VARS = [
16
+ 'GIT_DIR',
17
+ 'GIT_WORK_TREE',
18
+ 'GIT_INDEX_FILE',
19
+ 'GIT_COMMON_DIR',
20
+ 'GIT_OBJECT_DIRECTORY',
21
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
22
+ 'GIT_PREFIX',
23
+ 'GIT_NAMESPACE',
24
+ // `git -c key=val` exports its overrides to hook subprocesses through these;
25
+ // a core.worktree override can retarget the work tree just like GIT_WORK_TREE.
26
+ 'GIT_CONFIG_PARAMETERS',
27
+ 'GIT_CONFIG_COUNT',
28
+ ];
29
+
30
+ // GIT_CONFIG_KEY_0 / GIT_CONFIG_VALUE_0 ... — the enumerated form of the same mechanism.
31
+ const CONFIG_PAIR_VAR = /^GIT_CONFIG_(KEY|VALUE)_\d+$/;
32
+
33
+ /**
34
+ * Build the environment for a git child process.
35
+ * @param {Object} [extra] - Additional variables to set (e.g. GIT_TERMINAL_PROMPT)
36
+ * @returns {Object} process.env minus repo-targeting GIT_* vars, plus extras
37
+ */
38
+ function gitEnv(extra = {}) {
39
+ const env = { ...process.env, ...extra };
40
+ for (const name of REPO_TARGETING_VARS) delete env[name];
41
+ for (const name of Object.keys(env)) {
42
+ if (CONFIG_PAIR_VAR.test(name)) delete env[name];
43
+ }
44
+ return env;
45
+ }
46
+
47
+ module.exports = { gitEnv };
@@ -10,7 +10,7 @@ Before running inference-based validation, run the deterministic validator:
10
10
  node tools/validate-skills.js --json path/to/skill-dir
11
11
  ```
12
12
 
13
- This checks 12 rules deterministically: SKILL-01, SKILL-02, SKILL-03, SKILL-04, SKILL-05, SKILL-06, SKILL-07, PATH-02, STEP-01, STEP-06, STEP-07, SEQ-02.
13
+ This checks 13 rules deterministically: SKILL-01, SKILL-02, SKILL-03, SKILL-04, SKILL-05, SKILL-06, SKILL-07, PATH-02, STEP-01, STEP-06, STEP-07, SEQ-02, TPL-01.
14
14
 
15
15
  Review its JSON output. For any rule that produced **zero findings** in the first pass, **skip it** during inference-based validation below — it has already been verified. If a rule produced any findings, the inference validator should still review that rule (some rules like SKILL-04 and SKILL-06 have sub-checks that benefit from judgment). Focus your inference effort on the remaining rules that require judgment (PATH-01, PATH-03, PATH-04, PATH-05, WF-03, STEP-02, STEP-03, STEP-04, STEP-05, SEQ-01, REF-01, REF-02, REF-03).
16
16
 
@@ -253,6 +253,16 @@ If no findings are generated (from either pass), the skill passes validation.
253
253
 
254
254
  ---
255
255
 
256
+ ### TPL-01 — Template Files Must Not Contain Compile-Time Substitutions
257
+
258
+ - **Severity:** HIGH
259
+ - **Applies to:** `.md` files whose name contains `template` (case-insensitive)
260
+ - **Rule:** Template files seed durable, version-controlled artifacts (e.g. spec files) that execute on other machines. A `{{.var}}` compile-time substitution would be baked at render time and freeze a machine-local value into every artifact produced from the template.
261
+ - **Detection:** Regex `\{\{\.\w+\}\}` match anywhere in a file whose basename matches `/template/i`.
262
+ - **Fix:** Remove the `{{.var}}` reference. Use single-curly `{var}` if the value should be resolved at LLM runtime by the consumer of the generated artifact.
263
+
264
+ ---
265
+
256
266
  ### REF-01 — Variable References Must Be Defined
257
267
 
258
268
  - **Severity:** HIGH
@@ -17,6 +17,7 @@
17
17
  * - STEP-06: step frontmatter has no name/description
18
18
  * - STEP-07: step count 2-10
19
19
  * - SEQ-02: no time estimates
20
+ * - TPL-01: template files must not contain compile-time {{.var}} substitutions
20
21
  *
21
22
  * Usage:
22
23
  * node tools/validate-skills.js # All skills, human-readable
@@ -43,6 +44,8 @@ const positionalArgs = args.filter((a) => !a.startsWith('--'));
43
44
  const NAME_REGEX = /^bmad-[a-z0-9]+(-[a-z0-9]+)*$/;
44
45
  const STEP_FILENAME_REGEX = /^step-\d{2}[a-z]?-[a-z0-9-]+\.md$/;
45
46
  const TIME_ESTIMATE_PATTERNS = [/takes?\s+\d+\s*min/i, /~\s*\d+\s*min/i, /estimated\s+time/i, /\bETA\b/];
47
+ const TEMPLATE_FILENAME_REGEX = /template/i;
48
+ const COMPILE_TIME_SUB_REGEX = /\{\{\.\w+\}\}/;
46
49
 
47
50
  const SEVERITY_ORDER = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
48
51
 
@@ -535,6 +538,36 @@ function validateSkill(skillDir) {
535
538
  }
536
539
  }
537
540
 
541
+ // --- TPL-01: template files must not contain compile-time {{.var}} substitutions ---
542
+ // Template files seed durable, version-controlled artifacts (spec files) that
543
+ // execute on other machines. Baking a {{.var}} at render time would freeze a
544
+ // machine-local value into every downstream artifact.
545
+ for (const filePath of allFiles) {
546
+ if (path.extname(filePath) !== '.md') continue;
547
+ const base = path.basename(filePath);
548
+ if (!TEMPLATE_FILENAME_REGEX.test(base)) continue;
549
+
550
+ const relFile = path.relative(skillDir, filePath);
551
+ const content = safeReadFile(filePath, findings, relFile);
552
+ if (content === null) continue;
553
+
554
+ const lines = content.split('\n');
555
+ for (const [i, line] of lines.entries()) {
556
+ const match = line.match(COMPILE_TIME_SUB_REGEX);
557
+ if (match) {
558
+ findings.push({
559
+ rule: 'TPL-01',
560
+ title: 'Template files must not contain compile-time substitutions',
561
+ severity: 'HIGH',
562
+ file: relFile,
563
+ line: i + 1,
564
+ detail: `Template file contains compile-time substitution \`${match[0]}\` — this would be baked at render time and leak a machine-local value into every spec produced from the template.`,
565
+ fix: 'Remove the `{{.var}}` reference. Use single-curly `{var}` if the value should be resolved at LLM runtime by the consumer of the generated spec.',
566
+ });
567
+ }
568
+ }
569
+ }
570
+
538
571
  return findings;
539
572
  }
540
573