boost-skill-cli 1.0.152__tar.gz → 1.0.153__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.152 → boost_skill_cli-1.0.153}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost +6 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/cli.py +1 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/bmad.py +5 -5
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/configuration.py +5 -5
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/discovery.py +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/intelligence.py +5 -5
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/pkg.py +8 -15
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/quality.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/taps.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/team.py +7 -7
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/catalog.py +4 -4
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/claude_settings.py +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/config.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/dense.py +12 -4
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/journal.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/lockfile.py +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/output.py +17 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/paths.py +6 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/policy.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/rag.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/registry.py +7 -5
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/serve.py +6 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/store.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/updatediff.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/util.py +24 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/windows-in-the-ci-matrix.md +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap.html +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/build_registries.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/build_roadmap.py +5 -5
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/eval_retrieval.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/mutation_gate.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/discovery_steps.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_adapt.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_bmad.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_configuration.py +15 -15
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_discovery.py +50 -24
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_info.py +18 -10
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_intelligence.py +23 -23
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_pkg.py +23 -23
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_quality.py +10 -10
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_taps.py +9 -9
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_team.py +12 -9
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_everyday_loop.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/make_fixture.py +4 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_catalog.py +38 -33
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_claude_settings.py +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_config.py +9 -9
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_dense.py +38 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_docs_pages_linked.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_gitutil.py +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_journal.py +9 -9
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_lockfile.py +14 -14
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_logs.py +11 -11
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_mutation_hardening.py +3 -3
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_paths.py +6 -4
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_policy.py +4 -4
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_rag.py +15 -15
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_registries_fresh.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_registry.py +6 -7
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_roadmap_fresh.py +2 -2
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_serve.py +51 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_store.py +27 -27
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_token_parity.py +1 -1
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_util.py +10 -10
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/.gitattributes +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/.gitignore +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/LICENSE +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/Makefile +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/README.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/index.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/examples/README.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/index.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/setup.cfg +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/style/README.md +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/style/boost.css +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/style/boost.js +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/style/demo.html +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.152 → boost_skill_cli-1.0.153}/tests/unit/test_workflows.py +0 -0
|
@@ -12,6 +12,12 @@ while [ -h "$SOURCE" ]; do
|
|
|
12
12
|
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
13
13
|
done
|
|
14
14
|
ROOT="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)"
|
|
15
|
+
# Under Git Bash on Windows, $ROOT is an MSYS path (/d/a/boost/boost); native
|
|
16
|
+
# Windows Python can't resolve that, so convert to a mixed path (D:/a/...)
|
|
17
|
+
# before it's embedded in the -c script below. No-op on Linux/macOS.
|
|
18
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
19
|
+
ROOT="$(cygpath -m "$ROOT")"
|
|
20
|
+
fi
|
|
15
21
|
|
|
16
22
|
find_python() {
|
|
17
23
|
for py in python3 python; do
|
|
@@ -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.153'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 153)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -224,6 +224,7 @@ def _extract_globals(argv: List[str]) -> tuple[dict, List[str]]:
|
|
|
224
224
|
|
|
225
225
|
def main(argv: List[str] | None = None) -> int:
|
|
226
226
|
argv = list(sys.argv[1:] if argv is None else argv)
|
|
227
|
+
out.harden_console_encoding()
|
|
227
228
|
# Belt-and-suspenders fork safety: a host may fork() into `boost mcp
|
|
228
229
|
# --stdio` without the no_proxy env boost injects at registration, leaving
|
|
229
230
|
# the process one default getproxies() away from a macOS _scproxy SIGABRT.
|
|
@@ -192,7 +192,7 @@ def _copy_global_skills(modules) -> int:
|
|
|
192
192
|
continue
|
|
193
193
|
t = dest / d.name
|
|
194
194
|
if t.exists():
|
|
195
|
-
|
|
195
|
+
util.rmtree(t)
|
|
196
196
|
shutil.copytree(d, t)
|
|
197
197
|
n += 1
|
|
198
198
|
return n
|
|
@@ -278,7 +278,7 @@ def _disable(scope) -> int:
|
|
|
278
278
|
for d in sorted(_skills_dir(scope).glob("bmad-*")):
|
|
279
279
|
target = qdir / d.name
|
|
280
280
|
if target.exists():
|
|
281
|
-
|
|
281
|
+
util.rmtree(target)
|
|
282
282
|
shutil.move(str(d), str(target))
|
|
283
283
|
moved += 1
|
|
284
284
|
_set_scope_state(scope, startup=False, disabled=True)
|
|
@@ -298,7 +298,7 @@ def _enable(scope) -> int:
|
|
|
298
298
|
for d in sorted(qdir.glob("bmad-*")):
|
|
299
299
|
target = dest / d.name
|
|
300
300
|
if target.exists():
|
|
301
|
-
|
|
301
|
+
util.rmtree(target)
|
|
302
302
|
shutil.move(str(d), str(target))
|
|
303
303
|
restored += 1
|
|
304
304
|
_set_scope_state(scope, disabled=False)
|
|
@@ -366,7 +366,7 @@ def _state_read() -> dict:
|
|
|
366
366
|
if not p.exists():
|
|
367
367
|
return {"global": {}, "projects": {}}
|
|
368
368
|
try:
|
|
369
|
-
d = json.loads(p.read_text())
|
|
369
|
+
d = json.loads(p.read_text(encoding="utf-8"))
|
|
370
370
|
except (json.JSONDecodeError, OSError):
|
|
371
371
|
return {"global": {}, "projects": {}}
|
|
372
372
|
d.setdefault("global", {})
|
|
@@ -376,7 +376,7 @@ def _state_read() -> dict:
|
|
|
376
376
|
|
|
377
377
|
def _state_write(d: dict) -> None:
|
|
378
378
|
paths.ensure_dirs()
|
|
379
|
-
_state_path().write_text(json.dumps(d, indent=2) + "\n")
|
|
379
|
+
_state_path().write_text(json.dumps(d, indent=2) + "\n", encoding="utf-8")
|
|
380
380
|
|
|
381
381
|
|
|
382
382
|
def _proj_key() -> str:
|
|
@@ -152,7 +152,7 @@ def cmd_clean(argv) -> int:
|
|
|
152
152
|
if pth.is_symlink() or pth.is_file():
|
|
153
153
|
pth.unlink()
|
|
154
154
|
elif pth.is_dir():
|
|
155
|
-
|
|
155
|
+
util.rmtree(pth)
|
|
156
156
|
except OSError as e:
|
|
157
157
|
out.warn("could not remove %s: %s" % (_tilde(pth), e))
|
|
158
158
|
continue
|
|
@@ -224,7 +224,7 @@ def cmd_create(argv) -> int:
|
|
|
224
224
|
title = name.replace("-", " ").title()
|
|
225
225
|
target.mkdir(parents=True, exist_ok=True)
|
|
226
226
|
skill_md.write_text(frontmatter.dump(meta) + "\n\n"
|
|
227
|
-
+ _CREATE_BODY % {"title": title})
|
|
227
|
+
+ _CREATE_BODY % {"title": title}, encoding="utf-8")
|
|
228
228
|
journal.log("create", name, path=str(target))
|
|
229
229
|
out.ok("created %s" % _tilde(skill_md))
|
|
230
230
|
if args.install:
|
|
@@ -426,7 +426,7 @@ def cmd_onboard(argv) -> int:
|
|
|
426
426
|
for rel, content in files:
|
|
427
427
|
fp = repo / rel
|
|
428
428
|
fp.parent.mkdir(parents=True, exist_ok=True)
|
|
429
|
-
fp.write_text(content)
|
|
429
|
+
fp.write_text(content, encoding="utf-8")
|
|
430
430
|
out.ok("created %s" % _tilde(fp))
|
|
431
431
|
journal.log("onboard", _tilde(repo), pr=args.pr or None)
|
|
432
432
|
|
|
@@ -599,7 +599,7 @@ def cmd_schedule(argv) -> int:
|
|
|
599
599
|
if plist.exists():
|
|
600
600
|
present = True
|
|
601
601
|
m = re.search(r"<key>StartInterval</key>\s*<integer>(\d+)</integer>",
|
|
602
|
-
plist.read_text())
|
|
602
|
+
plist.read_text(encoding="utf-8"))
|
|
603
603
|
if m:
|
|
604
604
|
secs = int(m.group(1))
|
|
605
605
|
interval = _interval_label(secs)
|
|
@@ -641,7 +641,7 @@ def cmd_schedule(argv) -> int:
|
|
|
641
641
|
if darwin:
|
|
642
642
|
plist = _plist_path()
|
|
643
643
|
plist.parent.mkdir(parents=True, exist_ok=True)
|
|
644
|
-
plist.write_text(_plist_body(shim, seconds))
|
|
644
|
+
plist.write_text(_plist_body(shim, seconds), encoding="utf-8")
|
|
645
645
|
out.ok("wrote %s" % _tilde(plist))
|
|
646
646
|
try:
|
|
647
647
|
subprocess.run(["launchctl", "unload", str(plist)],
|
|
@@ -252,7 +252,7 @@ def cmd_index(argv):
|
|
|
252
252
|
paths.ensure_dirs()
|
|
253
253
|
_discovery_path().write_text(json.dumps(
|
|
254
254
|
{"generated": util.now_iso(), "github_total": total, "items": items},
|
|
255
|
-
indent=1))
|
|
255
|
+
indent=1), encoding="utf-8")
|
|
256
256
|
repos = len({it["repo"] for it in items})
|
|
257
257
|
out.ok("indexed %d skill files across %d repos (GitHub reports %d total)"
|
|
258
258
|
% (len(items), repos, total))
|
|
@@ -325,7 +325,7 @@ def cmd_discover(argv):
|
|
|
325
325
|
"gh auth login`), then run `boost index`")
|
|
326
326
|
return 0
|
|
327
327
|
try:
|
|
328
|
-
data = json.loads(dpath.read_text())
|
|
328
|
+
data = json.loads(dpath.read_text(encoding="utf-8"))
|
|
329
329
|
except (json.JSONDecodeError, OSError):
|
|
330
330
|
raise BoostError("the discovery index is corrupt",
|
|
331
331
|
hint="rebuild it with `boost index`")
|
|
@@ -979,7 +979,7 @@ def cmd_count(argv):
|
|
|
979
979
|
dpath = _discovery_path()
|
|
980
980
|
if dpath.exists():
|
|
981
981
|
try:
|
|
982
|
-
discovery = len(json.loads(dpath.read_text()).get("items") or [])
|
|
982
|
+
discovery = len(json.loads(dpath.read_text(encoding="utf-8")).get("items") or [])
|
|
983
983
|
except (json.JSONDecodeError, OSError):
|
|
984
984
|
discovery = None
|
|
985
985
|
if args.as_json:
|
|
@@ -60,14 +60,14 @@ def _load_state(fname: str, default: dict) -> dict:
|
|
|
60
60
|
if not p.exists():
|
|
61
61
|
return json.loads(json.dumps(default))
|
|
62
62
|
try:
|
|
63
|
-
return json.loads(p.read_text())
|
|
63
|
+
return json.loads(p.read_text(encoding="utf-8"))
|
|
64
64
|
except (json.JSONDecodeError, OSError):
|
|
65
65
|
return json.loads(json.dumps(default))
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
def _save_state(fname: str, data: dict) -> None:
|
|
69
69
|
paths.ensure_dirs()
|
|
70
|
-
(paths.state_dir() / fname).write_text(json.dumps(data, indent=2) + "\n")
|
|
70
|
+
(paths.state_dir() / fname).write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
def _install_generated(name: str, text: str) -> None:
|
|
@@ -75,7 +75,7 @@ def _install_generated(name: str, text: str) -> None:
|
|
|
75
75
|
with tempfile.TemporaryDirectory(prefix="boost-gen-") as td:
|
|
76
76
|
src = Path(td) / name
|
|
77
77
|
src.mkdir()
|
|
78
|
-
(src / "SKILL.md").write_text(text)
|
|
78
|
+
(src / "SKILL.md").write_text(text, encoding="utf-8")
|
|
79
79
|
res = store.install_from_path(src, name=name, tap_label="local")
|
|
80
80
|
out.ok("installed %s → %s" % (name, _tilde(res.dest)))
|
|
81
81
|
if res.linked:
|
|
@@ -88,7 +88,7 @@ def _write_generated(dest: Path, text: str) -> bool:
|
|
|
88
88
|
out.info("aborted — %s left untouched" % _tilde(dest))
|
|
89
89
|
return False
|
|
90
90
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
-
dest.write_text(text)
|
|
91
|
+
dest.write_text(text, encoding="utf-8")
|
|
92
92
|
out.ok("wrote %s" % _tilde(dest))
|
|
93
93
|
return True
|
|
94
94
|
|
|
@@ -649,7 +649,7 @@ def cmd_evolve(argv: List[str]) -> int:
|
|
|
649
649
|
if not args.apply:
|
|
650
650
|
out.info(out.role("re-run with --apply to write these changes", "muted"))
|
|
651
651
|
return 0
|
|
652
|
-
skill_md.write_text(new)
|
|
652
|
+
skill_md.write_text(new, encoding="utf-8")
|
|
653
653
|
entry["sha256"] = util.sha256_dir(store.skill_store_dir(args.name))
|
|
654
654
|
entry["updated_at"] = util.now_iso()
|
|
655
655
|
entry["version"] = new_ver
|
|
@@ -22,14 +22,7 @@ from ..core import output as out
|
|
|
22
22
|
from ..errors import BoostError
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
"""Contract $HOME to ~ for display (also matches the resolved home,
|
|
27
|
-
so /private/var vs /var symlinks on macOS still contract)."""
|
|
28
|
-
s = str(p)
|
|
29
|
-
for h in (str(paths.home()), str(paths.home().resolve())):
|
|
30
|
-
if s == h or s.startswith(h + os.sep):
|
|
31
|
-
return "~" + s[len(h):]
|
|
32
|
-
return s
|
|
25
|
+
_tilde = paths.tilde
|
|
33
26
|
|
|
34
27
|
|
|
35
28
|
def _plural(n: int, word: str) -> str:
|
|
@@ -338,7 +331,7 @@ def cmd_sync(argv: List[str]) -> int:
|
|
|
338
331
|
for name in orphans:
|
|
339
332
|
target = store.skill_store_dir(name)
|
|
340
333
|
if target.is_dir():
|
|
341
|
-
|
|
334
|
+
util.rmtree(target)
|
|
342
335
|
journal.log("prune", name)
|
|
343
336
|
pruned.append(name)
|
|
344
337
|
left = [n for n in orphans if n not in pruned]
|
|
@@ -572,7 +565,7 @@ def _bundle_dump(file: Optional[str]) -> int:
|
|
|
572
565
|
return 0
|
|
573
566
|
dest = paths.expand(file)
|
|
574
567
|
try:
|
|
575
|
-
dest.write_text(text)
|
|
568
|
+
dest.write_text(text, encoding="utf-8")
|
|
576
569
|
except OSError as e:
|
|
577
570
|
raise BoostError("cannot write %s: %s" % (_tilde(dest), e.strerror or e),
|
|
578
571
|
hint="check the path exists and is writable")
|
|
@@ -596,7 +589,7 @@ def _bundle_install(file: Optional[str]) -> int:
|
|
|
596
589
|
hint="point at the Boostfile itself, "
|
|
597
590
|
"or use `boost import` for skill directories")
|
|
598
591
|
try:
|
|
599
|
-
text, label = path.read_text(), str(path)
|
|
592
|
+
text, label = path.read_text(encoding="utf-8"), str(path)
|
|
600
593
|
except OSError as e:
|
|
601
594
|
raise BoostError("cannot read %s: %s" % (_tilde(path), e.strerror or e))
|
|
602
595
|
taps_added = installed_n = present = failed = 0
|
|
@@ -880,7 +873,7 @@ def _snapshot_save(label: Optional[str]) -> int:
|
|
|
880
873
|
manifest = {"id": snap_id, "label": label or "", "created": util.now_iso(),
|
|
881
874
|
"skills": skill_count}
|
|
882
875
|
(paths.snapshots_dir() / (snap_id + ".json")).write_text(
|
|
883
|
-
json.dumps(manifest, indent=2) + "\n")
|
|
876
|
+
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
|
884
877
|
journal.log("snapshot", snap_id, label=label)
|
|
885
878
|
out.ok("saved %s (%s, %s)" % (snap_id, _plural(skill_count, "skill"),
|
|
886
879
|
util.human_size(tar_path.stat().st_size)))
|
|
@@ -900,7 +893,7 @@ def _snapshot_list(as_json: bool) -> int:
|
|
|
900
893
|
meta = {}
|
|
901
894
|
if side.exists():
|
|
902
895
|
try:
|
|
903
|
-
meta = json.loads(side.read_text())
|
|
896
|
+
meta = json.loads(side.read_text(encoding="utf-8"))
|
|
904
897
|
except (json.JSONDecodeError, OSError):
|
|
905
898
|
meta = {}
|
|
906
899
|
snaps.append({"id": snap_id, "created": meta.get("created", ""),
|
|
@@ -950,7 +943,7 @@ def _snapshot_restore(snap_id: str) -> int:
|
|
|
950
943
|
"see `boost snapshot list` for other snapshots")
|
|
951
944
|
for child in root.iterdir():
|
|
952
945
|
if child.is_dir() and not child.is_symlink():
|
|
953
|
-
|
|
946
|
+
util.rmtree(child)
|
|
954
947
|
else:
|
|
955
948
|
child.unlink()
|
|
956
949
|
try:
|
|
@@ -1006,7 +999,7 @@ def cmd_export(argv: List[str]) -> int:
|
|
|
1006
999
|
for name in chosen:
|
|
1007
1000
|
sdir = store.skill_store_dir(name)
|
|
1008
1001
|
for f in sorted(p for p in sdir.rglob("*") if p.is_file()):
|
|
1009
|
-
zf.write(str(f), arcname="%s/%s" % (name, f.relative_to(sdir)))
|
|
1002
|
+
zf.write(str(f), arcname="%s/%s" % (name, f.relative_to(sdir).as_posix()))
|
|
1010
1003
|
else:
|
|
1011
1004
|
with tarfile.open(str(dest), "w:gz") as tf:
|
|
1012
1005
|
data = manifest.encode()
|
|
@@ -269,7 +269,7 @@ def cmd_doctor(argv):
|
|
|
269
269
|
lp = paths.lockfile_path()
|
|
270
270
|
if lp.exists():
|
|
271
271
|
try:
|
|
272
|
-
raw = json.loads(lp.read_text())
|
|
272
|
+
raw = json.loads(lp.read_text(encoding="utf-8"))
|
|
273
273
|
if raw.get("version") != lockfile.SCHEMA_VERSION:
|
|
274
274
|
bad("lock file schema is v%s, expected v%d"
|
|
275
275
|
% (raw.get("version"), lockfile.SCHEMA_VERSION))
|
|
@@ -499,7 +499,7 @@ def cmd_audit(argv):
|
|
|
499
499
|
text = f.read_text(encoding="utf-8", errors="replace")
|
|
500
500
|
except OSError:
|
|
501
501
|
continue
|
|
502
|
-
rel =
|
|
502
|
+
rel = f.relative_to(sdir).as_posix()
|
|
503
503
|
for pat, severity, label in _AUDIT_PATTERNS:
|
|
504
504
|
for m in pat.finditer(text):
|
|
505
505
|
line_no = text.count("\n", 0, m.start()) + 1
|
|
@@ -157,7 +157,7 @@ def _tap_updated(tap: "registry.Tap") -> str:
|
|
|
157
157
|
except BoostError:
|
|
158
158
|
pass
|
|
159
159
|
try:
|
|
160
|
-
data = json.loads(tap.cache_file.read_text())
|
|
160
|
+
data = json.loads(tap.cache_file.read_text(encoding="utf-8"))
|
|
161
161
|
return util.rel_time(data.get("generated", ""))
|
|
162
162
|
except (OSError, ValueError):
|
|
163
163
|
return "?"
|
|
@@ -40,14 +40,14 @@ def _load_cohorts() -> dict:
|
|
|
40
40
|
if not p.exists():
|
|
41
41
|
return {}
|
|
42
42
|
try:
|
|
43
|
-
return json.loads(p.read_text())
|
|
43
|
+
return json.loads(p.read_text(encoding="utf-8"))
|
|
44
44
|
except (json.JSONDecodeError, OSError):
|
|
45
45
|
return {}
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _save_cohorts(cohorts: dict) -> None:
|
|
49
49
|
paths.ensure_dirs()
|
|
50
|
-
_cohorts_path().write_text(json.dumps(cohorts, indent=2) + "\n")
|
|
50
|
+
_cohorts_path().write_text(json.dumps(cohorts, indent=2) + "\n", encoding="utf-8")
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def _is_member(user: str, cohort_name: str, percent: int) -> bool:
|
|
@@ -181,7 +181,7 @@ def _load_profile(name: str) -> dict:
|
|
|
181
181
|
raise BoostError("no profile named %s" % name,
|
|
182
182
|
hint="list profiles with `boost profile list`")
|
|
183
183
|
try:
|
|
184
|
-
return json.loads(p.read_text())
|
|
184
|
+
return json.loads(p.read_text(encoding="utf-8"))
|
|
185
185
|
except (json.JSONDecodeError, OSError) as e:
|
|
186
186
|
raise BoostError("profile %s is unreadable: %s" % (name, e))
|
|
187
187
|
|
|
@@ -217,7 +217,7 @@ def cmd_profile(argv) -> int:
|
|
|
217
217
|
profiles = []
|
|
218
218
|
for f in sorted(paths.profiles_dir().glob("*.json")):
|
|
219
219
|
try:
|
|
220
|
-
data = json.loads(f.read_text())
|
|
220
|
+
data = json.loads(f.read_text(encoding="utf-8"))
|
|
221
221
|
except (json.JSONDecodeError, OSError):
|
|
222
222
|
continue
|
|
223
223
|
profiles.append({"name": data.get("name", f.stem),
|
|
@@ -242,7 +242,7 @@ def cmd_profile(argv) -> int:
|
|
|
242
242
|
"version": e.get("version", "0.0.0")}
|
|
243
243
|
for n, e in installed.items()}}
|
|
244
244
|
paths.ensure_dirs()
|
|
245
|
-
_profile_path(args.name).write_text(json.dumps(profile, indent=2) + "\n")
|
|
245
|
+
_profile_path(args.name).write_text(json.dumps(profile, indent=2) + "\n", encoding="utf-8")
|
|
246
246
|
journal.log("profile", args.name, op="save", skills=len(installed))
|
|
247
247
|
out.ok("saved profile %s (%d skills)" % (args.name, len(installed)))
|
|
248
248
|
return 0
|
|
@@ -390,7 +390,7 @@ def cmd_protocol(argv) -> int:
|
|
|
390
390
|
script = _handler_script()
|
|
391
391
|
script.write_text("#!/usr/bin/env bash\n"
|
|
392
392
|
"# boost:// URL handler — invoked with the URL as $1\n"
|
|
393
|
-
'exec "%s" protocol open "$1"\n' % shim)
|
|
393
|
+
'exec "%s" protocol open "$1"\n' % shim, encoding="utf-8")
|
|
394
394
|
script.chmod(script.stat().st_mode | stat.S_IEXEC)
|
|
395
395
|
out.ok("wrote handler script %s" % _tilde(script))
|
|
396
396
|
if system == "Darwin":
|
|
@@ -404,7 +404,7 @@ def cmd_protocol(argv) -> int:
|
|
|
404
404
|
desktop.parent.mkdir(parents=True, exist_ok=True)
|
|
405
405
|
desktop.write_text("[Desktop Entry]\nType=Application\nName=boost protocol handler\n"
|
|
406
406
|
"Exec=%s %%u\nMimeType=x-scheme-handler/boost;\nNoDisplay=true\n"
|
|
407
|
-
% script)
|
|
407
|
+
% script, encoding="utf-8")
|
|
408
408
|
out.ok("wrote %s" % _tilde(desktop))
|
|
409
409
|
if shutil.which("xdg-mime"):
|
|
410
410
|
import subprocess
|
|
@@ -74,8 +74,8 @@ def _make_entry(root: Path, defining_file: Path, kind: str, default_name: str,
|
|
|
74
74
|
"tap": tap_name,
|
|
75
75
|
"curated": curated,
|
|
76
76
|
"kind": kind,
|
|
77
|
-
"rel_dir":
|
|
78
|
-
"skill_md":
|
|
77
|
+
"rel_dir": item_dir.relative_to(root).as_posix() if item_dir != root else ".",
|
|
78
|
+
"skill_md": defining_file.relative_to(root).as_posix(),
|
|
79
79
|
"meta": meta,
|
|
80
80
|
# Lowercased substring-search blob, flattened once at index time so
|
|
81
81
|
# search() never re-walks the frontmatter per query (see _search_blob).
|
|
@@ -166,7 +166,7 @@ def rebuild_tap(tap: "registry.Tap") -> List[dict]:
|
|
|
166
166
|
"generated": util.now_iso(),
|
|
167
167
|
"commit": gitutil.head_commit(tap.path),
|
|
168
168
|
"skills": entries,
|
|
169
|
-
}, indent=1))
|
|
169
|
+
}, indent=1), encoding="utf-8")
|
|
170
170
|
# Drop the mtime-keyed cache so a rebuild is visible to an immediately
|
|
171
171
|
# following load even when the filesystem mtime granularity is coarse.
|
|
172
172
|
_ENTRY_CACHE.pop(str(tap.cache_file), None)
|
|
@@ -197,7 +197,7 @@ def _cached_tap(tap: "registry.Tap") -> Optional[List[dict]]:
|
|
|
197
197
|
if cached is not None and cached[0] == stamp:
|
|
198
198
|
return cached[1]
|
|
199
199
|
try:
|
|
200
|
-
skills = json.loads(p.read_text()).get("skills", [])
|
|
200
|
+
skills = json.loads(p.read_text(encoding="utf-8")).get("skills", [])
|
|
201
201
|
except (json.JSONDecodeError, OSError):
|
|
202
202
|
_ENTRY_CACHE.pop(key, None)
|
|
203
203
|
return None
|
|
@@ -59,7 +59,7 @@ def load(scope: str, project_dir: Optional[Path] = None) -> dict:
|
|
|
59
59
|
if not p.exists():
|
|
60
60
|
return {}
|
|
61
61
|
try:
|
|
62
|
-
data = json.loads(p.read_text())
|
|
62
|
+
data = json.loads(p.read_text(encoding="utf-8"))
|
|
63
63
|
except (json.JSONDecodeError, OSError):
|
|
64
64
|
return {}
|
|
65
65
|
return data if isinstance(data, dict) else {}
|
|
@@ -78,9 +78,9 @@ def save(scope: str, data: dict, project_dir: Optional[Path] = None) -> None:
|
|
|
78
78
|
while dest.exists():
|
|
79
79
|
dest = hist / ("%s-%s-%d.json" % (scope, stamp, n))
|
|
80
80
|
n += 1
|
|
81
|
-
dest.write_text(p.read_text())
|
|
81
|
+
dest.write_text(p.read_text(encoding="utf-8"), encoding="utf-8")
|
|
82
82
|
_prune_history()
|
|
83
|
-
p.write_text(json.dumps(data, indent=2) + "\n")
|
|
83
|
+
p.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
def _prune_history() -> None:
|
|
@@ -61,7 +61,7 @@ REGISTRY_CATALOG = paths.package_root() / "data" / "registries.json"
|
|
|
61
61
|
def load_registry_catalog() -> list:
|
|
62
62
|
"""The bundled curated registries, or [] if the data file is missing."""
|
|
63
63
|
try:
|
|
64
|
-
data = json.loads(REGISTRY_CATALOG.read_text())
|
|
64
|
+
data = json.loads(REGISTRY_CATALOG.read_text(encoding="utf-8"))
|
|
65
65
|
except (OSError, json.JSONDecodeError):
|
|
66
66
|
return []
|
|
67
67
|
return data.get("registries", [])
|
|
@@ -105,7 +105,7 @@ def _read() -> dict:
|
|
|
105
105
|
if not p.exists():
|
|
106
106
|
return deepcopy(DEFAULTS)
|
|
107
107
|
try:
|
|
108
|
-
user = json.loads(p.read_text())
|
|
108
|
+
user = json.loads(p.read_text(encoding="utf-8"))
|
|
109
109
|
except (json.JSONDecodeError, OSError):
|
|
110
110
|
return deepcopy(DEFAULTS)
|
|
111
111
|
return _merge(DEFAULTS, user)
|
|
@@ -230,10 +230,18 @@ def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
|
|
|
230
230
|
return None
|
|
231
231
|
try:
|
|
232
232
|
pool = max(k * _POOL, 200)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
try:
|
|
234
|
+
knn = con.execute(
|
|
235
|
+
"SELECT rowid, distance FROM vec_chunks "
|
|
236
|
+
"WHERE embedding MATCH ? ORDER BY distance LIMIT ?",
|
|
237
|
+
(mod.serialize_float32(qv[0]), pool)).fetchall()
|
|
238
|
+
except sqlite3.OperationalError:
|
|
239
|
+
# Some sqlite3/sqlite-vec combinations (observed on Windows)
|
|
240
|
+
# reject this exact query shape ("a LIMIT or 'k = ?' constraint
|
|
241
|
+
# is required") despite the bound LIMIT above — dense retrieval
|
|
242
|
+
# is genuinely unusable here, so fall back to BM25 rather than
|
|
243
|
+
# crash the search command.
|
|
244
|
+
return None
|
|
237
245
|
if not knn:
|
|
238
246
|
return []
|
|
239
247
|
by_id = {r[0]: (r[1], r[2], r[3], r[4]) for r in con.execute(
|
|
@@ -31,7 +31,7 @@ def events(n: Optional[int] = None, action: Optional[str] = None,
|
|
|
31
31
|
if not p.exists():
|
|
32
32
|
return []
|
|
33
33
|
out = []
|
|
34
|
-
for line in p.read_text().splitlines():
|
|
34
|
+
for line in p.read_text(encoding="utf-8").splitlines():
|
|
35
35
|
try:
|
|
36
36
|
e = json.loads(line)
|
|
37
37
|
except json.JSONDecodeError:
|
|
@@ -55,7 +55,7 @@ def rotation_healthy() -> bool:
|
|
|
55
55
|
def _maybe_rotate() -> None:
|
|
56
56
|
p = paths.pulse_path()
|
|
57
57
|
try:
|
|
58
|
-
lines = p.read_text().splitlines()
|
|
58
|
+
lines = p.read_text(encoding="utf-8").splitlines()
|
|
59
59
|
except OSError:
|
|
60
60
|
return
|
|
61
61
|
if len(lines) > ROTATE_AT:
|
|
@@ -29,7 +29,7 @@ def read() -> dict:
|
|
|
29
29
|
if not p.exists():
|
|
30
30
|
return _skeleton() # empty: never installed anything yet
|
|
31
31
|
try:
|
|
32
|
-
lock = json.loads(p.read_text())
|
|
32
|
+
lock = json.loads(p.read_text(encoding="utf-8"))
|
|
33
33
|
except (json.JSONDecodeError, OSError):
|
|
34
34
|
# Corrupt (present but unparseable) is NOT the same as empty: returning
|
|
35
35
|
# a bare skeleton here would let the next write() overwrite the only
|
|
@@ -169,7 +169,7 @@ def history_list() -> List[dict]:
|
|
|
169
169
|
out = []
|
|
170
170
|
for p in _history_files():
|
|
171
171
|
try:
|
|
172
|
-
data = json.loads(p.read_text())
|
|
172
|
+
data = json.loads(p.read_text(encoding="utf-8"))
|
|
173
173
|
except (json.JSONDecodeError, OSError):
|
|
174
174
|
continue
|
|
175
175
|
out.append({
|
|
@@ -187,4 +187,4 @@ def history_read(hist_id: str) -> dict:
|
|
|
187
187
|
from ..errors import BoostError
|
|
188
188
|
raise BoostError("no lock history entry %s" % hist_id,
|
|
189
189
|
hint="list entries with `boost replay`")
|
|
190
|
-
return json.loads(p.read_text())
|
|
190
|
+
return json.loads(p.read_text(encoding="utf-8"))
|
|
@@ -25,6 +25,23 @@ MAGENTA = "\033[35m"
|
|
|
25
25
|
CYAN = "\033[36m"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def harden_console_encoding() -> None:
|
|
29
|
+
"""Reconfigure stdout/stderr to UTF-8.
|
|
30
|
+
|
|
31
|
+
boost's own output uses non-ASCII (checkmarks, em dashes, arrows); a
|
|
32
|
+
console whose codepage isn't UTF-8 — chiefly Windows' legacy codepages —
|
|
33
|
+
raises UnicodeEncodeError on that output otherwise. reconfigure() is
|
|
34
|
+
missing on some wrapped/piped streams (e.g. under certain test harnesses),
|
|
35
|
+
so skip a stream that doesn't have it rather than fail startup over it.
|
|
36
|
+
"""
|
|
37
|
+
for stream in (sys.stdout, sys.stderr):
|
|
38
|
+
if hasattr(stream, "reconfigure"):
|
|
39
|
+
try:
|
|
40
|
+
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
41
|
+
except (OSError, ValueError):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
28
45
|
def use_color(stream=None) -> bool:
|
|
29
46
|
# BOOST_COLOR is boost's own explicit override and wins over everything
|
|
30
47
|
# else (most-specific-wins): always/never force it on/off regardless of
|
|
@@ -39,14 +39,18 @@ def tilde(p) -> str:
|
|
|
39
39
|
directly beneath it (``home + os.sep``). A bare ``startswith(home)`` would
|
|
40
40
|
wrongly turn a sibling like ``/Users/bob-backup`` into ``~-backup``. Both
|
|
41
41
|
the raw and resolved forms of home() are tried so symlinked homes contract.
|
|
42
|
+
|
|
43
|
+
Always returns ``/``-separated output (even for a path outside $HOME) so
|
|
44
|
+
boost's display text is stable across platforms — Windows accepts forward
|
|
45
|
+
slashes natively, so nothing is lost for a Windows reader either.
|
|
42
46
|
"""
|
|
43
47
|
s = str(p)
|
|
44
48
|
for h in (str(home()), str(home().resolve())):
|
|
45
49
|
if s == h:
|
|
46
50
|
return "~"
|
|
47
51
|
if s.startswith(h + os.sep):
|
|
48
|
-
return "~" + s[len(h):]
|
|
49
|
-
return s
|
|
52
|
+
return ("~" + s[len(h):]).replace(os.sep, "/")
|
|
53
|
+
return s.replace(os.sep, "/")
|
|
50
54
|
|
|
51
55
|
|
|
52
56
|
def boost_home() -> Path:
|
|
@@ -27,7 +27,7 @@ def load() -> dict:
|
|
|
27
27
|
base = dict(DEFAULTS)
|
|
28
28
|
if p.exists():
|
|
29
29
|
try:
|
|
30
|
-
base.update(json.loads(p.read_text()))
|
|
30
|
+
base.update(json.loads(p.read_text(encoding="utf-8")))
|
|
31
31
|
except (json.JSONDecodeError, OSError):
|
|
32
32
|
pass
|
|
33
33
|
return base
|
|
@@ -36,7 +36,7 @@ def load() -> dict:
|
|
|
36
36
|
def save(pol: dict) -> None:
|
|
37
37
|
paths.ensure_dirs()
|
|
38
38
|
known = {k: pol.get(k, DEFAULTS[k]) for k in DEFAULTS}
|
|
39
|
-
paths.policy_path().write_text(json.dumps(known, indent=2) + "\n")
|
|
39
|
+
paths.policy_path().write_text(json.dumps(known, indent=2) + "\n", encoding="utf-8")
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def check_install(entry: dict, installed_count: int) -> List[str]:
|
|
@@ -122,7 +122,7 @@ def _tap_commits() -> Dict[str, str]:
|
|
|
122
122
|
for t in registry.list_taps():
|
|
123
123
|
commit = ""
|
|
124
124
|
try:
|
|
125
|
-
data = json.loads(t.cache_file.read_text())
|
|
125
|
+
data = json.loads(t.cache_file.read_text(encoding="utf-8"))
|
|
126
126
|
commit = str(data.get("commit") or "")
|
|
127
127
|
except (OSError, json.JSONDecodeError):
|
|
128
128
|
pass
|
|
@@ -288,7 +288,7 @@ def _load_raw() -> Optional[dict]:
|
|
|
288
288
|
if isinstance(cached, tuple) and cached[0] == mtime:
|
|
289
289
|
return cached[1] # type: ignore[return-value]
|
|
290
290
|
try:
|
|
291
|
-
data = json.loads(p.read_text())
|
|
291
|
+
data = json.loads(p.read_text(encoding="utf-8"))
|
|
292
292
|
except (OSError, json.JSONDecodeError):
|
|
293
293
|
return None
|
|
294
294
|
if not isinstance(data, dict) or data.get("version") != INDEX_VERSION:
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import difflib
|
|
5
|
-
import shutil
|
|
6
5
|
from dataclasses import dataclass
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
from typing import List, Optional
|
|
9
8
|
|
|
10
9
|
from ..errors import BoostError
|
|
11
|
-
from . import config, gitutil, paths
|
|
10
|
+
from . import config, gitutil, paths, util
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
@dataclass
|
|
@@ -40,7 +39,10 @@ def parse_spec(spec: str):
|
|
|
40
39
|
Accepts: owner/repo | full git URL | existing local path.
|
|
41
40
|
"""
|
|
42
41
|
spec = spec.strip().rstrip("/")
|
|
43
|
-
|
|
42
|
+
# paths.expand(), not Path.expanduser(): expanduser() consults the OS's
|
|
43
|
+
# own home-dir lookup (USERPROFILE on Windows), which ignores the $HOME
|
|
44
|
+
# override the whole test suite (and BOOST_HOME sandboxing) relies on.
|
|
45
|
+
p = paths.expand(spec)
|
|
44
46
|
if p.exists() and p.is_dir():
|
|
45
47
|
return (p.resolve().name, str(p.resolve()))
|
|
46
48
|
if spec.startswith(("http://", "https://", "git@", "ssh://")):
|
|
@@ -78,7 +80,7 @@ def add(spec: str, curated: bool = False) -> Tap:
|
|
|
78
80
|
tap = Tap(name=name, url=url, curated=curated)
|
|
79
81
|
paths.ensure_dirs()
|
|
80
82
|
if tap.path.exists():
|
|
81
|
-
|
|
83
|
+
util.rmtree(tap.path)
|
|
82
84
|
gitutil.clone_shallow(url, tap.path)
|
|
83
85
|
cfg = config.load()
|
|
84
86
|
cfg.setdefault("taps", []).append(
|
|
@@ -93,7 +95,7 @@ def remove(name: str) -> Tap:
|
|
|
93
95
|
cfg["taps"] = [t for t in cfg.get("taps", []) if t["name"] != tap.name]
|
|
94
96
|
config.save(cfg)
|
|
95
97
|
if tap.path.exists():
|
|
96
|
-
|
|
98
|
+
util.rmtree(tap.path)
|
|
97
99
|
if tap.cache_file.exists():
|
|
98
100
|
tap.cache_file.unlink()
|
|
99
101
|
return tap
|