boost-skill-cli 1.0.206__tar.gz → 1.0.208__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.206 → boost_skill_cli-1.0.208}/.vale/styles/config/vocabularies/boost/accept.txt +1 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/CONTRIBUTING.md +23 -2
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/Makefile +11 -2
- {boost_skill_cli-1.0.206/boost_skill_cli.egg-info → boost_skill_cli-1.0.208}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/info.py +6 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/pkg.py +69 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/quality.py +4 -1
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/safety.py +158 -2
- boost_skill_cli-1.0.208/boost_cli/core/mcpdecl.py +193 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/store.py +29 -0
- boost_skill_cli-1.0.208/boost_cli/core/trustaudit.py +224 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208/boost_skill_cli.egg-info}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_skill_cli.egg-info/SOURCES.txt +15 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/commands.html +8 -6
- boost_skill_cli-1.0.208/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +31 -0
- boost_skill_cli-1.0.208/docs/roadmap/items/installed-skill-trust-audit.md +29 -0
- boost_skill_cli-1.0.208/docs/roadmap/items/mcp-aware-skills.md +32 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap.html +48 -26
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/noxfile.py +15 -4
- boost_skill_cli-1.0.208/requirements/coverage-tools.in +11 -0
- boost_skill_cli-1.0.208/requirements/coverage-tools.txt +344 -0
- boost_skill_cli-1.0.206/requirements/lint-tools.txt → boost_skill_cli-1.0.208/requirements/lint-tools.in +12 -8
- boost_skill_cli-1.0.208/requirements/lint-tools.txt +593 -0
- boost_skill_cli-1.0.208/requirements/mutation-tools.in +16 -0
- boost_skill_cli-1.0.208/requirements/mutation-tools.txt +567 -0
- boost_skill_cli-1.0.208/requirements/release-tools.in +17 -0
- boost_skill_cli-1.0.208/requirements/release-tools.txt +773 -0
- boost_skill_cli-1.0.208/requirements/test-tools.in +16 -0
- boost_skill_cli-1.0.208/requirements/test-tools.txt +391 -0
- boost_skill_cli-1.0.208/scripts/lock_toolchain.py +153 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/setup.cfg +2 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_pkg.py +151 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_quality.py +65 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/smoke.sh +8 -0
- boost_skill_cli-1.0.208/tests/unit/test_mcpdecl.py +355 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_output.py +30 -0
- boost_skill_cli-1.0.208/tests/unit/test_toolchain_lock.py +137 -0
- boost_skill_cli-1.0.208/tests/unit/test_trustaudit.py +369 -0
- boost_skill_cli-1.0.206/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -20
- boost_skill_cli-1.0.206/docs/roadmap/items/installed-skill-trust-audit.md +0 -25
- boost_skill_cli-1.0.206/docs/roadmap/items/mcp-aware-skills.md +0 -25
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.gitattributes +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.gitignore +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.gitleaks.toml +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.htmlvalidate.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.lighthouserc.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.lycheeignore +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.markdownlint-cli2.jsonc +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.pre-commit-config.yaml +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.stylelintrc.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.vale/styles/boost/Terminology.yml +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/.vale.ini +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/LICENSE +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/README.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/_common.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/run.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/capabilities.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/ed25519.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/faithfulness.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/integrity.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/minisign.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/projectlock.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/provenance.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/resolve.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/scopes.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/serve.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/updatediff.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/index.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/eslint.config.mjs +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/examples/README.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/index.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/build_command_reference.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/check_anchors.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/import_budget.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/scripts/perf_gate.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/style/README.md +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/style/boost.css +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/style/boost.js +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/style/demo.html +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/steps/discovery_steps.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_capabilities_policy.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_integrity_enforce.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_run.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_tap_signing.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/functional/test_workspace_scope.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_adapters_multi.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_adapters_runner.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_capabilities.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_check_anchors.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_command_reference_fresh.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_docsite_chrome.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_ed25519.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_faithfulness.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_gitleaks_config.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_import_budget.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_integrity.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_minisign.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_perf_gate.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_projectlock.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_provenance.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_resolve.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_scopes.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_serve.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/unit/test_workflows.py +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/visual/package.json +0 -0
- {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.208}/tests/visual/visual_check.mjs +0 -0
|
@@ -7,12 +7,31 @@ so contributing is mostly about keeping the quality gates green.
|
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
git clone https://github.com/jonnyeclectic/boost && cd boost
|
|
10
|
-
make venv #
|
|
10
|
+
make venv # every gate tool, hash-pinned — runtime stays stdlib-only
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Every test runs against a throwaway `$HOME`, so nothing touches your real
|
|
14
14
|
agent configs.
|
|
15
15
|
|
|
16
|
+
### The toolchain is hash-pinned
|
|
17
|
+
|
|
18
|
+
`make venv` and every CI job install from the generated
|
|
19
|
+
[`requirements/*.txt`](requirements/), which pin an exact version **and every
|
|
20
|
+
artifact's sha256** for the full transitive closure — pip enforces those hashes,
|
|
21
|
+
so a yanked or tampered dependency fails the install instead of silently
|
|
22
|
+
changing a build. Your venv and the CI runner get identical bytes.
|
|
23
|
+
|
|
24
|
+
To change a tool, edit the `.in` file and regenerate:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python3 scripts/lock_toolchain.py # rewrite the .txt files
|
|
28
|
+
python3 scripts/lock_toolchain.py --upgrade # re-resolve to newest allowed
|
|
29
|
+
python3 scripts/lock_toolchain.py --check # what `make lint` runs
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Commit both the `.in` and the regenerated `.txt`; a stale lock fails `make lint`.
|
|
33
|
+
Regenerating needs `uv` (installed by `make venv`); *installing* never does.
|
|
34
|
+
|
|
16
35
|
### Reproduce the whole gate — `nox`
|
|
17
36
|
|
|
18
37
|
[`noxfile.py`](noxfile.py) runs the exact gate the CI jobs run, in isolated
|
|
@@ -50,7 +69,7 @@ auto-fixes straight back onto the branch.
|
|
|
50
69
|
|---|---|
|
|
51
70
|
| `make test` | unit + functional suites, **≥80% line coverage** |
|
|
52
71
|
| `make patch-coverage` | changed-line coverage vs `main`, **≥80% of the diff** (PRs) |
|
|
53
|
-
| `bash tests/smoke.sh` |
|
|
72
|
+
| `bash tests/smoke.sh` | 170 end-to-end checks through the real `./boost` shim |
|
|
54
73
|
| `make mutation` | mutmut over `boost_cli/core`, **≥80% mutants killed** |
|
|
55
74
|
| `ruff check boost_cli tests` | lint, zero findings |
|
|
56
75
|
| `mypy` | type check, zero errors |
|
|
@@ -58,6 +77,8 @@ auto-fixes straight back onto the branch.
|
|
|
58
77
|
| `lint-imports` | layering: `core/` imports no `commands/`/`cli`; CLI depends inward |
|
|
59
78
|
| `vulture` | dead-code radar: no unused imports / unreachable code (confidence ≥80) |
|
|
60
79
|
| `xenon` | complexity ratchet: aggregate cyclomatic complexity can't regress (avg ≤B) |
|
|
80
|
+
| `refurb boost_cli` | modernization smells the ruff families miss, zero findings |
|
|
81
|
+
| `scripts/lock_toolchain.py --check` | the hash-pinned toolchain matches its `.in` declarations |
|
|
61
82
|
|
|
62
83
|
## Ground rules
|
|
63
84
|
|
|
@@ -12,10 +12,18 @@ EVAL_HOME := $(CURDIR)/.eval-home
|
|
|
12
12
|
|
|
13
13
|
.PHONY: venv test unit functional smoke coverage patch-coverage mutation lint check demo clean-test eval eval-ai eval-rec eval-stats eval-explain audit dist-check bdd bench bench-cli
|
|
14
14
|
|
|
15
|
+
# Every tool comes from a hash-pinned requirements/*.txt — the same files CI
|
|
16
|
+
# installs (see scripts/lock_toolchain.py). pip enforces the hashes, so a dev
|
|
17
|
+
# venv and a CI runner resolve to identical bytes. mutation-tools.txt is a
|
|
18
|
+
# superset of test-tools.txt, so installing it alone covers both; the two are
|
|
19
|
+
# never layered because pip enforces hashes across the whole resolution.
|
|
15
20
|
venv:
|
|
16
21
|
python3 -m venv $(VENV)
|
|
17
|
-
$(VENV)/bin/pip -q install
|
|
18
|
-
$(VENV)/bin/pip -q install -r requirements/
|
|
22
|
+
$(VENV)/bin/pip -q install -r requirements/mutation-tools.txt
|
|
23
|
+
$(VENV)/bin/pip -q install -r requirements/coverage-tools.txt
|
|
24
|
+
$(VENV)/bin/pip -q install -r requirements/lint-tools.txt
|
|
25
|
+
$(VENV)/bin/pip -q install -r requirements/release-tools.txt
|
|
26
|
+
$(VENV)/bin/pip -q install uv # regenerates the locks; not itself locked
|
|
19
27
|
|
|
20
28
|
unit:
|
|
21
29
|
$(PYTEST) tests/unit -q
|
|
@@ -57,6 +65,7 @@ lint:
|
|
|
57
65
|
$(PY) scripts/build_registries.py --check
|
|
58
66
|
$(PY) scripts/build_roadmap.py --check
|
|
59
67
|
$(PY) scripts/build_command_reference.py --check
|
|
68
|
+
$(PY) scripts/lock_toolchain.py --check
|
|
60
69
|
$(PY) scripts/import_budget.py
|
|
61
70
|
$(PY) scripts/perf_gate.py
|
|
62
71
|
$(PY) scripts/check_anchors.py
|
|
@@ -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.208'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 208)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -239,6 +239,7 @@ def cmd_info(argv):
|
|
|
239
239
|
declared_caps = sorted(capabilities.declared(meta))
|
|
240
240
|
detected_extra = sorted(capabilities.detect(skill_text)
|
|
241
241
|
- set(declared_caps)) if skill_text else []
|
|
242
|
+
mcp_servers = store.declared_mcp_servers(skill_dir) if skill_dir else []
|
|
242
243
|
score = size = files = None
|
|
243
244
|
if skill_dir:
|
|
244
245
|
score, _notes = util.score_skill(skill_dir)
|
|
@@ -249,6 +250,7 @@ def cmd_info(argv):
|
|
|
249
250
|
"name": name, "description": desc,
|
|
250
251
|
"installed": lock, "project": plock,
|
|
251
252
|
"capabilities": declared_caps, "detected_capabilities": detected_extra,
|
|
253
|
+
"mcp_servers": [r["name"] for r in mcp_servers],
|
|
252
254
|
"latest": (cat or {}).get("version"),
|
|
253
255
|
"tap": (lock or plock or cat or {}).get("tap"),
|
|
254
256
|
"store": str(sdir) if lock and sdir.is_dir() else None,
|
|
@@ -322,6 +324,10 @@ def cmd_info(argv):
|
|
|
322
324
|
# under-declaration signal a reviewer wants to see.
|
|
323
325
|
out.kv("detected", out.role(", ".join(detected_extra) + " (not declared)",
|
|
324
326
|
"warn"))
|
|
327
|
+
if mcp_servers:
|
|
328
|
+
# A skill that needs an MCP server is useless without it, so this belongs
|
|
329
|
+
# next to capabilities: both answer "what does this expect of my setup?"
|
|
330
|
+
out.kv("mcp servers", ", ".join(r["name"] for r in mcp_servers))
|
|
325
331
|
if score is not None:
|
|
326
332
|
out.kv("quality", "%d/100" % score)
|
|
327
333
|
out.kv("size", util.human_size(size))
|
|
@@ -109,6 +109,67 @@ def _warn_secrets(res: store.InstallResult) -> None:
|
|
|
109
109
|
out.warn(" L%d [%s] %s" % (f.line, f.severity, f.description))
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
def _offer_mcp(res: store.InstallResult) -> None:
|
|
113
|
+
"""Surface the MCP servers a just-installed skill needs, and offer to wire them.
|
|
114
|
+
|
|
115
|
+
A skill that only works paired with an MCP server used to install "cleanly"
|
|
116
|
+
and then fail in the agent for a reason nothing surfaced. Now the requirement
|
|
117
|
+
is stated at install time, and boost offers to run the registration for the
|
|
118
|
+
servers that came with a runnable spec.
|
|
119
|
+
|
|
120
|
+
Advisory, never fatal: a declined prompt, a missing ``claude`` CLI or a failed
|
|
121
|
+
registration all leave the skill installed. Suppressed entirely by
|
|
122
|
+
``--no-mcp``, which sets ``BOOST_NO_MCP_OFFER``.
|
|
123
|
+
"""
|
|
124
|
+
rows = res.mcp_servers
|
|
125
|
+
if not rows or os.environ.get("BOOST_NO_MCP_OFFER"):
|
|
126
|
+
return
|
|
127
|
+
from ..core import mcpdecl
|
|
128
|
+
out.info("")
|
|
129
|
+
out.warn("%s needs %s to work:" % (res.name, _plural(len(rows), "MCP server")))
|
|
130
|
+
for row in rows:
|
|
131
|
+
how = ("`%s`" % row["spec"]["command"] if row["spec"]
|
|
132
|
+
and row["spec"].get("command") else "no command declared")
|
|
133
|
+
out.info(" %s %s" % (out.c(row["name"], out.BOLD),
|
|
134
|
+
out.role(how, "muted")))
|
|
135
|
+
wirable = mcpdecl.registrable(rows)
|
|
136
|
+
if not wirable:
|
|
137
|
+
# Name-only declarations: boost will not invent a command line.
|
|
138
|
+
out.dim(" install these yourself, then `boost mcp register`")
|
|
139
|
+
return
|
|
140
|
+
if not out.confirm("register %s with Claude Code now?"
|
|
141
|
+
% _plural(len(wirable), "server"), default=False):
|
|
142
|
+
out.dim(" skipped — `claude mcp add …` when you're ready")
|
|
143
|
+
return
|
|
144
|
+
for row in wirable:
|
|
145
|
+
_register_mcp_server(row["name"], row["spec"])
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _register_mcp_server(name: str, spec: dict) -> None:
|
|
149
|
+
"""Run one `claude mcp add` for a skill-declared server. Never raises."""
|
|
150
|
+
import subprocess
|
|
151
|
+
from ..core import mcpdecl
|
|
152
|
+
argv = mcpdecl.register_argv(name, spec)
|
|
153
|
+
if not shutil.which("claude"):
|
|
154
|
+
out.warn("`claude` CLI not found — run this yourself:")
|
|
155
|
+
out.info(" " + " ".join(argv))
|
|
156
|
+
return
|
|
157
|
+
try:
|
|
158
|
+
proc = subprocess.run(argv, capture_output=True, text=True, timeout=60)
|
|
159
|
+
except (OSError, subprocess.TimeoutExpired) as e:
|
|
160
|
+
out.warn("could not register %s: %s" % (name, e))
|
|
161
|
+
out.info(" run it yourself: " + " ".join(argv))
|
|
162
|
+
return
|
|
163
|
+
if proc.returncode != 0:
|
|
164
|
+
tail = (proc.stderr or "").strip().splitlines()
|
|
165
|
+
out.warn("could not register %s: %s"
|
|
166
|
+
% (name, tail[-1] if tail else "unknown error"))
|
|
167
|
+
out.info(" run it yourself: " + " ".join(argv))
|
|
168
|
+
return
|
|
169
|
+
out.ok("registered MCP server %s" % name)
|
|
170
|
+
journal.log("mcp-register", name)
|
|
171
|
+
|
|
172
|
+
|
|
112
173
|
def _report_result(res: store.InstallResult) -> None:
|
|
113
174
|
"""The canonical three-line install report."""
|
|
114
175
|
if res.kind in ("rule", "workflow"):
|
|
@@ -141,6 +202,7 @@ def _report_result(res: store.InstallResult) -> None:
|
|
|
141
202
|
out.ok("lock updated (.skill-lock.json)")
|
|
142
203
|
_warn_injection(res)
|
|
143
204
|
_warn_secrets(res)
|
|
205
|
+
_offer_mcp(res)
|
|
144
206
|
|
|
145
207
|
|
|
146
208
|
def _boostfile_text(skills: Dict[str, dict], via: str = "boost bundle dump") -> str:
|
|
@@ -238,8 +300,15 @@ def cmd_install(argv: List[str]) -> int:
|
|
|
238
300
|
help="show what would happen without changing anything")
|
|
239
301
|
ap.add_argument("--no-deps", action="store_true",
|
|
240
302
|
help="install only the named skills, not their `requires:`")
|
|
303
|
+
ap.add_argument("--no-mcp", action="store_true",
|
|
304
|
+
help="don't offer to register MCP servers a skill declares")
|
|
241
305
|
args = ap.parse_args(argv)
|
|
242
306
|
args.scope = args.scope or scopes.SCOPE_USER
|
|
307
|
+
if args.no_mcp:
|
|
308
|
+
# An env flag rather than threading a parameter through _report_result's
|
|
309
|
+
# eight call sites: the offer is a leaf-level side effect, and this keeps
|
|
310
|
+
# every other install path (update, reinstall, team, MCP tool) unchanged.
|
|
311
|
+
os.environ["BOOST_NO_MCP_OFFER"] = "1"
|
|
243
312
|
only = _check_agents(args.agent)
|
|
244
313
|
multi = len(args.names) > 1
|
|
245
314
|
entries, failed = [], 0
|
|
@@ -848,7 +848,10 @@ def cmd_health(argv):
|
|
|
848
848
|
_PROVENANCE_STYLE = {
|
|
849
849
|
provenance.VERIFIED: "success",
|
|
850
850
|
provenance.UNTRUSTED: "warn",
|
|
851
|
-
|
|
851
|
+
# "danger", not "err": out.role() looks the name up in output.ROLES, which
|
|
852
|
+
# has no "err" key — so an invalid tap signature raised KeyError on any
|
|
853
|
+
# color terminal. The suite never caught it because conftest sets NO_COLOR.
|
|
854
|
+
provenance.INVALID: "danger",
|
|
852
855
|
provenance.UNSIGNED: "muted",
|
|
853
856
|
}
|
|
854
857
|
|
|
@@ -10,11 +10,13 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import json
|
|
12
12
|
import re
|
|
13
|
+
import time
|
|
14
|
+
from contextlib import suppress
|
|
13
15
|
|
|
14
16
|
from .. import cliparse
|
|
15
|
-
from ..core import integrity, journal, lockfile
|
|
17
|
+
from ..core import catalog, frontmatter, gitutil, integrity, journal, lockfile
|
|
16
18
|
from ..core import output as out
|
|
17
|
-
from ..core import policy, store, util
|
|
19
|
+
from ..core import policy, provenance, registry, staleness, store, trustaudit, util
|
|
18
20
|
from ..errors import BoostError
|
|
19
21
|
from ._common import _iter_installed, _s
|
|
20
22
|
|
|
@@ -42,8 +44,14 @@ def cmd_audit(argv):
|
|
|
42
44
|
prog="boost audit",
|
|
43
45
|
description="Check installed skills against a safety blocklist")
|
|
44
46
|
ap.add_argument("--json", action="store_true")
|
|
47
|
+
ap.add_argument("--skills", action="store_true",
|
|
48
|
+
help="trust/staleness report for installed skills instead "
|
|
49
|
+
"of the content scan")
|
|
45
50
|
args = ap.parse_args(argv)
|
|
46
51
|
|
|
52
|
+
if args.skills:
|
|
53
|
+
return _trust_audit(args.json)
|
|
54
|
+
|
|
47
55
|
pol = policy.load()
|
|
48
56
|
installed = _iter_installed()
|
|
49
57
|
findings: dict = {}
|
|
@@ -110,6 +118,154 @@ def cmd_audit(argv):
|
|
|
110
118
|
return 1 if counts["HIGH"] or counts["MED"] else 0
|
|
111
119
|
|
|
112
120
|
|
|
121
|
+
# --- audit --skills: trust & staleness across the whole installed set ------
|
|
122
|
+
#
|
|
123
|
+
# The signals already existed, one per command: `trust` reports tap signing,
|
|
124
|
+
# `outdated` reports drift vs the tap, `deps` shows the conflicts: graph. This
|
|
125
|
+
# gathers all three over the installed set and hands the decision to
|
|
126
|
+
# core/trustaudit.py, which is where every branch of it is unit-tested.
|
|
127
|
+
|
|
128
|
+
_TRUST_ROLE = {trustaudit.HIGH: "danger", trustaudit.MED: "warn",
|
|
129
|
+
trustaudit.LOW: "muted"}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _tap_age_days(tap):
|
|
133
|
+
"""Days since a tap clone's last commit, or ``None`` when unknowable."""
|
|
134
|
+
try:
|
|
135
|
+
proc = gitutil.run(["-C", str(tap.path), "log", "-1", "--format=%ct"],
|
|
136
|
+
check=False)
|
|
137
|
+
except BoostError:
|
|
138
|
+
return None
|
|
139
|
+
if proc.returncode != 0 or not proc.stdout.strip():
|
|
140
|
+
return None
|
|
141
|
+
try:
|
|
142
|
+
committed = int(proc.stdout.strip())
|
|
143
|
+
except ValueError:
|
|
144
|
+
return None
|
|
145
|
+
return max(0, int((time.time() - committed) // 86400))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _tap_signals(tap_name, cache):
|
|
149
|
+
"""``(provenance status, age in days, HEAD commit)`` for a tap, memoized.
|
|
150
|
+
|
|
151
|
+
A tap boost cannot read — unknown name, never cloned — yields
|
|
152
|
+
``(None, None, "")``, which ``trustaudit`` renders as "signature cannot be
|
|
153
|
+
checked" rather than silently passing.
|
|
154
|
+
"""
|
|
155
|
+
if tap_name not in cache:
|
|
156
|
+
try:
|
|
157
|
+
tap = registry.get(tap_name)
|
|
158
|
+
except BoostError:
|
|
159
|
+
cache[tap_name] = (None, None, "")
|
|
160
|
+
else:
|
|
161
|
+
cache[tap_name] = ((provenance.verify_dir(tap.path).status,
|
|
162
|
+
_tap_age_days(tap), gitutil.head_commit(tap.path))
|
|
163
|
+
if tap.is_cloned else (None, None, ""))
|
|
164
|
+
return cache[tap_name]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _upstream_reason(name, lk, tap_name, head):
|
|
168
|
+
"""``staleness.upstream_reason`` for one installed skill, or ``None``.
|
|
169
|
+
|
|
170
|
+
Same signal-gathering order as ``cmd_outdated`` so the two commands can
|
|
171
|
+
never disagree about whether a skill is behind its tap.
|
|
172
|
+
"""
|
|
173
|
+
matches = [e for e in catalog.find(name) if e["tap"] == tap_name]
|
|
174
|
+
if not matches:
|
|
175
|
+
return None
|
|
176
|
+
entry = matches[0]
|
|
177
|
+
latest = str(entry.get("version") or "0.0.0")
|
|
178
|
+
installed_v = str(lk.get("version") or "0.0.0")
|
|
179
|
+
src_sha = None
|
|
180
|
+
if not util.semver_gt(latest, installed_v) and head and head != lk.get("commit"):
|
|
181
|
+
try:
|
|
182
|
+
src_sha = util.sha256_dir(store.source_dir_for(entry))
|
|
183
|
+
except BoostError:
|
|
184
|
+
return None # source checkout is gone; `verify` owns that finding
|
|
185
|
+
return staleness.upstream_reason(installed_v, latest, lk.get("commit", ""),
|
|
186
|
+
head, lk.get("sha256", ""), src_sha)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _installed_conflicts(names):
|
|
190
|
+
"""A ``conflicts_of`` callable reading each installed skill's own copy.
|
|
191
|
+
|
|
192
|
+
Reads the store copy rather than the catalog on purpose: the audit is about
|
|
193
|
+
what is *installed*, which can be an older revision than the tap now
|
|
194
|
+
advertises.
|
|
195
|
+
"""
|
|
196
|
+
cache: dict = {}
|
|
197
|
+
|
|
198
|
+
def conflicts_of(name):
|
|
199
|
+
if name not in cache:
|
|
200
|
+
meta: dict = {}
|
|
201
|
+
# unreadable store copy: `verify` owns that finding, not this
|
|
202
|
+
with suppress(OSError, BoostError):
|
|
203
|
+
text = (store.skill_store_dir(name) / "SKILL.md").read_text(
|
|
204
|
+
encoding="utf-8", errors="replace")
|
|
205
|
+
meta, _body = frontmatter.parse(text)
|
|
206
|
+
cache[name] = trustaudit.relation_list(meta, "conflicts")
|
|
207
|
+
return cache[name]
|
|
208
|
+
|
|
209
|
+
return conflicts_of
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _trust_audit(as_json: bool) -> int:
|
|
213
|
+
"""`boost audit --skills` — one trust-health answer for the installed set."""
|
|
214
|
+
installed = dict(_iter_installed())
|
|
215
|
+
peers: dict = {}
|
|
216
|
+
for name, peer in trustaudit.conflict_pairs(list(installed),
|
|
217
|
+
_installed_conflicts(installed)):
|
|
218
|
+
peers.setdefault(name, []).append(peer)
|
|
219
|
+
|
|
220
|
+
taps: dict = {}
|
|
221
|
+
findings: dict = {}
|
|
222
|
+
for name, lk in sorted(installed.items()):
|
|
223
|
+
tap_name = str(lk.get("tap") or "local")
|
|
224
|
+
is_local = tap_name == "local"
|
|
225
|
+
status, age, head = ((None, None, "") if is_local
|
|
226
|
+
else _tap_signals(tap_name, taps))
|
|
227
|
+
rows = trustaudit.skill_findings(
|
|
228
|
+
is_local=is_local, provenance_status=status, tap_age_days=age,
|
|
229
|
+
upstream_reason=(None if is_local
|
|
230
|
+
else _upstream_reason(name, lk, tap_name, head)),
|
|
231
|
+
conflicts_with=peers.get(name, ()))
|
|
232
|
+
if rows:
|
|
233
|
+
findings[name] = rows
|
|
234
|
+
|
|
235
|
+
counts = trustaudit.count_severities(findings)
|
|
236
|
+
rc = trustaudit.exit_code(counts)
|
|
237
|
+
if as_json:
|
|
238
|
+
print(json.dumps({"skills_scanned": len(installed),
|
|
239
|
+
"findings": findings, "counts": counts}, indent=2))
|
|
240
|
+
return rc
|
|
241
|
+
return _render_trust_audit(installed, findings, counts, rc)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _render_trust_audit(installed, findings, counts, rc: int) -> int:
|
|
245
|
+
"""Human rendering for `boost audit --skills`."""
|
|
246
|
+
total = len(installed)
|
|
247
|
+
out.heading("trust audit — %d skill%s" % (total, _s(total)))
|
|
248
|
+
if not total:
|
|
249
|
+
out.info("nothing installed yet — `boost install <skill>` to start")
|
|
250
|
+
return rc
|
|
251
|
+
if not findings:
|
|
252
|
+
out.ok("all %d installed skill%s signed, current and conflict-free"
|
|
253
|
+
% (total, _s(total)))
|
|
254
|
+
return rc
|
|
255
|
+
for name in sorted(findings):
|
|
256
|
+
print(" " + out.c(name, out.BOLD))
|
|
257
|
+
for f in findings[name]:
|
|
258
|
+
print(" %s %s %s"
|
|
259
|
+
% (out.role(f["severity"].ljust(4), _TRUST_ROLE[f["severity"]]),
|
|
260
|
+
f["label"], out.role(f["detail"], "muted")))
|
|
261
|
+
print()
|
|
262
|
+
out.verdict(trustaudit.is_healthy(counts),
|
|
263
|
+
"%d high · %d medium · %d low across %d of %d skill%s"
|
|
264
|
+
% (counts[trustaudit.HIGH], counts[trustaudit.MED],
|
|
265
|
+
counts[trustaudit.LOW], len(findings), total, _s(total)))
|
|
266
|
+
return rc
|
|
267
|
+
|
|
268
|
+
|
|
113
269
|
def cmd_verify(argv):
|
|
114
270
|
ap = cliparse.parser(
|
|
115
271
|
prog="boost verify",
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""What MCP servers does a skill need, and how do we wire them?
|
|
2
|
+
|
|
3
|
+
Mining boost's catalog surfaces a recurring shape: skills that only work paired
|
|
4
|
+
with a Model Context Protocol server (``manage-mcp-servers``, ``mcp-builder``,
|
|
5
|
+
``mcp-integration`` and dozens more). boost already *is* an MCP server and
|
|
6
|
+
registers itself with ``boost mcp register`` — but a skill had no way to say
|
|
7
|
+
"I need server Y," so installing one left the user to discover that by hand.
|
|
8
|
+
|
|
9
|
+
Two declaration forms, both deliberately **flat**:
|
|
10
|
+
|
|
11
|
+
* ``mcp: github, playwright`` in SKILL.md frontmatter (a comma string or a YAML
|
|
12
|
+
block list) — just the names, reusing the exact contract ``requires:`` and
|
|
13
|
+
``conflicts:`` already use.
|
|
14
|
+
* a bundled ``.mcp.json`` in the skill directory — the standard MCP shape
|
|
15
|
+
(``{"mcpServers": {name: {command, args, env}}}``), carrying full specs.
|
|
16
|
+
|
|
17
|
+
Why flat, and not the nested ``mcp:`` mapping the roadmap card sketched:
|
|
18
|
+
:mod:`boost_cli.core.frontmatter` is a stdlib-only YAML *subset* with no nested
|
|
19
|
+
mapping support, and it does not fail loudly on one — it hoists the inner keys to
|
|
20
|
+
top level and clobbers their siblings. A nested block parses to
|
|
21
|
+
``{'mcp': '', 'servers': [...], 'command': 'npx'}``, silently corrupting the
|
|
22
|
+
entry's metadata. Teaching the parser nested mappings would change the shape of
|
|
23
|
+
every catalog entry's ``meta`` (and every consumer of it), so the declaration
|
|
24
|
+
meets the parser where it already works and the full specs live in the standard
|
|
25
|
+
sidecar file that needs no parser at all.
|
|
26
|
+
|
|
27
|
+
Everything here is pure and I/O-free, like :mod:`boost_cli.core.resolve` and
|
|
28
|
+
:mod:`boost_cli.core.staleness`: callers read the files and pass text in, so
|
|
29
|
+
every branch of the *decision* is unit-testable and reachable by the mutation
|
|
30
|
+
gate.
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import json
|
|
35
|
+
from typing import Dict, List, Optional, Tuple
|
|
36
|
+
|
|
37
|
+
# The frontmatter key a skill uses to name the servers it needs.
|
|
38
|
+
DECL_KEY = "mcp"
|
|
39
|
+
|
|
40
|
+
# The sidecar file a skill bundles to carry full server specs. Standard MCP
|
|
41
|
+
# filename and shape, so an author can lift the file straight from a server's
|
|
42
|
+
# own README.
|
|
43
|
+
SIDECAR = ".mcp.json"
|
|
44
|
+
|
|
45
|
+
# Top-level key inside .mcp.json, per the MCP config convention.
|
|
46
|
+
SERVERS_KEY = "mcpServers"
|
|
47
|
+
|
|
48
|
+
# Ownership tag written alongside a server boost registered, mirroring
|
|
49
|
+
# claude_settings.MARKER. Without it an uninstall could not tell a
|
|
50
|
+
# boost-registered server from one the user configured by hand, so it would
|
|
51
|
+
# either strip the user's config or leak boost's.
|
|
52
|
+
MARKER_KEY = "x-boost-skill"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def declared_names(meta: Optional[dict]) -> List[str]:
|
|
56
|
+
"""Server names a skill's frontmatter declares, de-duped, order preserved.
|
|
57
|
+
|
|
58
|
+
Accepts a YAML list or a comma-separated string and drops blanks — the same
|
|
59
|
+
contract ``requires:``/``conflicts:`` use, so an author who knows one knows
|
|
60
|
+
this one. A missing/empty/``False`` value means "declares nothing".
|
|
61
|
+
"""
|
|
62
|
+
val = (meta or {}).get(DECL_KEY)
|
|
63
|
+
if val in (None, "", False):
|
|
64
|
+
return []
|
|
65
|
+
raw = ([str(x) for x in val] if isinstance(val, list)
|
|
66
|
+
else str(val).split(","))
|
|
67
|
+
out: List[str] = []
|
|
68
|
+
for item in raw:
|
|
69
|
+
name = item.strip()
|
|
70
|
+
if name and name not in out:
|
|
71
|
+
out.append(name)
|
|
72
|
+
return out
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def parse_sidecar(text: Optional[str]) -> Dict[str, dict]:
|
|
76
|
+
"""``{name: spec}`` from a bundled ``.mcp.json``'s text.
|
|
77
|
+
|
|
78
|
+
Tolerant by design: unreadable JSON, a non-object document, a missing or
|
|
79
|
+
non-object ``mcpServers`` key, and non-object individual specs all read as
|
|
80
|
+
"no specs". A malformed sidecar must never make an otherwise-fine install
|
|
81
|
+
fail — the skill still works, the user just does not get the wiring offer.
|
|
82
|
+
"""
|
|
83
|
+
if not text:
|
|
84
|
+
return {}
|
|
85
|
+
try:
|
|
86
|
+
doc = json.loads(text)
|
|
87
|
+
except (ValueError, TypeError):
|
|
88
|
+
return {}
|
|
89
|
+
if not isinstance(doc, dict):
|
|
90
|
+
return {}
|
|
91
|
+
servers = doc.get(SERVERS_KEY)
|
|
92
|
+
if not isinstance(servers, dict):
|
|
93
|
+
return {}
|
|
94
|
+
return {str(k): v for k, v in servers.items()
|
|
95
|
+
if k and isinstance(v, dict)}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def servers_for(meta: Optional[dict], sidecar_text: Optional[str] = None,
|
|
99
|
+
) -> List[dict]:
|
|
100
|
+
"""Every MCP server a skill needs, as ``{"name", "spec", "source"}`` rows.
|
|
101
|
+
|
|
102
|
+
Merges the two declaration forms. A name in *both* uses the sidecar's spec —
|
|
103
|
+
the frontmatter only names a server, so a bundled spec is strictly more
|
|
104
|
+
information. ``spec`` is ``None`` for a name-only declaration, which the
|
|
105
|
+
caller renders as "you need this, here is no command to run it" rather than
|
|
106
|
+
offering a registration it cannot perform.
|
|
107
|
+
|
|
108
|
+
``source`` is ``"sidecar"`` or ``"frontmatter"``, so output can tell the user
|
|
109
|
+
where a requirement came from when the two disagree. Rows are sorted by name
|
|
110
|
+
for deterministic output.
|
|
111
|
+
"""
|
|
112
|
+
specs = parse_sidecar(sidecar_text)
|
|
113
|
+
rows: Dict[str, dict] = {
|
|
114
|
+
name: {"name": name, "spec": spec, "source": "sidecar"}
|
|
115
|
+
for name, spec in specs.items()}
|
|
116
|
+
for name in declared_names(meta):
|
|
117
|
+
if name not in rows:
|
|
118
|
+
rows[name] = {"name": name, "spec": None, "source": "frontmatter"}
|
|
119
|
+
return [rows[k] for k in sorted(rows)]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def registrable(rows) -> List[dict]:
|
|
123
|
+
"""The rows boost can actually wire up — those carrying a runnable spec.
|
|
124
|
+
|
|
125
|
+
A spec is runnable when it names a ``command``. Anything else (a name-only
|
|
126
|
+
frontmatter declaration, or a sidecar entry describing a remote/URL server
|
|
127
|
+
boost has no launcher for) is reported to the user but never auto-registered:
|
|
128
|
+
boost will not invent a command line on an author's behalf.
|
|
129
|
+
"""
|
|
130
|
+
return [r for r in rows
|
|
131
|
+
if isinstance(r.get("spec"), dict)
|
|
132
|
+
and str(r["spec"].get("command") or "").strip()]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def register_argv(name: str, spec: dict, *, scope: str = "user") -> List[str]:
|
|
136
|
+
"""The ``claude mcp add`` argv that registers one declared server.
|
|
137
|
+
|
|
138
|
+
Mirrors ``cmd_mcp``'s invariant exactly: the server **name** precedes every
|
|
139
|
+
``-e`` flag, and a bare ``--`` separates boost's flags from the server's own
|
|
140
|
+
command and arguments. Getting that order wrong makes the CLI read the
|
|
141
|
+
command as another flag value, which is why it is pinned by a test.
|
|
142
|
+
"""
|
|
143
|
+
argv = ["claude", "mcp", "add", name, "--scope", scope]
|
|
144
|
+
env = spec.get("env")
|
|
145
|
+
if isinstance(env, dict):
|
|
146
|
+
for key in sorted(env):
|
|
147
|
+
argv += ["-e", "%s=%s" % (key, env[key])]
|
|
148
|
+
argv.extend(("--", str(spec["command"])))
|
|
149
|
+
args = spec.get("args")
|
|
150
|
+
if isinstance(args, list):
|
|
151
|
+
argv += [str(a) for a in args]
|
|
152
|
+
return argv
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def merge_into(existing: Optional[dict], rows, skill: str) -> Tuple[dict, List[str]]:
|
|
156
|
+
"""Add ``rows``' specs to an ``.mcp.json`` document -> ``(doc, added)``.
|
|
157
|
+
|
|
158
|
+
Returns a **new** document rather than mutating the caller's, and never
|
|
159
|
+
overwrites a server the user already configured: a name already present is
|
|
160
|
+
left exactly as-is and omitted from ``added``. Each entry boost does add
|
|
161
|
+
carries a :data:`MARKER_KEY` naming the skill that asked for it, so a later
|
|
162
|
+
uninstall can remove precisely what boost wrote and nothing else.
|
|
163
|
+
"""
|
|
164
|
+
doc = dict(existing) if isinstance(existing, dict) else {}
|
|
165
|
+
servers = dict(doc.get(SERVERS_KEY) or {})
|
|
166
|
+
added: List[str] = []
|
|
167
|
+
for row in registrable(rows):
|
|
168
|
+
name = row["name"]
|
|
169
|
+
if name in servers:
|
|
170
|
+
continue
|
|
171
|
+
entry = dict(row["spec"])
|
|
172
|
+
entry[MARKER_KEY] = skill
|
|
173
|
+
servers[name] = entry
|
|
174
|
+
added.append(name)
|
|
175
|
+
doc[SERVERS_KEY] = servers
|
|
176
|
+
return doc, added
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def strip_owned(existing: Optional[dict], skill: str) -> Tuple[dict, List[str]]:
|
|
180
|
+
"""Remove the servers boost registered for ``skill`` -> ``(doc, removed)``.
|
|
181
|
+
|
|
182
|
+
Only entries whose :data:`MARKER_KEY` names this skill are removed, so a
|
|
183
|
+
server the user configured by hand — or one another skill still needs —
|
|
184
|
+
survives an uninstall untouched.
|
|
185
|
+
"""
|
|
186
|
+
doc = dict(existing) if isinstance(existing, dict) else {}
|
|
187
|
+
servers = dict(doc.get(SERVERS_KEY) or {})
|
|
188
|
+
removed = [name for name, spec in servers.items()
|
|
189
|
+
if isinstance(spec, dict) and spec.get(MARKER_KEY) == skill]
|
|
190
|
+
for name in removed:
|
|
191
|
+
del servers[name]
|
|
192
|
+
doc[SERVERS_KEY] = servers
|
|
193
|
+
return doc, sorted(removed)
|