boost-skill-cli 1.0.263__tar.gz → 1.0.265__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.263 → boost_skill_cli-1.0.265}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/info.py +21 -1
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/config.py +4 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/logs.py +66 -8
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/DEBUGGING.md +28 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/build_roadmap.py +51 -9
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_logs.py +140 -0
- boost_skill_cli-1.0.265/tests/unit/test_roadmap_fresh.py +151 -0
- boost_skill_cli-1.0.263/tests/unit/test_roadmap_fresh.py +0 -86
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.gitattributes +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.gitignore +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.gitleaks.toml +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.htmlvalidate.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.lighthouserc.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.lycheeignore +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.markdownlint-cli2.jsonc +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.pre-commit-config.yaml +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.stylelintrc.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.vale/styles/boost/Terminology.yml +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.vale/styles/config/vocabularies/boost/accept.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/.vale.ini +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/CODE_OF_CONDUCT.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/LICENSE +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/Makefile +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/README.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/_common.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/run.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/safety.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/capabilities.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/ed25519.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/faithfulness.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/integrity.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/mcpdecl.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/minisign.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/projectlock.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/provenance.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/resolve.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/scopes.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/selfupdate.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/serve.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/trustaudit.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/updatediff.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/codecov.yml +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/distill.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/doctor.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/explain.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/hooks.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/install.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/pulse.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/search.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/gifs/tap.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/distill.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/doctor.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/explain.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/hooks.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/install.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/pulse.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/search.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel/tapes/tap.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/carousel.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/commands.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/index.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/adapter-conformance-langgraph-leg-never-ran.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/ai-bridge-silent-failure-logging.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/ambiguous-tap-short-name-resolution.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/ci-job-timeout-minutes.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/ci-summary-step-fails-itself.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/command-carousel-page.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/crash-listing-branch-untested.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/demo-gif-workflow-has-never-succeeded.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/dense-search-fallback-and-stale-tap-pruning.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/dependabot-missing-pip-ecosystem.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/github-community-health-files.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/install-docs-never-mentioned-upgrading.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/install-from-path-bypasses-policy-and-pin-checks.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/installed-skill-trust-audit.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/journal-rotation-race-and-handle-leak.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/license-compliance-scanning.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/lint-tap-misscopes-rule-workflow-entries.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/lock-invariant-cannot-parse-extras-pins.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/main-has-no-branch-protection.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/main-ruleset-ref-pattern-has-literal-quotes.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/markdownlint-lints-the-fuzz-corpus.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mcp-aware-skills.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/merge-queue-would-deadlock-most-required-checks.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mutation-gate-was-the-whole-critical-path.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/mypy-strict-mode-for-commands.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/negative-limit-inverts-log-pulse-output.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/onboard-overwrites-generated-files-without-confirm.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/path-traversal-unsanitized-rule-workflow-name.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/post-deploy-always-defeated-by-skipped-setup.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/publish-gate-ignores-pip-audit-and-metadata.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/publish-trigger-was-reachable-from-a-fork.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/pytest-tmpdir-cve-blocked-by-py39-floor.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/rel-time-tests-race-the-wall-clock.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/required-checks-can-declare-a-check-that-deadlocks-prs.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/required-checks-config-drift.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/roadmap-html-goes-stale-on-every-rebase.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/ruff-016-widens-the-default-rule-set.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/sbom-release-event-never-fires.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/scorecard-findings-triage.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/self-update-broken-for-pip-pipx-installs.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/serve-py-traversal-guards-untested.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/structured-json-log-output-mode.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/sync-deletes-unowned-broken-symlinks.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/uninstall-has-no-confirmation-prompt.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/update-reinstall-widens-agent-scope.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/wire-bdd-suite-into-ci.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/docs/roadmap.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/eslint.config.mjs +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/examples/README.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/index.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/noxfile.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/coverage-tools.in +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/coverage-tools.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/lint-tools.in +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/lint-tools.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/mutation-tools.in +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/mutation-tools.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/release-tools.in +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/release-tools.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/test-tools.in +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/requirements/test-tools.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/a11y_check.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/build_command_reference.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/check_anchors.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/check_required_checks.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/import_budget.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/lock_toolchain.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/mutation_shards.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/mutation_weights.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/perf_gate.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/post_deploy_smoke.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/scripts/release_preflight.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/setup.cfg +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/sonar-project.properties +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/style/README.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/style/boost.css +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/style/boost.js +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/style/demo.html +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/steps/discovery_steps.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_capabilities_policy.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_integrity_enforce.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_run.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_tap_signing.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/functional/test_workspace_scope.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/README.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/01-typical.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/02-lossy-version.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/03-lossy-numbers.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/04-lists.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/05-nested.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/06-empty-block.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/07-unterminated.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/08-none.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/09-unbalanced-quote.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/10-keywords.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/11-crlf.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/frontmatter/12-bom.md +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/01-shorthand.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/02-https.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/03-ssh.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/04-traversal.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/05-empty.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/06-space.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/07-slash.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/corpus/registry/08-bare-host.txt +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/fuzz_frontmatter.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/fuzz/fuzz_registry.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_a11y_check.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_adapters_multi.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_adapters_runner.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_capabilities.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_check_anchors.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_command_reference_fresh.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_community_health_files.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_dependabot_config.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_docsite_chrome.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_ed25519.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_faithfulness.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_fuzz_targets.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_gitleaks_config.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_import_budget.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_integrity.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_marketing_counts.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_mcpdecl.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_minisign.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_mutation_shards.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_mutmut_source_paths.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_perf_gate.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_post_deploy_smoke.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_projectlock.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_provenance.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_release_preflight.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_required_checks.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_resolve.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_scopes.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_selfupdate.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_serve.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_toolchain_lock.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_trustaudit.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_workflow_timeouts.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/unit/test_workflows.py +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/visual/a11y_check.mjs +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/visual/console_check.mjs +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/tests/visual/package.json +0 -0
- {boost_skill_cli-1.0.263 → boost_skill_cli-1.0.265}/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.265'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 265)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -540,6 +540,26 @@ def cmd_explain(argv):
|
|
|
540
540
|
return 0
|
|
541
541
|
|
|
542
542
|
|
|
543
|
+
def _diag_line(line: str) -> str:
|
|
544
|
+
"""Render one stored log line for display, whether it is text or JSON.
|
|
545
|
+
|
|
546
|
+
``BOOST_LOG_FORMAT=json`` makes the file JSONL, which is the point — but
|
|
547
|
+
this command is the *human* view of that same file, and raw JSONL is not
|
|
548
|
+
it. Anything that is not a boost log object passes through untouched, so a
|
|
549
|
+
file whose format changed mid-life still reads end to end.
|
|
550
|
+
"""
|
|
551
|
+
if not line.startswith("{"):
|
|
552
|
+
return line
|
|
553
|
+
try:
|
|
554
|
+
rec = json.loads(line)
|
|
555
|
+
except ValueError:
|
|
556
|
+
return line
|
|
557
|
+
if not isinstance(rec, dict) or "msg" not in rec:
|
|
558
|
+
return line
|
|
559
|
+
return "%s %-7s %s: %s" % (rec.get("ts", ""), rec.get("level", ""),
|
|
560
|
+
rec.get("logger", ""), rec["msg"])
|
|
561
|
+
|
|
562
|
+
|
|
543
563
|
def _show_diagnostics(limit):
|
|
544
564
|
lp = logs.log_path()
|
|
545
565
|
if not lp.exists():
|
|
@@ -548,7 +568,7 @@ def _show_diagnostics(limit):
|
|
|
548
568
|
lines = lp.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
549
569
|
out.heading("diagnostic log — %s" % lp)
|
|
550
570
|
for line in lines[-limit:]:
|
|
551
|
-
out.info(line)
|
|
571
|
+
out.info(_diag_line(line))
|
|
552
572
|
return 0
|
|
553
573
|
|
|
554
574
|
|
|
@@ -30,6 +30,10 @@ DEFAULTS = {
|
|
|
30
30
|
# See core/logs.py and docs/DEBUGGING.md.
|
|
31
31
|
"level": "OFF",
|
|
32
32
|
"file": True, # set false (or BOOST_NO_LOG=1) to disable the log file
|
|
33
|
+
# "text" (default) or "json". json emits one JSON object per line —
|
|
34
|
+
# the same fields, but readable by jq or a log collector without a
|
|
35
|
+
# regex. Overridden by BOOST_LOG_FORMAT.
|
|
36
|
+
"format": "text",
|
|
33
37
|
},
|
|
34
38
|
}
|
|
35
39
|
|
|
@@ -28,10 +28,21 @@ to disable the file.
|
|
|
28
28
|
Each invocation bookends the trail with an ``invoke:`` line and a ``done:`` line
|
|
29
29
|
that carries the exit code and wall-clock duration, so the log doubles as a
|
|
30
30
|
lightweight timing record for spotting slow commands after the fact.
|
|
31
|
+
|
|
32
|
+
Line *format* is resolved separately from verbosity, same precedence shape:
|
|
33
|
+
|
|
34
|
+
1. ``BOOST_LOG_FORMAT=json|text``
|
|
35
|
+
2. config ``logging.format`` (default ``text``)
|
|
36
|
+
|
|
37
|
+
``json`` emits one JSON object per line — the same fields the text formatter
|
|
38
|
+
renders — so ``~/.boost/logs/boost.log`` pipes straight into ``jq`` or a log
|
|
39
|
+
collector instead of needing a regex. It is the same JSONL shape
|
|
40
|
+
``core.journal`` already writes for the pulse feed.
|
|
31
41
|
"""
|
|
32
42
|
from __future__ import annotations
|
|
33
43
|
|
|
34
44
|
import contextlib
|
|
45
|
+
import json
|
|
35
46
|
import logging
|
|
36
47
|
import logging.handlers
|
|
37
48
|
import os
|
|
@@ -51,6 +62,9 @@ BACKUP_COUNT = 3 # … times (N+1) files kept = ~4 MB ceiling
|
|
|
51
62
|
KEEP_CRASH_REPORTS = 20
|
|
52
63
|
|
|
53
64
|
_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
|
|
65
|
+
_FORMATS = ("text", "json")
|
|
66
|
+
_TEXT_FMT = "%(asctime)s %(levelname)-7s %(name)s: %(message)s"
|
|
67
|
+
_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ"
|
|
54
68
|
|
|
55
69
|
# Set by configure(); read by main()'s exception handler to decide whether to
|
|
56
70
|
# print a full traceback or a friendly one-liner.
|
|
@@ -98,6 +112,54 @@ def _console_level(verbose: bool, debug: bool, quiet: bool) -> Optional[int]:
|
|
|
98
112
|
return getattr(logging, cfg) if cfg in _LEVELS else None
|
|
99
113
|
|
|
100
114
|
|
|
115
|
+
class JsonFormatter(logging.Formatter):
|
|
116
|
+
"""One JSON object per line, carrying the text formatter's own fields.
|
|
117
|
+
|
|
118
|
+
Hand-rolled rather than pulled from a dependency: the runtime is
|
|
119
|
+
stdlib-only, ``core.journal`` already writes JSONL by hand for the pulse
|
|
120
|
+
feed, and a formatter that can raise is a formatter that loses the trail it
|
|
121
|
+
exists to keep — hence ``default=str``, so an unexpected object in a record
|
|
122
|
+
degrades to its repr instead of killing the handler.
|
|
123
|
+
|
|
124
|
+
``converter`` is set on the instance by :func:`_formatter`, not here: as a
|
|
125
|
+
class attribute mypy binds it as a method and the signature stops matching
|
|
126
|
+
the base class's.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
130
|
+
payload = {
|
|
131
|
+
"ts": self.formatTime(record, _DATE_FMT),
|
|
132
|
+
"level": record.levelname,
|
|
133
|
+
"logger": record.name,
|
|
134
|
+
"msg": record.getMessage(),
|
|
135
|
+
}
|
|
136
|
+
if record.exc_info:
|
|
137
|
+
payload["exc"] = self.formatException(record.exc_info)
|
|
138
|
+
return json.dumps(payload, ensure_ascii=False, default=str)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def log_format() -> str:
|
|
142
|
+
"""Resolve the log line format: env first, then config, else ``text``."""
|
|
143
|
+
env = (os.environ.get("BOOST_LOG_FORMAT") or "").strip().lower()
|
|
144
|
+
if env in _FORMATS:
|
|
145
|
+
return env
|
|
146
|
+
from . import config
|
|
147
|
+
cfg = str(config.get("logging.format", "text") or "text").strip().lower()
|
|
148
|
+
return cfg if cfg in _FORMATS else "text"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _formatter(fmt_name: str) -> logging.Formatter:
|
|
152
|
+
"""Build the formatter both handlers share."""
|
|
153
|
+
fmt: logging.Formatter = (JsonFormatter() if fmt_name == "json"
|
|
154
|
+
else logging.Formatter(_TEXT_FMT,
|
|
155
|
+
datefmt=_DATE_FMT))
|
|
156
|
+
# Both stamp a literal ``Z`` (UTC) suffix, so the timestamp has to be
|
|
157
|
+
# rendered in UTC — otherwise it uses local time and every line is
|
|
158
|
+
# mislabelled by the machine's offset. gmtime keeps it honest.
|
|
159
|
+
fmt.converter = time.gmtime
|
|
160
|
+
return fmt
|
|
161
|
+
|
|
162
|
+
|
|
101
163
|
def _file_enabled() -> bool:
|
|
102
164
|
if os.environ.get("BOOST_NO_LOG"):
|
|
103
165
|
return False
|
|
@@ -141,14 +203,10 @@ def configure(verbose: bool = False, debug: bool = False,
|
|
|
141
203
|
return logger
|
|
142
204
|
_configured = True
|
|
143
205
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
# datefmt stamps a literal ``Z`` (UTC) suffix, so the timestamp must be
|
|
149
|
-
# rendered in UTC — otherwise ``%(asctime)s`` uses local time and every
|
|
150
|
-
# line is mislabelled by the machine's offset. gmtime keeps it honest.
|
|
151
|
-
fmt.converter = time.gmtime
|
|
206
|
+
# One formatter for both handlers: a user who asked for JSON asked for it
|
|
207
|
+
# everywhere, and a file/console split would make `--debug` disagree with
|
|
208
|
+
# the file it is meant to be showing.
|
|
209
|
+
fmt = _formatter(log_format())
|
|
152
210
|
|
|
153
211
|
# File handler — always DEBUG, best-effort (never break the CLI over a log).
|
|
154
212
|
if _file_enabled():
|
|
@@ -80,6 +80,33 @@ BOOST_NO_LOG=1 boost install foo # this run writes no file
|
|
|
80
80
|
boost config set logging.file false # permanently
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Structured (JSON) log lines
|
|
84
|
+
|
|
85
|
+
The trail is plain text by default. `json` emits **one JSON object per line**
|
|
86
|
+
carrying the same fields, so the file pipes straight into `jq` or a log
|
|
87
|
+
collector instead of needing a regex:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
BOOST_LOG_FORMAT=json boost install foo # this run
|
|
91
|
+
boost config set logging.format json # permanently
|
|
92
|
+
|
|
93
|
+
jq -r 'select(.level=="WARNING") | .msg' ~/.boost/logs/boost.log
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Field | Meaning |
|
|
97
|
+
|-------|---------|
|
|
98
|
+
| `ts` | UTC timestamp, `2026-07-27T14:03:11Z` |
|
|
99
|
+
| `level` | `DEBUG` … `CRITICAL` |
|
|
100
|
+
| `logger` | always `boost` today |
|
|
101
|
+
| `msg` | the formatted message, arguments already interpolated |
|
|
102
|
+
| `exc` | the traceback — present only on records logged with an exception |
|
|
103
|
+
|
|
104
|
+
The format applies to the console channel too, so `--debug` shows the same
|
|
105
|
+
shape the file is recording. `boost log --diagnostics` renders JSON lines back
|
|
106
|
+
as text, so switching format doesn't cost you the built-in viewer, and a file
|
|
107
|
+
whose format changed mid-life still reads end to end. An unrecognised value
|
|
108
|
+
falls back to `text` rather than failing the run.
|
|
109
|
+
|
|
83
110
|
---
|
|
84
111
|
|
|
85
112
|
## Crash reports
|
|
@@ -149,6 +176,7 @@ Everything that changes boost's runtime behaviour, in one place:
|
|
|
149
176
|
| `BOOST_AGENTS_STORE` | override `~/.agents/skills` (the canonical store) |
|
|
150
177
|
| `BOOST_DEBUG` | `=1` → console `DEBUG` + tracebacks |
|
|
151
178
|
| `BOOST_LOG_LEVEL` | explicit console diagnostic level |
|
|
179
|
+
| `BOOST_LOG_FORMAT` | `text` (default) or `json` — one JSON object per log line |
|
|
152
180
|
| `BOOST_NO_LOG` | `=1` → don't write the diagnostic log file |
|
|
153
181
|
| `BOOST_NO_AI` | `=1` → disable all AI calls (offline / deterministic) |
|
|
154
182
|
| `BOOST_ASSUME_YES` | `=1` → auto-confirm prompts (also `--yes`/`-y`) |
|
|
@@ -265,6 +265,40 @@ def build_design() -> tuple[Path, str]:
|
|
|
265
265
|
return DESIGN_HTML, html
|
|
266
266
|
|
|
267
267
|
|
|
268
|
+
_CARD_ID = re.compile(r'<article class="(?:cap rcard|ritem)" id="([^"]+)"')
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def diagnose(path, committed: str, fresh: str) -> list[str]:
|
|
272
|
+
"""Explain *why* a board drifted, in the terms the reader needs.
|
|
273
|
+
|
|
274
|
+
The common cause is not a hand-edit. It is a rebase: another PR's card
|
|
275
|
+
merges in as a clean new ``docs/roadmap/items/*.md``, git merges the
|
|
276
|
+
generated HTML textually and succeeds, and the board is silently missing
|
|
277
|
+
that card. The old message said only "out of date", so a PR that changed no
|
|
278
|
+
Python at all went red across lint and every test leg with nothing in the
|
|
279
|
+
log pointing at the actual cause.
|
|
280
|
+
"""
|
|
281
|
+
was = _CARD_ID.findall(committed)
|
|
282
|
+
now = _CARD_ID.findall(fresh)
|
|
283
|
+
missing = [i for i in now if i not in set(was)]
|
|
284
|
+
extra = [i for i in was if i not in set(now)]
|
|
285
|
+
lines = []
|
|
286
|
+
if missing:
|
|
287
|
+
lines.append(" missing %d card(s) present in docs/roadmap/items/: %s"
|
|
288
|
+
% (len(missing), ", ".join(missing[:8])
|
|
289
|
+
+ (" …" if len(missing) > 8 else "")))
|
|
290
|
+
lines.append(" ↳ almost always a rebase: someone else's card landed on "
|
|
291
|
+
"main and the generated HTML merged textually.")
|
|
292
|
+
if extra:
|
|
293
|
+
lines.append(" %d card(s) in the HTML have no item file: %s"
|
|
294
|
+
% (len(extra), ", ".join(extra[:8])
|
|
295
|
+
+ (" …" if len(extra) > 8 else "")))
|
|
296
|
+
if not missing and not extra:
|
|
297
|
+
lines.append(" the same cards, rendered differently — an item file's "
|
|
298
|
+
"fields or body changed, or the generator did.")
|
|
299
|
+
return lines
|
|
300
|
+
|
|
301
|
+
|
|
268
302
|
def main(argv: list[str] | None = None) -> int:
|
|
269
303
|
parser = argparse.ArgumentParser(description="Regenerate the roadmap boards.")
|
|
270
304
|
parser.add_argument(
|
|
@@ -277,21 +311,29 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
277
311
|
drift = []
|
|
278
312
|
for path, fresh in targets:
|
|
279
313
|
if args.check:
|
|
280
|
-
|
|
281
|
-
|
|
314
|
+
committed = path.read_text(encoding="utf-8")
|
|
315
|
+
if committed != fresh:
|
|
316
|
+
drift.append((path, committed, fresh))
|
|
282
317
|
else:
|
|
283
318
|
path.write_text(fresh, encoding="utf-8")
|
|
284
319
|
print("wrote %s" % path)
|
|
285
320
|
|
|
286
321
|
if args.check:
|
|
287
322
|
if drift:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
323
|
+
report = ["ERROR: the roadmap board is stale — regenerate with",
|
|
324
|
+
" python3 scripts/build_roadmap.py",
|
|
325
|
+
"and commit the result (see CONTRIBUTING.md).",
|
|
326
|
+
""]
|
|
327
|
+
for path, committed, fresh in drift:
|
|
328
|
+
report.append("%s" % path)
|
|
329
|
+
report.extend(diagnose(path, committed, fresh))
|
|
330
|
+
print("\n".join(report), file=sys.stderr)
|
|
331
|
+
# One annotation so the cause is visible on the checks tab without
|
|
332
|
+
# opening a log: this failure lands on every matrix leg at once and
|
|
333
|
+
# reads as a broken build rather than a stale generated file.
|
|
334
|
+
print("::error::roadmap board is stale — run "
|
|
335
|
+
"`python3 scripts/build_roadmap.py` and commit "
|
|
336
|
+
"(usually needed after a rebase)")
|
|
295
337
|
return 1
|
|
296
338
|
print("roadmap boards are up to date.")
|
|
297
339
|
return 0
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Diagnostic logging & crash reporting."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import json
|
|
4
5
|
import logging
|
|
5
6
|
import logging.handlers
|
|
6
7
|
import pathlib
|
|
8
|
+
import re
|
|
7
9
|
import time
|
|
10
|
+
from datetime import datetime, timezone
|
|
8
11
|
|
|
9
12
|
import pytest
|
|
10
13
|
|
|
@@ -471,3 +474,140 @@ def test_a_real_crash_shows_up_in_the_listing(boost, monkeypatch):
|
|
|
471
474
|
res = boost("log", "--crashes")
|
|
472
475
|
assert crashed == ["count"]
|
|
473
476
|
assert "command: boost count" in res.out
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
# ── BOOST_LOG_FORMAT=json ────────────────────────────────────────────────────
|
|
480
|
+
|
|
481
|
+
class TestLogFormatResolution:
|
|
482
|
+
"""Env beats config beats the `text` default — same shape as the level."""
|
|
483
|
+
|
|
484
|
+
def test_defaults_to_text(self, env):
|
|
485
|
+
assert logs.log_format() == "text"
|
|
486
|
+
|
|
487
|
+
def test_env_selects_json(self, env, monkeypatch):
|
|
488
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "json")
|
|
489
|
+
assert logs.log_format() == "json"
|
|
490
|
+
|
|
491
|
+
def test_env_is_case_and_space_insensitive(self, env, monkeypatch):
|
|
492
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", " JSON ")
|
|
493
|
+
assert logs.log_format() == "json"
|
|
494
|
+
|
|
495
|
+
def test_an_unknown_env_value_falls_back_to_text(self, env, monkeypatch):
|
|
496
|
+
# A typo must not silently disable logging or crash configure().
|
|
497
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "yaml")
|
|
498
|
+
assert logs.log_format() == "text"
|
|
499
|
+
|
|
500
|
+
def test_config_selects_json_when_env_is_unset(self, env, monkeypatch):
|
|
501
|
+
from boost_cli.core import config
|
|
502
|
+
monkeypatch.delenv("BOOST_LOG_FORMAT", raising=False)
|
|
503
|
+
config.set_value("logging.format", "json")
|
|
504
|
+
assert logs.log_format() == "json"
|
|
505
|
+
|
|
506
|
+
def test_env_wins_over_config(self, env, monkeypatch):
|
|
507
|
+
from boost_cli.core import config
|
|
508
|
+
config.set_value("logging.format", "json")
|
|
509
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "text")
|
|
510
|
+
assert logs.log_format() == "text"
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class TestJsonFileOutput:
|
|
514
|
+
def _records(self, env, monkeypatch):
|
|
515
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "json")
|
|
516
|
+
logs.configure()
|
|
517
|
+
logs.get_logger().info("hello %s", "world")
|
|
518
|
+
text = logs.log_path().read_text(encoding="utf-8")
|
|
519
|
+
return [json.loads(ln) for ln in text.splitlines() if ln.strip()]
|
|
520
|
+
|
|
521
|
+
def test_one_json_object_per_line(self, env, monkeypatch):
|
|
522
|
+
recs = self._records(env, monkeypatch)
|
|
523
|
+
assert len(recs) == 1
|
|
524
|
+
assert recs[0]["msg"] == "hello world", "args must be interpolated"
|
|
525
|
+
assert recs[0]["level"] == "INFO"
|
|
526
|
+
assert recs[0]["logger"] == "boost"
|
|
527
|
+
|
|
528
|
+
def test_the_timestamp_is_utc_with_a_z(self, env, monkeypatch):
|
|
529
|
+
ts = self._records(env, monkeypatch)[0]["ts"]
|
|
530
|
+
assert re.match(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$", ts), ts
|
|
531
|
+
# Parsed as UTC it must be within a minute of now; a local-time stamp
|
|
532
|
+
# wearing a Z would be off by the machine's offset.
|
|
533
|
+
parsed = datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(
|
|
534
|
+
tzinfo=timezone.utc)
|
|
535
|
+
assert abs((datetime.now(timezone.utc) - parsed).total_seconds()) < 60
|
|
536
|
+
|
|
537
|
+
def test_an_exception_is_carried_in_its_own_field(self, env, monkeypatch):
|
|
538
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "json")
|
|
539
|
+
logs.configure()
|
|
540
|
+
try:
|
|
541
|
+
raise ValueError("boom")
|
|
542
|
+
except ValueError:
|
|
543
|
+
logs.get_logger().exception("caught it")
|
|
544
|
+
rec = [json.loads(ln) for ln in
|
|
545
|
+
logs.log_path().read_text(encoding="utf-8").splitlines()][-1]
|
|
546
|
+
assert rec["msg"] == "caught it"
|
|
547
|
+
assert "ValueError: boom" in rec["exc"]
|
|
548
|
+
assert "\n" not in json.dumps(rec), "one physical line per record"
|
|
549
|
+
|
|
550
|
+
def test_an_unserializable_arg_degrades_instead_of_losing_the_line(
|
|
551
|
+
self, env, monkeypatch):
|
|
552
|
+
# A formatter that raises loses the trail it exists to keep.
|
|
553
|
+
class Weird:
|
|
554
|
+
def __repr__(self):
|
|
555
|
+
return "<weird>"
|
|
556
|
+
|
|
557
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "json")
|
|
558
|
+
logs.configure()
|
|
559
|
+
logs.get_logger().info("got %r", Weird())
|
|
560
|
+
rec = [json.loads(ln) for ln in
|
|
561
|
+
logs.log_path().read_text(encoding="utf-8").splitlines()][-1]
|
|
562
|
+
assert rec["msg"] == "got <weird>"
|
|
563
|
+
|
|
564
|
+
def test_text_mode_is_unchanged(self, env, monkeypatch):
|
|
565
|
+
monkeypatch.delenv("BOOST_LOG_FORMAT", raising=False)
|
|
566
|
+
logs.configure()
|
|
567
|
+
logs.get_logger().info("plain line")
|
|
568
|
+
text = logs.log_path().read_text(encoding="utf-8")
|
|
569
|
+
assert "INFO" in text and "boost: plain line" in text
|
|
570
|
+
assert not text.lstrip().startswith("{")
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
class TestJsonConsoleOutput:
|
|
574
|
+
def test_the_console_handler_uses_the_same_format(self, env, monkeypatch,
|
|
575
|
+
capsys):
|
|
576
|
+
# A `--debug` stream that disagrees with the file it is showing would
|
|
577
|
+
# be its own bug report.
|
|
578
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "json")
|
|
579
|
+
logs.configure(debug=True)
|
|
580
|
+
logs.get_logger().error("stderr line")
|
|
581
|
+
err = capsys.readouterr().err.strip().splitlines()[-1]
|
|
582
|
+
assert json.loads(err)["msg"] == "stderr line"
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class TestDiagnosticsViewerReadsBothFormats:
|
|
586
|
+
"""`boost log --diagnostics` is the human view of that same file."""
|
|
587
|
+
|
|
588
|
+
def _diag(self):
|
|
589
|
+
from boost_cli.commands import info
|
|
590
|
+
return info._diag_line
|
|
591
|
+
|
|
592
|
+
def test_a_json_line_is_rendered_back_as_text(self):
|
|
593
|
+
line = json.dumps({"ts": "2026-07-27T00:00:00Z", "level": "INFO",
|
|
594
|
+
"logger": "boost", "msg": "invoke: boost count"})
|
|
595
|
+
assert self._diag()(line) == \
|
|
596
|
+
"2026-07-27T00:00:00Z INFO boost: invoke: boost count"
|
|
597
|
+
|
|
598
|
+
def test_a_plain_text_line_passes_through(self):
|
|
599
|
+
line = "2026-07-27T00:00:00Z INFO boost: invoke: boost count"
|
|
600
|
+
assert self._diag()(line) == line
|
|
601
|
+
|
|
602
|
+
def test_a_line_that_only_looks_like_json_passes_through(self):
|
|
603
|
+
# A mixed file (the format changed mid-life) has to read end to end.
|
|
604
|
+
for line in ("{not json at all", json.dumps({"other": "shape"}),
|
|
605
|
+
json.dumps([1, 2, 3])):
|
|
606
|
+
assert self._diag()(line) == line
|
|
607
|
+
|
|
608
|
+
def test_the_command_renders_a_json_trail(self, boost, monkeypatch):
|
|
609
|
+
monkeypatch.setenv("BOOST_LOG_FORMAT", "json")
|
|
610
|
+
boost("count")
|
|
611
|
+
res = boost("log", "--diagnostics")
|
|
612
|
+
assert "invoke: boost count" in res.out
|
|
613
|
+
assert "{" not in res.out, "raw JSONL is not the human view"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Committed roadmap HTML stays in lockstep with its item source files.
|
|
2
|
+
|
|
3
|
+
The roadmap boards are generated from ``docs/roadmap/items/*.md`` by
|
|
4
|
+
``scripts/build_roadmap.py`` and injected into the marker regions of the
|
|
5
|
+
hand-authored pages. This test — the in-suite twin of the
|
|
6
|
+
``build_roadmap.py --check`` CI step — fails if the committed HTML drifts from a
|
|
7
|
+
fresh render, so a stale board can't merge.
|
|
8
|
+
|
|
9
|
+
Skips when the repo-root files aren't reachable (e.g. the mutation sandbox,
|
|
10
|
+
which only copies ``boost_cli/``).
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import importlib.util
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
_ROOT = Path(__file__).resolve().parents[2]
|
|
20
|
+
_SCRIPT = _ROOT / "scripts" / "build_roadmap.py"
|
|
21
|
+
_ITEMS = _ROOT / "docs" / "roadmap" / "items"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _load_builder():
|
|
25
|
+
spec = importlib.util.spec_from_file_location("build_roadmap", _SCRIPT)
|
|
26
|
+
assert spec and spec.loader
|
|
27
|
+
module = importlib.util.module_from_spec(spec)
|
|
28
|
+
spec.loader.exec_module(module)
|
|
29
|
+
return module
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.skipif(not (_SCRIPT.exists() and _ITEMS.exists()),
|
|
33
|
+
reason="repo-root files not reachable (e.g. mutation sandbox)")
|
|
34
|
+
def test_roadmap_html_is_regenerated():
|
|
35
|
+
builder = _load_builder()
|
|
36
|
+
path, fresh = builder.build_code()
|
|
37
|
+
committed = path.read_text(encoding="utf-8")
|
|
38
|
+
# Reuse the script's own diagnosis rather than restating "out of date".
|
|
39
|
+
# This assertion fires on all nine test legs at once, so what it says is
|
|
40
|
+
# what nine failures say — and the cause is a rebase far more often than a
|
|
41
|
+
# hand-edit.
|
|
42
|
+
assert committed == fresh, "\n".join(
|
|
43
|
+
["docs/roadmap.html is stale — regenerate with",
|
|
44
|
+
" python3 scripts/build_roadmap.py",
|
|
45
|
+
"and commit the result (see CONTRIBUTING.md).",
|
|
46
|
+
*builder.diagnose(path, committed, fresh)])
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.skipif(not (_SCRIPT.exists() and _ITEMS.exists()),
|
|
50
|
+
reason="repo-root files not reachable (e.g. mutation sandbox)")
|
|
51
|
+
def test_design_roadmap_html_is_regenerated():
|
|
52
|
+
builder = _load_builder()
|
|
53
|
+
path, fresh = builder.build_design()
|
|
54
|
+
committed = path.read_text(encoding="utf-8")
|
|
55
|
+
assert committed == fresh, "\n".join(
|
|
56
|
+
["docs/design-roadmap.html is stale — regenerate with",
|
|
57
|
+
" python3 scripts/build_roadmap.py",
|
|
58
|
+
"and commit the result (see CONTRIBUTING.md).",
|
|
59
|
+
*builder.diagnose(path, committed, fresh)])
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.skipif(not (_SCRIPT.exists() and _ITEMS.exists()),
|
|
63
|
+
reason="repo-root files not reachable (e.g. mutation sandbox)")
|
|
64
|
+
def test_check_flag_passes_on_fresh_tree():
|
|
65
|
+
builder = _load_builder()
|
|
66
|
+
assert builder.main(["--check"]) == 0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.skipif(not (_SCRIPT.exists() and _ITEMS.exists()),
|
|
70
|
+
reason="repo-root files not reachable (e.g. mutation sandbox)")
|
|
71
|
+
def test_every_code_item_has_required_fields():
|
|
72
|
+
builder = _load_builder()
|
|
73
|
+
for item in builder.load_items("code"):
|
|
74
|
+
for field in ("id", "section", "status", "title"):
|
|
75
|
+
assert item.get(field), "%s: missing %r" % (item["_file"], field)
|
|
76
|
+
assert item["status"] in builder.STATUS_LABEL, (
|
|
77
|
+
"%s: bad status %r" % (item["_file"], item["status"]))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.skipif(not (_SCRIPT.exists() and _ITEMS.exists()),
|
|
81
|
+
reason="repo-root files not reachable (e.g. mutation sandbox)")
|
|
82
|
+
def test_every_design_item_has_required_fields():
|
|
83
|
+
builder = _load_builder()
|
|
84
|
+
for item in builder.load_items("design"):
|
|
85
|
+
for field in ("id", "track", "status", "title"):
|
|
86
|
+
assert item.get(field), "%s: missing %r" % (item["_file"], field)
|
|
87
|
+
assert item["status"] in builder.DESIGN_STATUS, (
|
|
88
|
+
"%s: bad status %r" % (item["_file"], item["status"]))
|
|
89
|
+
assert str(item.get("impact", "")).lower() in builder.IMPACT_LABEL, (
|
|
90
|
+
"%s: bad impact %r" % (item["_file"], item.get("impact")))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ── the diagnosis itself ─────────────────────────────────────────────────────
|
|
94
|
+
#
|
|
95
|
+
# The failure above lands on lint and on all nine test legs simultaneously, for
|
|
96
|
+
# a PR that may have changed no Python at all. Whether it reads as "the build is
|
|
97
|
+
# broken" or as "regenerate the board after your rebase" is entirely down to
|
|
98
|
+
# what these strings say, so they are tested rather than trusted.
|
|
99
|
+
|
|
100
|
+
CARD = '<article class="cap rcard" id="%s">body</article>'
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.skipif(not _SCRIPT.exists(), reason="repo-root files not reachable")
|
|
104
|
+
class TestDiagnose:
|
|
105
|
+
def _lines(self, committed, fresh):
|
|
106
|
+
return _load_builder().diagnose(Path("docs/roadmap.html"),
|
|
107
|
+
committed, fresh)
|
|
108
|
+
|
|
109
|
+
def test_a_card_missing_from_the_html_names_it_and_blames_the_rebase(self):
|
|
110
|
+
# THE case this exists for: another PR's item file merged in cleanly,
|
|
111
|
+
# the generated HTML merged textually, and the card is simply absent.
|
|
112
|
+
lines = self._lines(CARD % "old-one",
|
|
113
|
+
(CARD % "old-one") + (CARD % "someone-elses-card"))
|
|
114
|
+
text = "\n".join(lines)
|
|
115
|
+
assert "someone-elses-card" in text
|
|
116
|
+
assert "rebase" in text
|
|
117
|
+
assert "missing 1 card" in text
|
|
118
|
+
|
|
119
|
+
def test_several_missing_cards_are_summarised_not_dumped(self):
|
|
120
|
+
fresh = "".join(CARD % ("card-%d" % i) for i in range(20))
|
|
121
|
+
text = "\n".join(self._lines("", fresh))
|
|
122
|
+
assert "missing 20 card(s)" in text
|
|
123
|
+
assert text.count("card-") <= 9, "8 named plus the ellipsis"
|
|
124
|
+
assert "…" in text
|
|
125
|
+
|
|
126
|
+
def test_a_card_with_no_item_file_is_called_out_separately(self):
|
|
127
|
+
lines = self._lines(CARD % "deleted-item", "")
|
|
128
|
+
text = "\n".join(lines)
|
|
129
|
+
assert "no item file" in text
|
|
130
|
+
assert "deleted-item" in text
|
|
131
|
+
assert "rebase" not in text, "nothing is missing — different cause"
|
|
132
|
+
|
|
133
|
+
def test_same_cards_rendered_differently_says_so(self):
|
|
134
|
+
# A field or body edit, or a generator change — not a rebase.
|
|
135
|
+
lines = self._lines('<article class="cap rcard" id="a">old</article>',
|
|
136
|
+
'<article class="cap rcard" id="a">new</article>')
|
|
137
|
+
text = "\n".join(lines)
|
|
138
|
+
assert "rendered differently" in text
|
|
139
|
+
assert "missing" not in text
|
|
140
|
+
|
|
141
|
+
def test_the_design_board_shape_is_recognised_too(self):
|
|
142
|
+
# design-roadmap.html uses <article class="ritem" …>, not "cap rcard".
|
|
143
|
+
fresh = ('<article class="ritem" id="BOOST-D99" data-track="x" '
|
|
144
|
+
'data-impact="y">body</article>')
|
|
145
|
+
assert "BOOST-D99" in "\n".join(self._lines("", fresh))
|
|
146
|
+
|
|
147
|
+
def test_it_never_returns_nothing(self):
|
|
148
|
+
# An empty diagnosis would leave the assertion message ending on a
|
|
149
|
+
# colon with no explanation — worse than the message it replaced.
|
|
150
|
+
for committed, fresh in (("", ""), ("x", "y"), (CARD % "a", CARD % "a")):
|
|
151
|
+
assert self._lines(committed, fresh)
|