boost-skill-cli 1.0.153__tar.gz → 1.0.155__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.153 → boost_skill_cli-1.0.155}/Makefile +1 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/cli.py +5 -6
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/cliparse.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/bmad.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/configuration.py +20 -14
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/discovery.py +17 -12
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/info.py +9 -7
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/intelligence.py +4 -6
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/pkg.py +7 -7
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/quality.py +12 -12
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/team.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/claude_settings.py +2 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/frontmatter.py +1 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/gitutil.py +2 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/injectscan.py +6 -4
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/logs.py +4 -9
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/mcp.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/output.py +3 -4
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/policy.py +2 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/rag.py +2 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/serve.py +5 -6
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/staleness.py +2 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/store.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/updatediff.py +1 -4
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/util.py +2 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_skill_cli.egg-info/SOURCES.txt +2 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +3 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +3 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap.html +2 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/pyproject.toml +17 -4
- boost_skill_cli-1.0.155/scripts/import_budget.py +156 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_configuration.py +2 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_discovery.py +1 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_intelligence.py +1 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_taps.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_property_parsers.py +3 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_adapters.py +2 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_catalog.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_config.py +1 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_dense_fallback.py +3 -2
- boost_skill_cli-1.0.155/tests/unit/test_import_budget.py +135 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_mcp.py +4 -4
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_output.py +2 -2
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_rag.py +3 -3
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_serve.py +4 -4
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_store.py +2 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_typosquat.py +3 -1
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/.gitattributes +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/.gitignore +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/LICENSE +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/README.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/index.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/examples/README.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/index.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/setup.cfg +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/style/README.md +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/style/boost.css +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/style/boost.js +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/style/demo.html +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/steps/discovery_steps.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.153 → boost_skill_cli-1.0.155}/tests/unit/test_workflows.py +0 -0
|
@@ -43,6 +43,7 @@ lint:
|
|
|
43
43
|
@command -v actionlint >/dev/null 2>&1 && actionlint || echo "actionlint not on PATH — skipping (CI enforces it)"
|
|
44
44
|
$(PY) scripts/build_registries.py --check
|
|
45
45
|
$(PY) scripts/build_roadmap.py --check
|
|
46
|
+
$(PY) scripts/import_budget.py
|
|
46
47
|
|
|
47
48
|
# Supply-chain CVE gate: fail on a known OSV/PyPI advisory in the project's
|
|
48
49
|
# dependency closure. Mirrors the pip-audit CI workflow; run before releasing.
|
|
@@ -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.155'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 155)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -17,6 +17,7 @@ from .core import logs
|
|
|
17
17
|
from .core import nethttp
|
|
18
18
|
from .core import output as out
|
|
19
19
|
from .errors import BoostError
|
|
20
|
+
import contextlib
|
|
20
21
|
|
|
21
22
|
# Global flags handled before dispatch, stripped from the command's own argv.
|
|
22
23
|
_GLOBAL_FLAGS = {
|
|
@@ -174,7 +175,7 @@ def print_command_help(name: str) -> int:
|
|
|
174
175
|
meta = resolve(name)
|
|
175
176
|
if not meta:
|
|
176
177
|
return _unknown(name)
|
|
177
|
-
_group,
|
|
178
|
+
_group, _module, summary = meta
|
|
178
179
|
print(out.c("boost %s" % name, out.BOLD) + " — " + summary)
|
|
179
180
|
# delegate to the command's own --help for flags
|
|
180
181
|
return _dispatch(name, ["--help"], soft=True)
|
|
@@ -196,7 +197,7 @@ def _unknown(name: str) -> int:
|
|
|
196
197
|
|
|
197
198
|
|
|
198
199
|
def _dispatch(name: str, argv: List[str], soft: bool = False) -> int:
|
|
199
|
-
|
|
200
|
+
_group, module, _summary = _BY_NAME[name]
|
|
200
201
|
mod = importlib.import_module("boost_cli.commands.%s" % module)
|
|
201
202
|
func = getattr(mod, "cmd_%s" % name.replace("-", "_"), None)
|
|
202
203
|
if func is None:
|
|
@@ -264,13 +265,11 @@ def main(argv: List[str] | None = None) -> int:
|
|
|
264
265
|
print()
|
|
265
266
|
return rc
|
|
266
267
|
except BrokenPipeError:
|
|
267
|
-
|
|
268
|
+
with contextlib.suppress(Exception):
|
|
268
269
|
sys.stdout.close()
|
|
269
|
-
except Exception:
|
|
270
|
-
pass
|
|
271
270
|
rc = 0
|
|
272
271
|
return rc
|
|
273
|
-
except Exception as e:
|
|
272
|
+
except Exception as e:
|
|
274
273
|
report = logs.write_crash_report(e, [name, *rest])
|
|
275
274
|
if logs.is_debug():
|
|
276
275
|
raise
|
|
@@ -19,7 +19,7 @@ from .core import output as out
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class BoostArgumentParser(argparse.ArgumentParser):
|
|
22
|
-
def error(self, message: str):
|
|
22
|
+
def error(self, message: str):
|
|
23
23
|
"""Print a branded error + dimmed usage, then exit 2 (argparse's code)."""
|
|
24
24
|
out.err(message)
|
|
25
25
|
sys.stderr.write(out.c(self.format_usage(), out.DIM))
|
|
@@ -119,7 +119,7 @@ def _run_installer(directory: Path, modules: str) -> "subprocess.CompletedProces
|
|
|
119
119
|
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=900)
|
|
120
120
|
except (OSError, subprocess.TimeoutExpired) as e:
|
|
121
121
|
raise BoostError("bmad install failed: %s" % e,
|
|
122
|
-
hint="run it yourself: " + " ".join(cmd))
|
|
122
|
+
hint="run it yourself: " + " ".join(cmd)) from e
|
|
123
123
|
if proc.returncode != 0:
|
|
124
124
|
tail = (proc.stderr or proc.stdout or "").strip().splitlines()
|
|
125
125
|
raise BoostError(
|
|
@@ -72,7 +72,7 @@ def cmd_config(argv) -> int:
|
|
|
72
72
|
except TypeError as e:
|
|
73
73
|
raise BoostError(str(e),
|
|
74
74
|
hint="the parent key holds a plain value — "
|
|
75
|
-
"`boost config unset` it first")
|
|
75
|
+
"`boost config unset` it first") from e
|
|
76
76
|
val = config.get(args.key)
|
|
77
77
|
journal.log("config", args.key, op="set")
|
|
78
78
|
out.ok("set %s = %s"
|
|
@@ -101,25 +101,31 @@ def cmd_clean(argv) -> int:
|
|
|
101
101
|
help="also remove snapshots older than 90 days")
|
|
102
102
|
args = p.parse_args(argv)
|
|
103
103
|
|
|
104
|
-
items = [] # (path, kind, bytes)
|
|
105
|
-
for
|
|
104
|
+
items: list[tuple[Path, str, int]] = [] # (path, kind, bytes)
|
|
105
|
+
for spec in agents.known_agents().values():
|
|
106
106
|
adir = spec["dir"]
|
|
107
107
|
if not adir.is_dir():
|
|
108
108
|
continue
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
items.extend(
|
|
110
|
+
(link, "broken symlink", 0)
|
|
111
|
+
for link in sorted(adir.iterdir())
|
|
112
|
+
if link.is_symlink() and not link.exists()
|
|
113
|
+
)
|
|
112
114
|
|
|
113
115
|
configured = {t.safe_name for t in registry.list_taps()}
|
|
114
116
|
if paths.cache_dir().is_dir():
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
items.extend(
|
|
118
|
+
(f, "stale tap cache", f.stat().st_size)
|
|
119
|
+
for f in sorted(paths.cache_dir().glob("*.json"))
|
|
120
|
+
if f.stem not in configured
|
|
121
|
+
)
|
|
118
122
|
|
|
119
123
|
if paths.lock_history_dir().is_dir():
|
|
120
124
|
snaps = sorted(paths.lock_history_dir().glob("lock-*.json"))
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
items.extend(
|
|
126
|
+
(old, "old lock history", old.stat().st_size)
|
|
127
|
+
for old in snaps[:-50]
|
|
128
|
+
)
|
|
123
129
|
|
|
124
130
|
if paths.store_dir().is_dir():
|
|
125
131
|
for pth in sorted(paths.store_dir().rglob("*")):
|
|
@@ -442,7 +448,7 @@ def cmd_onboard(argv) -> int:
|
|
|
442
448
|
text=True, timeout=120)
|
|
443
449
|
except (subprocess.TimeoutExpired, OSError) as e:
|
|
444
450
|
raise BoostError("gh pr create failed: %s" % e,
|
|
445
|
-
hint="branch %s is committed — push it and open the PR manually" % branch)
|
|
451
|
+
hint="branch %s is committed — push it and open the PR manually" % branch) from e
|
|
446
452
|
if proc.returncode != 0:
|
|
447
453
|
tail = (proc.stderr or proc.stdout or "").strip().splitlines()
|
|
448
454
|
raise BoostError("gh pr create failed: %s" % (tail[-1] if tail else "unknown error"),
|
|
@@ -669,7 +675,7 @@ def cmd_schedule(argv) -> int:
|
|
|
669
675
|
kept = [ln for ln in lines if not ln.rstrip().endswith(_CRON_MARK)]
|
|
670
676
|
try:
|
|
671
677
|
proc = subprocess.run(["crontab", "-"],
|
|
672
|
-
input="\n".join(kept
|
|
678
|
+
input="\n".join([*kept, entry]) + "\n",
|
|
673
679
|
capture_output=True, text=True, timeout=30)
|
|
674
680
|
except (OSError, subprocess.TimeoutExpired):
|
|
675
681
|
proc = None
|
|
@@ -943,7 +949,7 @@ def cmd_mcp(argv) -> int:
|
|
|
943
949
|
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
944
950
|
except (OSError, subprocess.TimeoutExpired) as e:
|
|
945
951
|
raise BoostError("claude mcp %s failed: %s" % (args.action, e),
|
|
946
|
-
hint="run it yourself: " + " ".join(cmd))
|
|
952
|
+
hint="run it yourself: " + " ".join(cmd)) from e
|
|
947
953
|
for ln in (proc.stdout or "").strip().splitlines():
|
|
948
954
|
out.info(ln)
|
|
949
955
|
if proc.returncode != 0:
|
|
@@ -17,11 +17,12 @@ import urllib.parse
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
19
|
from .. import cliparse, spin
|
|
20
|
-
from ..core import (agents, ai, catalog, config,
|
|
20
|
+
from ..core import (agents, ai, catalog, config, gitutil,
|
|
21
21
|
journal, lockfile, paths, rag, registry, store, util)
|
|
22
22
|
from ..core import output as out
|
|
23
23
|
from ..core.stackprobe import detect_stack # re-exported: shared with Quality
|
|
24
24
|
from ..errors import BoostError
|
|
25
|
+
import contextlib
|
|
25
26
|
|
|
26
27
|
__all__ = ["detect_stack"]
|
|
27
28
|
|
|
@@ -31,7 +32,7 @@ def _positive_int(s: str) -> int:
|
|
|
31
32
|
try:
|
|
32
33
|
v = int(s)
|
|
33
34
|
except ValueError:
|
|
34
|
-
raise argparse.ArgumentTypeError("invalid int value: %r" % s)
|
|
35
|
+
raise argparse.ArgumentTypeError("invalid int value: %r" % s) from None
|
|
35
36
|
if v < 1:
|
|
36
37
|
raise argparse.ArgumentTypeError("must be >= 1")
|
|
37
38
|
return v
|
|
@@ -164,6 +165,11 @@ def cmd_reindex(argv):
|
|
|
164
165
|
stats = rag.build(force=args.force)
|
|
165
166
|
dense_stats = None
|
|
166
167
|
if args.dense:
|
|
168
|
+
# Imported lazily (only on `--dense`) so the optional dense/embedding
|
|
169
|
+
# stack never loads for the common offline commands this module also
|
|
170
|
+
# holds — count, stats, trending, plain search. See the import-budget
|
|
171
|
+
# gate (scripts/import_budget.py).
|
|
172
|
+
from ..core import embed
|
|
167
173
|
with spin.Spinner("embedding chunks into the dense store"):
|
|
168
174
|
dense_stats = _reindex_dense(args.force)
|
|
169
175
|
if args.as_json:
|
|
@@ -191,6 +197,9 @@ def cmd_reindex(argv):
|
|
|
191
197
|
|
|
192
198
|
def _reindex_dense(force):
|
|
193
199
|
"""Build the dense vector store; returns stats or None when unavailable."""
|
|
200
|
+
# Local import: the dense/embedding engines are opt-in (`search --dense`),
|
|
201
|
+
# so they stay out of startup for every other command in this module.
|
|
202
|
+
from ..core import dense, embed
|
|
194
203
|
if not dense.have_backend() or not embed.available():
|
|
195
204
|
return None
|
|
196
205
|
return dense.build(force=force)
|
|
@@ -219,7 +228,7 @@ def cmd_index(argv):
|
|
|
219
228
|
"search/code?q=filename:SKILL.md&per_page=100&page=%d" % page],
|
|
220
229
|
capture_output=True, text=True, timeout=120)
|
|
221
230
|
except (subprocess.TimeoutExpired, OSError) as e:
|
|
222
|
-
raise BoostError("gh api timed out on page %d" % page, hint=str(e))
|
|
231
|
+
raise BoostError("gh api timed out on page %d" % page, hint=str(e)) from e
|
|
223
232
|
if proc.returncode != 0:
|
|
224
233
|
tail = "\n".join((proc.stderr or proc.stdout or "").strip()
|
|
225
234
|
.splitlines()[-3:])
|
|
@@ -233,7 +242,7 @@ def cmd_index(argv):
|
|
|
233
242
|
data = json.loads(proc.stdout)
|
|
234
243
|
except json.JSONDecodeError:
|
|
235
244
|
raise BoostError("gh api returned unparseable JSON",
|
|
236
|
-
hint="try `boost index` again, or `gh auth status`")
|
|
245
|
+
hint="try `boost index` again, or `gh auth status`") from None
|
|
237
246
|
if page == 1:
|
|
238
247
|
total = int(data.get("total_count") or 0)
|
|
239
248
|
batch = data.get("items") or []
|
|
@@ -328,7 +337,7 @@ def cmd_discover(argv):
|
|
|
328
337
|
data = json.loads(dpath.read_text(encoding="utf-8"))
|
|
329
338
|
except (json.JSONDecodeError, OSError):
|
|
330
339
|
raise BoostError("the discovery index is corrupt",
|
|
331
|
-
hint="rebuild it with `boost index`")
|
|
340
|
+
hint="rebuild it with `boost index`") from None
|
|
332
341
|
all_items = data.get("items") or []
|
|
333
342
|
tokens = [t.lower() for t in args.query if t.strip()]
|
|
334
343
|
items = [it for it in all_items
|
|
@@ -598,7 +607,7 @@ def _aurora_pairs(curses):
|
|
|
598
607
|
"""Allocate Aurora curses colour pairs; token-name -> attr int (0 if none)."""
|
|
599
608
|
names = ("cyan", "violet", "pink", "green", "yellow")
|
|
600
609
|
try:
|
|
601
|
-
custom = curses.can_change_color() and
|
|
610
|
+
custom = curses.can_change_color() and 16 + len(names) <= curses.COLORS
|
|
602
611
|
except curses.error:
|
|
603
612
|
custom = False
|
|
604
613
|
fg = dict(_nearest_base(curses))
|
|
@@ -695,19 +704,15 @@ def _browse_tui(curses, entries):
|
|
|
695
704
|
return "loading description…"
|
|
696
705
|
|
|
697
706
|
def ui(scr):
|
|
698
|
-
|
|
707
|
+
with contextlib.suppress(curses.error):
|
|
699
708
|
curses.curs_set(0)
|
|
700
|
-
except curses.error:
|
|
701
|
-
pass
|
|
702
709
|
scr.timeout(80) # short poll so a finished lazy-load redraws promptly
|
|
703
710
|
th = _aurora_theme(curses)
|
|
704
711
|
|
|
705
712
|
def put(y, x, s, attr=0): # clip-safe cell writer
|
|
706
713
|
if 0 <= y < _h and 0 <= x < _w - 1 and s:
|
|
707
|
-
|
|
714
|
+
with contextlib.suppress(curses.error):
|
|
708
715
|
scr.addnstr(y, x, s, _w - 1 - x, attr)
|
|
709
|
-
except curses.error:
|
|
710
|
-
pass
|
|
711
716
|
|
|
712
717
|
filt, sel, detail = "", 0, False
|
|
713
718
|
selected: set = set()
|
|
@@ -31,7 +31,7 @@ def _read(p: Path) -> str:
|
|
|
31
31
|
try:
|
|
32
32
|
return Path(p).read_text(encoding="utf-8", errors="replace")
|
|
33
33
|
except OSError as e:
|
|
34
|
-
raise BoostError("cannot read %s: %s" % (_tilde(p), e))
|
|
34
|
+
raise BoostError("cannot read %s: %s" % (_tilde(p), e)) from e
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def _resolve_skill_md(name: str):
|
|
@@ -115,7 +115,7 @@ def _kind_table(heading, items, extra=None):
|
|
|
115
115
|
row.append(str(e.get(extra[1], "") or ""))
|
|
116
116
|
rows.append(tuple(row))
|
|
117
117
|
if extra:
|
|
118
|
-
headers = headers
|
|
118
|
+
headers = (*headers, extra[0])
|
|
119
119
|
out.table(rows, headers=headers)
|
|
120
120
|
noun = heading.split()[-1][:-1] # "installed rules" -> "rule"
|
|
121
121
|
print(" " + out.aurora("%d %s%s installed"
|
|
@@ -319,10 +319,10 @@ def cmd_edit(argv):
|
|
|
319
319
|
editor = os.environ.get("VISUAL") or os.environ.get("EDITOR") or "vi"
|
|
320
320
|
cmd = shlex.split(editor) or ["vi"] # support EDITOR="code -w" etc.
|
|
321
321
|
try:
|
|
322
|
-
rc = subprocess.call(cmd
|
|
322
|
+
rc = subprocess.call([*cmd, str(path)])
|
|
323
323
|
except OSError as e:
|
|
324
324
|
raise BoostError("cannot launch editor %r: %s" % (editor, e),
|
|
325
|
-
hint="set $VISUAL or $EDITOR to a valid command")
|
|
325
|
+
hint="set $VISUAL or $EDITOR to a valid command") from e
|
|
326
326
|
if rc != 0:
|
|
327
327
|
out.warn("editor exited with status %d" % rc)
|
|
328
328
|
sha = util.sha256_dir(sdir)
|
|
@@ -595,9 +595,11 @@ def cmd_deps(argv):
|
|
|
595
595
|
unmet, pairs, seen = [], [], set()
|
|
596
596
|
for name in sorted(inst):
|
|
597
597
|
meta = _skill_meta(name) or {}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
598
|
+
unmet.extend(
|
|
599
|
+
{"skill": name, "requires": r}
|
|
600
|
+
for r in _as_list(meta.get("requires"))
|
|
601
|
+
if r not in inst
|
|
602
|
+
)
|
|
601
603
|
for c_name in _as_list(meta.get("conflicts")):
|
|
602
604
|
if c_name in inst:
|
|
603
605
|
key = tuple(sorted((name, c_name)))
|
|
@@ -22,6 +22,7 @@ from ..core import (ai, catalog, frontmatter, gitutil, imperative, journal,
|
|
|
22
22
|
lockfile, paths, store, util)
|
|
23
23
|
from ..core import output as out
|
|
24
24
|
from ..errors import BoostError
|
|
25
|
+
import contextlib
|
|
25
26
|
|
|
26
27
|
# ---------------------------------------------------------------- helpers
|
|
27
28
|
|
|
@@ -777,9 +778,8 @@ def cmd_context(argv: List[str]) -> int:
|
|
|
777
778
|
inst = lockfile.installed()
|
|
778
779
|
for name in sorted(_mentioned_skills(state)):
|
|
779
780
|
entry = inst.get(name)
|
|
780
|
-
if entry and not entry.get("quarantined"):
|
|
781
|
-
|
|
782
|
-
restored += 1
|
|
781
|
+
if entry and not entry.get("quarantined") and store.link_agents(name).linked:
|
|
782
|
+
restored += 1
|
|
783
783
|
state["enabled"] = False
|
|
784
784
|
_save_state(_CONTEXT_STATE, state)
|
|
785
785
|
journal.log("context", "disable", restored=restored)
|
|
@@ -1005,10 +1005,8 @@ def _repo_activity(since: str) -> Tuple[Optional[int], Optional[int]]:
|
|
|
1005
1005
|
proc = gitutil.run(["rev-list", "--count", "--since=" + since, "HEAD"],
|
|
1006
1006
|
cwd=Path.cwd(), check=False)
|
|
1007
1007
|
if proc.returncode == 0:
|
|
1008
|
-
|
|
1008
|
+
with contextlib.suppress(ValueError):
|
|
1009
1009
|
commits = int(proc.stdout.strip() or 0)
|
|
1010
|
-
except ValueError:
|
|
1011
|
-
pass
|
|
1012
1010
|
proc = gitutil.run(["log", "--name-only", "--since=" + since,
|
|
1013
1011
|
"--pretty=format:"], cwd=Path.cwd(), check=False)
|
|
1014
1012
|
if proc.returncode == 0:
|
|
@@ -568,7 +568,7 @@ def _bundle_dump(file: Optional[str]) -> int:
|
|
|
568
568
|
dest.write_text(text, encoding="utf-8")
|
|
569
569
|
except OSError as e:
|
|
570
570
|
raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
|
|
571
|
-
hint="check the path exists and is writable")
|
|
571
|
+
hint="check the path exists and is writable") from e
|
|
572
572
|
n_taps = sum(1 for ln in text.splitlines() if ln.startswith("tap "))
|
|
573
573
|
n_skills = sum(1 for ln in text.splitlines() if ln.startswith("skill "))
|
|
574
574
|
out.ok("wrote %s (%s, %s)" % (_tilde(dest), _plural(n_taps, "tap"),
|
|
@@ -591,7 +591,7 @@ def _bundle_install(file: Optional[str]) -> int:
|
|
|
591
591
|
try:
|
|
592
592
|
text, label = path.read_text(encoding="utf-8"), str(path)
|
|
593
593
|
except OSError as e:
|
|
594
|
-
raise BoostError("cannot read %s: %s" % (_tilde(path), e.strerror or e))
|
|
594
|
+
raise BoostError("cannot read %s: %s" % (_tilde(path), e.strerror or e)) from e
|
|
595
595
|
taps_added = installed_n = present = failed = 0
|
|
596
596
|
have_taps = {t.name for t in registry.list_taps()}
|
|
597
597
|
have_skills = set(lockfile.installed())
|
|
@@ -929,18 +929,18 @@ def _snapshot_restore(snap_id: str) -> int:
|
|
|
929
929
|
# Read the whole archive BEFORE touching the store, so a corrupt
|
|
930
930
|
# snapshot can never leave us with an emptied environment.
|
|
931
931
|
try:
|
|
932
|
-
tf = tarfile.open(str(tar_path), "r:gz")
|
|
932
|
+
tf = tarfile.open(str(tar_path), "r:gz") # noqa: SIM115 (opened in a try to translate the error; used via `with tf:` below)
|
|
933
933
|
except (tarfile.TarError, OSError, EOFError) as e:
|
|
934
934
|
raise BoostError("snapshot %s is unreadable: %s" % (snap_id, e),
|
|
935
935
|
hint="the store was left untouched — "
|
|
936
|
-
"see `boost snapshot list` for other snapshots")
|
|
936
|
+
"see `boost snapshot list` for other snapshots") from e
|
|
937
937
|
with tf:
|
|
938
938
|
try:
|
|
939
939
|
members = tf.getmembers()
|
|
940
940
|
except (tarfile.TarError, OSError, EOFError) as e:
|
|
941
941
|
raise BoostError("snapshot %s is corrupt: %s" % (snap_id, e),
|
|
942
942
|
hint="the store was left untouched — "
|
|
943
|
-
"see `boost snapshot list` for other snapshots")
|
|
943
|
+
"see `boost snapshot list` for other snapshots") from e
|
|
944
944
|
for child in root.iterdir():
|
|
945
945
|
if child.is_dir() and not child.is_symlink():
|
|
946
946
|
util.rmtree(child)
|
|
@@ -1012,7 +1012,7 @@ def cmd_export(argv: List[str]) -> int:
|
|
|
1012
1012
|
tf.add(str(store.skill_store_dir(name)), arcname=name)
|
|
1013
1013
|
except OSError as e:
|
|
1014
1014
|
raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
|
|
1015
|
-
hint="check the output path exists and is writable")
|
|
1015
|
+
hint="check the output path exists and is writable") from e
|
|
1016
1016
|
out.ok("exported %s → %s (%s)" % (_plural(len(chosen), "skill"),
|
|
1017
1017
|
_tilde(dest.resolve()),
|
|
1018
1018
|
util.human_size(dest.stat().st_size)))
|
|
@@ -1063,7 +1063,7 @@ def cmd_adapt(argv: List[str]) -> int:
|
|
|
1063
1063
|
util.atomic_write_text(dest, source)
|
|
1064
1064
|
except OSError as e:
|
|
1065
1065
|
raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
|
|
1066
|
-
hint="check the output path exists and is writable")
|
|
1066
|
+
hint="check the output path exists and is writable") from e
|
|
1067
1067
|
out.ok("adapted %s → %s (%s · %s)" % (display, _tilde(dest.resolve()),
|
|
1068
1068
|
args.to, model or "framework default"))
|
|
1069
1069
|
else:
|
|
@@ -96,9 +96,10 @@ def _broken_links() -> List[Path]:
|
|
|
96
96
|
for adir in agents.enabled_agents().values():
|
|
97
97
|
if not adir.is_dir():
|
|
98
98
|
continue
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
broken.extend(
|
|
100
|
+
link for link in sorted(adir.iterdir())
|
|
101
|
+
if link.is_symlink() and not link.exists()
|
|
102
|
+
)
|
|
102
103
|
return broken
|
|
103
104
|
|
|
104
105
|
|
|
@@ -353,7 +354,7 @@ def cmd_doctor(argv):
|
|
|
353
354
|
bad("%d broken symlink%s in agent dirs — run `boost heal`"
|
|
354
355
|
% (len(broken), _s(len(broken))))
|
|
355
356
|
|
|
356
|
-
for
|
|
357
|
+
for adir in enabled.values():
|
|
357
358
|
if adir.is_dir() and not os.access(str(adir), os.W_OK):
|
|
358
359
|
bad("agent dir %s is not writable" % _tilde(adir))
|
|
359
360
|
|
|
@@ -776,10 +777,7 @@ def cmd_heal(argv):
|
|
|
776
777
|
dry = args.dry_run
|
|
777
778
|
actions: List[str] = []
|
|
778
779
|
|
|
779
|
-
wanted = [paths.boost_home(), paths.repos_dir(), paths.cache_dir(),
|
|
780
|
-
paths.logs_dir(), paths.state_dir(), paths.snapshots_dir(),
|
|
781
|
-
paths.lock_history_dir(), paths.profiles_dir(),
|
|
782
|
-
paths.store_dir()] + list(agents.enabled_agents().values())
|
|
780
|
+
wanted = [paths.boost_home(), paths.repos_dir(), paths.cache_dir(), paths.logs_dir(), paths.state_dir(), paths.snapshots_dir(), paths.lock_history_dir(), paths.profiles_dir(), paths.store_dir(), *list(agents.enabled_agents().values())]
|
|
783
781
|
missing = [d for d in wanted if not d.is_dir()]
|
|
784
782
|
if missing:
|
|
785
783
|
if dry:
|
|
@@ -859,9 +857,11 @@ def cmd_conflict(argv):
|
|
|
859
857
|
for name, _entry in installed:
|
|
860
858
|
meta, body = _read_skill(store.skill_store_dir(name))
|
|
861
859
|
conflicts = meta.get("conflicts") or []
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
860
|
+
declared.extend(
|
|
861
|
+
(name, str(other))
|
|
862
|
+
for other in (conflicts if isinstance(conflicts, list) else [conflicts])
|
|
863
|
+
if str(other) in installed_names and str(other) != name
|
|
864
|
+
)
|
|
865
865
|
for raw in body.splitlines():
|
|
866
866
|
m = imperative.RULE_RE.match(raw)
|
|
867
867
|
if not m:
|
|
@@ -879,7 +879,7 @@ def cmd_conflict(argv):
|
|
|
879
879
|
|
|
880
880
|
pairs, seen = [], set()
|
|
881
881
|
for da, db in declared:
|
|
882
|
-
key = tuple(sorted((da, db)))
|
|
882
|
+
key = (*tuple(sorted((da, db))), "declared")
|
|
883
883
|
if key in seen:
|
|
884
884
|
continue
|
|
885
885
|
seen.add(key)
|
|
@@ -183,7 +183,7 @@ def _load_profile(name: str) -> dict:
|
|
|
183
183
|
try:
|
|
184
184
|
return json.loads(p.read_text(encoding="utf-8"))
|
|
185
185
|
except (json.JSONDecodeError, OSError) as e:
|
|
186
|
-
raise BoostError("profile %s is unreadable: %s" % (name, e))
|
|
186
|
+
raise BoostError("profile %s is unreadable: %s" % (name, e)) from e
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
def _profile_diff(profile: dict):
|
|
@@ -26,6 +26,7 @@ from typing import List, Optional
|
|
|
26
26
|
|
|
27
27
|
from . import paths, util
|
|
28
28
|
from ..errors import BoostError
|
|
29
|
+
import contextlib
|
|
29
30
|
|
|
30
31
|
SCOPES = ("global", "project")
|
|
31
32
|
MARKER = "# boost:"
|
|
@@ -87,10 +88,8 @@ def _prune_history() -> None:
|
|
|
87
88
|
snaps = sorted(_history_dir().glob("*.json"),
|
|
88
89
|
key=lambda f: (f.stat().st_mtime, f.name))
|
|
89
90
|
for old in snaps[:-HISTORY_KEEP]:
|
|
90
|
-
|
|
91
|
+
with contextlib.suppress(OSError):
|
|
91
92
|
old.unlink()
|
|
92
|
-
except OSError:
|
|
93
|
-
pass
|
|
94
93
|
|
|
95
94
|
|
|
96
95
|
# --------------------------------------------------------------- marker helpers
|
|
@@ -150,8 +150,7 @@ def dump(meta: dict) -> str:
|
|
|
150
150
|
for k, v in meta.items():
|
|
151
151
|
if isinstance(v, list):
|
|
152
152
|
lines.append("%s:" % k)
|
|
153
|
-
for item in v
|
|
154
|
-
lines.append(" - %s" % item)
|
|
153
|
+
lines.extend(" - %s" % item for item in v)
|
|
155
154
|
elif isinstance(v, bool):
|
|
156
155
|
lines.append("%s: %s" % (k, "true" if v else "false"))
|
|
157
156
|
elif v is None:
|
|
@@ -20,11 +20,11 @@ def run(args: List[str], cwd: Optional[Path] = None, check: bool = True,
|
|
|
20
20
|
hint="install git, e.g. `xcode-select --install` or `brew install git`")
|
|
21
21
|
try:
|
|
22
22
|
proc = subprocess.run(
|
|
23
|
-
["git"
|
|
23
|
+
["git", *args], cwd=str(cwd) if cwd else None,
|
|
24
24
|
capture_output=True, text=True, timeout=timeout,
|
|
25
25
|
)
|
|
26
26
|
except subprocess.TimeoutExpired:
|
|
27
|
-
raise BoostError("git %s timed out after %ds" % (args[0], timeout))
|
|
27
|
+
raise BoostError("git %s timed out after %ds" % (args[0], timeout)) from None
|
|
28
28
|
if check and proc.returncode != 0:
|
|
29
29
|
detail = (proc.stderr or proc.stdout or "").strip().splitlines()
|
|
30
30
|
raise BoostError("git %s failed: %s" % (args[0], detail[-1] if detail else "unknown error"))
|
|
@@ -107,10 +107,12 @@ def scan_text(text: str) -> List[Finding]:
|
|
|
107
107
|
"""
|
|
108
108
|
findings: List[Finding] = []
|
|
109
109
|
for lineno, raw in enumerate(text.splitlines(), start=1):
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
findings.extend(
|
|
111
|
+
Finding(rule.id, rule.severity, lineno,
|
|
112
|
+
raw.strip()[:200], rule.description)
|
|
113
|
+
for rule in RULES
|
|
114
|
+
if rule.pattern.search(raw)
|
|
115
|
+
)
|
|
114
116
|
findings.sort(key=lambda f: (-_SEVERITY_RANK[f.severity], f.line,
|
|
115
117
|
f.rule_id))
|
|
116
118
|
return findings
|
|
@@ -43,6 +43,7 @@ from pathlib import Path
|
|
|
43
43
|
from typing import List, Optional
|
|
44
44
|
|
|
45
45
|
from . import paths
|
|
46
|
+
import contextlib
|
|
46
47
|
|
|
47
48
|
LOGGER_NAME = "boost"
|
|
48
49
|
MAX_BYTES = 1_000_000 # ~1 MB per file …
|
|
@@ -117,10 +118,8 @@ def reset() -> None:
|
|
|
117
118
|
global _configured, _debug_console
|
|
118
119
|
logger = logging.getLogger(LOGGER_NAME)
|
|
119
120
|
for h in list(logger.handlers):
|
|
120
|
-
|
|
121
|
+
with contextlib.suppress(Exception):
|
|
121
122
|
h.close()
|
|
122
|
-
except Exception:
|
|
123
|
-
pass
|
|
124
123
|
logger.removeHandler(h)
|
|
125
124
|
_configured = False
|
|
126
125
|
_debug_console = False
|
|
@@ -236,10 +235,8 @@ def write_crash_report(exc: BaseException, argv: List[str]) -> Optional[Path]:
|
|
|
236
235
|
"",
|
|
237
236
|
])
|
|
238
237
|
# Always try to get it into the rotating trail, even if the file write fails.
|
|
239
|
-
|
|
238
|
+
with contextlib.suppress(Exception):
|
|
240
239
|
get_logger().error("crash: %s: %s", type(exc).__name__, exc)
|
|
241
|
-
except Exception:
|
|
242
|
-
pass
|
|
243
240
|
try:
|
|
244
241
|
paths.logs_dir().mkdir(parents=True, exist_ok=True)
|
|
245
242
|
report = paths.logs_dir() / ("crash-%s.log" % _file_stamp())
|
|
@@ -257,7 +254,5 @@ def _prune_crash_reports() -> None:
|
|
|
257
254
|
except OSError:
|
|
258
255
|
return
|
|
259
256
|
for stale in reports[:-KEEP_CRASH_REPORTS]:
|
|
260
|
-
|
|
257
|
+
with contextlib.suppress(OSError):
|
|
261
258
|
stale.unlink()
|
|
262
|
-
except OSError:
|
|
263
|
-
pass
|
|
@@ -110,7 +110,7 @@ def handle_request(req: dict, *, version: str,
|
|
|
110
110
|
except BoostError as e:
|
|
111
111
|
text = "Error: %s" % e.message + ("\nhint: %s" % e.hint if e.hint else "")
|
|
112
112
|
is_err = True
|
|
113
|
-
except Exception as e:
|
|
113
|
+
except Exception as e:
|
|
114
114
|
text, is_err = "Error: %s" % e, True
|
|
115
115
|
if text is None:
|
|
116
116
|
resp["error"] = {"code": -32602, "message": "unknown tool %r" % tool}
|
|
@@ -13,6 +13,7 @@ import os
|
|
|
13
13
|
import re
|
|
14
14
|
import shutil
|
|
15
15
|
import sys
|
|
16
|
+
import contextlib
|
|
16
17
|
|
|
17
18
|
RESET = "\033[0m"
|
|
18
19
|
BOLD = "\033[1m"
|
|
@@ -36,10 +37,8 @@ def harden_console_encoding() -> None:
|
|
|
36
37
|
"""
|
|
37
38
|
for stream in (sys.stdout, sys.stderr):
|
|
38
39
|
if hasattr(stream, "reconfigure"):
|
|
39
|
-
|
|
40
|
+
with contextlib.suppress(OSError, ValueError):
|
|
40
41
|
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
41
|
-
except (OSError, ValueError):
|
|
42
|
-
pass
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
def use_color(stream=None) -> bool:
|
|
@@ -273,7 +272,7 @@ def panel(lines, title: str | None = None, hue: str = "cyan") -> str:
|
|
|
273
272
|
lines = [lines]
|
|
274
273
|
widths = [visible_len(x) for x in lines]
|
|
275
274
|
tw = visible_len(title) if title else 0
|
|
276
|
-
inner = max(widths
|
|
275
|
+
inner = max([*widths, tw + 2]) # a titled rule needs a space each side
|
|
277
276
|
|
|
278
277
|
def b(s: str) -> str:
|
|
279
278
|
return aurora(s, hue)
|
|
@@ -9,6 +9,7 @@ import json
|
|
|
9
9
|
from typing import List
|
|
10
10
|
|
|
11
11
|
from . import config, paths
|
|
12
|
+
import contextlib
|
|
12
13
|
|
|
13
14
|
DEFAULTS = {
|
|
14
15
|
"blocked_skills": [], # names never allowed
|
|
@@ -26,10 +27,8 @@ def load() -> dict:
|
|
|
26
27
|
p = paths.policy_path()
|
|
27
28
|
base = dict(DEFAULTS)
|
|
28
29
|
if p.exists():
|
|
29
|
-
|
|
30
|
+
with contextlib.suppress(json.JSONDecodeError, OSError):
|
|
30
31
|
base.update(json.loads(p.read_text(encoding="utf-8")))
|
|
31
|
-
except (json.JSONDecodeError, OSError):
|
|
32
|
-
pass
|
|
33
32
|
return base
|
|
34
33
|
|
|
35
34
|
|