boost-skill-cli 1.0.7__tar.gz → 1.0.15__tar.gz

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. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/.gitignore +5 -0
  2. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/CLAUDE.md +22 -0
  3. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/PKG-INFO +56 -5
  4. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/README.md +55 -4
  5. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/_version.py +2 -2
  6. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/cli.py +6 -3
  7. boost_skill_cli-1.0.15/boost_cli/commands/bmad.py +403 -0
  8. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/configuration.py +17 -5
  9. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/discovery.py +41 -3
  10. boost_skill_cli-1.0.15/boost_cli/commands/hooks.py +92 -0
  11. boost_skill_cli-1.0.15/boost_cli/core/claude_settings.py +203 -0
  12. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/config.py +2 -2
  13. boost_skill_cli-1.0.15/boost_cli/core/rag.py +394 -0
  14. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_skill_cli.egg-info/PKG-INFO +56 -5
  15. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_skill_cli.egg-info/SOURCES.txt +13 -0
  16. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/docs/index.html +4 -3
  17. boost_skill_cli-1.0.15/docs/roadmap.html +469 -0
  18. boost_skill_cli-1.0.15/style/README.md +92 -0
  19. boost_skill_cli-1.0.15/style/boost.css +352 -0
  20. boost_skill_cli-1.0.15/style/boost.js +55 -0
  21. boost_skill_cli-1.0.15/style/demo.html +140 -0
  22. boost_skill_cli-1.0.15/tests/functional/test_cli_bmad.py +223 -0
  23. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_configuration.py +22 -3
  24. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_discovery.py +38 -0
  25. boost_skill_cli-1.0.15/tests/functional/test_cli_hooks.py +58 -0
  26. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_everyday_loop.py +1 -1
  27. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/smoke.sh +1 -0
  28. boost_skill_cli-1.0.15/tests/unit/test_claude_settings.py +124 -0
  29. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_config.py +1 -1
  30. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_errors_and_cli_table.py +8 -8
  31. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_frontmatter.py +103 -0
  32. boost_skill_cli-1.0.15/tests/unit/test_rag.py +824 -0
  33. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/CONTRIBUTING.md +0 -0
  34. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/LICENSE +0 -0
  35. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/MANIFEST.in +0 -0
  36. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/Makefile +0 -0
  37. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/SECURITY.md +0 -0
  38. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost +0 -0
  39. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/__init__.py +0 -0
  40. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/__main__.py +0 -0
  41. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/__init__.py +0 -0
  42. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/info.py +0 -0
  43. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/intelligence.py +0 -0
  44. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/pkg.py +0 -0
  45. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/quality.py +0 -0
  46. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/taps.py +0 -0
  47. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/commands/team.py +0 -0
  48. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/__init__.py +0 -0
  49. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/agents.py +0 -0
  50. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/ai.py +0 -0
  51. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/catalog.py +0 -0
  52. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/frontmatter.py +0 -0
  53. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/gitutil.py +0 -0
  54. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/journal.py +0 -0
  55. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/lockfile.py +0 -0
  56. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/logs.py +0 -0
  57. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/output.py +0 -0
  58. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/paths.py +0 -0
  59. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/policy.py +0 -0
  60. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/registry.py +0 -0
  61. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/store.py +0 -0
  62. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/core/util.py +0 -0
  63. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/data/registries.json +0 -0
  64. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_cli/errors.py +0 -0
  65. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  66. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  67. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/boost_skill_cli.egg-info/top_level.txt +0 -0
  68. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/docs/.claude/settings.local.json +0 -0
  69. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/docs/DEBUGGING.md +0 -0
  70. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/docs/demo.gif +0 -0
  71. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/docs/demo.tape +0 -0
  72. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/docs/rag-architecture.md +0 -0
  73. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/pyproject.toml +0 -0
  74. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/scripts/build_registries.py +0 -0
  75. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/scripts/mutation_gate.py +0 -0
  76. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/setup.cfg +0 -0
  77. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/conftest.py +0 -0
  78. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_info.py +0 -0
  79. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_intelligence.py +0 -0
  80. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_pkg.py +0 -0
  81. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_quality.py +0 -0
  82. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_taps.py +0 -0
  83. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/functional/test_cli_team.py +0 -0
  84. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/make_fixture.py +0 -0
  85. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_agents.py +0 -0
  86. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_ai.py +0 -0
  87. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_catalog.py +0 -0
  88. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_gitutil.py +0 -0
  89. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_journal.py +0 -0
  90. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_lockfile.py +0 -0
  91. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_logs.py +0 -0
  92. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_mutation_hardening.py +0 -0
  93. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_output.py +0 -0
  94. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_paths.py +0 -0
  95. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_policy.py +0 -0
  96. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_registry.py +0 -0
  97. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_store.py +0 -0
  98. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_util.py +0 -0
  99. {boost_skill_cli-1.0.7 → boost_skill_cli-1.0.15}/tests/unit/test_version.py +0 -0
@@ -219,3 +219,8 @@ __marimo__/
219
219
 
220
220
  # setuptools-scm generated version file
221
221
  boost_cli/_version.py
222
+
223
+ # BMAD Method — generated artifacts (provisioned via `boost bmad`, not source)
224
+ _bmad/
225
+ _bmad-output/
226
+ .claude/skills/bmad-*/
@@ -50,6 +50,28 @@ line coverage. Target `boost_cli/core` behavior with assertions, not just import
50
50
  `workflow` (commands/agents/workflows Markdown). **Only `skill` installs** —
51
51
  `store.install` refuses non-skill kinds; rules/workflows are search/tap-only.
52
52
 
53
+ ## Parallel work & concurrent loops
54
+
55
+ More than one agent (or `/loop` session) may be working this repo at once. To
56
+ keep them from clobbering each other:
57
+
58
+ - **Never edit a checkout that has someone else's branch checked out.** The user
59
+ works live in `~/boost`; long-running loops use a separate `git worktree`
60
+ (e.g. `~/boost-loop`, which needs its **own** `.venv` because mutmut wants an
61
+ editable `pip install -e .` pointed at that tree). Run `git worktree list` and
62
+ `git status` before touching a tree — staged or unstaged changes mean another
63
+ agent is mid-edit there, so pick a different worktree.
64
+ - **Branch off `origin/main`, one topic per branch.** `git fetch origin main`
65
+ then `git checkout -B loop/<topic> origin/main`. Keep changes additive and
66
+ file-scoped so parallel branches merge without conflicts.
67
+ - **Coordinate through PRs, not the working tree.** `gh pr merge` is server-side
68
+ and won't touch anyone's local checkout. Before committing a file, inspect
69
+ `git status` / `git diff` — if another session has it staged or dirty, leave
70
+ it to that owner rather than committing over their in-flight work.
71
+ - **Every merge to `main` cuts a PyPI release.** Land one coherent change per PR,
72
+ never merge onto a red release, and after merging confirm the publish workflow
73
+ goes green — not just the PR checks.
74
+
53
75
  ## Layout
54
76
 
55
77
  - `boost_cli/commands/` — CLI command groups · `boost_cli/core/` — engine (the mutation-gated code)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.7
3
+ Version: 1.0.15
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -718,7 +718,11 @@ pulled from GitHub-hosted registries, then hooks them straight into
718
718
  skill files into three different agent folders and hoping you remember to
719
719
  update all of them, you run one command.
720
720
 
721
- ![boost demo](docs/demo.gif)
721
+ <p align="center">
722
+ <img src="docs/demo.gif" alt="boost demo: tap a registry, search for a skill, install it into every agent, then run doctor" width="820">
723
+ <br>
724
+ <sub><em>tap a registry → search → install into every agent → doctor — one flow</em></sub>
725
+ </p>
722
726
 
723
727
  ## What's a "skill," exactly?
724
728
 
@@ -797,7 +801,7 @@ boost doctor # sanity check: broken links, lock drift, stale taps
797
801
  boost bundle dump > Boostfile # everyone else runs: boost bundle install
798
802
  ```
799
803
 
800
- ## 72 commands, organized into 8 groups
804
+ ## 75 commands, organized into 8 groups
801
805
 
802
806
  `boost --help` prints the full grouped command list; for a visual tour see
803
807
  [`docs/overview.html`](docs/overview.html).
@@ -805,12 +809,12 @@ boost bundle dump > Boostfile # everyone else runs: boost bundle install
805
809
  | Group | Commands |
806
810
  |---|---|
807
811
  | **Package Management** | install · uninstall · sync · update · reinstall · bundle · import · migrate · pin · unpin · snapshot · export |
808
- | **Discovery & Search** | search · discover · recommend · browse · index · trending · stats · count |
812
+ | **Discovery & Search** | search · reindex · discover · recommend · browse · index · trending · stats · count |
809
813
  | **Skill Information** | list · info · cat · edit · preview · explain · log · home · deps · tag |
810
814
  | **Registry (Taps)** | tap · untap · taps · outdated |
811
815
  | **Intelligence** | distill · simulate · infer · absorb · evolve · context · focus · impact |
812
816
  | **Quality & Health** | doctor · lint · audit · verify · drift · test · fingerprint · quarantine · decay · heal · conflict · changelog · attest · health |
813
- | **Configuration** | config · clean · create · policy · onboard · completions · schedule · serve · mcp · self-update |
817
+ | **Configuration** | config · clean · create · policy · onboard · completions · schedule · serve · mcp · hooks · bmad · self-update |
814
818
  | **Team & Collaboration** | cohort · profile · protocol · pulse · replay · who |
815
819
 
816
820
  The AI-assisted commands (`search --smart`, `explain`, `distill`, `infer`,
@@ -819,6 +823,53 @@ available on your PATH (or `ANTHROPIC_API_KEY` is set), and fall back to
819
823
  plain heuristics when it isn't — so the tool still works without an API key,
820
824
  just less cleverly.
821
825
 
826
+ ## Claude Code hooks & the BMAD Method
827
+
828
+ `boost hooks` manages Claude Code hooks in `settings.json` at either scope —
829
+ `--scope project` (`./.claude/settings.json`) or `--scope global`
830
+ (`~/.claude/settings.json`). boost only ever touches hooks it created (tagged
831
+ with a `# boost:<name>` marker in the command), never your own, and snapshots
832
+ the prior file before each write.
833
+
834
+ ```bash
835
+ boost hooks add SessionStart -c 'echo hello' -n greet --scope project
836
+ boost hooks list
837
+ boost hooks remove -n greet --scope project
838
+ ```
839
+
840
+ `boost bmad` installs and manages the [BMAD Method](https://bmadcode.com/)
841
+ (agentic Analyst / PM / Architect / Dev / QA personas + workflows) as a
842
+ scope-aware, toggleable startup experience. Provisioning is delegated to the
843
+ canonical `npx bmad-method install` (needs Node.js 20.12+); boost owns scope,
844
+ the startup toggle, and teardown.
845
+
846
+ ```bash
847
+ boost bmad install --scope project # skills + per-project _bmad/ runtime
848
+ boost bmad install --scope global # skills into ~/.claude/skills for every session
849
+ boost bmad init # add the _bmad/ runtime to the current repo
850
+ boost bmad startup on # inject light BMAD orientation on session start
851
+ boost bmad startup off # stop injecting it (skills stay installed)
852
+ boost bmad disable / enable # quarantine / restore skills (recoverable)
853
+ boost bmad uninstall # delete skills + _bmad/ for a scope
854
+ boost bmad doctor # what's installed where
855
+ ```
856
+
857
+ When startup is on, a `SessionStart` hook runs `boost bmad orient`, which prints
858
+ a short orientation into the session only while the toggle is enabled. Global
859
+ installs stage the installer in a temp dir and copy only the `bmad-*` skills, so
860
+ `$HOME` never gets a stray `_bmad/` — the workflow runtime stays per-project.
861
+
862
+ ## The boost style
863
+
864
+ The visual identity behind the docs and demos ships as a small, dependency-free
865
+ design system in [`style/`](style/) — the *Aurora living-glass* look: a
866
+ cyan → violet → pink triad on a near-black ground, an ambient aurora that drifts
867
+ behind the page, and glass cards that light up under the cursor. Drop
868
+ [`style/boost.css`](style/boost.css) (and the optional
869
+ [`style/boost.js`](style/boost.js)) into any static page; see
870
+ [`style/demo.html`](style/demo.html) for the whole system on one page and
871
+ [`style/README.md`](style/README.md) for the tokens and class reference.
872
+
822
873
  ## Working on boost
823
874
 
824
875
  ```bash
@@ -14,7 +14,11 @@ pulled from GitHub-hosted registries, then hooks them straight into
14
14
  skill files into three different agent folders and hoping you remember to
15
15
  update all of them, you run one command.
16
16
 
17
- ![boost demo](docs/demo.gif)
17
+ <p align="center">
18
+ <img src="docs/demo.gif" alt="boost demo: tap a registry, search for a skill, install it into every agent, then run doctor" width="820">
19
+ <br>
20
+ <sub><em>tap a registry → search → install into every agent → doctor — one flow</em></sub>
21
+ </p>
18
22
 
19
23
  ## What's a "skill," exactly?
20
24
 
@@ -93,7 +97,7 @@ boost doctor # sanity check: broken links, lock drift, stale taps
93
97
  boost bundle dump > Boostfile # everyone else runs: boost bundle install
94
98
  ```
95
99
 
96
- ## 72 commands, organized into 8 groups
100
+ ## 75 commands, organized into 8 groups
97
101
 
98
102
  `boost --help` prints the full grouped command list; for a visual tour see
99
103
  [`docs/overview.html`](docs/overview.html).
@@ -101,12 +105,12 @@ boost bundle dump > Boostfile # everyone else runs: boost bundle install
101
105
  | Group | Commands |
102
106
  |---|---|
103
107
  | **Package Management** | install · uninstall · sync · update · reinstall · bundle · import · migrate · pin · unpin · snapshot · export |
104
- | **Discovery & Search** | search · discover · recommend · browse · index · trending · stats · count |
108
+ | **Discovery & Search** | search · reindex · discover · recommend · browse · index · trending · stats · count |
105
109
  | **Skill Information** | list · info · cat · edit · preview · explain · log · home · deps · tag |
106
110
  | **Registry (Taps)** | tap · untap · taps · outdated |
107
111
  | **Intelligence** | distill · simulate · infer · absorb · evolve · context · focus · impact |
108
112
  | **Quality & Health** | doctor · lint · audit · verify · drift · test · fingerprint · quarantine · decay · heal · conflict · changelog · attest · health |
109
- | **Configuration** | config · clean · create · policy · onboard · completions · schedule · serve · mcp · self-update |
113
+ | **Configuration** | config · clean · create · policy · onboard · completions · schedule · serve · mcp · hooks · bmad · self-update |
110
114
  | **Team & Collaboration** | cohort · profile · protocol · pulse · replay · who |
111
115
 
112
116
  The AI-assisted commands (`search --smart`, `explain`, `distill`, `infer`,
@@ -115,6 +119,53 @@ available on your PATH (or `ANTHROPIC_API_KEY` is set), and fall back to
115
119
  plain heuristics when it isn't — so the tool still works without an API key,
116
120
  just less cleverly.
117
121
 
122
+ ## Claude Code hooks & the BMAD Method
123
+
124
+ `boost hooks` manages Claude Code hooks in `settings.json` at either scope —
125
+ `--scope project` (`./.claude/settings.json`) or `--scope global`
126
+ (`~/.claude/settings.json`). boost only ever touches hooks it created (tagged
127
+ with a `# boost:<name>` marker in the command), never your own, and snapshots
128
+ the prior file before each write.
129
+
130
+ ```bash
131
+ boost hooks add SessionStart -c 'echo hello' -n greet --scope project
132
+ boost hooks list
133
+ boost hooks remove -n greet --scope project
134
+ ```
135
+
136
+ `boost bmad` installs and manages the [BMAD Method](https://bmadcode.com/)
137
+ (agentic Analyst / PM / Architect / Dev / QA personas + workflows) as a
138
+ scope-aware, toggleable startup experience. Provisioning is delegated to the
139
+ canonical `npx bmad-method install` (needs Node.js 20.12+); boost owns scope,
140
+ the startup toggle, and teardown.
141
+
142
+ ```bash
143
+ boost bmad install --scope project # skills + per-project _bmad/ runtime
144
+ boost bmad install --scope global # skills into ~/.claude/skills for every session
145
+ boost bmad init # add the _bmad/ runtime to the current repo
146
+ boost bmad startup on # inject light BMAD orientation on session start
147
+ boost bmad startup off # stop injecting it (skills stay installed)
148
+ boost bmad disable / enable # quarantine / restore skills (recoverable)
149
+ boost bmad uninstall # delete skills + _bmad/ for a scope
150
+ boost bmad doctor # what's installed where
151
+ ```
152
+
153
+ When startup is on, a `SessionStart` hook runs `boost bmad orient`, which prints
154
+ a short orientation into the session only while the toggle is enabled. Global
155
+ installs stage the installer in a temp dir and copy only the `bmad-*` skills, so
156
+ `$HOME` never gets a stray `_bmad/` — the workflow runtime stays per-project.
157
+
158
+ ## The boost style
159
+
160
+ The visual identity behind the docs and demos ships as a small, dependency-free
161
+ design system in [`style/`](style/) — the *Aurora living-glass* look: a
162
+ cyan → violet → pink triad on a near-black ground, an ambient aurora that drifts
163
+ behind the page, and glass cards that light up under the cursor. Drop
164
+ [`style/boost.css`](style/boost.css) (and the optional
165
+ [`style/boost.js`](style/boost.js)) into any static page; see
166
+ [`style/demo.html`](style/demo.html) for the whole system on one page and
167
+ [`style/README.md`](style/README.md) for the tokens and class reference.
168
+
118
169
  ## Working on boost
119
170
 
120
171
  ```bash
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.7'
22
- __version_tuple__ = version_tuple = (1, 0, 7)
21
+ __version__ = version = '1.0.15'
22
+ __version_tuple__ = version_tuple = (1, 0, 15)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -1,4 +1,4 @@
1
- """boost CLI dispatcher — 72 commands across 8 groups.
1
+ """boost CLI dispatcher — 75 commands across 8 groups.
2
2
 
3
3
  Each command lives in boost_cli/commands/<module>.py as
4
4
  def cmd_<name_with_underscores>(argv: list[str]) -> int
@@ -60,8 +60,9 @@ COMMANDS = [
60
60
  ("unpin", "pkg", "pkg", "Allow a pinned skill to update again"),
61
61
  ("snapshot", "pkg", "pkg", "Save & restore whole skill environments"),
62
62
  ("export", "pkg", "pkg", "Package skills as shareable zip/tar archives"),
63
- # Discovery & Search (8)
63
+ # Discovery & Search (9)
64
64
  ("search", "find", "discovery", "Search skills across tap registries (AI-ranked)"),
65
+ ("reindex", "find", "discovery", "Build/refresh the full-content search index"),
65
66
  ("discover", "find", "discovery", "Browse & search the GitHub-wide skill discovery index"),
66
67
  ("recommend", "find", "discovery", "Suggest skills based on your project's tech stack"),
67
68
  ("browse", "find", "discovery", "Interactive full-screen TUI with fuzzy search"),
@@ -109,7 +110,7 @@ COMMANDS = [
109
110
  ("changelog", "chk", "quality", "Show a skill's upstream change history"),
110
111
  ("attest", "chk", "quality", "Display/verify the install record for skills"),
111
112
  ("health", "chk", "quality", "Dashboard of skill-environment health"),
112
- # Configuration (10)
113
+ # Configuration (12)
113
114
  ("config", "cfg", "configuration", "Display or modify boost configuration"),
114
115
  ("clean", "cfg", "configuration", "Clear stale caches & broken symlinks"),
115
116
  ("create", "cfg", "configuration", "Scaffold a new skill from a template"),
@@ -119,6 +120,8 @@ COMMANDS = [
119
120
  ("schedule", "cfg", "configuration", "Manage automatic skill-sync scheduling"),
120
121
  ("serve", "cfg", "configuration", "Serve the skill catalog over HTTP (port 8787)"),
121
122
  ("mcp", "cfg", "configuration", "Register boost as an MCP server for Claude Code"),
123
+ ("hooks", "cfg", "hooks", "Manage Claude Code hooks (scope-aware) in settings.json"),
124
+ ("bmad", "cfg", "bmad", "Install & manage the BMAD Method (scope-aware, toggleable)"),
122
125
  ("self-update", "cfg", "configuration", "Update boost itself to the latest version"),
123
126
  # Team & Collaboration (6)
124
127
  ("cohort", "team", "team", "Controlled skill rollouts & team A/B testing"),
@@ -0,0 +1,403 @@
1
+ """boost bmad — install & manage the BMAD Method, scope-aware and toggleable.
2
+
3
+ BMAD ships as Claude Code skills PLUS a per-project `_bmad/` config/runtime that
4
+ the skills read on activation. boost owns scope, the startup toggle, and teardown;
5
+ full provisioning is delegated to the canonical `npx bmad-method install`.
6
+
7
+ boost bmad install --scope project # skills + _bmad/ runtime in this repo
8
+ boost bmad install --scope global # skills into ~/.claude/skills (+ toggle)
9
+ boost bmad init # add _bmad/ runtime to the current repo
10
+ boost bmad startup on|off|status # toggle the SessionStart orientation
11
+ boost bmad disable / enable # quarantine / restore skills (recoverable)
12
+ boost bmad uninstall # delete skills + _bmad/ for a scope
13
+ boost bmad doctor # what's installed where
14
+
15
+ Global installs stage the installer in a temp dir and copy only the `bmad-*`
16
+ skills into ~/.claude/skills, so $HOME never gets a stray `_bmad/`. The `_bmad/`
17
+ runtime is per-project (`boost bmad init`).
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ import getpass
23
+ import json
24
+ import os
25
+ import re
26
+ import shlex
27
+ import shutil
28
+ import subprocess
29
+ import tempfile
30
+ from pathlib import Path
31
+
32
+ from ..core import claude_settings as cs
33
+ from ..core import journal
34
+ from ..core import output as out
35
+ from ..core import paths, util
36
+ from ..errors import BoostError
37
+
38
+ DEFAULT_MODULES = "bmm"
39
+ HOOK_NAME = "bmad"
40
+ HOOK_MATCHER = "startup|resume|clear"
41
+
42
+ ORIENT = """[BMAD MODE ACTIVE — light orientation]
43
+ This environment has the BMAD Method installed as Claude Code skills. Be
44
+ BMAD-aware without hijacking simple requests: use the workflow when the user is
45
+ planning or building a feature; answer trivial one-off asks directly.
46
+
47
+ Entry point: invoke the `bmad-help` skill to assess state and recommend a step.
48
+
49
+ Persona agents (skills): bmad-agent-analyst (Mary, research/brief),
50
+ bmad-agent-pm (John, PRD), bmad-agent-architect (Winston, architecture),
51
+ bmad-agent-ux-designer (Sally), bmad-agent-dev (Amelia, implementation),
52
+ bmad-agent-tech-writer (Paige).
53
+
54
+ Phases: Analysis (bmad-brainstorming -> bmad-product-brief -> research) ->
55
+ Planning (bmad-prd -> bmad-architecture -> bmad-create-epics-and-stories) ->
56
+ Build (bmad-sprint-planning -> bmad-create-story -> bmad-dev-story ->
57
+ bmad-code-review -> bmad-qa-generate-e2e-tests -> bmad-retrospective).
58
+
59
+ Full workflows need a per-project `_bmad/` runtime — run `boost bmad init` in a
60
+ repo if it isn't there yet. Turn this orientation off with `boost bmad startup off`."""
61
+
62
+ _ACTIONS = ("install", "init", "startup", "orient", "uninstall",
63
+ "disable", "enable", "doctor", "status")
64
+
65
+
66
+ def cmd_bmad(argv) -> int:
67
+ p = argparse.ArgumentParser(
68
+ prog="boost bmad",
69
+ description="Install & manage the BMAD Method (scope-aware, toggleable)")
70
+ p.add_argument("action", choices=_ACTIONS, help=" | ".join(_ACTIONS))
71
+ p.add_argument("value", nargs="?", help="for startup: on | off | status")
72
+ p.add_argument("-s", "--scope", choices=("project", "global"), default=None,
73
+ help="target scope (default: project)")
74
+ p.add_argument("--modules", default=DEFAULT_MODULES,
75
+ help="BMAD modules to install (default: %s)" % DEFAULT_MODULES)
76
+ p.add_argument("-y", "--yes", action="store_true",
77
+ help="skip confirmation prompts")
78
+ p.add_argument("--startup", action="store_true",
79
+ help="enable the startup toggle right after install")
80
+ args = p.parse_args(argv)
81
+
82
+ if args.action == "install":
83
+ return _install(args.scope, args.modules, args.startup)
84
+ if args.action == "init":
85
+ return _init(args.modules, args.startup)
86
+ if args.action == "startup":
87
+ return _startup(args.value or "status", args.scope)
88
+ if args.action == "orient":
89
+ return _orient(args.scope)
90
+ if args.action == "uninstall":
91
+ return _uninstall(args.scope, args.yes)
92
+ if args.action == "disable":
93
+ return _disable(args.scope)
94
+ if args.action == "enable":
95
+ return _enable(args.scope)
96
+ return _doctor() # doctor | status
97
+
98
+
99
+ # ------------------------------------------------------------------- provisioning
100
+
101
+ def _require_npx() -> None:
102
+ if not shutil.which("npx"):
103
+ raise BoostError(
104
+ "npx not found — BMAD is provisioned via `npx bmad-method install`",
105
+ hint="install Node.js 20.12+ (e.g. `brew install node`), then retry")
106
+
107
+
108
+ def _run_installer(directory: Path, modules: str) -> "subprocess.CompletedProcess":
109
+ cmd = ["npx", "--yes", "bmad-method@latest", "install",
110
+ "--yes", "--directory", str(directory),
111
+ "--tools", "claude-code", "--modules", modules,
112
+ "--user-name", _whoami()]
113
+ try:
114
+ proc = subprocess.run(cmd, capture_output=True, text=True, timeout=900)
115
+ except (OSError, subprocess.TimeoutExpired) as e:
116
+ raise BoostError("bmad install failed: %s" % e,
117
+ hint="run it yourself: " + " ".join(cmd))
118
+ if proc.returncode != 0:
119
+ tail = (proc.stderr or proc.stdout or "").strip().splitlines()
120
+ raise BoostError(
121
+ "bmad install failed: %s" % (tail[-1] if tail else "unknown error"),
122
+ hint="run it yourself: " + " ".join(cmd))
123
+ return proc
124
+
125
+
126
+ def _whoami() -> str:
127
+ try:
128
+ return getpass.getuser()
129
+ except Exception:
130
+ return "developer"
131
+
132
+
133
+ def _parse_version(text: str) -> str:
134
+ m = re.search(r"v(\d+\.\d+\.\d+)", text or "")
135
+ return m.group(1) if m else "unknown"
136
+
137
+
138
+ def _install(scope, modules, do_startup) -> int:
139
+ scope = scope or "project"
140
+ _require_npx()
141
+ if scope == "global":
142
+ n = _copy_global_skills(modules)
143
+ _set_scope_state("global", installed=True, skills=n,
144
+ modules=modules.split(","), installed_at=util.now_iso())
145
+ out.ok("installed %d BMAD skill(s) globally → %s"
146
+ % (n, _skills_dir("global")))
147
+ out.dim(" run `boost bmad init` in a project for its _bmad/ workflow runtime")
148
+ else:
149
+ ver, n = _install_project_runtime(modules)
150
+ out.ok("installed BMAD in %s (%d skills, v%s)" % (Path.cwd(), n, ver))
151
+ if do_startup:
152
+ _startup("on", scope)
153
+ return 0
154
+
155
+
156
+ def _init(modules, do_startup) -> int:
157
+ _require_npx()
158
+ ver, n = _install_project_runtime(modules)
159
+ out.ok("BMAD runtime ready in %s (_bmad/, %d skills, v%s)"
160
+ % (Path.cwd(), n, ver))
161
+ if do_startup:
162
+ _startup("on", "project")
163
+ return 0
164
+
165
+
166
+ def _install_project_runtime(modules):
167
+ target = Path.cwd()
168
+ proc = _run_installer(target, modules)
169
+ ver = _parse_version((proc.stdout or "") + (proc.stderr or ""))
170
+ n = _count_skills(_skills_dir("project"))
171
+ _set_scope_state("project", installed=True, skills=n, version=ver,
172
+ modules=modules.split(","), installed_at=util.now_iso())
173
+ return ver, n
174
+
175
+
176
+ def _copy_global_skills(modules) -> int:
177
+ """Stage the installer in a temp dir; copy only bmad-* skills into ~/.claude."""
178
+ stage = Path(tempfile.mkdtemp(prefix="boost-bmad-"))
179
+ try:
180
+ _run_installer(stage, modules)
181
+ src = stage / ".claude" / "skills"
182
+ dest = _skills_dir("global")
183
+ dest.mkdir(parents=True, exist_ok=True)
184
+ n = 0
185
+ for d in sorted(src.glob("bmad-*")):
186
+ if not d.is_dir():
187
+ continue
188
+ t = dest / d.name
189
+ if t.exists():
190
+ shutil.rmtree(t)
191
+ shutil.copytree(d, t)
192
+ n += 1
193
+ return n
194
+ finally:
195
+ shutil.rmtree(stage, ignore_errors=True)
196
+
197
+
198
+ # ----------------------------------------------------------------------- toggle
199
+
200
+ def _startup(value, scope) -> int:
201
+ scope = scope or "project"
202
+ if value == "on":
203
+ cmd = "%s bmad orient --scope %s" % (
204
+ shlex.quote(str(paths.launcher())), scope)
205
+ cs.add_hook(scope, "SessionStart", HOOK_NAME, cmd, matcher=HOOK_MATCHER)
206
+ _set_scope_state(scope, startup=True)
207
+ journal.log("bmad-startup", "on", scope=scope)
208
+ out.ok("BMAD startup ON (%s) — new sessions get orientation" % scope)
209
+ out.dim(" hook → %s" % cs.settings_path(scope))
210
+ return 0
211
+ if value == "off":
212
+ cs.remove_hook(scope, "SessionStart", HOOK_NAME)
213
+ _set_scope_state(scope, startup=False)
214
+ journal.log("bmad-startup", "off", scope=scope)
215
+ out.ok("BMAD startup OFF (%s) — skills stay installed" % scope)
216
+ return 0
217
+ return _status(scope)
218
+
219
+
220
+ def _orient(scope) -> int:
221
+ """SessionStart hook target: print orientation iff enabled (else silent)."""
222
+ scope = scope or "project"
223
+ try:
224
+ if _get_scope_state(scope).get("startup"):
225
+ print(ORIENT)
226
+ except Exception:
227
+ pass # a hook must never break the session
228
+ return 0
229
+
230
+
231
+ def _status(scope) -> int:
232
+ st = _get_scope_state(scope)
233
+ hook = cs.has_hook(scope, "SessionStart", HOOK_NAME)
234
+ n = _count_skills(_skills_dir(scope))
235
+ out.heading("BMAD startup — %s" % scope)
236
+ out.kv("enabled", str(bool(st.get("startup"))))
237
+ out.kv("hook", "present" if hook else "absent")
238
+ out.kv("skills", str(n))
239
+ out.kv("installed", str(bool(st.get("installed"))))
240
+ return 0
241
+
242
+
243
+ # ------------------------------------------------------------------- teardown
244
+
245
+ def _uninstall(scope, yes) -> int:
246
+ scope = scope or "project"
247
+ if not (yes or out.confirm(
248
+ "Remove BMAD (%s)? deletes bmad-* skills%s" % (
249
+ scope, " + _bmad/, _bmad-output/" if scope == "project" else ""))):
250
+ out.info("aborted")
251
+ return 0
252
+ cs.remove_hook(scope, "SessionStart", HOOK_NAME)
253
+ removed = _rm_skills(_skills_dir(scope))
254
+ if scope == "project":
255
+ for extra in ("_bmad", "_bmad-output"):
256
+ pth = Path.cwd() / extra
257
+ if pth.exists():
258
+ shutil.rmtree(pth, ignore_errors=True)
259
+ removed += 1
260
+ _clear_scope_state(scope)
261
+ journal.log("bmad-uninstall", scope, count=removed)
262
+ out.ok("removed BMAD (%s): %d item(s)" % (scope, removed))
263
+ return 0
264
+
265
+
266
+ def _disable(scope) -> int:
267
+ """Quarantine: turn startup off and move skills aside (recoverable)."""
268
+ scope = scope or "project"
269
+ cs.remove_hook(scope, "SessionStart", HOOK_NAME)
270
+ qdir = _quarantine_dir(scope)
271
+ qdir.mkdir(parents=True, exist_ok=True)
272
+ moved = 0
273
+ for d in sorted(_skills_dir(scope).glob("bmad-*")):
274
+ target = qdir / d.name
275
+ if target.exists():
276
+ shutil.rmtree(target)
277
+ shutil.move(str(d), str(target))
278
+ moved += 1
279
+ _set_scope_state(scope, startup=False, disabled=True)
280
+ journal.log("bmad-disable", scope, count=moved)
281
+ out.ok("quarantined %d BMAD skill(s) (%s)" % (moved, scope))
282
+ out.dim(" restore with `boost bmad enable --scope %s`" % scope)
283
+ return 0
284
+
285
+
286
+ def _enable(scope) -> int:
287
+ scope = scope or "project"
288
+ qdir = _quarantine_dir(scope)
289
+ restored = 0
290
+ if qdir.exists():
291
+ dest = _skills_dir(scope)
292
+ dest.mkdir(parents=True, exist_ok=True)
293
+ for d in sorted(qdir.glob("bmad-*")):
294
+ target = dest / d.name
295
+ if target.exists():
296
+ shutil.rmtree(target)
297
+ shutil.move(str(d), str(target))
298
+ restored += 1
299
+ _set_scope_state(scope, disabled=False)
300
+ if restored:
301
+ journal.log("bmad-enable", scope, count=restored)
302
+ out.ok("restored %d BMAD skill(s) (%s)" % (restored, scope))
303
+ else:
304
+ out.warn("nothing quarantined for %s scope" % scope)
305
+ return 0
306
+
307
+
308
+ def _doctor() -> int:
309
+ out.heading("BMAD status")
310
+ out.kv("npx", shutil.which("npx") or "MISSING")
311
+ out.kv("node", shutil.which("node") or "MISSING")
312
+ for scope in ("global", "project"):
313
+ st = _get_scope_state(scope)
314
+ n = _count_skills(_skills_dir(scope))
315
+ hook = cs.has_hook(scope, "SessionStart", HOOK_NAME)
316
+ out.kv(scope, "skills=%d startup=%s hook=%s installed=%s"
317
+ % (n, bool(st.get("startup")), hook, bool(st.get("installed"))))
318
+ out.dim(" project = %s" % Path.cwd())
319
+ return 0
320
+
321
+
322
+ # -------------------------------------------------------------------- helpers
323
+
324
+ def _skills_dir(scope) -> Path:
325
+ base = paths.home() if scope == "global" else Path.cwd()
326
+ return base / ".claude" / "skills"
327
+
328
+
329
+ def _count_skills(skills_dir: Path) -> int:
330
+ if not skills_dir.exists():
331
+ return 0
332
+ return sum(1 for d in skills_dir.glob("bmad-*") if d.is_dir())
333
+
334
+
335
+ def _rm_skills(skills_dir: Path) -> int:
336
+ n = 0
337
+ if skills_dir.exists():
338
+ for d in sorted(skills_dir.glob("bmad-*")):
339
+ shutil.rmtree(d, ignore_errors=True)
340
+ n += 1
341
+ return n
342
+
343
+
344
+ def _quarantine_dir(scope) -> Path:
345
+ key = "global" if scope == "global" else _fs_slug(str(Path.cwd().resolve()))
346
+ return paths.state_dir() / "bmad-quarantine" / key
347
+
348
+
349
+ def _fs_slug(p: str) -> str:
350
+ return p.replace(os.sep, "__").strip("_") or "root"
351
+
352
+
353
+ # ---------------------------------------------------------------------- state
354
+
355
+ def _state_path() -> Path:
356
+ return paths.state_dir() / "bmad.json"
357
+
358
+
359
+ def _state_read() -> dict:
360
+ p = _state_path()
361
+ if not p.exists():
362
+ return {"global": {}, "projects": {}}
363
+ try:
364
+ d = json.loads(p.read_text())
365
+ except (json.JSONDecodeError, OSError):
366
+ return {"global": {}, "projects": {}}
367
+ d.setdefault("global", {})
368
+ d.setdefault("projects", {})
369
+ return d
370
+
371
+
372
+ def _state_write(d: dict) -> None:
373
+ paths.ensure_dirs()
374
+ _state_path().write_text(json.dumps(d, indent=2) + "\n")
375
+
376
+
377
+ def _proj_key() -> str:
378
+ return str(Path.cwd().resolve())
379
+
380
+
381
+ def _get_scope_state(scope) -> dict:
382
+ d = _state_read()
383
+ if scope == "global":
384
+ return d["global"]
385
+ return d["projects"].get(_proj_key(), {})
386
+
387
+
388
+ def _set_scope_state(scope, **patch) -> None:
389
+ d = _state_read()
390
+ if scope == "global":
391
+ d["global"].update(patch)
392
+ else:
393
+ d["projects"].setdefault(_proj_key(), {}).update(patch)
394
+ _state_write(d)
395
+
396
+
397
+ def _clear_scope_state(scope) -> None:
398
+ d = _state_read()
399
+ if scope == "global":
400
+ d["global"] = {}
401
+ else:
402
+ d["projects"].pop(_proj_key(), None)
403
+ _state_write(d)