boost-skill-cli 1.0.265__tar.gz → 1.0.267__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.265 → boost_skill_cli-1.0.267}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/discovery.py +15 -8
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/info.py +17 -11
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/quality.py +15 -11
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/safety.py +6 -5
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_skill_cli.egg-info/SOURCES.txt +2 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/pyproject.toml +9 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/release-tools.in +4 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/release-tools.txt +12 -0
- boost_skill_cli-1.0.267/scripts/check_licenses.py +108 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_discovery.py +11 -0
- boost_skill_cli-1.0.267/tests/unit/test_check_licenses.py +163 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.gitattributes +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.gitignore +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.gitleaks.toml +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.htmlvalidate.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.lighthouserc.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.lycheeignore +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.markdownlint-cli2.jsonc +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.pre-commit-config.yaml +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.stylelintrc.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.vale/styles/boost/Terminology.yml +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.vale/styles/config/vocabularies/boost/accept.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/.vale.ini +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/CODE_OF_CONDUCT.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/LICENSE +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/Makefile +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/README.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/_common.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/run.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/capabilities.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/ed25519.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/faithfulness.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/integrity.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/mcpdecl.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/minisign.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/projectlock.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/provenance.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/resolve.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/scopes.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/selfupdate.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/serve.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/trustaudit.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/updatediff.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/codecov.yml +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/distill.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/doctor.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/explain.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/hooks.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/install.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/pulse.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/search.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/gifs/tap.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/distill.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/doctor.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/explain.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/hooks.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/install.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/pulse.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/search.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel/tapes/tap.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/carousel.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/commands.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/index.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/adapter-conformance-langgraph-leg-never-ran.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/ai-bridge-silent-failure-logging.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/ambiguous-tap-short-name-resolution.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/ci-job-timeout-minutes.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/ci-summary-step-fails-itself.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/command-carousel-page.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/crash-listing-branch-untested.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/demo-gif-workflow-has-never-succeeded.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/dense-search-fallback-and-stale-tap-pruning.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/dependabot-missing-pip-ecosystem.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/github-community-health-files.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/install-docs-never-mentioned-upgrading.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/install-from-path-bypasses-policy-and-pin-checks.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/installed-skill-trust-audit.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/journal-rotation-race-and-handle-leak.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/license-compliance-scanning.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/lint-tap-misscopes-rule-workflow-entries.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/lock-invariant-cannot-parse-extras-pins.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/main-has-no-branch-protection.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/main-ruleset-ref-pattern-has-literal-quotes.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/markdownlint-lints-the-fuzz-corpus.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mcp-aware-skills.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/merge-queue-would-deadlock-most-required-checks.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mutation-gate-was-the-whole-critical-path.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/mypy-strict-mode-for-commands.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/negative-limit-inverts-log-pulse-output.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/onboard-overwrites-generated-files-without-confirm.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/path-traversal-unsanitized-rule-workflow-name.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/post-deploy-always-defeated-by-skipped-setup.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/publish-gate-ignores-pip-audit-and-metadata.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/publish-trigger-was-reachable-from-a-fork.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/pytest-tmpdir-cve-blocked-by-py39-floor.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/rel-time-tests-race-the-wall-clock.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/required-checks-can-declare-a-check-that-deadlocks-prs.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/required-checks-config-drift.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/roadmap-html-goes-stale-on-every-rebase.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/ruff-016-widens-the-default-rule-set.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/sbom-release-event-never-fires.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/scorecard-findings-triage.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/self-update-broken-for-pip-pipx-installs.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/serve-py-traversal-guards-untested.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/structured-json-log-output-mode.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/sync-deletes-unowned-broken-symlinks.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/uninstall-has-no-confirmation-prompt.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/update-reinstall-widens-agent-scope.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/wire-bdd-suite-into-ci.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/docs/roadmap.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/eslint.config.mjs +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/examples/README.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/index.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/noxfile.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/coverage-tools.in +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/coverage-tools.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/lint-tools.in +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/lint-tools.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/mutation-tools.in +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/mutation-tools.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/test-tools.in +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/requirements/test-tools.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/a11y_check.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/build_command_reference.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/check_anchors.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/check_required_checks.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/import_budget.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/lock_toolchain.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/mutation_shards.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/mutation_weights.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/perf_gate.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/post_deploy_smoke.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/scripts/release_preflight.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/setup.cfg +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/sonar-project.properties +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/style/README.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/style/boost.css +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/style/boost.js +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/style/demo.html +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/steps/discovery_steps.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_capabilities_policy.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_integrity_enforce.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_run.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_tap_signing.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/functional/test_workspace_scope.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/README.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/01-typical.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/02-lossy-version.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/03-lossy-numbers.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/04-lists.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/05-nested.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/06-empty-block.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/07-unterminated.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/08-none.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/09-unbalanced-quote.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/10-keywords.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/11-crlf.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/frontmatter/12-bom.md +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/01-shorthand.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/02-https.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/03-ssh.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/04-traversal.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/05-empty.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/06-space.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/07-slash.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/corpus/registry/08-bare-host.txt +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/fuzz_frontmatter.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/fuzz/fuzz_registry.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_a11y_check.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_adapters_multi.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_adapters_runner.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_capabilities.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_check_anchors.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_command_reference_fresh.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_community_health_files.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_dependabot_config.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_docsite_chrome.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_ed25519.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_faithfulness.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_fuzz_targets.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_gitleaks_config.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_import_budget.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_integrity.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_marketing_counts.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_mcpdecl.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_minisign.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_mutation_shards.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_mutmut_source_paths.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_perf_gate.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_post_deploy_smoke.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_projectlock.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_provenance.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_release_preflight.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_required_checks.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_resolve.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_scopes.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_selfupdate.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_serve.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_toolchain_lock.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_trustaudit.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_workflow_timeouts.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/unit/test_workflows.py +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/visual/a11y_check.mjs +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/visual/console_check.mjs +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/visual/package.json +0 -0
- {boost_skill_cli-1.0.265 → boost_skill_cli-1.0.267}/tests/visual/visual_check.mjs +0 -0
|
@@ -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.267'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 267)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -16,6 +16,7 @@ import threading
|
|
|
16
16
|
import time
|
|
17
17
|
import urllib.parse
|
|
18
18
|
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, List
|
|
19
20
|
|
|
20
21
|
from .. import cliparse, spin
|
|
21
22
|
from ..core import (
|
|
@@ -217,7 +218,8 @@ def cmd_index(argv):
|
|
|
217
218
|
if not shutil.which("gh"):
|
|
218
219
|
raise BoostError("the GitHub CLI (gh) is required to build the index",
|
|
219
220
|
hint="brew install gh && gh auth login")
|
|
220
|
-
items
|
|
221
|
+
items: List[dict] = []
|
|
222
|
+
total = 0
|
|
221
223
|
pages = min((max(1, args.limit) + 99) // 100, 10) # code search caps at 1000
|
|
222
224
|
for page in range(1, pages + 1):
|
|
223
225
|
spin.progress(page, pages, "searching GitHub for SKILL.md")
|
|
@@ -399,7 +401,7 @@ def cmd_recommend(argv):
|
|
|
399
401
|
raise BoostError("no skills in any tap to recommend from",
|
|
400
402
|
hint="add registries with `boost tap --defaults`")
|
|
401
403
|
stack = detect_stack(target)
|
|
402
|
-
agg = {}
|
|
404
|
+
agg: Dict[str, Dict[str, Any]] = {}
|
|
403
405
|
for kw in stack["keywords"]:
|
|
404
406
|
for e, s in catalog.search(kw, entries):
|
|
405
407
|
rec = agg.setdefault(e["name"], {"entry": e, "score": 0,
|
|
@@ -420,10 +422,15 @@ def cmd_recommend(argv):
|
|
|
420
422
|
if stack["frameworks"]:
|
|
421
423
|
line += " · frameworks: " + ", ".join(stack["frameworks"])
|
|
422
424
|
out.info(out.role("%s (%s)" % (line, _tilde(target)), "muted"))
|
|
423
|
-
shown = ranked[:args.limit]
|
|
425
|
+
shown: List[Dict[str, Any]] = ranked[:args.limit]
|
|
424
426
|
if not shown:
|
|
425
|
-
|
|
426
|
-
|
|
427
|
+
# Annotated rather than inlined: an unannotated literal of mixed value
|
|
428
|
+
# types infers dict[str, object], which then makes every r["entry"][…]
|
|
429
|
+
# read below an error about indexing `object`.
|
|
430
|
+
curated: List[Dict[str, Any]] = [
|
|
431
|
+
{"entry": e, "score": 0, "because": {"curated"}}
|
|
432
|
+
for e in entries if e.get("curated")]
|
|
433
|
+
shown = curated[:args.limit]
|
|
427
434
|
if not shown:
|
|
428
435
|
out.info("no recommendations for this stack — try `boost search <keyword>`")
|
|
429
436
|
return 0
|
|
@@ -680,7 +687,7 @@ def _browse_tui(curses, entries):
|
|
|
680
687
|
"""Run the curses UI. Returns the list of entries picked for install
|
|
681
688
|
(one or more, via multi-select), or None if the user quit without picking.
|
|
682
689
|
"""
|
|
683
|
-
state = {"picks": None}
|
|
690
|
+
state: Dict[str, Any] = {"picks": None}
|
|
684
691
|
categories = _tap_categories()
|
|
685
692
|
desc_cache: dict = {}
|
|
686
693
|
loading: set = set()
|
|
@@ -889,7 +896,7 @@ def cmd_trending(argv):
|
|
|
889
896
|
out.truncate(e["description"], descw))
|
|
890
897
|
for e in sorted(curated, key=operator.itemgetter("name"))[:args.limit]])
|
|
891
898
|
return 0
|
|
892
|
-
agg = {}
|
|
899
|
+
agg: Dict[str, Any] = {}
|
|
893
900
|
for ev in evs: # most-recent-first, so first ts per subject is the latest
|
|
894
901
|
name = ev.get("subject") or "?"
|
|
895
902
|
rec = agg.setdefault(name, {"count": 0, "last": ev.get("ts", "")})
|
|
@@ -951,7 +958,7 @@ def cmd_stats(argv):
|
|
|
951
958
|
out.kv("sha256", str(lock.get("sha256", ""))[:12])
|
|
952
959
|
if size is not None:
|
|
953
960
|
out.kv("size", util.human_size(size))
|
|
954
|
-
|
|
961
|
+
elif cat: # no lock means the guard above found a catalog entry
|
|
955
962
|
out.kv("version", cat["version"])
|
|
956
963
|
out.kv("tap", cat["tap"])
|
|
957
964
|
out.kv("description", cat["description"])
|
|
@@ -14,6 +14,7 @@ import sys
|
|
|
14
14
|
import textwrap
|
|
15
15
|
import webbrowser
|
|
16
16
|
from pathlib import Path
|
|
17
|
+
from typing import Dict, List
|
|
17
18
|
|
|
18
19
|
from .. import cliparse
|
|
19
20
|
from ..core import (
|
|
@@ -126,8 +127,8 @@ def _kind_table(heading, items, extra=None):
|
|
|
126
127
|
``(column, key)`` pair for a per-kind column (e.g. a workflow's slot); the
|
|
127
128
|
count line reuses the heading's trailing noun (`installed rules` -> `rule`)."""
|
|
128
129
|
out.heading(heading)
|
|
129
|
-
headers = ("NAME", "VERSION", "TAP", "AGENTS")
|
|
130
|
-
rows = []
|
|
130
|
+
headers: tuple = ("NAME", "VERSION", "TAP", "AGENTS") # `extra` adds a 5th
|
|
131
|
+
rows: List[tuple] = [] # 4 columns, or 5 when `extra` adds one
|
|
131
132
|
for name in sorted(items):
|
|
132
133
|
e = items[name]
|
|
133
134
|
row = [name, e.get("version", "?"), e.get("tap", "?"),
|
|
@@ -179,7 +180,7 @@ def cmd_list(argv):
|
|
|
179
180
|
return 0
|
|
180
181
|
if skills:
|
|
181
182
|
out.heading("installed skills")
|
|
182
|
-
rows = []
|
|
183
|
+
rows: List[tuple] = [] # 5 columns here, 4 for project skills below
|
|
183
184
|
for name in sorted(skills):
|
|
184
185
|
e = skills[name]
|
|
185
186
|
# Aurora-tinted flags — now that table() aligns by visible width,
|
|
@@ -227,7 +228,8 @@ def cmd_info(argv):
|
|
|
227
228
|
if lock:
|
|
228
229
|
matches = catalog.find(name)
|
|
229
230
|
same_tap = [e for e in matches if e["tap"] == lock.get("tap")]
|
|
230
|
-
|
|
231
|
+
candidates = same_tap or matches
|
|
232
|
+
cat = candidates[0] if candidates else None
|
|
231
233
|
else:
|
|
232
234
|
cat = catalog.resolve_one(name) # raises if unknown anywhere
|
|
233
235
|
|
|
@@ -245,7 +247,7 @@ def cmd_info(argv):
|
|
|
245
247
|
except BoostError:
|
|
246
248
|
skill_dir = None
|
|
247
249
|
desc = str((cat or {}).get("description") or "")
|
|
248
|
-
meta = {}
|
|
250
|
+
meta: dict = {}
|
|
249
251
|
skill_text = ""
|
|
250
252
|
if skill_dir and (skill_dir / "SKILL.md").exists():
|
|
251
253
|
skill_text = _read(skill_dir / "SKILL.md")
|
|
@@ -308,11 +310,11 @@ def cmd_info(argv):
|
|
|
308
310
|
out.kv("latest", out.role(latest, "warn", bold=True)
|
|
309
311
|
+ out.role(" (update available)", "muted"))
|
|
310
312
|
else:
|
|
311
|
-
out.kv("latest", str(cat.get("version", "?")))
|
|
312
|
-
out.kv("tap", (lock or cat).get("tap", "?"))
|
|
313
|
+
out.kv("latest", str((cat or {}).get("version", "?")))
|
|
314
|
+
out.kv("tap", (lock or cat or {}).get("tap", "?"))
|
|
313
315
|
if lock and sdir.is_dir():
|
|
314
316
|
out.kv("store", _tilde(sdir))
|
|
315
|
-
src = lock.get("source_dir") if lock else cat.get("rel_dir")
|
|
317
|
+
src = lock.get("source_dir") if lock else (cat or {}).get("rel_dir")
|
|
316
318
|
if src:
|
|
317
319
|
out.kv("source", _tilde(src))
|
|
318
320
|
if lock:
|
|
@@ -343,7 +345,9 @@ def cmd_info(argv):
|
|
|
343
345
|
# A skill that needs an MCP server is useless without it, so this belongs
|
|
344
346
|
# next to capabilities: both answer "what does this expect of my setup?"
|
|
345
347
|
out.kv("mcp servers", ", ".join(r["name"] for r in mcp_servers))
|
|
346
|
-
if
|
|
348
|
+
# All three are set together under `if skill_dir` above; naming that here
|
|
349
|
+
# states the invariant instead of leaving it for a reader to reconstruct.
|
|
350
|
+
if score is not None and size is not None and files is not None:
|
|
347
351
|
out.kv("quality", "%d/100" % score)
|
|
348
352
|
out.kv("size", util.human_size(size))
|
|
349
353
|
out.kv("files", str(files))
|
|
@@ -715,7 +719,9 @@ def cmd_deps(argv):
|
|
|
715
719
|
out.info(" conflicts: %s %s" % (c_name, state))
|
|
716
720
|
return 1 if problems else 0
|
|
717
721
|
|
|
718
|
-
unmet
|
|
722
|
+
unmet: List[dict] = []
|
|
723
|
+
pairs: List[list] = [] # JSON-dumped, so lists rather than tuples
|
|
724
|
+
seen: set = set()
|
|
719
725
|
for name in sorted(inst):
|
|
720
726
|
meta = _skill_meta(name) or {}
|
|
721
727
|
unmet.extend(
|
|
@@ -769,7 +775,7 @@ def cmd_tag(argv):
|
|
|
769
775
|
mods = operands[1:]
|
|
770
776
|
|
|
771
777
|
if args.list_all:
|
|
772
|
-
mapping = {}
|
|
778
|
+
mapping: Dict[str, List[str]] = {}
|
|
773
779
|
for name, e in sorted(lockfile.installed().items()):
|
|
774
780
|
for t in e.get("tags") or []:
|
|
775
781
|
mapping.setdefault(t, []).append(name)
|
|
@@ -15,7 +15,7 @@ from contextlib import suppress
|
|
|
15
15
|
from datetime import datetime, timedelta, timezone
|
|
16
16
|
from itertools import starmap
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import List, Optional, Tuple
|
|
18
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
19
19
|
|
|
20
20
|
from .. import cliparse
|
|
21
21
|
from ..core import (
|
|
@@ -431,7 +431,7 @@ def cmd_lint(argv):
|
|
|
431
431
|
out.info("nothing to lint")
|
|
432
432
|
return 0
|
|
433
433
|
|
|
434
|
-
results = []
|
|
434
|
+
results: List[Dict[str, Any]] = []
|
|
435
435
|
for name, sdir in targets:
|
|
436
436
|
score, notes = util.score_skill(sdir)
|
|
437
437
|
meta, _ = _read_skill(sdir)
|
|
@@ -679,8 +679,8 @@ def cmd_conflict(argv):
|
|
|
679
679
|
args = ap.parse_args(argv)
|
|
680
680
|
|
|
681
681
|
installed = _iter_installed()
|
|
682
|
-
rules = [] #
|
|
683
|
-
declared = []
|
|
682
|
+
rules: List[Tuple[str, str, str, set]] = [] # skill, line, polarity, stems
|
|
683
|
+
declared: List[Tuple[str, str]] = [] # skill, conflicting skill
|
|
684
684
|
installed_names = {n for n, _e in installed}
|
|
685
685
|
for name, _entry in installed:
|
|
686
686
|
meta, body = _read_skill(store.skill_store_dir(name))
|
|
@@ -705,12 +705,16 @@ def cmd_conflict(argv):
|
|
|
705
705
|
if stem:
|
|
706
706
|
rules.append((name, raw.strip(), polarity, stem))
|
|
707
707
|
|
|
708
|
-
pairs,
|
|
708
|
+
pairs: List[Dict[str, str]] = []
|
|
709
|
+
seen: set = set() # holds both key shapes below, declared and heuristic
|
|
709
710
|
for da, db in declared:
|
|
710
|
-
key
|
|
711
|
-
|
|
711
|
+
# Two different key SHAPES share `seen` — a flat triple here, a pair of
|
|
712
|
+
# (skill, line) pairs below. Distinct names because reusing one made the
|
|
713
|
+
# second `sorted()` type-check against the first one's element type.
|
|
714
|
+
dkey = (*tuple(sorted((da, db))), "declared")
|
|
715
|
+
if dkey in seen:
|
|
712
716
|
continue
|
|
713
|
-
seen.add(
|
|
717
|
+
seen.add(dkey)
|
|
714
718
|
pairs.append({"kind": "declared", "a": da, "b": db,
|
|
715
719
|
"a_line": "frontmatter declares conflicts: %s" % db,
|
|
716
720
|
"b_line": ""})
|
|
@@ -720,10 +724,10 @@ def cmd_conflict(argv):
|
|
|
720
724
|
continue
|
|
721
725
|
small = min(len(a_stem), len(b_stem))
|
|
722
726
|
if small and len(a_stem & b_stem) / small >= _CONFLICT_OVERLAP:
|
|
723
|
-
|
|
724
|
-
if
|
|
727
|
+
hkey = tuple(sorted(((a_skill, a_line), (b_skill, b_line))))
|
|
728
|
+
if hkey in seen:
|
|
725
729
|
continue
|
|
726
|
-
seen.add(
|
|
730
|
+
seen.add(hkey)
|
|
727
731
|
pairs.append({"kind": "heuristic", "a": a_skill, "b": b_skill,
|
|
728
732
|
"a_line": a_line, "b_line": b_line})
|
|
729
733
|
|
|
@@ -12,6 +12,7 @@ import json
|
|
|
12
12
|
import re
|
|
13
13
|
import time
|
|
14
14
|
from contextlib import suppress
|
|
15
|
+
from typing import Any, Dict, List
|
|
15
16
|
|
|
16
17
|
from .. import cliparse
|
|
17
18
|
from ..core import (
|
|
@@ -299,7 +300,7 @@ def cmd_verify(argv):
|
|
|
299
300
|
hint="see what is with `boost list`")
|
|
300
301
|
user_names = [n for n in (args.names or []) if n in lockfile.installed()]
|
|
301
302
|
|
|
302
|
-
results = []
|
|
303
|
+
results: List[Dict[str, Any]] = []
|
|
303
304
|
for name, entry in _iter_installed(user_names or (None if not args.names else [])):
|
|
304
305
|
missing_fields = [f for f in ("version", "tap", "sha256", "installed_at")
|
|
305
306
|
if not entry.get(f)]
|
|
@@ -381,13 +382,13 @@ def cmd_quarantine(argv):
|
|
|
381
382
|
|
|
382
383
|
if args.list_mode:
|
|
383
384
|
rows = []
|
|
384
|
-
for name,
|
|
385
|
-
if not
|
|
385
|
+
for name, rec in _iter_installed():
|
|
386
|
+
if not rec.get("quarantined"):
|
|
386
387
|
continue
|
|
387
388
|
evs = journal.events(action="quarantine", subject=name)
|
|
388
389
|
since = util.rel_time(evs[0].get("ts", "")) if evs else "?"
|
|
389
|
-
rows.append((name,
|
|
390
|
-
|
|
390
|
+
rows.append((name, rec.get("version", "?"),
|
|
391
|
+
rec.get("tap", "?"), since))
|
|
391
392
|
if not rows:
|
|
392
393
|
out.info("no skills in quarantine")
|
|
393
394
|
return 0
|
|
@@ -333,6 +333,7 @@ scripts/build_command_reference.py
|
|
|
333
333
|
scripts/build_registries.py
|
|
334
334
|
scripts/build_roadmap.py
|
|
335
335
|
scripts/check_anchors.py
|
|
336
|
+
scripts/check_licenses.py
|
|
336
337
|
scripts/check_required_checks.py
|
|
337
338
|
scripts/ensure_eval_corpus.sh
|
|
338
339
|
scripts/eval_explain.py
|
|
@@ -426,6 +427,7 @@ tests/unit/test_ai.py
|
|
|
426
427
|
tests/unit/test_capabilities.py
|
|
427
428
|
tests/unit/test_catalog.py
|
|
428
429
|
tests/unit/test_check_anchors.py
|
|
430
|
+
tests/unit/test_check_licenses.py
|
|
429
431
|
tests/unit/test_claude_settings.py
|
|
430
432
|
tests/unit/test_cliparse.py
|
|
431
433
|
tests/unit/test_command_reference_fresh.py
|
|
@@ -177,6 +177,15 @@ files = ["boost_cli"]
|
|
|
177
177
|
module = "boost_cli._version"
|
|
178
178
|
ignore_missing_imports = true
|
|
179
179
|
|
|
180
|
+
# Every cmd_* function takes a bare `argv`, and mypy's default mode skips the
|
|
181
|
+
# BODY of any function with an untyped parameter — so the required "zero mypy
|
|
182
|
+
# errors" gate was checking almost none of the CLI's dispatch logic. This turns
|
|
183
|
+
# body checking on for the command layer without demanding annotated signatures
|
|
184
|
+
# (76 of them), which would be churn for no extra checking.
|
|
185
|
+
[[tool.mypy.overrides]]
|
|
186
|
+
module = "boost_cli.commands.*"
|
|
187
|
+
check_untyped_defs = true
|
|
188
|
+
|
|
180
189
|
[tool.pytest.ini_options]
|
|
181
190
|
testpaths = ["tests"]
|
|
182
191
|
addopts = "-q -p no:cacheprovider"
|
|
@@ -15,3 +15,7 @@ twine
|
|
|
15
15
|
check-wheel-contents
|
|
16
16
|
pyroma
|
|
17
17
|
pip-audit
|
|
18
|
+
# SPDX license compliance of a resolved closure (licenses.yml). Pinned for the
|
|
19
|
+
# same reason as the rest: a verdict that changes on an upstream release of the
|
|
20
|
+
# scanner is a verdict about the scanner, not about the dependencies.
|
|
21
|
+
pip-licenses
|
|
@@ -483,6 +483,10 @@ pip-audit==2.10.1 \
|
|
|
483
483
|
--hash=sha256:1eb4565d19ebe5d48996f4b770b4d2b32887e12cb12cfa637f1a064011b55ffc \
|
|
484
484
|
--hash=sha256:99ef3f600a317c1945f1e89e227ef26e1c2d618429b8bd3fa6f4f7c440c4611a
|
|
485
485
|
# via -r requirements/release-tools.in
|
|
486
|
+
pip-licenses==5.5.5 \
|
|
487
|
+
--hash=sha256:60750c006adf7a0910347b726e8ee9fee3bc8d2e7c8307a5c4ec0776c8e2a276 \
|
|
488
|
+
--hash=sha256:f4c4c6d9e6a03612cf59f29f19dc8ab54904d82e055b8e191498f2279a224e14
|
|
489
|
+
# via -r requirements/release-tools.in
|
|
486
490
|
pip-requirements-parser==32.0.1 \
|
|
487
491
|
--hash=sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526 \
|
|
488
492
|
--hash=sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3
|
|
@@ -491,6 +495,10 @@ platformdirs==4.11.0 \
|
|
|
491
495
|
--hash=sha256:0555d18370482847566ffabcaa53ad7c6c1c29f195989ae1ed634a05f76ea1e0 \
|
|
492
496
|
--hash=sha256:360ccded2b7fce0af0ff80cc8f5942a1c5d99b0e856033acb030bfc634709e74
|
|
493
497
|
# via pip-audit
|
|
498
|
+
prettytable==3.18.0 \
|
|
499
|
+
--hash=sha256:439217116152244369caf3d9f1caf2f9fe29b03bd79e88d2928c8e718c95d680 \
|
|
500
|
+
--hash=sha256:b3346e0e6f79180833aebaac088ae926340586cf6d7d991b9eb125b65f72313a
|
|
501
|
+
# via pip-licenses
|
|
494
502
|
py-serializable==2.1.0 \
|
|
495
503
|
--hash=sha256:9d5db56154a867a9b897c0163b33a793c804c80cee984116d02d49e4578fc103 \
|
|
496
504
|
--hash=sha256:b56d5d686b5a03ba4f4db5e769dc32336e142fc3bd4d68a8c25579ebb0a67304
|
|
@@ -767,6 +775,10 @@ urllib3==2.7.0 \
|
|
|
767
775
|
# id
|
|
768
776
|
# requests
|
|
769
777
|
# twine
|
|
778
|
+
wcwidth==0.8.2 \
|
|
779
|
+
--hash=sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda \
|
|
780
|
+
--hash=sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85
|
|
781
|
+
# via prettytable
|
|
770
782
|
wheel-filename==1.4.2 \
|
|
771
783
|
--hash=sha256:3fa599046443d4ca830d06e3d180cd0a675d5871af0a68daa5623318bb4d17e3 \
|
|
772
784
|
--hash=sha256:87891c465dcbb40b40394a906f01a93214bdd51aa5d25e3a9a59cae62bc298fd
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Fail when a resolved dependency's licence is incompatible with GPL-3.0.
|
|
3
|
+
|
|
4
|
+
`pip-audit` gates known CVEs; nothing gated licence *terms*. boost's shipped
|
|
5
|
+
runtime is stdlib-only, so the exposure is entirely in the opt-in extras —
|
|
6
|
+
`[eval]` alone resolves to 94 packages, including the pinned langchain stack —
|
|
7
|
+
and none of them had ever been checked against the project's own GPL-3.0.
|
|
8
|
+
|
|
9
|
+
Reads ``pip-licenses --format=json`` (stdin, or a path) rather than shelling out,
|
|
10
|
+
so the check is a pure function of its input and can be unit-tested without a
|
|
11
|
+
resolved environment.
|
|
12
|
+
|
|
13
|
+
Why not ``pip-licenses --fail-on``: it compares the declared licence string
|
|
14
|
+
exactly, and that string is not a well-formed identifier. Real values observed
|
|
15
|
+
in boost's own `[eval]` closure:
|
|
16
|
+
|
|
17
|
+
ragas -> "UNKNOWN" (no licence metadata at all)
|
|
18
|
+
tiktoken -> "MIT License\\n\\nCopyright (c) 2022 OpenAI, …" (the whole text)
|
|
19
|
+
sqlite-vec -> "MIT License, Apache License, Version 2.0" (two, comma-joined)
|
|
20
|
+
|
|
21
|
+
so matching is done here by regex over the whole field instead.
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
pip-licenses --format=json | python3 scripts/check_licenses.py
|
|
25
|
+
python3 scripts/check_licenses.py licenses.json
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import json
|
|
30
|
+
import re
|
|
31
|
+
import sys
|
|
32
|
+
from typing import List, Tuple
|
|
33
|
+
|
|
34
|
+
# (pattern, why). Matched case-insensitively against the whole declared licence
|
|
35
|
+
# string. Deliberately short: the aim is high-confidence incompatibility, not a
|
|
36
|
+
# taxonomy. LGPL, MPL and EPL are all fine to *consume* from a GPL-3.0 project
|
|
37
|
+
# and are not listed.
|
|
38
|
+
DENIED: Tuple[Tuple[str, str], ...] = (
|
|
39
|
+
(r"affero|\bagpl\b",
|
|
40
|
+
"AGPL adds network-use copyleft that GPL-3.0 does not carry"),
|
|
41
|
+
# GPLv2-*only* cannot be combined with GPL-3.0. "v2 or later" / "GPLv2+"
|
|
42
|
+
# can (it permits taking v3), so both spellings are excluded explicitly.
|
|
43
|
+
(r"gplv2(?!\+)|gpl-2\.0(?!-or-later)|general public license v2 \(",
|
|
44
|
+
"GPLv2-only is incompatible with GPL-3.0"),
|
|
45
|
+
(r"proprietary|all rights reserved|\bsspl\b|commons clause|\bbusl\b",
|
|
46
|
+
"not an open-source licence"),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Packages whose *metadata* omits a licence even though the project has one.
|
|
50
|
+
# Each needs the upstream licence named, so the exception is auditable rather
|
|
51
|
+
# than a silence. Anything undeclared and unlisted fails.
|
|
52
|
+
UNDECLARED_OK = {
|
|
53
|
+
# https://github.com/explodinggradients/ragas — Apache-2.0 in the repo;
|
|
54
|
+
# the published wheel carries no License field or classifier.
|
|
55
|
+
"ragas": "Apache-2.0 upstream; wheel metadata omits it",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_UNDECLARED = re.compile(r"^\s*(unknown|none|)\s*$", re.IGNORECASE)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def is_undeclared(licence: str) -> bool:
|
|
62
|
+
"""True when the package declared no licence at all."""
|
|
63
|
+
return bool(_UNDECLARED.match(licence or ""))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def violations(rows: List[dict]) -> List[str]:
|
|
67
|
+
"""Human-readable problems in a ``pip-licenses --format=json`` payload."""
|
|
68
|
+
problems = []
|
|
69
|
+
for row in rows:
|
|
70
|
+
name = str(row.get("Name", "?"))
|
|
71
|
+
licence = str(row.get("License", ""))
|
|
72
|
+
if is_undeclared(licence):
|
|
73
|
+
if name.lower() not in UNDECLARED_OK:
|
|
74
|
+
problems.append(
|
|
75
|
+
"%s declares no licence — add it to UNDECLARED_OK with the "
|
|
76
|
+
"upstream licence, or drop the dependency" % name)
|
|
77
|
+
continue
|
|
78
|
+
for pattern, why in DENIED:
|
|
79
|
+
if re.search(pattern, licence, re.IGNORECASE):
|
|
80
|
+
problems.append("%s: %s (%s)" % (
|
|
81
|
+
name, why, licence.splitlines()[0][:60]))
|
|
82
|
+
break
|
|
83
|
+
return problems
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main(argv: List[str]) -> int:
|
|
87
|
+
raw = (open(argv[0], encoding="utf-8").read() if argv
|
|
88
|
+
else sys.stdin.read())
|
|
89
|
+
try:
|
|
90
|
+
rows = json.loads(raw)
|
|
91
|
+
except ValueError as e:
|
|
92
|
+
print("::error::could not parse pip-licenses JSON: %s" % e)
|
|
93
|
+
return 2
|
|
94
|
+
if not isinstance(rows, list) or not rows:
|
|
95
|
+
# An empty scan is not a pass: it means the environment was never
|
|
96
|
+
# populated, which is how a licence gate quietly stops gating.
|
|
97
|
+
print("::error::no packages in the licence report — nothing was scanned")
|
|
98
|
+
return 2
|
|
99
|
+
|
|
100
|
+
problems = violations(rows)
|
|
101
|
+
for p in problems:
|
|
102
|
+
print("::error::%s" % p)
|
|
103
|
+
print("checked %d package(s): %d problem(s)" % (len(rows), len(problems)))
|
|
104
|
+
return 1 if problems else 0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -986,6 +986,17 @@ class TestStats:
|
|
|
986
986
|
assert "Sync commits and PRs to Jira tickets" in r.out
|
|
987
987
|
assert "0 installs · 0 updates · 0 uninstalls" in r.out
|
|
988
988
|
|
|
989
|
+
def test_installed_but_no_longer_in_any_tap(self, boost, installed):
|
|
990
|
+
# The asymmetric case: `lock` exists, `cat` is None. cmd_stats guards
|
|
991
|
+
# "neither" and then relies on that guard two screens later — enabling
|
|
992
|
+
# check_untyped_defs on the command layer is what surfaced the reliance,
|
|
993
|
+
# so it gets a test rather than an implicit invariant.
|
|
994
|
+
boost("untap", "fixture-tap", "--force")
|
|
995
|
+
r = boost("stats", "brainstorming")
|
|
996
|
+
assert "1.4.0" in r.out # version from the lock
|
|
997
|
+
assert "not installed" not in r.out
|
|
998
|
+
assert "0 installs" in r.out or "installs" in r.out
|
|
999
|
+
|
|
989
1000
|
def test_unknown(self, boost, tapped):
|
|
990
1001
|
r = boost("stats", "nope", expect=1)
|
|
991
1002
|
assert "no skill named 'nope' installed or in any tap" in r.err
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Unit tests: scripts/check_licenses.py — the SPDX compatibility gate.
|
|
2
|
+
|
|
3
|
+
Driven against the licence strings dependencies *actually* publish, not against
|
|
4
|
+
tidy identifiers. Every awkward value below was observed in boost's own
|
|
5
|
+
resolved closures, which is the whole reason the check matches by regex instead
|
|
6
|
+
of using ``pip-licenses --fail-on``.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import importlib.util
|
|
11
|
+
import json
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "check_licenses.py"
|
|
17
|
+
|
|
18
|
+
# Verbatim from `pip-licenses --format=json` over `pip install .[eval]`.
|
|
19
|
+
TIKTOKEN = ("MIT License\n\nCopyright (c) 2022 OpenAI, Shantanu Jain\n\n"
|
|
20
|
+
"Permission is hereby granted, free of charge, …")
|
|
21
|
+
SQLITE_VEC = "MIT License, Apache License, Version 2.0"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture(scope="module")
|
|
25
|
+
def mod():
|
|
26
|
+
spec = importlib.util.spec_from_file_location("check_licenses", SCRIPT)
|
|
27
|
+
m = importlib.util.module_from_spec(spec)
|
|
28
|
+
assert spec.loader is not None
|
|
29
|
+
spec.loader.exec_module(m)
|
|
30
|
+
return m
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def rows(*pairs):
|
|
34
|
+
return [{"Name": n, "Version": "1.0", "License": lic} for n, lic in pairs]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TestPermissiveLicencesPass:
|
|
38
|
+
@pytest.mark.parametrize("licence", [
|
|
39
|
+
"MIT", "MIT License", "BSD License", "BSD-3-Clause", "Apache-2.0",
|
|
40
|
+
"Apache Software License", "Python Software Foundation License",
|
|
41
|
+
"PSF-2.0", "MIT-CMU", "Apache-2.0 OR BSD-2-Clause",
|
|
42
|
+
"BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0",
|
|
43
|
+
"MPL-2.0 AND (Apache-2.0 OR MIT)",
|
|
44
|
+
"Mozilla Public License 2.0 (MPL 2.0)",
|
|
45
|
+
SQLITE_VEC, TIKTOKEN,
|
|
46
|
+
])
|
|
47
|
+
def test_no_violation(self, mod, licence):
|
|
48
|
+
assert mod.violations(rows(("pkg", licence))) == []
|
|
49
|
+
|
|
50
|
+
def test_lgpl_and_mpl_are_fine_to_consume(self, mod):
|
|
51
|
+
# Weak copyleft is not a problem for a GPL-3.0 project, and banning it
|
|
52
|
+
# would fail builds for no reason.
|
|
53
|
+
assert mod.violations(rows(
|
|
54
|
+
("a", "GNU Lesser General Public License v3 (LGPLv3)"),
|
|
55
|
+
("b", "GNU Lesser General Public License v2 or later (LGPLv2+)"),
|
|
56
|
+
("c", "Mozilla Public License 2.0 (MPL 2.0)"),
|
|
57
|
+
("d", "Eclipse Public License 2.0"))) == []
|
|
58
|
+
|
|
59
|
+
def test_gplv3_and_gplv2_or_later_pass(self, mod):
|
|
60
|
+
# GPL-3.0 is the project's own licence; "v2 or later" permits taking
|
|
61
|
+
# v3, so both combine cleanly. Only v2-*only* does not.
|
|
62
|
+
assert mod.violations(rows(
|
|
63
|
+
("a", "GNU General Public License v3 (GPLv3)"),
|
|
64
|
+
("b", "GNU General Public License v2 or later (GPLv2+)"),
|
|
65
|
+
("c", "GPL-2.0-or-later"))) == []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TestIncompatibleLicencesFail:
|
|
69
|
+
@pytest.mark.parametrize("licence", [
|
|
70
|
+
"GNU Affero General Public License v3",
|
|
71
|
+
"AGPL-3.0", "AGPL-3.0-only", "agpl-3.0-or-later",
|
|
72
|
+
])
|
|
73
|
+
def test_agpl(self, mod, licence):
|
|
74
|
+
[problem] = mod.violations(rows(("pkg", licence)))
|
|
75
|
+
assert "network-use copyleft" in problem
|
|
76
|
+
assert problem.startswith("pkg: ")
|
|
77
|
+
|
|
78
|
+
@pytest.mark.parametrize("licence", [
|
|
79
|
+
"GNU General Public License v2 (GPLv2)", "GPL-2.0", "GPL-2.0-only",
|
|
80
|
+
])
|
|
81
|
+
def test_gplv2_only(self, mod, licence):
|
|
82
|
+
[problem] = mod.violations(rows(("pkg", licence)))
|
|
83
|
+
assert "GPLv2-only" in problem
|
|
84
|
+
|
|
85
|
+
@pytest.mark.parametrize("licence", [
|
|
86
|
+
"Other/Proprietary License", "Proprietary", "All Rights Reserved",
|
|
87
|
+
"SSPL-1.0", "BUSL-1.1", "MIT with Commons Clause",
|
|
88
|
+
])
|
|
89
|
+
def test_non_open_source(self, mod, licence):
|
|
90
|
+
[problem] = mod.violations(rows(("pkg", licence)))
|
|
91
|
+
assert "not an open-source licence" in problem
|
|
92
|
+
|
|
93
|
+
def test_a_multi_line_licence_reports_only_its_first_line(self, mod):
|
|
94
|
+
# A whole licence text in the failure message would bury the finding.
|
|
95
|
+
[problem] = mod.violations(rows(
|
|
96
|
+
("pkg", "Other/Proprietary License\n\nblah\n" * 40)))
|
|
97
|
+
assert "\n" not in problem
|
|
98
|
+
assert len(problem) < 200
|
|
99
|
+
|
|
100
|
+
def test_every_offender_is_reported_not_just_the_first(self, mod):
|
|
101
|
+
problems = mod.violations(rows(("a", "AGPL-3.0"), ("b", "MIT"),
|
|
102
|
+
("c", "Other/Proprietary License")))
|
|
103
|
+
assert len(problems) == 2
|
|
104
|
+
assert problems[0].startswith("a: ") and problems[1].startswith("c: ")
|
|
105
|
+
|
|
106
|
+
def test_a_package_is_reported_once_even_if_it_trips_twice(self, mod):
|
|
107
|
+
[problem] = mod.violations(rows(("pkg", "AGPL-3.0 AND Proprietary")))
|
|
108
|
+
assert problem.startswith("pkg: ")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class TestUndeclaredLicences:
|
|
112
|
+
@pytest.mark.parametrize("licence", ["UNKNOWN", "unknown", "", " ", "None"])
|
|
113
|
+
def test_recognised_as_undeclared(self, mod, licence):
|
|
114
|
+
assert mod.is_undeclared(licence) is True
|
|
115
|
+
|
|
116
|
+
@pytest.mark.parametrize("licence", ["MIT", "Unknown Software License"])
|
|
117
|
+
def test_not_undeclared(self, mod, licence):
|
|
118
|
+
assert mod.is_undeclared(licence) is False
|
|
119
|
+
|
|
120
|
+
def test_an_undeclared_package_fails_by_default(self, mod):
|
|
121
|
+
[problem] = mod.violations(rows(("mystery", "UNKNOWN")))
|
|
122
|
+
assert "declares no licence" in problem
|
|
123
|
+
|
|
124
|
+
def test_the_documented_exception_passes(self, mod):
|
|
125
|
+
# ragas is Apache-2.0 upstream; its wheel carries no License field.
|
|
126
|
+
assert mod.violations(rows(("ragas", "UNKNOWN"))) == []
|
|
127
|
+
assert "ragas" in mod.UNDECLARED_OK
|
|
128
|
+
|
|
129
|
+
def test_every_exception_names_the_upstream_licence(self, mod):
|
|
130
|
+
# An allowlist entry with no reason is indistinguishable from silence.
|
|
131
|
+
for pkg, reason in mod.UNDECLARED_OK.items():
|
|
132
|
+
assert reason.strip(), pkg
|
|
133
|
+
assert pkg == pkg.lower(), "keys are matched lowercased"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class TestMain:
|
|
137
|
+
def test_a_clean_report_exits_zero(self, mod, tmp_path, capsys):
|
|
138
|
+
p = tmp_path / "l.json"
|
|
139
|
+
p.write_text(json.dumps(rows(("a", "MIT"))), encoding="utf-8")
|
|
140
|
+
assert mod.main([str(p)]) == 0
|
|
141
|
+
assert "1 package(s): 0 problem(s)" in capsys.readouterr().out
|
|
142
|
+
|
|
143
|
+
def test_a_violation_exits_one_with_an_annotation(self, mod, tmp_path,
|
|
144
|
+
capsys):
|
|
145
|
+
p = tmp_path / "l.json"
|
|
146
|
+
p.write_text(json.dumps(rows(("a", "AGPL-3.0"))), encoding="utf-8")
|
|
147
|
+
assert mod.main([str(p)]) == 1
|
|
148
|
+
assert "::error::a: AGPL" in capsys.readouterr().out
|
|
149
|
+
|
|
150
|
+
def test_an_empty_report_is_a_failure_not_a_pass(self, mod, tmp_path,
|
|
151
|
+
capsys):
|
|
152
|
+
# This is how a licence gate quietly stops gating: the environment was
|
|
153
|
+
# never populated, every package passes, the step goes green.
|
|
154
|
+
p = tmp_path / "l.json"
|
|
155
|
+
p.write_text("[]", encoding="utf-8")
|
|
156
|
+
assert mod.main([str(p)]) == 2
|
|
157
|
+
assert "nothing was scanned" in capsys.readouterr().out
|
|
158
|
+
|
|
159
|
+
def test_unparseable_input_is_a_failure(self, mod, tmp_path, capsys):
|
|
160
|
+
p = tmp_path / "l.json"
|
|
161
|
+
p.write_text("not json", encoding="utf-8")
|
|
162
|
+
assert mod.main([str(p)]) == 2
|
|
163
|
+
assert "could not parse" in capsys.readouterr().out
|
|
File without changes
|
|
File without changes
|
|
File without changes
|