bmad-method 6.11.1-next.2 → 6.11.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 (99) hide show
  1. package/package.json +2 -2
  2. package/src/bmm-skills/agents/bmad-agent-analyst/customize.toml +1 -3
  3. package/src/bmm-skills/agents/bmad-agent-architect/customize.toml +1 -3
  4. package/src/bmm-skills/agents/bmad-agent-dev/customize.toml +1 -3
  5. package/src/bmm-skills/agents/bmad-agent-pm/customize.toml +1 -3
  6. package/src/bmm-skills/agents/bmad-agent-ux-designer/customize.toml +1 -3
  7. package/src/bmm-skills/plan/bmad-architecture/SKILL.md +1 -1
  8. package/src/bmm-skills/plan/bmad-architecture/customize.toml +5 -6
  9. package/src/bmm-skills/plan/bmad-create-epics-and-stories/customize.toml +1 -3
  10. package/src/bmm-skills/plan/bmad-generate-project-context/SKILL.md +2 -0
  11. package/src/bmm-skills/plan/bmad-prd/customize.toml +5 -6
  12. package/src/bmm-skills/plan/bmad-prfaq/customize.toml +1 -3
  13. package/src/bmm-skills/plan/bmad-product-brief/customize.toml +5 -6
  14. package/src/bmm-skills/plan/bmad-project-context/SKILL.md +23 -12
  15. package/src/bmm-skills/plan/bmad-project-context/references/best-practices.md +26 -7
  16. package/src/bmm-skills/plan/bmad-project-context/references/template.md +1 -1
  17. package/src/bmm-skills/plan/bmad-spec/customize.toml +5 -5
  18. package/src/bmm-skills/plan/bmad-sprint-planning/customize.toml +1 -3
  19. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
  20. package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
  21. package/src/bmm-skills/plan/bmad-ux/customize.toml +1 -3
  22. package/src/bmm-skills/ship/bmad-build/SKILL.md +1 -1
  23. package/src/bmm-skills/ship/bmad-build/customize.toml +13 -16
  24. package/src/bmm-skills/ship/bmad-build/references/claims-check.md +14 -0
  25. package/src/bmm-skills/ship/bmad-build/review-prompts/edge-case-hunter.md +29 -7
  26. package/src/bmm-skills/ship/bmad-build/review-prompts/verification-gap.md +1 -1
  27. package/src/bmm-skills/ship/bmad-build/step-01-clarify-and-route.md +12 -7
  28. package/src/bmm-skills/ship/bmad-build/step-02-plan.md +13 -10
  29. package/src/bmm-skills/ship/bmad-build/step-03-implement.md +3 -1
  30. package/src/bmm-skills/ship/bmad-build/step-04-review.md +7 -5
  31. package/src/bmm-skills/ship/bmad-build/step-oneshot.md +7 -8
  32. package/src/bmm-skills/ship/bmad-build-auto/customize.toml +16 -18
  33. package/src/bmm-skills/ship/bmad-build-auto/references/claims-check.md +14 -0
  34. package/src/bmm-skills/ship/bmad-build-auto/review-prompts/edge-case-hunter.md +29 -7
  35. package/src/bmm-skills/ship/bmad-build-auto/review-prompts/verification-gap.md +1 -1
  36. package/src/bmm-skills/ship/bmad-build-auto/step-03-implement.md +5 -1
  37. package/src/bmm-skills/ship/bmad-build-auto/step-04-review.md +7 -5
  38. package/src/bmm-skills/ship/bmad-checkpoint-preview/customize.toml +1 -3
  39. package/src/bmm-skills/ship/bmad-code-review/customize.toml +14 -17
  40. package/src/bmm-skills/ship/bmad-code-review/references/claims-check.md +14 -0
  41. package/src/bmm-skills/ship/bmad-code-review/review-prompts/edge-case-hunter.md +29 -7
  42. package/src/bmm-skills/ship/bmad-code-review/review-prompts/verification-gap.md +1 -1
  43. package/src/bmm-skills/ship/bmad-code-review/steps/step-01-gather-context.md +22 -15
  44. package/src/bmm-skills/ship/bmad-code-review/steps/step-02-review.md +2 -2
  45. package/src/bmm-skills/ship/bmad-correct-course/SKILL.md +11 -3
  46. package/src/bmm-skills/ship/bmad-correct-course/customize.toml +1 -3
  47. package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/customize.toml +1 -3
  48. package/src/bmm-skills/ship/bmad-retrospective/customize.toml +1 -3
  49. package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
  50. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
  51. package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
  52. package/src/bmm-skills/v6-shims/bmad-create-architecture/SKILL.md +2 -0
  53. package/src/bmm-skills/v6-shims/bmad-create-architecture/customize.toml +1 -3
  54. package/src/bmm-skills/v6-shims/bmad-create-prd/SKILL.md +2 -0
  55. package/src/bmm-skills/v6-shims/bmad-create-prd/customize.toml +1 -3
  56. package/src/bmm-skills/v6-shims/bmad-create-story/SKILL.md +3 -1
  57. package/src/bmm-skills/v6-shims/bmad-create-story/customize.toml +1 -3
  58. package/src/bmm-skills/v6-shims/bmad-dev-auto/SKILL.md +2 -0
  59. package/src/bmm-skills/v6-shims/bmad-dev-story/SKILL.md +2 -0
  60. package/src/bmm-skills/v6-shims/bmad-dev-story/customize.toml +1 -6
  61. package/src/bmm-skills/v6-shims/bmad-document-project/SKILL.md +2 -0
  62. package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +2 -0
  63. package/src/bmm-skills/v6-shims/bmad-edit-prd/SKILL.md +2 -0
  64. package/src/bmm-skills/v6-shims/bmad-edit-prd/customize.toml +1 -3
  65. package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +2 -0
  66. package/src/bmm-skills/v6-shims/bmad-quick-dev/SKILL.md +2 -0
  67. package/src/bmm-skills/v6-shims/bmad-sprint-status/SKILL.md +2 -0
  68. package/src/bmm-skills/v6-shims/bmad-sprint-status/customize.toml +1 -3
  69. package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +2 -0
  70. package/src/bmm-skills/v6-shims/bmad-validate-prd/SKILL.md +2 -0
  71. package/src/bmm-skills/v6-shims/bmad-validate-prd/customize.toml +1 -3
  72. package/src/core-skills/bmad-advanced-elicitation/SKILL.md +21 -22
  73. package/src/core-skills/bmad-brainstorming/customize.toml +5 -6
  74. package/src/core-skills/bmad-forge-idea/customize.toml +5 -6
  75. package/src/core-skills/bmad-party-mode/customize.toml +4 -4
  76. package/src/core-skills/bmad-review/SKILL.md +5 -4
  77. package/src/core-skills/bmad-review/customize.toml +1 -1
  78. package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +16 -0
  79. package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +2 -0
  80. package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +2 -0
  81. package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +2 -0
  82. package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +2 -0
  83. package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +2 -0
  84. package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +2 -0
  85. package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
  86. package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
  87. package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
  88. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
  89. package/tools/installer/commands/install.js +2 -0
  90. package/tools/installer/core/config.js +3 -0
  91. package/tools/installer/core/installer.js +57 -1
  92. package/tools/installer/core/manifest-generator.js +6 -0
  93. package/tools/installer/core/manifest.js +6 -0
  94. package/tools/installer/core/shim-policy.js +159 -0
  95. package/tools/installer/ide/_config-driven.js +3 -3
  96. package/tools/installer/ide/platform-codes.yaml +7 -0
  97. package/tools/installer/modules/official-modules.js +38 -6
  98. package/tools/installer/prompts.js +15 -2
  99. package/tools/installer/ui.js +68 -3
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bmad-review
3
- description: 'Review any diff, document, spec, or other artifact with one or more installed review methods (lenses)shipped ones cover adversarial critique, edge cases, verification gaps, document structure, and prose. Runs whichever fit the content, or exactly the ones asked for. Use when the user says "review this", "critical review", "editorial review", "hunt edge cases", "review the structure", or "review the prose"'
3
+ description: 'Runs one or more installed review lenses — adversarial critique, edge cases, verification gaps, structure, prose — and reports triaged findings. Use when, and only when, the user asks you to review a diff, a pull request, or an artifact code or documents, one or many — and actually says "review"; an explicit skill:bmad-review directive from another skill counts as that ask. A request to act on feedback from an earlier review is a change, not a review. Never invoke this uninvited, including on edits you just made.'
4
4
  ---
5
5
 
6
6
  # BMad Review
@@ -14,6 +14,7 @@ The lens set is whatever `{workflow.lenses}` resolves to, not a fixed list — o
14
14
  - **content** — what to review: a diff, branch, uncommitted changes, file, spec, story, or any document. Args: `[path]`.
15
15
  - **lenses** (optional) — one or more lens codes or names, however the caller expresses them: a spoken request, or a directive of the form `skill:bmad-review lenses=<code>[,<code>...]` (the form bmm's `doc_standards` uses). Default: every applicable lens (a full review).
16
16
  - **also_consider** (optional) — areas to keep in mind alongside each lens's normal analysis.
17
+ - **claims** (optional) — the change's own narrative: the commit messages it covers, or whatever description of it the caller supplied. Goes to the edge-case lens alone.
17
18
  - **pre-resolved customization** (optional) — `[workflow]` field values supplied by a forwarding caller. See Execution step 1.
18
19
 
19
20
  ## Conventions
@@ -25,11 +26,11 @@ The lens set is whatever `{workflow.lenses}` resolves to, not a fixed list — o
25
26
  ## Execution
26
27
 
27
28
  1. **Resolve customization:** `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`. On failure, read `{skill-root}/customize.toml` directly and use defaults. **Forwarded activation:** if a caller invoked you with pre-resolved customization fields (e.g. the `bmad-editorial-review` shim), honor them verbatim for those named fields — they already carry the user's overrides — and resolve only the remaining fields from your own `customize.toml`. Then execute each `{workflow.activation_steps_prepend}` entry in order, hold `{workflow.persistent_facts}` as standing context for the session, and treat `{workflow.review_guidance}` entries as standing review directives for every lens.
28
- 2. **Load the content.** If it is empty or cannot be decoded as text: when the caller expects the raw findings JSON array (e.g. the legacy edge-case forwarder), return `[{"location":"N/A","trigger_condition":"Input empty or undecodable","guard_snippet":"Provide valid content to review","potential_consequence":"Review skipped — no analysis performed"}]` (no `lens` field) and stop; otherwise say what's wrong and ask for reviewable content. Classify the content — diff, source file, function, or document — and whether it is **code** or **docs**; scope rules and lens applicability both depend on it. A document that defines behavior (spec, requirements, plan, story) is `docs` that a behavioral lens may still apply to; judge by `when`.
29
+ 2. **Load the content.** Stage it once as a file: when the content is a branch, uncommitted work, or a commit range, use the repository's version-control tooling to write the unified diff to a uniquely-named file in the system temp directory and take that file's absolute path as the content. A branch means its diff against the merge base with its base branch; uncommitted work includes untracked files. Stage `claims` to its own file the same way — it is input for one lens, staged separately precisely so the other lenses never see it. If the content is empty or cannot be decoded as text: when the caller expects the raw findings JSON array (e.g. the legacy edge-case forwarder), return `[{"location":"N/A","trigger_condition":"Input empty or undecodable","guard_snippet":"Provide valid content to review","potential_consequence":"Review skipped — no analysis performed"}]` (no `lens` field) and stop; otherwise say what's wrong and ask for reviewable content. Classify the content — diff, source file, function, or document — and whether it is **code** or **docs**; scope rules and lens applicability both depend on it. A document that defines behavior (spec, requirements, plan, story) is `docs` that a behavioral lens may still apply to; judge by `when`.
29
30
  3. **Select lenses** from `{workflow.lenses}`. A lens with an empty `instruction` is disabled. If the user or caller named lenses, run exactly those only — `applies_to` and `when` do not filter an explicit request. Otherwise run every enabled lens whose `applies_to` covers the content class (`any` always covers) and whose `when` applies.
30
31
  4. **Announce the plan** in one line before running anything: the content class, the lenses about to run, and — when any lens has `after` set — that it runs on top of the named lens's findings. Skip the announcement entirely when the caller pinned an exact output contract (the legacy forwarders that demand raw JSON or one exact line) — their contract covers everything you emit, not just the findings block. Then execute each `{workflow.activation_steps_append}` entry in order.
31
- 5. **Run the independent lenses** — every selected lens without `after`. Each sees the content and `also_consider`, never another lens's findings. Follow each lens's `instruction`; the shipped lenses load their reference file just-in-time, so load only what runs. When subagents are available, spawn one per lens in parallel: give it the lens `instruction` with `{skill-root}` and paths resolved absolute, the content or where to read it, any `also_consider` areas, the standing review directives, and the constraint "Return ONLY your findings — no other output." Otherwise run the lenses sequentially yourself, completing one before starting the next.
32
- 6. **Run the dependent lenses** — every selected lens with `after`, once the lens it names has completed, passing that lens's findings in. A lens whose `after` target was not selected or produced nothing still runs, with no prior findings. Dependent lenses that name different targets are independent of each other and may run in parallel.
32
+ 5. **Run the independent lenses** — every selected lens without `after`. Each sees the content and `also_consider`, never another lens's findings. Follow each lens's `instruction`; the shipped lenses load their reference file just-in-time, so load only what runs. When subagents are available, launch every independent lens before handling any lens's result. Try running them simultaneously: spawn one per lens; give it the lens `instruction` with `{skill-root}` and paths resolved absolute, the absolute path of the staged content file (a lens prompt carries the path and the lens reads the file, never the content bytes; inline the content only when it was never staged as a file), any `also_consider` areas, the standing review directives, the `claims` path to the edge-case lens alone (marked to leave unread until its instructions call for it), and the constraint "Return ONLY your findings — no other output. Do not invoke any skill, and do not spawn subagents of your own — you are the reviewer. Return your findings as text in your final message; do not route them through any findings-reporting tool the host may offer." Otherwise run the lenses sequentially yourself, completing one before starting the next.
33
+ 6. **Run the dependent lenses** — every selected lens with `after`, once the lens it names has completed, passing that lens's findings in. A lens whose `after` target was not selected or produced nothing still runs, with no prior findings. Dependent lenses that name different targets are independent of each other: launch every ready one before handling any of their results. Try running them simultaneously. When subagents are available, spawn them with the same constraint as independent lenses: "Return ONLY your findings — no other output. Do not invoke any skill, and do not spawn subagents of your own — you are the reviewer. Return your findings as text in your final message; do not route them through any findings-reporting tool the host may offer."
33
34
  7. **Assemble and present** per Output below. Keep every lens's findings — overlap between lenses is signal, not duplication; note it in the markdown report rather than deduping. Execute `{workflow.on_complete}` if set.
34
35
 
35
36
  ## Output
@@ -23,7 +23,7 @@ activation_steps_append = []
23
23
  # prefixed `file:` are paths or globs whose contents load as facts; all others
24
24
  # are literal facts. The shipped entry is a project-wide glob — set it to []
25
25
  # if you don't want every review scanning for it.
26
- persistent_facts = ["file:{project-root}/**/project-context.md"]
26
+ persistent_facts = []
27
27
 
28
28
  # Standing review directives applied on every run alongside each lens's own
29
29
  # method. Each entry is a literal sentence or a `file:`-prefixed path/glob
@@ -9,6 +9,7 @@ You are a pure path tracer. Never comment on whether the content is good or bad;
9
9
  - When the content is a diff, scan only the diff hunks and list boundaries that are directly reachable from the changed lines and lack an explicit guard in the diff.
10
10
  - When it is not a diff (full file, function, or document), the entire provided content is the scope.
11
11
  - Ignore the rest of the codebase unless the provided content explicitly references external functions.
12
+ - When the launch message names a claims file, do NOT read it before Step 4: the path tracing in Steps 1–2 must finish before the narrative is seen.
12
13
 
13
14
  ## Step 1: Exhaustive path analysis
14
15
 
@@ -38,6 +39,21 @@ Deletion findings go in the same array with the four standard fields plus:
38
39
 
39
40
  For a deletion finding the standard fields read as: `location` = the removed item; `trigger_condition` = the behavior or contract it enforced; `guard_snippet` = where or how to re-establish it; `potential_consequence` = the regression or orphan.
40
41
 
42
+ ## Step 4: Claims check
43
+
44
+ Runs only when the message that launched you named a claims file. Read that file now, for the first time; the path tracing is finished and the claims cannot steer it retroactively.
45
+
46
+ The file holds the change's own narrative — commit messages and any stated description. The narrative is the author's testimony, not evidence: a claim repeated in a code comment is still the same claim, not confirmation. Extract each checkable claim — what the change does, what it preserves, ordering, arithmetic, and parity with existing code ("exactly as X does") — then try to falsify each one against the code you have already traced. Where your trace is not enough to decide, read the code that decides it: the compared-to function, the actual callee, the state the claim assumes.
47
+
48
+ Claim findings go in the same array with the four standard fields plus:
49
+
50
+ - `kind`: `"claim"`
51
+ - `confidence`: `"high"`, `"medium"`, or `"low"`
52
+
53
+ For a claim finding the standard fields read as: `location` = where the code contradicts the claim; `trigger_condition` = the claim, quoted or tightly paraphrased; `guard_snippet` = what the code actually does; `potential_consequence` = what goes wrong for someone who believed the claim.
54
+
55
+ Verified claims produce nothing. Add nothing if nothing is falsified.
56
+
41
57
  ## Findings shape
42
58
 
43
59
  Each edge-case finding contains exactly these four fields:
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: bmad-editorial-review
3
3
  description: 'Deprecated — forwards to bmad-review'
4
+ metadata:
5
+ lifecycle: shim
4
6
  ---
5
7
 
6
8
  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.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: bmad-editorial-review-prose
3
3
  description: 'Deprecated — forwards to bmad-review'
4
+ metadata:
5
+ lifecycle: shim
4
6
  ---
5
7
 
6
8
  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`.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: bmad-editorial-review-structure
3
3
  description: 'Deprecated — forwards to bmad-review'
4
+ metadata:
5
+ lifecycle: shim
4
6
  ---
5
7
 
6
8
  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`.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: bmad-review-adversarial-general
3
3
  description: 'Deprecated — forwards to bmad-review'
4
+ metadata:
5
+ lifecycle: shim
4
6
  ---
5
7
 
6
8
  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.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: bmad-review-edge-case-hunter
3
3
  description: 'Deprecated — forwards to bmad-review'
4
+ metadata:
5
+ lifecycle: shim
4
6
  ---
5
7
 
6
8
  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.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: bmad-review-verification-gap
3
3
  description: 'Deprecated — forwards to bmad-review'
4
+ metadata:
5
+ lifecycle: shim
4
6
  ---
5
7
 
6
8
  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.`
@@ -34,6 +34,8 @@ module.exports = {
34
34
  ['--document-output-language <lang>', 'Language for document output (default: English)'],
35
35
  ['--output-folder <path>', 'Output folder path relative to project root (default: _bmad-output)'],
36
36
  ['--custom-source <sources>', 'Comma-separated Git URLs or local paths to install custom modules from'],
37
+ ['--shims', 'Install deprecated compatibility shim skills when the selected modules provide them'],
38
+ ['--no-shims', 'Do not install deprecated compatibility shim skills'],
37
39
  ['-y, --yes', 'Accept all defaults and skip prompts where possible'],
38
40
  [
39
41
  '--channel <channel>',
@@ -15,6 +15,7 @@ class Config {
15
15
  quickUpdate,
16
16
  channelOptions,
17
17
  setOverrides,
18
+ installShims,
18
19
  }) {
19
20
  this.directory = directory;
20
21
  this.modules = Object.freeze([...modules]);
@@ -32,6 +33,7 @@ class Config {
32
33
  // Intentionally NOT integrated with the prompt/template/schema flow; see
33
34
  // `tools/installer/set-overrides.js` for the rationale and tradeoffs.
34
35
  this.setOverrides = setOverrides || {};
36
+ this.installShims = typeof installShims === 'boolean' ? installShims : undefined;
35
37
  Object.freeze(this);
36
38
  }
37
39
 
@@ -58,6 +60,7 @@ class Config {
58
60
  quickUpdate: userInput._quickUpdate || false,
59
61
  channelOptions: userInput.channelOptions || null,
60
62
  setOverrides: userInput.setOverrides || {},
63
+ installShims: userInput.installShims,
61
64
  });
62
65
  }
63
66
 
@@ -13,6 +13,14 @@ const { InstallPaths } = require('./install-paths');
13
13
  const { ExternalModuleManager } = require('../modules/external-manager');
14
14
  const { resolveModuleVersion } = require('../modules/version-resolver');
15
15
  const { MODULE_HELP_CSV_HEADER } = require('../modules/module-help-schema');
16
+ const {
17
+ formatRemovedShimNotice,
18
+ formatRetainedShimNotice,
19
+ inferShimPreference,
20
+ readInstalledShims,
21
+ readInstalledSkillIds,
22
+ selectShimOutcome,
23
+ } = require('./shim-policy');
16
24
 
17
25
  const { ExistingInstall } = require('./existing-install');
18
26
  const { warnPreNativeSkillsLegacy } = require('./legacy-warnings');
@@ -42,6 +50,39 @@ class Installer {
42
50
  const paths = await InstallPaths.create(config);
43
51
  const officialModules = await OfficialModules.build(config, paths);
44
52
  const existingInstall = await ExistingInstall.detect(paths.bmadDir);
53
+ const availableShims = await officialModules.discoverShims(config.modules, {
54
+ channelOptions: config.channelOptions,
55
+ });
56
+ const previousManifest = existingInstall.installed ? await this.manifest.read(paths.bmadDir) : null;
57
+ const installedSkillIds = existingInstall.installed ? await readInstalledSkillIds(paths.bmadDir) : new Set();
58
+ const shimPolicy = {
59
+ available: availableShims.length > 0,
60
+ install: inferShimPreference({
61
+ requested: config.installShims,
62
+ persisted: previousManifest?.installShims,
63
+ availableShims,
64
+ installedSkillIds,
65
+ existing: existingInstall.installed,
66
+ }),
67
+ };
68
+
69
+ const installedShims = existingInstall.installed ? await readInstalledShims(paths.bmadDir) : [];
70
+ const { retained: retainedShims, removed: removedShims } = selectShimOutcome({
71
+ installedShims,
72
+ availableShims,
73
+ install: shimPolicy.install,
74
+ });
75
+
76
+ // Reported here, not at the prompt, so --yes/--shims/scripted runs get it too.
77
+ if (retainedShims.length > 0) {
78
+ await prompts.note(formatRetainedShimNotice(retainedShims), 'Deprecated shim skills retained');
79
+ }
80
+ if (removedShims.length > 0) {
81
+ await prompts.note(formatRemovedShimNotice(removedShims, { canReinstall: shimPolicy.available }), 'Deprecated shim skills removed');
82
+ }
83
+
84
+ // The notices above scroll away on a long install; repeat them in the summary.
85
+ const shimStatus = { retained: retainedShims.length, removed: removedShims.length };
45
86
 
46
87
  try {
47
88
  await warnPreNativeSkillsLegacy({
@@ -92,6 +133,7 @@ class Installer {
92
133
  addResult,
93
134
  officialModules,
94
135
  previousSkillManifestRows,
136
+ shimPolicy,
95
137
  );
96
138
 
97
139
  await this._setupIdes(config, allModules, paths, addResult, previousSkillIds);
@@ -115,6 +157,7 @@ class Installer {
115
157
  customFiles: restoreResult.customFiles.length > 0 ? restoreResult.customFiles : undefined,
116
158
  modifiedFiles: restoreResult.modifiedFiles.length > 0 ? restoreResult.modifiedFiles : undefined,
117
159
  preInstallVersions,
160
+ shimStatus,
118
161
  });
119
162
 
120
163
  return {
@@ -225,9 +268,11 @@ class Installer {
225
268
  addResult,
226
269
  officialModules,
227
270
  previousSkillManifestRows = [],
271
+ shimPolicy = null,
228
272
  ) {
229
273
  const isQuickUpdate = config.isQuickUpdate();
230
274
  const moduleConfigs = officialModules.moduleConfigs;
275
+ const resolvedShimPolicy = shimPolicy || { available: false, install: false };
231
276
 
232
277
  const dirResults = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
233
278
 
@@ -251,6 +296,7 @@ class Installer {
251
296
  await this._installOfficialModules(config, paths, officialModuleIds, addResult, isQuickUpdate, officialModules, {
252
297
  message,
253
298
  installedModuleNames,
299
+ shimPolicy: resolvedShimPolicy,
254
300
  });
255
301
 
256
302
  return `${allModules.length} module(s) ${isQuickUpdate ? 'updated' : 'installed'}`;
@@ -325,6 +371,8 @@ class Installer {
325
371
  ides: config.ides || [],
326
372
  preservedModules: modulesForCsvPreserve,
327
373
  moduleConfigs,
374
+ installShims: resolvedShimPolicy.install,
375
+ shimsAvailable: resolvedShimPolicy.available,
328
376
  });
329
377
  await this._appendPreservedSkillManifestRows(paths.bmadDir, previousSkillManifestRows, preservedModules);
330
378
 
@@ -727,7 +775,7 @@ class Installer {
727
775
  * @param {Object} ctx - Shared context: { message, installedModuleNames }
728
776
  */
729
777
  async _installOfficialModules(config, paths, officialModuleIds, addResult, isQuickUpdate, officialModules, ctx) {
730
- const { message, installedModuleNames } = ctx;
778
+ const { message, installedModuleNames, shimPolicy } = ctx;
731
779
  const { CustomModuleManager } = require('../modules/custom-module-manager');
732
780
 
733
781
  for (const moduleName of officialModuleIds) {
@@ -749,6 +797,7 @@ class Installer {
749
797
  installer: this,
750
798
  silent: true,
751
799
  channelOptions: config.channelOptions,
800
+ installShims: shimPolicy.install,
752
801
  },
753
802
  );
754
803
 
@@ -1238,6 +1287,12 @@ class Installer {
1238
1287
  if (context.modifiedFiles && context.modifiedFiles.length > 0) {
1239
1288
  lines.push(` ${color.yellow(`Modified files backed up (.bak): ${context.modifiedFiles.length}`)}`);
1240
1289
  }
1290
+ if (context.shimStatus?.retained > 0) {
1291
+ lines.push(` ${color.yellow(`Deprecated shim skills retained: ${context.shimStatus.retained}`)} (re-run to remove them)`);
1292
+ }
1293
+ if (context.shimStatus?.removed > 0) {
1294
+ lines.push(` ${color.green(`Deprecated shim skills removed: ${context.shimStatus.removed}`)}`);
1295
+ }
1241
1296
 
1242
1297
  // Next steps
1243
1298
  lines.push(
@@ -1500,6 +1555,7 @@ class Installer {
1500
1555
  // (`applySetOverrides`) runs at the end of quick-update too. The
1501
1556
  // installer.install path applies them after writeCentralConfig.
1502
1557
  setOverrides: config.setOverrides || {},
1558
+ installShims: config.installShims,
1503
1559
  actionType: 'install',
1504
1560
  _quickUpdate: true,
1505
1561
  _preserveModules: skippedModules,
@@ -58,6 +58,8 @@ class ManifestGenerator {
58
58
  this.bmadDir = bmadDir;
59
59
  this.bmadFolderName = path.basename(bmadDir); // Get the actual folder name (e.g., '_bmad' or 'bmad')
60
60
  this.allInstalledFiles = installedFiles;
61
+ this.installShims = options.installShims === true;
62
+ this.shimsAvailable = options.shimsAvailable === true;
61
63
 
62
64
  if (!Object.prototype.hasOwnProperty.call(options, 'ides')) {
63
65
  throw new Error('ManifestGenerator requires `options.ides` to be provided – installer should supply the selected IDEs array.');
@@ -378,6 +380,10 @@ class ManifestGenerator {
378
380
  ides: this.selectedIdes,
379
381
  };
380
382
 
383
+ if (this.shimsAvailable) {
384
+ manifest.installation.installShims = this.installShims;
385
+ }
386
+
381
387
  // Clean the manifest to remove any non-serializable values
382
388
  const cleanManifest = structuredClone(manifest);
383
389
 
@@ -61,6 +61,10 @@ class Manifest {
61
61
  ides: data.ides || [],
62
62
  };
63
63
 
64
+ if (typeof data.installShims === 'boolean') {
65
+ manifestData.installation.installShims = data.installShims;
66
+ }
67
+
64
68
  // Write YAML manifest
65
69
  // Clean the manifest data to remove any non-serializable values
66
70
  const cleanManifestData = structuredClone(manifestData);
@@ -106,6 +110,7 @@ class Manifest {
106
110
  version: manifestData.installation?.version,
107
111
  installDate: manifestData.installation?.installDate,
108
112
  lastUpdated: manifestData.installation?.lastUpdated,
113
+ installShims: manifestData.installation?.installShims,
109
114
  modules: moduleNames, // Simple array of module names for backward compatibility
110
115
  modulesDetailed: hasDetailedModules ? modules : null, // New detailed format
111
116
  ides: manifestData.ides || [],
@@ -153,6 +158,7 @@ class Manifest {
153
158
  version: manifest.installation?.version,
154
159
  installDate: manifest.installation?.installDate,
155
160
  lastUpdated: manifest.installation?.lastUpdated,
161
+ installShims: manifest.installation?.installShims,
156
162
  modules: moduleNames,
157
163
  modulesDetailed: hasDetailedModules ? modules : null,
158
164
  ides: manifest.ides || [],
@@ -0,0 +1,159 @@
1
+ const path = require('node:path');
2
+ const fs = require('../fs-native');
3
+ const yaml = require('yaml');
4
+ const csv = require('csv-parse/sync');
5
+
6
+ function parseSkillMetadata(content) {
7
+ const normalized = content.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
8
+ const match = normalized.match(/^---\n([\s\S]*?)\n---/);
9
+ if (!match) return null;
10
+
11
+ try {
12
+ const frontmatter = yaml.parse(match[1]);
13
+ return frontmatter && typeof frontmatter === 'object' ? frontmatter : null;
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+
19
+ function isShimSkill(metadata) {
20
+ return metadata?.metadata?.lifecycle === 'shim';
21
+ }
22
+
23
+ async function discoverShims(modulePath) {
24
+ const shims = [];
25
+
26
+ const walk = async (dir) => {
27
+ let entries;
28
+ try {
29
+ entries = await fs.readdir(dir, { withFileTypes: true });
30
+ } catch {
31
+ return;
32
+ }
33
+
34
+ const skillFile = path.join(dir, 'SKILL.md');
35
+ if (await fs.pathExists(skillFile)) {
36
+ const metadata = parseSkillMetadata(await fs.readFile(skillFile, 'utf8'));
37
+ if (isShimSkill(metadata)) {
38
+ shims.push({
39
+ id: metadata.name || path.basename(dir),
40
+ description: typeof metadata.description === 'string' ? metadata.description : '',
41
+ directory: dir,
42
+ relativeDirectory: path.relative(modulePath, dir),
43
+ });
44
+ }
45
+ return;
46
+ }
47
+
48
+ for (const entry of entries) {
49
+ if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name.startsWith('_')) continue;
50
+ await walk(path.join(dir, entry.name));
51
+ }
52
+ };
53
+
54
+ await walk(modulePath);
55
+ return shims;
56
+ }
57
+
58
+ async function readSkillManifest(bmadDir) {
59
+ const manifestPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
60
+ if (!(await fs.pathExists(manifestPath))) return [];
61
+
62
+ try {
63
+ const content = await fs.readFile(manifestPath, 'utf8');
64
+ return csv.parse(content, { columns: true, skip_empty_lines: true });
65
+ } catch {
66
+ // A missing or unreadable legacy manifest means there is no reliable
67
+ // evidence that compatibility shims were installed.
68
+ return [];
69
+ }
70
+ }
71
+
72
+ async function readInstalledSkillIds(bmadDir) {
73
+ const ids = new Set();
74
+ for (const record of await readSkillManifest(bmadDir)) {
75
+ if (record.canonicalId) ids.add(record.canonicalId);
76
+ }
77
+ return ids;
78
+ }
79
+
80
+ // The installed manifest carries no lifecycle column, so the description
81
+ // prefix every shim ships with is the only record of what was a shim. This
82
+ // is the same signal validate-skills.js uses to exempt them.
83
+ async function readInstalledShims(bmadDir) {
84
+ const shims = [];
85
+ for (const record of await readSkillManifest(bmadDir)) {
86
+ if (!record.canonicalId || !/^\s*deprecated\b/i.test(record.description || '')) continue;
87
+ shims.push({ id: record.canonicalId, description: record.description || '', module: record.module || '' });
88
+ }
89
+ return shims;
90
+ }
91
+
92
+ function inferShimPreference({ requested, persisted, availableShims = [], installedSkillIds = new Set(), existing = false }) {
93
+ if (availableShims.length === 0) return false;
94
+ if (typeof requested === 'boolean') return requested;
95
+ if (typeof persisted === 'boolean') return persisted;
96
+ if (!existing) return false;
97
+
98
+ return availableShims.some((shim) => installedSkillIds.has(shim.id));
99
+ }
100
+
101
+ // Removal is driven by what is installed, not by what this release ships: a
102
+ // shim retired from source is still deleted by the update cleanup.
103
+ function selectShimOutcome({ installedShims = [], availableShims = [], install = false }) {
104
+ const availableShimIds = new Set(availableShims.map((shim) => shim.id));
105
+ return {
106
+ retained: install ? availableShims : [],
107
+ removed: installedShims.filter((shim) => !(install && availableShimIds.has(shim.id))),
108
+ };
109
+ }
110
+
111
+ // Shim descriptions all open with "Deprecated — "; the notice heading says it once.
112
+ function describeShim(shim) {
113
+ const cleaned = (shim.description || '').replace(/^\s*deprecated\s*[-–—:]*\s*/i, '').trim();
114
+ const source = shim.module ? ` (${shim.module})` : '';
115
+ return cleaned ? ` ${shim.id}${source}: ${cleaned}` : ` ${shim.id}${source}`;
116
+ }
117
+
118
+ function formatRetainedShimNotice(availableShims = []) {
119
+ const lines = availableShims.map((shim) => describeShim(shim)).sort();
120
+
121
+ return [
122
+ `${availableShims.length} deprecated shim skill(s) are still installed. Each one only forwards to the skill that replaced it:`,
123
+ '',
124
+ ...lines,
125
+ '',
126
+ 'Shims will be removed with v7, and anything still calling the old name stops working then.',
127
+ 'Only keep a shim if you customized it and still need to move that customization to the replacement.',
128
+ 'Once you have, re-run Quick Update and answer No to this question so the shims come off.',
129
+ ].join('\n');
130
+ }
131
+
132
+ function formatRemovedShimNotice(removedShims = [], { canReinstall = true } = {}) {
133
+ const lines = removedShims.map((shim) => describeShim(shim)).sort();
134
+ const recovery = canReinstall
135
+ ? 'these shims, move it to the replacement, or re-run with --shims to put the shims back.'
136
+ : 'these shims, move it to the replacement. This release no longer ships them, so --shims cannot bring them back.';
137
+
138
+ return [
139
+ `${removedShims.length} deprecated shim skill(s) are being removed. Invoking these names will no longer work:`,
140
+ '',
141
+ ...lines,
142
+ '',
143
+ 'Each replacement named above is installed and ready. If you still had a customization on one of',
144
+ recovery,
145
+ ].join('\n');
146
+ }
147
+
148
+ module.exports = {
149
+ describeShim,
150
+ formatRemovedShimNotice,
151
+ discoverShims,
152
+ formatRetainedShimNotice,
153
+ inferShimPreference,
154
+ isShimSkill,
155
+ parseSkillMetadata,
156
+ readInstalledShims,
157
+ readInstalledSkillIds,
158
+ selectShimOutcome,
159
+ };
@@ -448,9 +448,9 @@ class ConfigDrivenIdeSetup {
448
448
  await fs.ensureDir(skillDir);
449
449
  this.skillWriteTracker?.add(canonicalId);
450
450
 
451
- // Copy all skill files, filtering OS/editor artifacts recursively
452
- const skipPatterns = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
453
- const skipSuffixes = ['~', '.swp', '.swo', '.bak'];
451
+ // Copy all skill files, filtering OS/editor artifacts and Python caches recursively
452
+ const skipPatterns = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini', '__pycache__']);
453
+ const skipSuffixes = ['~', '.swp', '.swo', '.bak', '.pyc', '.pyo'];
454
454
  const filter = (src) => {
455
455
  const name = path.basename(src);
456
456
  if (src === sourceDir) return true;
@@ -169,6 +169,13 @@ platforms:
169
169
  target_dir: .agents/skills
170
170
  global_target_dir: ~/.config/agents/skills
171
171
 
172
+ grok:
173
+ name: "Grok"
174
+ preferred: false
175
+ installer:
176
+ target_dir: .agents/skills
177
+ global_target_dir: ~/.grok/skills
178
+
172
179
  hermes:
173
180
  name: "Hermes Agent"
174
181
  preferred: false