boost-skill-cli 1.0.186__tar.gz → 1.0.188__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.186 → boost_skill_cli-1.0.188}/.vale/styles/config/vocabularies/boost/accept.txt +2 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/PKG-INFO +39 -1
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/README.md +38 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/info.py +39 -6
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/pkg.py +72 -12
- boost_skill_cli-1.0.188/boost_cli/core/projectlock.py +120 -0
- boost_skill_cli-1.0.188/boost_cli/core/scopes.py +205 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/store.py +269 -16
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_skill_cli.egg-info/PKG-INFO +39 -1
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_skill_cli.egg-info/SOURCES.txt +6 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/commands.html +17 -10
- boost_skill_cli-1.0.188/docs/roadmap/items/project-scope-across-every-command.md +32 -0
- boost_skill_cli-1.0.188/docs/roadmap/items/workspace-scope-install.md +36 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap.html +52 -17
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_info.py +4 -2
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_pkg.py +2 -1
- boost_skill_cli-1.0.188/tests/functional/test_workspace_scope.py +214 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/smoke.sh +16 -0
- boost_skill_cli-1.0.188/tests/unit/test_projectlock.py +136 -0
- boost_skill_cli-1.0.188/tests/unit/test_scopes.py +286 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_store.py +440 -1
- boost_skill_cli-1.0.186/docs/roadmap/items/workspace-scope-install.md +0 -29
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.gitattributes +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.gitignore +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.gitleaks.toml +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.htmlvalidate.json +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.lycheeignore +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.markdownlint-cli2.jsonc +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.pre-commit-config.yaml +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.stylelintrc.json +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.vale/styles/boost/Terminology.yml +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/.vale.ini +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/LICENSE +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/Makefile +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/serve.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/updatediff.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/index.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/eslint.config.mjs +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/examples/README.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/index.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/noxfile.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/build_command_reference.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/check_anchors.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/import_budget.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/scripts/perf_gate.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/setup.cfg +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/style/README.md +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/style/boost.css +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/style/boost.js +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/style/demo.html +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/steps/discovery_steps.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_check_anchors.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_command_reference_fresh.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_gitleaks_config.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_import_budget.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_perf_gate.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_serve.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/unit/test_workflows.py +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/visual/package.json +0 -0
- {boost_skill_cli-1.0.186 → boost_skill_cli-1.0.188}/tests/visual/visual_check.mjs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: boost-skill-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.188
|
|
4
4
|
Summary: boost — Homebrew for AI coding skills
|
|
5
5
|
Author: Jonathan Reyes
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -826,6 +826,44 @@ GitHub registries ──boost update──▶ ~/.boost/repos/ (shallow clon
|
|
|
826
826
|
────symlinks───▶ ~/.claude/skills/ ~/.windsurf/skills/ ~/.cursor/skills/
|
|
827
827
|
```
|
|
828
828
|
|
|
829
|
+
### User scope vs project scope
|
|
830
|
+
|
|
831
|
+
That's the default — **user scope**, the skills you want everywhere. The other
|
|
832
|
+
half is the npm `--save` half: skills a *team* agrees on, which belong in the
|
|
833
|
+
repo where they can be reviewed in a PR and arrive with a `git clone`.
|
|
834
|
+
|
|
835
|
+
```bash
|
|
836
|
+
boost install code-review --local # into THIS repo (= --scope project)
|
|
837
|
+
boost install code-review # into your user config (default)
|
|
838
|
+
boost list --local # just what this repo carries
|
|
839
|
+
boost uninstall code-review --local
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
A `--local` install writes **real directories** into the repo's own agent config
|
|
843
|
+
and records them in a committable per-repo lock:
|
|
844
|
+
|
|
845
|
+
```text
|
|
846
|
+
<repo>/.claude/skills/<name>/ real copies, not symlinks
|
|
847
|
+
<repo>/.cursor/skills/<name>/
|
|
848
|
+
<repo>/.boost/skill-lock.json commit this — it's what teammates clone
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
Real copies, deliberately: a symlink into *your* `~/.agents/skills` is a
|
|
852
|
+
dangling pointer on anyone else's machine, so committing one would ship
|
|
853
|
+
something that only works on the author's laptop. The two scopes use separate
|
|
854
|
+
lock files, so vendoring a skill into a repo never fights with the copy you use
|
|
855
|
+
everywhere — and `boost install --local` run from `src/deep/nested` walks up to
|
|
856
|
+
the repo root rather than scattering a `.claude/` three directories down.
|
|
857
|
+
|
|
858
|
+
After a fresh clone, `boost sync` re-materializes anything the project lock
|
|
859
|
+
records but the checkout is missing — the lock stores repo-relative paths, so it
|
|
860
|
+
works from whatever directory your teammate cloned into. It never deletes a
|
|
861
|
+
skill directory boost didn't write, and it won't overwrite a hand-written
|
|
862
|
+
`.claude/skills/<name>/` unless you pass `--force`.
|
|
863
|
+
|
|
864
|
+
`boost update` covers user scope only for now; to refresh a vendored skill use
|
|
865
|
+
`boost install <skill> --local --force`.
|
|
866
|
+
|
|
829
867
|
## The usual workflow
|
|
830
868
|
|
|
831
869
|
```bash
|
|
@@ -110,6 +110,44 @@ GitHub registries ──boost update──▶ ~/.boost/repos/ (shallow clon
|
|
|
110
110
|
────symlinks───▶ ~/.claude/skills/ ~/.windsurf/skills/ ~/.cursor/skills/
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
### User scope vs project scope
|
|
114
|
+
|
|
115
|
+
That's the default — **user scope**, the skills you want everywhere. The other
|
|
116
|
+
half is the npm `--save` half: skills a *team* agrees on, which belong in the
|
|
117
|
+
repo where they can be reviewed in a PR and arrive with a `git clone`.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
boost install code-review --local # into THIS repo (= --scope project)
|
|
121
|
+
boost install code-review # into your user config (default)
|
|
122
|
+
boost list --local # just what this repo carries
|
|
123
|
+
boost uninstall code-review --local
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A `--local` install writes **real directories** into the repo's own agent config
|
|
127
|
+
and records them in a committable per-repo lock:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
<repo>/.claude/skills/<name>/ real copies, not symlinks
|
|
131
|
+
<repo>/.cursor/skills/<name>/
|
|
132
|
+
<repo>/.boost/skill-lock.json commit this — it's what teammates clone
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Real copies, deliberately: a symlink into *your* `~/.agents/skills` is a
|
|
136
|
+
dangling pointer on anyone else's machine, so committing one would ship
|
|
137
|
+
something that only works on the author's laptop. The two scopes use separate
|
|
138
|
+
lock files, so vendoring a skill into a repo never fights with the copy you use
|
|
139
|
+
everywhere — and `boost install --local` run from `src/deep/nested` walks up to
|
|
140
|
+
the repo root rather than scattering a `.claude/` three directories down.
|
|
141
|
+
|
|
142
|
+
After a fresh clone, `boost sync` re-materializes anything the project lock
|
|
143
|
+
records but the checkout is missing — the lock stores repo-relative paths, so it
|
|
144
|
+
works from whatever directory your teammate cloned into. It never deletes a
|
|
145
|
+
skill directory boost didn't write, and it won't overwrite a hand-written
|
|
146
|
+
`.claude/skills/<name>/` unless you pass `--force`.
|
|
147
|
+
|
|
148
|
+
`boost update` covers user scope only for now; to refresh a vendored skill use
|
|
149
|
+
`boost install <skill> --local --force`.
|
|
150
|
+
|
|
113
151
|
## The usual workflow
|
|
114
152
|
|
|
115
153
|
```bash
|
|
@@ -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.188'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 188)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -17,7 +17,8 @@ from pathlib import Path
|
|
|
17
17
|
|
|
18
18
|
from .. import cliparse
|
|
19
19
|
from ..core import (ai, catalog, frontmatter, gitutil, imperative, journal,
|
|
20
|
-
lockfile, logs, paths, registry, store,
|
|
20
|
+
lockfile, logs, paths, projectlock, registry, scopes, store,
|
|
21
|
+
util)
|
|
21
22
|
from ..core import output as out
|
|
22
23
|
from ..errors import BoostError
|
|
23
24
|
|
|
@@ -127,21 +128,30 @@ def cmd_list(argv):
|
|
|
127
128
|
ap = cliparse.parser(prog="boost list",
|
|
128
129
|
description="List installed skills, rules and workflows")
|
|
129
130
|
ap.add_argument("--tag", help="only show skills carrying this tag")
|
|
131
|
+
ap.add_argument("--local", action="store_true",
|
|
132
|
+
help="only the skills installed into this repo")
|
|
130
133
|
ap.add_argument("--json", action="store_true", help="machine-readable output")
|
|
131
134
|
args = ap.parse_args(argv)
|
|
132
135
|
skills = lockfile.installed()
|
|
133
136
|
rules = lockfile.installed_rules()
|
|
134
137
|
workflows = lockfile.installed_workflows()
|
|
138
|
+
# Project skills live in the repo's own lock, never the user's — read it
|
|
139
|
+
# separately so a checkout's committed skills show up alongside yours.
|
|
140
|
+
pbase = scopes.project_root()
|
|
141
|
+
project = projectlock.installed(pbase) if pbase is not None else {}
|
|
142
|
+
if args.local:
|
|
143
|
+
skills, rules, workflows = {}, {}, {}
|
|
135
144
|
if args.tag:
|
|
136
145
|
# Tags are a skill-only concept, so --tag narrows to skills.
|
|
137
146
|
want = args.tag.lstrip("#")
|
|
138
147
|
skills = {n: e for n, e in skills.items() if want in (e.get("tags") or [])}
|
|
139
|
-
rules, workflows = {}, {}
|
|
148
|
+
rules, workflows, project = {}, {}, {}
|
|
140
149
|
if args.json:
|
|
141
|
-
print(json.dumps({"skills": skills, "rules": rules,
|
|
150
|
+
print(json.dumps({"skills": skills, "rules": rules,
|
|
151
|
+
"workflows": workflows, "project": project},
|
|
142
152
|
indent=2, sort_keys=True))
|
|
143
153
|
return 0
|
|
144
|
-
if not skills and not rules and not workflows:
|
|
154
|
+
if not skills and not rules and not workflows and not project:
|
|
145
155
|
print(out.empty_state(
|
|
146
156
|
"no skills installed"
|
|
147
157
|
+ (" with tag #%s" % args.tag.lstrip("#") if args.tag else ""),
|
|
@@ -165,6 +175,15 @@ def cmd_list(argv):
|
|
|
165
175
|
out.table(rows, headers=("NAME", "VERSION", "TAP", "AGENTS", "FLAGS"))
|
|
166
176
|
print(" " + out.aurora("%d skill%s installed"
|
|
167
177
|
% (len(rows), "" if len(rows) == 1 else "s"), "cyan"))
|
|
178
|
+
if project:
|
|
179
|
+
out.heading("project skills (%s)" % paths.tilde(pbase))
|
|
180
|
+
rows = [(name, e.get("version", "?"), e.get("tap", "?"),
|
|
181
|
+
"·".join(a.split("-")[0] for a in e.get("agents") or []))
|
|
182
|
+
for name, e in sorted(project.items())]
|
|
183
|
+
out.table(rows, headers=("NAME", "VERSION", "TAP", "AGENTS"))
|
|
184
|
+
print(" " + out.role("committed with the repo — %s/%s"
|
|
185
|
+
% (projectlock.LOCK_DIRNAME,
|
|
186
|
+
projectlock.LOCK_FILENAME), "muted"))
|
|
168
187
|
if rules:
|
|
169
188
|
_kind_table("installed rules", rules)
|
|
170
189
|
if workflows:
|
|
@@ -180,6 +199,11 @@ def cmd_info(argv):
|
|
|
180
199
|
args = ap.parse_args(argv)
|
|
181
200
|
name = args.name
|
|
182
201
|
lock = lockfile.get_skill(name)
|
|
202
|
+
# A project-scoped skill is installed — just not at user scope. Without this
|
|
203
|
+
# `boost info` would call it "not installed" while it sits in the repo, and
|
|
204
|
+
# the install banner's own "next: boost info <name>" would lead nowhere.
|
|
205
|
+
pbase = scopes.project_root()
|
|
206
|
+
plock = projectlock.get_skill(pbase, name) if pbase is not None else None
|
|
183
207
|
if lock:
|
|
184
208
|
matches = catalog.find(name)
|
|
185
209
|
same_tap = [e for e in matches if e["tap"] == lock.get("tap")]
|
|
@@ -189,6 +213,12 @@ def cmd_info(argv):
|
|
|
189
213
|
|
|
190
214
|
sdir = store.skill_store_dir(name)
|
|
191
215
|
skill_dir = sdir if lock and sdir.is_dir() else None
|
|
216
|
+
if skill_dir is None and plock:
|
|
217
|
+
for m in plock.get("materializations") or []:
|
|
218
|
+
cand = scopes.resolve_in_base(pbase, m.get("path"))
|
|
219
|
+
if cand is not None and cand.is_dir():
|
|
220
|
+
skill_dir = cand
|
|
221
|
+
break
|
|
192
222
|
if skill_dir is None and cat:
|
|
193
223
|
try:
|
|
194
224
|
skill_dir = store.source_dir_for(cat)
|
|
@@ -207,8 +237,9 @@ def cmd_info(argv):
|
|
|
207
237
|
if args.json:
|
|
208
238
|
print(json.dumps({
|
|
209
239
|
"name": name, "description": desc,
|
|
210
|
-
"installed": lock, "
|
|
211
|
-
"
|
|
240
|
+
"installed": lock, "project": plock,
|
|
241
|
+
"latest": (cat or {}).get("version"),
|
|
242
|
+
"tap": (lock or plock or cat or {}).get("tap"),
|
|
212
243
|
"store": str(sdir) if lock and sdir.is_dir() else None,
|
|
213
244
|
"quality": score, "size": size, "files": files,
|
|
214
245
|
}, indent=2))
|
|
@@ -227,6 +258,8 @@ def cmd_info(argv):
|
|
|
227
258
|
latest = str((cat or {}).get("version") or "")
|
|
228
259
|
if cat and latest != str(lock.get("version", "?")):
|
|
229
260
|
badges.append(out.badge("update available", "yellow"))
|
|
261
|
+
elif plock:
|
|
262
|
+
badges.append(out.badge("installed in this project", "green"))
|
|
230
263
|
else:
|
|
231
264
|
badges.append(out.badge("not installed", "cyan"))
|
|
232
265
|
tapname = (lock or cat or {}).get("tap")
|
|
@@ -16,8 +16,9 @@ from typing import Dict, List, Optional
|
|
|
16
16
|
|
|
17
17
|
from .. import cliparse
|
|
18
18
|
from ..core import (adapters, agents, catalog, config, frontmatter, gitutil,
|
|
19
|
-
injectscan, journal, lockfile, paths, registry,
|
|
20
|
-
secretscan, staleness, store, typosquat, updatediff,
|
|
19
|
+
injectscan, journal, lockfile, paths, projectlock, registry,
|
|
20
|
+
scopes, secretscan, staleness, store, typosquat, updatediff,
|
|
21
|
+
util)
|
|
21
22
|
from ..core import output as out
|
|
22
23
|
from ..errors import BoostError
|
|
23
24
|
|
|
@@ -118,6 +119,18 @@ def _report_result(res: store.InstallResult) -> None:
|
|
|
118
119
|
_warn_injection(res)
|
|
119
120
|
_warn_secrets(res)
|
|
120
121
|
return
|
|
122
|
+
if res.scope == scopes.SCOPE_PROJECT:
|
|
123
|
+
# No canonical store and no symlinks — a real copy per agent, so the
|
|
124
|
+
# report says "copied into" for each rather than "copied … then linked".
|
|
125
|
+
out.ok("copied into this repo → %s"
|
|
126
|
+
% (" · ".join(res.linked) or "(no enabled agents)"))
|
|
127
|
+
out.ok("project lock updated (%s/%s)"
|
|
128
|
+
% (projectlock.LOCK_DIRNAME, projectlock.LOCK_FILENAME))
|
|
129
|
+
out.dim(" commit %s/ to share these with the team"
|
|
130
|
+
% projectlock.LOCK_DIRNAME)
|
|
131
|
+
_warn_injection(res)
|
|
132
|
+
_warn_secrets(res)
|
|
133
|
+
return
|
|
121
134
|
out.ok("copied to %s" % _tilde(res.dest))
|
|
122
135
|
if res.linked:
|
|
123
136
|
out.ok("linked → %s" % " · ".join(res.linked))
|
|
@@ -161,12 +174,20 @@ def cmd_install(argv: List[str]) -> int:
|
|
|
161
174
|
help="reinstall even if already installed or pinned")
|
|
162
175
|
ap.add_argument("--agent", action="append", metavar="A",
|
|
163
176
|
help="link only into this agent (repeatable)")
|
|
164
|
-
ap.
|
|
165
|
-
|
|
166
|
-
"
|
|
177
|
+
scope_g = ap.add_mutually_exclusive_group()
|
|
178
|
+
scope_g.add_argument("--scope", choices=scopes.SCOPES, default=None,
|
|
179
|
+
help="install into your user config (default) or the "
|
|
180
|
+
"current repo (project)")
|
|
181
|
+
scope_g.add_argument("--local", dest="scope", action="store_const",
|
|
182
|
+
const=scopes.SCOPE_PROJECT,
|
|
183
|
+
help="into this repo, committable (= --scope project)")
|
|
184
|
+
scope_g.add_argument("--global", dest="scope", action="store_const",
|
|
185
|
+
const=scopes.SCOPE_USER,
|
|
186
|
+
help="into your user config (= --scope user, default)")
|
|
167
187
|
ap.add_argument("--dry-run", action="store_true",
|
|
168
188
|
help="show what would happen without changing anything")
|
|
169
189
|
args = ap.parse_args(argv)
|
|
190
|
+
args.scope = args.scope or scopes.SCOPE_USER
|
|
170
191
|
only = _check_agents(args.agent)
|
|
171
192
|
multi = len(args.names) > 1
|
|
172
193
|
entries, failed = [], 0
|
|
@@ -183,7 +204,23 @@ def cmd_install(argv: List[str]) -> int:
|
|
|
183
204
|
|
|
184
205
|
if args.dry_run:
|
|
185
206
|
targets = [a for a in agents.enabled_agents() if not only or a in only]
|
|
207
|
+
pbase = scopes.resolve_base(args.scope)
|
|
208
|
+
if args.scope == scopes.SCOPE_PROJECT and pbase is None:
|
|
209
|
+
raise BoostError(
|
|
210
|
+
"there is no project here to install into",
|
|
211
|
+
hint="cd into a repo, or drop --local to install for your user")
|
|
186
212
|
for e in entries:
|
|
213
|
+
if args.scope == scopes.SCOPE_PROJECT and e.get("kind", "skill") == "skill":
|
|
214
|
+
seen = projectlock.get_skill(pbase, e["name"])
|
|
215
|
+
out.info("would %s %s v%s from %s into %s"
|
|
216
|
+
% ("reinstall" if seen else "install", e["name"],
|
|
217
|
+
e["version"], e["tap"], _tilde(pbase)))
|
|
218
|
+
for agent_name, sdir in agents.enabled_agents().items():
|
|
219
|
+
if only and agent_name not in only:
|
|
220
|
+
continue
|
|
221
|
+
out.info(" copy → %s"
|
|
222
|
+
% _tilde(scopes.skill_target(sdir, e["name"], base=pbase)))
|
|
223
|
+
continue
|
|
187
224
|
if e.get("kind") in ("rule", "workflow"):
|
|
188
225
|
k = e["kind"]
|
|
189
226
|
seen = (lockfile.get_rule if k == "rule"
|
|
@@ -253,12 +290,17 @@ def cmd_uninstall(argv: List[str]) -> int:
|
|
|
253
290
|
prog="boost uninstall",
|
|
254
291
|
description="Remove an installed skill, rule, workflow, or config")
|
|
255
292
|
ap.add_argument("names", nargs="+", metavar="NAME")
|
|
293
|
+
ap.add_argument("--local", dest="scope", action="store_const",
|
|
294
|
+
const=scopes.SCOPE_PROJECT, default=None,
|
|
295
|
+
help="remove from this repo rather than your user config")
|
|
256
296
|
args = ap.parse_args(argv)
|
|
257
297
|
removed, failed = 0, 0
|
|
258
298
|
for name in args.names:
|
|
259
|
-
dest = store.skill_store_dir(name)
|
|
260
299
|
try:
|
|
261
|
-
|
|
300
|
+
if args.scope == scopes.SCOPE_PROJECT:
|
|
301
|
+
info = store.uninstall_project(name)
|
|
302
|
+
else:
|
|
303
|
+
info = store.uninstall(name)
|
|
262
304
|
except BoostError as err:
|
|
263
305
|
if len(args.names) == 1:
|
|
264
306
|
raise
|
|
@@ -266,8 +308,15 @@ def cmd_uninstall(argv: List[str]) -> int:
|
|
|
266
308
|
failed += 1
|
|
267
309
|
continue
|
|
268
310
|
if info["unlinked"]:
|
|
269
|
-
|
|
270
|
-
|
|
311
|
+
verb = "removed from" if info.get("scope") == scopes.SCOPE_PROJECT \
|
|
312
|
+
else "unlinked ←"
|
|
313
|
+
out.ok("%s %s" % (verb, " · ".join(info["unlinked"])))
|
|
314
|
+
# Report where it actually went, not where a user-scope install would
|
|
315
|
+
# have put it — a project skill never touches the canonical store.
|
|
316
|
+
if info.get("scope") == scopes.SCOPE_PROJECT:
|
|
317
|
+
out.ok("removed from %s" % _tilde(info.get("base", "this repo")))
|
|
318
|
+
else:
|
|
319
|
+
out.ok("removed %s" % _tilde(store.skill_store_dir(name)))
|
|
271
320
|
removed += 1
|
|
272
321
|
if removed:
|
|
273
322
|
lines = ["Uninstalled %s" % _plural(removed, "skill")]
|
|
@@ -299,14 +348,25 @@ def cmd_sync(argv: List[str]) -> int:
|
|
|
299
348
|
ap.add_argument("--json", action="store_true", help="machine-readable output")
|
|
300
349
|
args = ap.parse_args(argv)
|
|
301
350
|
plan = store.sync_plan()
|
|
351
|
+
# A checkout can be missing skills its project lock records — that is the
|
|
352
|
+
# normal state right after `git clone` — so sync repairs the repo too.
|
|
353
|
+
pplan = store.project_sync_plan()
|
|
302
354
|
|
|
303
355
|
if args.diff:
|
|
304
356
|
if args.json:
|
|
305
|
-
print(json.dumps(plan, indent=2))
|
|
357
|
+
print(json.dumps(dict(plan, project=pplan), indent=2))
|
|
306
358
|
return 0
|
|
307
|
-
if not any(plan.values()):
|
|
359
|
+
if not any(plan.values()) and not any(pplan.values()):
|
|
308
360
|
out.ok("everything in sync")
|
|
309
361
|
return 0
|
|
362
|
+
if pplan["missing"]:
|
|
363
|
+
out.heading("missing from this project (%d)" % len(pplan["missing"]))
|
|
364
|
+
for name, agent in pplan["missing"]:
|
|
365
|
+
out.info("%s → %s" % (name, agent))
|
|
366
|
+
if pplan["orphaned"]:
|
|
367
|
+
out.heading("unclaimed project skill dirs (%d)" % len(pplan["orphaned"]))
|
|
368
|
+
for item in pplan["orphaned"]:
|
|
369
|
+
out.info(_tilde(item))
|
|
310
370
|
for key, label in _PLAN_LABELS:
|
|
311
371
|
if not plan[key]:
|
|
312
372
|
continue
|
|
@@ -320,7 +380,7 @@ def cmd_sync(argv: List[str]) -> int:
|
|
|
320
380
|
out.info(_tilde(item))
|
|
321
381
|
return 0
|
|
322
382
|
|
|
323
|
-
actions = store.sync_apply(plan)
|
|
383
|
+
actions = store.sync_apply(plan) + store.project_sync_apply(pplan)
|
|
324
384
|
orphans = plan["orphaned_store"]
|
|
325
385
|
pruned = []
|
|
326
386
|
if args.prune and orphans:
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""The per-project lock file: ``<repo>/.boost/skill-lock.json``.
|
|
2
|
+
|
|
3
|
+
Project-scoped skills deliberately do **not** share the user lock. Two reasons,
|
|
4
|
+
one practical and one about ownership:
|
|
5
|
+
|
|
6
|
+
* Practical — roughly forty call sites across the command layer read the user
|
|
7
|
+
lock and then look the skill up at ``~/.agents/skills/<name>``. A project skill
|
|
8
|
+
does not live there, so putting it in that lock would hand every one of those
|
|
9
|
+
a path that does not exist.
|
|
10
|
+
* Ownership — this file is meant to be **committed**. It is the record a teammate
|
|
11
|
+
gets from ``git clone``, which is the entire point of installing into the repo.
|
|
12
|
+
|
|
13
|
+
So it is a small, self-contained document with the same shape as the user lock's
|
|
14
|
+
``skills`` section and none of its machinery. In particular there is no history
|
|
15
|
+
directory: the user lock snapshots itself on every write because nothing else
|
|
16
|
+
would remember, whereas this file is versioned by the repo it lives in — git is
|
|
17
|
+
its history, and duplicating that inside ``.boost/`` would just be litter in
|
|
18
|
+
someone's diff.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Optional
|
|
25
|
+
|
|
26
|
+
from . import util
|
|
27
|
+
|
|
28
|
+
SCHEMA_VERSION = 1
|
|
29
|
+
LOCK_DIRNAME = ".boost"
|
|
30
|
+
LOCK_FILENAME = "skill-lock.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def lock_path(base) -> Path:
|
|
34
|
+
"""Path of the project lock for the repo at ``base``."""
|
|
35
|
+
return Path(base) / LOCK_DIRNAME / LOCK_FILENAME
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def exists(base) -> bool:
|
|
39
|
+
"""True when ``base`` already carries a project lock file."""
|
|
40
|
+
return lock_path(base).is_file()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _skeleton() -> dict:
|
|
44
|
+
return {"version": SCHEMA_VERSION, "updated": util.now_iso(), "skills": {}}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def read(base) -> dict:
|
|
48
|
+
"""Load the project lock, guaranteeing a ``skills`` key.
|
|
49
|
+
|
|
50
|
+
A missing file is simply an empty project. A corrupt one is preserved beside
|
|
51
|
+
itself as ``skill-lock.json.corrupt`` before falling back to the skeleton,
|
|
52
|
+
for the same reason the user lock does it: an unparseable file is not an
|
|
53
|
+
empty one, and the next write would otherwise erase the only record of what
|
|
54
|
+
the project had installed.
|
|
55
|
+
"""
|
|
56
|
+
p = lock_path(base)
|
|
57
|
+
if not p.is_file():
|
|
58
|
+
return _skeleton()
|
|
59
|
+
try:
|
|
60
|
+
lock = json.loads(p.read_text(encoding="utf-8"))
|
|
61
|
+
except (json.JSONDecodeError, OSError):
|
|
62
|
+
_preserve_corrupt(p)
|
|
63
|
+
return _skeleton()
|
|
64
|
+
if not isinstance(lock, dict):
|
|
65
|
+
_preserve_corrupt(p)
|
|
66
|
+
return _skeleton()
|
|
67
|
+
lock.setdefault("version", SCHEMA_VERSION)
|
|
68
|
+
skills = lock.get("skills")
|
|
69
|
+
lock["skills"] = skills if isinstance(skills, dict) else {}
|
|
70
|
+
return lock
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _preserve_corrupt(p: Path) -> None:
|
|
74
|
+
"""Copy an unparseable lock aside as ``.corrupt``; never raise."""
|
|
75
|
+
try:
|
|
76
|
+
import shutil
|
|
77
|
+
shutil.copy(p, p.with_name(p.name + ".corrupt"))
|
|
78
|
+
except OSError:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def write(base, lock: dict) -> None:
|
|
83
|
+
"""Stamp version/updated onto ``lock`` and write it atomically."""
|
|
84
|
+
p = lock_path(base)
|
|
85
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
lock["version"] = SCHEMA_VERSION
|
|
87
|
+
lock["updated"] = util.now_iso()
|
|
88
|
+
util.atomic_write_text(p, json.dumps(lock, indent=2, sort_keys=True) + "\n")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def installed(base) -> dict:
|
|
92
|
+
"""Return the project's ``{name: entry}`` mapping of installed skills."""
|
|
93
|
+
return read(base)["skills"]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_skill(base, name: str) -> Optional[dict]:
|
|
97
|
+
"""Return the project lock entry for ``name``, or None."""
|
|
98
|
+
return read(base)["skills"].get(name)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def set_skill(base, name: str, entry: dict) -> None:
|
|
102
|
+
"""Insert or replace the project lock entry for ``name`` and persist."""
|
|
103
|
+
lock = read(base)
|
|
104
|
+
lock["skills"][name] = entry
|
|
105
|
+
write(base, lock)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def remove_skill(base, name: str) -> bool:
|
|
109
|
+
"""Drop ``name`` from the project lock; True if it was present.
|
|
110
|
+
|
|
111
|
+
When the last skill goes the file is left in place (empty) rather than
|
|
112
|
+
deleted — it is a tracked file in someone's repo, and removing it would show
|
|
113
|
+
up as a spurious deletion in their next ``git status``.
|
|
114
|
+
"""
|
|
115
|
+
lock = read(base)
|
|
116
|
+
if name in lock["skills"]:
|
|
117
|
+
del lock["skills"][name]
|
|
118
|
+
write(base, lock)
|
|
119
|
+
return True
|
|
120
|
+
return False
|