boost-skill-cli 1.0.11__tar.gz → 1.0.16__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.
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/.gitignore +5 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/PKG-INFO +51 -4
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/README.md +50 -3
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/cli.py +6 -3
- boost_skill_cli-1.0.16/boost_cli/commands/bmad.py +403 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/configuration.py +17 -5
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/discovery.py +41 -3
- boost_skill_cli-1.0.16/boost_cli/commands/hooks.py +92 -0
- boost_skill_cli-1.0.16/boost_cli/core/claude_settings.py +203 -0
- boost_skill_cli-1.0.16/boost_cli/core/rag.py +394 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_skill_cli.egg-info/PKG-INFO +51 -4
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_skill_cli.egg-info/SOURCES.txt +13 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/docs/index.html +4 -3
- boost_skill_cli-1.0.16/docs/roadmap.html +469 -0
- boost_skill_cli-1.0.16/style/README.md +92 -0
- boost_skill_cli-1.0.16/style/boost.css +352 -0
- boost_skill_cli-1.0.16/style/boost.js +55 -0
- boost_skill_cli-1.0.16/style/demo.html +140 -0
- boost_skill_cli-1.0.16/tests/functional/test_cli_bmad.py +223 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_configuration.py +22 -3
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_discovery.py +38 -0
- boost_skill_cli-1.0.16/tests/functional/test_cli_hooks.py +58 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_everyday_loop.py +1 -1
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/smoke.sh +1 -0
- boost_skill_cli-1.0.16/tests/unit/test_claude_settings.py +124 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_errors_and_cli_table.py +8 -8
- boost_skill_cli-1.0.16/tests/unit/test_rag.py +824 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/LICENSE +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/Makefile +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/setup.cfg +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.11 → boost_skill_cli-1.0.16}/tests/unit/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: boost-skill-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.16
|
|
4
4
|
Summary: boost — Homebrew for AI coding skills
|
|
5
5
|
Author: Jonathan Reyes
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -801,7 +801,7 @@ boost doctor # sanity check: broken links, lock drift, stale taps
|
|
|
801
801
|
boost bundle dump > Boostfile # everyone else runs: boost bundle install
|
|
802
802
|
```
|
|
803
803
|
|
|
804
|
-
##
|
|
804
|
+
## 75 commands, organized into 8 groups
|
|
805
805
|
|
|
806
806
|
`boost --help` prints the full grouped command list; for a visual tour see
|
|
807
807
|
[`docs/overview.html`](docs/overview.html).
|
|
@@ -809,12 +809,12 @@ boost bundle dump > Boostfile # everyone else runs: boost bundle install
|
|
|
809
809
|
| Group | Commands |
|
|
810
810
|
|---|---|
|
|
811
811
|
| **Package Management** | install · uninstall · sync · update · reinstall · bundle · import · migrate · pin · unpin · snapshot · export |
|
|
812
|
-
| **Discovery & Search** | search · discover · recommend · browse · index · trending · stats · count |
|
|
812
|
+
| **Discovery & Search** | search · reindex · discover · recommend · browse · index · trending · stats · count |
|
|
813
813
|
| **Skill Information** | list · info · cat · edit · preview · explain · log · home · deps · tag |
|
|
814
814
|
| **Registry (Taps)** | tap · untap · taps · outdated |
|
|
815
815
|
| **Intelligence** | distill · simulate · infer · absorb · evolve · context · focus · impact |
|
|
816
816
|
| **Quality & Health** | doctor · lint · audit · verify · drift · test · fingerprint · quarantine · decay · heal · conflict · changelog · attest · health |
|
|
817
|
-
| **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 |
|
|
818
818
|
| **Team & Collaboration** | cohort · profile · protocol · pulse · replay · who |
|
|
819
819
|
|
|
820
820
|
The AI-assisted commands (`search --smart`, `explain`, `distill`, `infer`,
|
|
@@ -823,6 +823,53 @@ available on your PATH (or `ANTHROPIC_API_KEY` is set), and fall back to
|
|
|
823
823
|
plain heuristics when it isn't — so the tool still works without an API key,
|
|
824
824
|
just less cleverly.
|
|
825
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
|
+
|
|
826
873
|
## Working on boost
|
|
827
874
|
|
|
828
875
|
```bash
|
|
@@ -97,7 +97,7 @@ boost doctor # sanity check: broken links, lock drift, stale taps
|
|
|
97
97
|
boost bundle dump > Boostfile # everyone else runs: boost bundle install
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
##
|
|
100
|
+
## 75 commands, organized into 8 groups
|
|
101
101
|
|
|
102
102
|
`boost --help` prints the full grouped command list; for a visual tour see
|
|
103
103
|
[`docs/overview.html`](docs/overview.html).
|
|
@@ -105,12 +105,12 @@ boost bundle dump > Boostfile # everyone else runs: boost bundle install
|
|
|
105
105
|
| Group | Commands |
|
|
106
106
|
|---|---|
|
|
107
107
|
| **Package Management** | install · uninstall · sync · update · reinstall · bundle · import · migrate · pin · unpin · snapshot · export |
|
|
108
|
-
| **Discovery & Search** | search · discover · recommend · browse · index · trending · stats · count |
|
|
108
|
+
| **Discovery & Search** | search · reindex · discover · recommend · browse · index · trending · stats · count |
|
|
109
109
|
| **Skill Information** | list · info · cat · edit · preview · explain · log · home · deps · tag |
|
|
110
110
|
| **Registry (Taps)** | tap · untap · taps · outdated |
|
|
111
111
|
| **Intelligence** | distill · simulate · infer · absorb · evolve · context · focus · impact |
|
|
112
112
|
| **Quality & Health** | doctor · lint · audit · verify · drift · test · fingerprint · quarantine · decay · heal · conflict · changelog · attest · health |
|
|
113
|
-
| **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 |
|
|
114
114
|
| **Team & Collaboration** | cohort · profile · protocol · pulse · replay · who |
|
|
115
115
|
|
|
116
116
|
The AI-assisted commands (`search --smart`, `explain`, `distill`, `infer`,
|
|
@@ -119,6 +119,53 @@ available on your PATH (or `ANTHROPIC_API_KEY` is set), and fall back to
|
|
|
119
119
|
plain heuristics when it isn't — so the tool still works without an API key,
|
|
120
120
|
just less cleverly.
|
|
121
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
|
+
|
|
122
169
|
## Working on boost
|
|
123
170
|
|
|
124
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
21
|
+
__version__ = version = '1.0.16'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 16)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""boost CLI dispatcher —
|
|
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 (
|
|
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 (
|
|
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)
|
|
@@ -20,7 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
|
|
21
21
|
from .. import __version__
|
|
22
22
|
from ..core import agents, catalog, config, frontmatter, gitutil, journal
|
|
23
|
-
from ..core import lockfile, paths, policy, registry, store, util
|
|
23
|
+
from ..core import lockfile, paths, policy, rag, registry, store, util
|
|
24
24
|
from ..core import output as out
|
|
25
25
|
from ..errors import BoostError
|
|
26
26
|
|
|
@@ -895,11 +895,23 @@ _MCP_TOOLS = [
|
|
|
895
895
|
def _mcp_tool(tool: str, args: dict):
|
|
896
896
|
"""Run one MCP tool -> (text, is_error). (None, _) for unknown tools."""
|
|
897
897
|
if tool == "boost_search":
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
898
|
+
query = str(args.get("query", ""))
|
|
899
|
+
rag_result = rag.search(query, limit=10)
|
|
900
|
+
if rag_result is not None: # full-content index is built
|
|
901
|
+
hits, _ranker = rag_result
|
|
902
|
+
if not hits:
|
|
903
|
+
return "no skills match %r" % query, False
|
|
904
|
+
return "\n".join(
|
|
905
|
+
"%s — %s (%s)" % (h["entry"]["name"],
|
|
906
|
+
h["entry"].get("description", ""),
|
|
907
|
+
h["entry"]["tap"])
|
|
908
|
+
for h in hits), False
|
|
909
|
+
# no index yet -> keep today's frontmatter search so nothing regresses
|
|
910
|
+
scored = catalog.search(query)[:10]
|
|
911
|
+
if not scored:
|
|
912
|
+
return "no skills match %r" % query, False
|
|
901
913
|
return "\n".join("%s — %s (%s)" % (e["name"], e["description"], e["tap"])
|
|
902
|
-
for e, _score in
|
|
914
|
+
for e, _score in scored), False
|
|
903
915
|
if tool == "boost_list":
|
|
904
916
|
skills = lockfile.installed()
|
|
905
917
|
if not skills:
|