boost-skill-cli 1.0.146__tar.gz → 1.0.148__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.146 → boost_skill_cli-1.0.148}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/pkg.py +14 -5
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/rules.py +19 -9
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/store.py +47 -13
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/workflows.py +11 -5
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_skill_cli.egg-info/SOURCES.txt +1 -0
- boost_skill_cli-1.0.148/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/demo.tape +6 -6
- boost_skill_cli-1.0.148/docs/roadmap/items/install-scope-user-or-project.md +27 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap.html +24 -1
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_pkg.py +16 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_rules.py +18 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_store.py +48 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_workflows.py +10 -0
- boost_skill_cli-1.0.146/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/.gitattributes +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/.gitignore +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/LICENSE +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/Makefile +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/README.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/serve.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/updatediff.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/index.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/examples/README.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/index.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/setup.cfg +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/style/README.md +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/style/boost.css +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/style/boost.js +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/style/demo.html +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_serve.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.146 → boost_skill_cli-1.0.148}/tests/unit/test_version.py +0 -0
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
21
|
+
__version__ = version = '1.0.148'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 148)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -118,7 +118,9 @@ def _warn_secrets(res: store.InstallResult) -> None:
|
|
|
118
118
|
def _report_result(res: store.InstallResult) -> None:
|
|
119
119
|
"""The canonical three-line install report."""
|
|
120
120
|
if res.kind in ("rule", "workflow"):
|
|
121
|
-
|
|
121
|
+
where = " (this repo)" if res.scope == "project" else ""
|
|
122
|
+
out.ok("materialized%s → %s"
|
|
123
|
+
% (where, " · ".join(res.linked) or "(no enabled agents)"))
|
|
122
124
|
out.ok("lock updated (.skill-lock.json)")
|
|
123
125
|
_warn_injection(res)
|
|
124
126
|
_warn_secrets(res)
|
|
@@ -166,6 +168,9 @@ def cmd_install(argv: List[str]) -> int:
|
|
|
166
168
|
help="reinstall even if already installed or pinned")
|
|
167
169
|
ap.add_argument("--agent", action="append", metavar="A",
|
|
168
170
|
help="link only into this agent (repeatable)")
|
|
171
|
+
ap.add_argument("--scope", choices=("user", "project"), default="user",
|
|
172
|
+
help="rules/workflows: install into your user config (default) "
|
|
173
|
+
"or the current repo (project)")
|
|
169
174
|
ap.add_argument("--dry-run", action="store_true",
|
|
170
175
|
help="show what would happen without changing anything")
|
|
171
176
|
args = ap.parse_args(argv)
|
|
@@ -191,8 +196,10 @@ def cmd_install(argv: List[str]) -> int:
|
|
|
191
196
|
seen = (lockfile.get_rule if k == "rule"
|
|
192
197
|
else lockfile.get_workflow)(e["name"])
|
|
193
198
|
verb = "reinstall" if seen else "install"
|
|
194
|
-
|
|
195
|
-
|
|
199
|
+
where = "into this repo" if args.scope == "project" else "user config"
|
|
200
|
+
out.info("would %s %s %s v%s from %s (%s)" % (verb, k, e["name"],
|
|
201
|
+
e["version"], e["tap"],
|
|
202
|
+
where))
|
|
196
203
|
out.info(" materialize → %s" % (" · ".join(targets)
|
|
197
204
|
or "(no enabled agents)"))
|
|
198
205
|
continue
|
|
@@ -210,7 +217,7 @@ def cmd_install(argv: List[str]) -> int:
|
|
|
210
217
|
if multi:
|
|
211
218
|
out.heading("%s v%s (%s)" % (e["name"], e["version"], e["tap"]))
|
|
212
219
|
try:
|
|
213
|
-
res = store.install(e, force=args.force, only_agents=only)
|
|
220
|
+
res = store.install(e, force=args.force, only_agents=only, scope=args.scope)
|
|
214
221
|
except BoostError as err:
|
|
215
222
|
if not multi:
|
|
216
223
|
raise
|
|
@@ -414,7 +421,9 @@ def _update_materialized(kind: str, installed: Dict[str, dict], results) -> int:
|
|
|
414
421
|
if not changed:
|
|
415
422
|
continue
|
|
416
423
|
try:
|
|
417
|
-
|
|
424
|
+
# keep the item where it was installed (user vs a specific repo).
|
|
425
|
+
store.install(entry, force=True,
|
|
426
|
+
scope=lk.get("scope", "user"), base=lk.get("base"))
|
|
418
427
|
except BoostError as err:
|
|
419
428
|
out.warn("%s: %s" % (name, err.message))
|
|
420
429
|
continue
|
|
@@ -15,7 +15,7 @@ the filesystem writes and the lock record so uninstall can reverse them.
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Tuple
|
|
18
|
+
from typing import Optional, Tuple
|
|
19
19
|
|
|
20
20
|
# Name-scoped managed-block markers for CLAUDE.md. Stable + per-rule so install
|
|
21
21
|
# is idempotent (a re-install replaces the block) and uninstall strips exactly
|
|
@@ -40,17 +40,27 @@ def markers(name: str) -> Tuple[str, str]:
|
|
|
40
40
|
return BLOCK_START % name, BLOCK_END % name
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def rule_target(agent: str, skills_dir: Path, name: str
|
|
44
|
-
|
|
43
|
+
def rule_target(agent: str, skills_dir: Path, name: str,
|
|
44
|
+
base: "Optional[Path]" = None) -> Tuple[str, Path]:
|
|
45
|
+
"""Where rule ``name`` materializes for ``agent``.
|
|
45
46
|
|
|
46
|
-
Returns ``(mode, path)``: ``MODE_CLAUDE`` writes/merges
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
Returns ``(mode, path)``: ``MODE_CLAUDE`` writes/merges a CLAUDE.md;
|
|
48
|
+
``MODE_FILE`` drops a file into the agent's ``rules/`` dir.
|
|
49
|
+
|
|
50
|
+
``base`` selects the scope:
|
|
51
|
+
* ``None`` (user scope) — the agent's user config: the parent of its
|
|
52
|
+
configured skills dir (``~/.cursor/skills`` -> ``~/.cursor``), and
|
|
53
|
+
``~/.claude/CLAUDE.md`` for Claude.
|
|
54
|
+
* a directory (project scope) — that repo: ``<base>/.cursor/rules/…`` and,
|
|
55
|
+
since Claude reads per-repo memory from the root, ``<base>/CLAUDE.local.md``
|
|
56
|
+
(the personal, git-ignored file) for Claude.
|
|
50
57
|
"""
|
|
51
|
-
|
|
58
|
+
dotdir = Path(skills_dir).parent.name # ".claude" / ".cursor" / …
|
|
52
59
|
if agent in CLAUDE_MD_AGENTS:
|
|
53
|
-
|
|
60
|
+
if base is not None:
|
|
61
|
+
return MODE_CLAUDE, Path(base) / "CLAUDE.local.md"
|
|
62
|
+
return MODE_CLAUDE, Path(skills_dir).parent / "CLAUDE.md"
|
|
63
|
+
root = (Path(base) / dotdir) if base is not None else Path(skills_dir).parent
|
|
54
64
|
ext = RULE_DIR_EXT.get(agent, DEFAULT_RULE_EXT)
|
|
55
65
|
return MODE_FILE, root / "rules" / (name + ext)
|
|
56
66
|
|
|
@@ -26,6 +26,7 @@ class InstallResult:
|
|
|
26
26
|
score: int = 0
|
|
27
27
|
upgraded: bool = False
|
|
28
28
|
kind: str = "skill"
|
|
29
|
+
scope: str = "user" # "user" or "project" — where a rule/workflow landed
|
|
29
30
|
# For rules/workflows the installed content is a single file (or a merged
|
|
30
31
|
# CLAUDE.md block), not a SKILL.md tree — carry the raw source so the caller
|
|
31
32
|
# scans exactly what it installed instead of a non-existent SKILL.md.
|
|
@@ -117,14 +118,32 @@ def _copy_skill(src: Path, dest: Path) -> None:
|
|
|
117
118
|
shutil.rmtree(backup, ignore_errors=True)
|
|
118
119
|
|
|
119
120
|
|
|
121
|
+
def _resolve_base(scope: str, base) -> Optional[Path]:
|
|
122
|
+
"""Directory a project-scoped install materializes under (the repo), or None
|
|
123
|
+
for user scope. An explicit ``base`` (e.g. from a lock record on update/sync)
|
|
124
|
+
wins so a re-materialization lands where the original install did, not in
|
|
125
|
+
whatever the current working directory happens to be."""
|
|
126
|
+
if base:
|
|
127
|
+
return Path(base)
|
|
128
|
+
return Path.cwd() if scope == "project" else None
|
|
129
|
+
|
|
130
|
+
|
|
120
131
|
def install(entry: dict, force: bool = False,
|
|
121
|
-
only_agents: Optional[List[str]] = None
|
|
122
|
-
|
|
132
|
+
only_agents: Optional[List[str]] = None,
|
|
133
|
+
scope: str = "user", base=None) -> InstallResult:
|
|
134
|
+
"""Install a catalog entry. Raises BoostError on policy block or conflict.
|
|
135
|
+
|
|
136
|
+
``scope`` is ``"user"`` (default — the agent's user config dirs) or
|
|
137
|
+
``"project"`` (the current repo). Skills ignore scope (they always live in
|
|
138
|
+
the canonical store); rules/workflows honor it.
|
|
139
|
+
"""
|
|
123
140
|
kind = entry.get("kind", "skill")
|
|
124
141
|
if kind == "rule":
|
|
125
|
-
return _install_rule(entry, force=force, only_agents=only_agents
|
|
142
|
+
return _install_rule(entry, force=force, only_agents=only_agents,
|
|
143
|
+
scope=scope, base=base)
|
|
126
144
|
if kind == "workflow":
|
|
127
|
-
return _install_workflow(entry, force=force, only_agents=only_agents
|
|
145
|
+
return _install_workflow(entry, force=force, only_agents=only_agents,
|
|
146
|
+
scope=scope, base=base)
|
|
128
147
|
if kind != "skill":
|
|
129
148
|
raise BoostError(
|
|
130
149
|
"%s is a %s, which boost does not know how to install" % (entry["name"], kind),
|
|
@@ -172,14 +191,16 @@ def install(entry: dict, force: bool = False,
|
|
|
172
191
|
|
|
173
192
|
|
|
174
193
|
def _install_rule(entry: dict, force: bool = False,
|
|
175
|
-
only_agents: Optional[List[str]] = None
|
|
194
|
+
only_agents: Optional[List[str]] = None,
|
|
195
|
+
scope: str = "user", base=None) -> InstallResult:
|
|
176
196
|
"""Materialize a rule into each enabled agent's native format.
|
|
177
197
|
|
|
178
198
|
Cursor/Windsurf/Cline get a verbatim file drop in their ``rules/`` dir
|
|
179
199
|
(frontmatter preserved — it is native rule metadata); Claude Code has no
|
|
180
200
|
rules folder, so the rule merges into ``CLAUDE.md`` as an idempotent managed
|
|
181
|
-
block.
|
|
182
|
-
|
|
201
|
+
block. With ``scope="project"`` these land under the repo (and Claude uses
|
|
202
|
+
``CLAUDE.local.md``). Every materialization is recorded so ``uninstall``
|
|
203
|
+
reverses exactly what was written.
|
|
183
204
|
"""
|
|
184
205
|
import hashlib
|
|
185
206
|
|
|
@@ -204,13 +225,14 @@ def _install_rule(entry: dict, force: bool = False,
|
|
|
204
225
|
meta, body = frontmatter.parse(raw)
|
|
205
226
|
claude_body = rules.render_claude_body(str(meta.get("name") or name), body)
|
|
206
227
|
|
|
228
|
+
resolved_base = _resolve_base(scope, base)
|
|
207
229
|
paths.ensure_dirs()
|
|
208
230
|
materializations: List[dict] = []
|
|
209
231
|
linked: List[str] = []
|
|
210
232
|
for agent, skills_dir in agents.enabled_agents().items():
|
|
211
233
|
if only_agents and agent not in only_agents:
|
|
212
234
|
continue
|
|
213
|
-
mode, path = rules.rule_target(agent, skills_dir, name)
|
|
235
|
+
mode, path = rules.rule_target(agent, skills_dir, name, base=resolved_base)
|
|
214
236
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
215
237
|
if mode == rules.MODE_CLAUDE:
|
|
216
238
|
current = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
@@ -228,6 +250,8 @@ def _install_rule(entry: dict, force: bool = False,
|
|
|
228
250
|
"source_file": entry.get("skill_md", ""),
|
|
229
251
|
"commit": gitutil.head_commit(tap.path),
|
|
230
252
|
"sha256": hashlib.sha256(raw.encode("utf-8")).hexdigest(),
|
|
253
|
+
"scope": scope,
|
|
254
|
+
"base": str(resolved_base) if resolved_base is not None else None,
|
|
231
255
|
"installed_at": (existing or {}).get("installed_at", now),
|
|
232
256
|
"updated_at": now,
|
|
233
257
|
"materializations": materializations,
|
|
@@ -240,6 +264,7 @@ def _install_rule(entry: dict, force: bool = False,
|
|
|
240
264
|
kind="rule", scan_text=raw)
|
|
241
265
|
res.linked = linked
|
|
242
266
|
res.upgraded = existing is not None
|
|
267
|
+
res.scope = scope
|
|
243
268
|
return res
|
|
244
269
|
|
|
245
270
|
|
|
@@ -266,12 +291,14 @@ def _uninstall_rule(name: str, rule: dict) -> dict:
|
|
|
266
291
|
|
|
267
292
|
|
|
268
293
|
def _install_workflow(entry: dict, force: bool = False,
|
|
269
|
-
only_agents: Optional[List[str]] = None
|
|
294
|
+
only_agents: Optional[List[str]] = None,
|
|
295
|
+
scope: str = "user", base=None) -> InstallResult:
|
|
270
296
|
"""Materialize a workflow (slash command / subagent) into each enabled agent.
|
|
271
297
|
|
|
272
298
|
A verbatim Markdown drop into the agent's ``commands/`` or ``agents/`` dir —
|
|
273
|
-
the slot derived from the source path — with no transformation.
|
|
274
|
-
|
|
299
|
+
the slot derived from the source path — with no transformation. With
|
|
300
|
+
``scope="project"`` the drop lands under the repo (``<repo>/.claude/…``).
|
|
301
|
+
Every drop is recorded so ``uninstall`` removes exactly the files it wrote.
|
|
275
302
|
"""
|
|
276
303
|
import hashlib
|
|
277
304
|
|
|
@@ -296,13 +323,14 @@ def _install_workflow(entry: dict, force: bool = False,
|
|
|
296
323
|
raw = src.read_text(encoding="utf-8", errors="replace")
|
|
297
324
|
slot = workflows.detect_slot(source_rel)
|
|
298
325
|
|
|
326
|
+
resolved_base = _resolve_base(scope, base)
|
|
299
327
|
paths.ensure_dirs()
|
|
300
328
|
materializations: List[dict] = []
|
|
301
329
|
linked: List[str] = []
|
|
302
330
|
for agent, skills_dir in agents.enabled_agents().items():
|
|
303
331
|
if only_agents and agent not in only_agents:
|
|
304
332
|
continue
|
|
305
|
-
path = workflows.workflow_target(skills_dir, slot, name)
|
|
333
|
+
path = workflows.workflow_target(skills_dir, slot, name, base=resolved_base)
|
|
306
334
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
307
335
|
util.atomic_write_text(path, raw)
|
|
308
336
|
materializations.append({"agent": agent, "slot": slot, "path": str(path)})
|
|
@@ -317,6 +345,8 @@ def _install_workflow(entry: dict, force: bool = False,
|
|
|
317
345
|
"source_file": source_rel,
|
|
318
346
|
"commit": gitutil.head_commit(tap.path),
|
|
319
347
|
"sha256": hashlib.sha256(raw.encode("utf-8")).hexdigest(),
|
|
348
|
+
"scope": scope,
|
|
349
|
+
"base": str(resolved_base) if resolved_base is not None else None,
|
|
320
350
|
"installed_at": (existing or {}).get("installed_at", now),
|
|
321
351
|
"updated_at": now,
|
|
322
352
|
"materializations": materializations,
|
|
@@ -329,6 +359,7 @@ def _install_workflow(entry: dict, force: bool = False,
|
|
|
329
359
|
kind="workflow", scan_text=raw)
|
|
330
360
|
res.linked = linked
|
|
331
361
|
res.upgraded = existing is not None
|
|
362
|
+
res.scope = scope
|
|
332
363
|
return res
|
|
333
364
|
|
|
334
365
|
|
|
@@ -506,7 +537,10 @@ def sync_apply(plan: Dict[str, list]) -> List[str]:
|
|
|
506
537
|
matches = [e for e in catalog.find(name)
|
|
507
538
|
if e["tap"] == tap_name and e.get("kind", "skill") == kind]
|
|
508
539
|
if matches:
|
|
509
|
-
|
|
540
|
+
# preserve the original scope/base so a project rule repairs
|
|
541
|
+
# into its repo, not wherever sync happens to run.
|
|
542
|
+
install(matches[0], force=True,
|
|
543
|
+
scope=entry.get("scope", "user"), base=entry.get("base"))
|
|
510
544
|
actions.append("re-materialized %s %s from %s"
|
|
511
545
|
% (kind, name, tap_name))
|
|
512
546
|
continue
|
|
@@ -14,6 +14,7 @@ the filesystem writes and the lock record so uninstall can reverse them.
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
16
|
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
17
18
|
|
|
18
19
|
SLOT_COMMANDS = "commands"
|
|
19
20
|
SLOT_AGENTS = "agents"
|
|
@@ -36,11 +37,16 @@ def detect_slot(source_rel: str) -> str:
|
|
|
36
37
|
return SLOT_AGENTS if parts & AGENT_DIRS else SLOT_COMMANDS
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
def workflow_target(skills_dir: Path, slot: str, name: str
|
|
40
|
+
def workflow_target(skills_dir: Path, slot: str, name: str,
|
|
41
|
+
base: Optional[Path] = None) -> Path:
|
|
40
42
|
"""Where workflow ``name`` (in ``slot``) materializes for an agent.
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
``base`` selects the scope:
|
|
45
|
+
* ``None`` (user scope) — the parent of the agent's configured skills dir
|
|
46
|
+
(``~/.claude/skills`` -> ``~/.claude/<slot>/<name>.md``).
|
|
47
|
+
* a directory (project scope) — that repo's per-agent dotdir
|
|
48
|
+
(``<base>/.claude/<slot>/<name>.md``).
|
|
45
49
|
"""
|
|
46
|
-
|
|
50
|
+
root = (Path(base) / Path(skills_dir).parent.name) if base is not None \
|
|
51
|
+
else Path(skills_dir).parent
|
|
52
|
+
return root / slot / (name + ".md")
|
|
@@ -141,6 +141,7 @@ docs/roadmap/items/github-pages-deploy-is-broken-every-push.md
|
|
|
141
141
|
docs/roadmap/items/golden-set-statistical-power.md
|
|
142
142
|
docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md
|
|
143
143
|
docs/roadmap/items/html-validation-html-validate.md
|
|
144
|
+
docs/roadmap/items/install-scope-user-or-project.md
|
|
144
145
|
docs/roadmap/items/integrity-verification-boost-verify.md
|
|
145
146
|
docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md
|
|
146
147
|
docs/roadmap/items/layering-guard-import-linter.md
|
|
Binary file
|
|
@@ -11,26 +11,26 @@ Set TypingSpeed 35ms
|
|
|
11
11
|
Set Theme "Dracula"
|
|
12
12
|
|
|
13
13
|
Hide
|
|
14
|
-
Type "export HOME=$(mktemp -d) COLORTERM=truecolor; python3 tests/make_fixture.py /tmp/boost-demo-tap >/dev/null 2>&1; clear"
|
|
14
|
+
Type "export HOME=$(mktemp -d) COLORTERM=truecolor PATH=$PWD:$PATH; python3 tests/make_fixture.py /tmp/boost-demo-tap >/dev/null 2>&1; clear"
|
|
15
15
|
Enter
|
|
16
16
|
Show
|
|
17
17
|
|
|
18
|
-
Type "
|
|
18
|
+
Type "boost tap /tmp/boost-demo-tap"
|
|
19
19
|
Enter
|
|
20
20
|
Sleep 2s
|
|
21
21
|
|
|
22
|
-
Type "
|
|
22
|
+
Type "boost search tdd"
|
|
23
23
|
Enter
|
|
24
24
|
Sleep 2s
|
|
25
25
|
|
|
26
|
-
Type "
|
|
26
|
+
Type "boost install brainstorming"
|
|
27
27
|
Enter
|
|
28
28
|
Sleep 2.5s
|
|
29
29
|
|
|
30
|
-
Type "
|
|
30
|
+
Type "boost list"
|
|
31
31
|
Enter
|
|
32
32
|
Sleep 2s
|
|
33
33
|
|
|
34
|
-
Type "
|
|
34
|
+
Type "boost doctor"
|
|
35
35
|
Enter
|
|
36
36
|
Sleep 4s
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: install-scope-user-or-project
|
|
3
|
+
board: code
|
|
4
|
+
section: internals
|
|
5
|
+
status: shipped
|
|
6
|
+
category: Install engine · UX
|
|
7
|
+
complexity: M
|
|
8
|
+
impact: High
|
|
9
|
+
wow: 3
|
|
10
|
+
note: rules/workflows were user-global only
|
|
11
|
+
order: 27
|
|
12
|
+
owner: loop/install-scope
|
|
13
|
+
pr: 169
|
|
14
|
+
title: <code>boost install --scope user|project</code> for rules/workflows
|
|
15
|
+
---
|
|
16
|
+
Rule and workflow install always materialized into user-global agent config
|
|
17
|
+
(<code>~/.claude/CLAUDE.md</code>, <code>~/.cursor/rules</code>, …), so a
|
|
18
|
+
rule meant for one repo leaked into every session. Add
|
|
19
|
+
<code>--scope project</code>: materialize into the current repo instead —
|
|
20
|
+
<code><repo>/.cursor/rules/</code>, <code><repo>/.claude/commands|agents/</code>,
|
|
21
|
+
and, since Claude reads per-repo memory from the root and has no rules
|
|
22
|
+
folder, <code><repo>/CLAUDE.local.md</code> (the personal, git-ignored
|
|
23
|
+
file). The chosen scope + base dir are recorded in the lock so
|
|
24
|
+
<code>update</code>/<code>sync</code> re-materialize back into the same
|
|
25
|
+
repo rather than wherever they happen to run. Default stays
|
|
26
|
+
<code>--scope user</code> (unchanged behavior). Uninstall already reverses
|
|
27
|
+
by recorded path, so it works for either scope.
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
<div class="stat"><b>16</b><span>Shipped</span></div>
|
|
119
119
|
<div class="stat"><b>1</b><span>Next up</span></div>
|
|
120
120
|
<div class="stat"><b>4</b><span>Planned</span></div>
|
|
121
|
-
<div class="stat"><b>
|
|
121
|
+
<div class="stat"><b>108</b><span>Loop finds</span></div>
|
|
122
122
|
<!-- /ROADMAP:stats -->
|
|
123
123
|
</div>
|
|
124
124
|
</section>
|
|
@@ -840,6 +840,29 @@ chunks instead cost ~70%.</p>
|
|
|
840
840
|
<span class="m">close the last skill-only gaps for rules/workflows</span>
|
|
841
841
|
</div>
|
|
842
842
|
</article>
|
|
843
|
+
|
|
844
|
+
<article class="cap rcard">
|
|
845
|
+
<div class="head"><span class="pill shipped">Shipped</span><span class="cat">Install engine · UX</span></div>
|
|
846
|
+
<h3><code>boost install --scope user|project</code> for rules/workflows</h3>
|
|
847
|
+
<p>Rule and workflow install always materialized into user-global agent config
|
|
848
|
+
(<code>~/.claude/CLAUDE.md</code>, <code>~/.cursor/rules</code>, …), so a
|
|
849
|
+
rule meant for one repo leaked into every session. Add
|
|
850
|
+
<code>--scope project</code>: materialize into the current repo instead —
|
|
851
|
+
<code><repo>/.cursor/rules/</code>, <code><repo>/.claude/commands|agents/</code>,
|
|
852
|
+
and, since Claude reads per-repo memory from the root and has no rules
|
|
853
|
+
folder, <code><repo>/CLAUDE.local.md</code> (the personal, git-ignored
|
|
854
|
+
file). The chosen scope + base dir are recorded in the lock so
|
|
855
|
+
<code>update</code>/<code>sync</code> re-materialize back into the same
|
|
856
|
+
repo rather than wherever they happen to run. Default stays
|
|
857
|
+
<code>--scope user</code> (unchanged behavior). Uninstall already reverses
|
|
858
|
+
by recorded path, so it works for either scope.</p>
|
|
859
|
+
<div class="meta">
|
|
860
|
+
<span class="m">Complexity <b>M</b></span>
|
|
861
|
+
<span class="m hi">Impact <b>High</b></span>
|
|
862
|
+
<span class="m">Wow <b class="wow">★★★</b></span>
|
|
863
|
+
<span class="m">rules/workflows were user-global only</span>
|
|
864
|
+
</div>
|
|
865
|
+
</article>
|
|
843
866
|
<!-- /ROADMAP:cards -->
|
|
844
867
|
|
|
845
868
|
</div>
|
|
@@ -68,6 +68,22 @@ class TestInstall:
|
|
|
68
68
|
assert "suspicious pattern" in r.out
|
|
69
69
|
assert "in rule content" in r.out # not "in SKILL.md"
|
|
70
70
|
|
|
71
|
+
def test_rule_scope_project_lands_in_repo_not_global(self, boost,
|
|
72
|
+
fixture_tap_src,
|
|
73
|
+
tmp_path, monkeypatch):
|
|
74
|
+
tap_dir = _copy_tap(fixture_tap_src, tmp_path / "sp-tap")
|
|
75
|
+
_add_and_commit(tap_dir, "rules/team.mdc",
|
|
76
|
+
"---\nname: team-rules\n---\n\nAlways TDD.\n", "add rule")
|
|
77
|
+
boost("tap", tap_dir)
|
|
78
|
+
repo = tmp_path / "proj"
|
|
79
|
+
repo.mkdir()
|
|
80
|
+
monkeypatch.chdir(repo) # project scope == cwd
|
|
81
|
+
r = boost("install", "team-rules", "--scope", "project")
|
|
82
|
+
assert "materialized (this repo)" in r.out
|
|
83
|
+
assert (repo / "CLAUDE.local.md").is_file() # Claude -> personal repo file
|
|
84
|
+
assert (repo / ".cursor" / "rules" / "team-rules.mdc").is_file()
|
|
85
|
+
assert not (paths.home() / ".claude" / "CLAUDE.md").exists() # not global
|
|
86
|
+
|
|
71
87
|
def test_exact_report_lines(self, boost, tapped):
|
|
72
88
|
r = boost("install", "brainstorming")
|
|
73
89
|
assert "copied to ~/.agents/skills/brainstorming" in r.out
|
|
@@ -43,6 +43,24 @@ class TestRuleTarget:
|
|
|
43
43
|
assert mode == rules.MODE_FILE
|
|
44
44
|
assert path == Path("/h/.zed/rules/r.md")
|
|
45
45
|
|
|
46
|
+
# --- project scope (base=<repo>) ---
|
|
47
|
+
def test_project_claude_uses_claude_local_md_at_repo_root(self):
|
|
48
|
+
mode, path = rules.rule_target("claude-code", Path("/h/.claude/skills"),
|
|
49
|
+
"r", base=Path("/repo"))
|
|
50
|
+
assert mode == rules.MODE_CLAUDE
|
|
51
|
+
assert path == Path("/repo/CLAUDE.local.md") # personal, not ~/.claude
|
|
52
|
+
|
|
53
|
+
def test_project_cursor_uses_repo_dotcursor_rules(self):
|
|
54
|
+
mode, path = rules.rule_target("cursor", Path("/h/.cursor/skills"),
|
|
55
|
+
"r", base=Path("/repo"))
|
|
56
|
+
assert mode == rules.MODE_FILE
|
|
57
|
+
assert path == Path("/repo/.cursor/rules/r.mdc")
|
|
58
|
+
|
|
59
|
+
def test_project_windsurf_uses_repo_dotwindsurf(self):
|
|
60
|
+
_mode, path = rules.rule_target("windsurf", Path("/h/.windsurf/skills"),
|
|
61
|
+
"r", base=Path("/repo"))
|
|
62
|
+
assert path == Path("/repo/.windsurf/rules/r.md")
|
|
63
|
+
|
|
46
64
|
|
|
47
65
|
class TestRenderClaudeBody:
|
|
48
66
|
def test_title_header_then_body(self):
|
|
@@ -700,3 +700,51 @@ class TestSyncMaterializations:
|
|
|
700
700
|
res = store.install(_workflow_entry(tap))
|
|
701
701
|
assert res.scan_text is not None
|
|
702
702
|
assert "Run the release." in res.scan_text
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
class TestInstallScope:
|
|
706
|
+
def test_resolve_base(self):
|
|
707
|
+
from pathlib import Path as P
|
|
708
|
+
assert store._resolve_base("user", None) is None
|
|
709
|
+
assert store._resolve_base("project", None) == P.cwd()
|
|
710
|
+
assert store._resolve_base("project", "/x") == P("/x")
|
|
711
|
+
|
|
712
|
+
def test_rule_project_scope_materializes_under_base(self, tap, tmp_path):
|
|
713
|
+
repo = tmp_path / "repo"
|
|
714
|
+
repo.mkdir()
|
|
715
|
+
res = store.install(_rule_entry(tap), scope="project", base=str(repo))
|
|
716
|
+
assert res.scope == "project"
|
|
717
|
+
# Claude -> personal repo file, not ~/.claude/CLAUDE.md
|
|
718
|
+
cm = repo / "CLAUDE.local.md"
|
|
719
|
+
assert cm.is_file()
|
|
720
|
+
assert "boost:rule:team-conventions start" in cm.read_text()
|
|
721
|
+
assert (repo / ".cursor" / "rules" / "team-conventions.mdc").is_file()
|
|
722
|
+
assert (repo / ".windsurf" / "rules" / "team-conventions.md").is_file()
|
|
723
|
+
assert not (paths.home() / ".claude" / "CLAUDE.md").exists() # not global
|
|
724
|
+
rec = lockfile.get_rule("team-conventions")
|
|
725
|
+
assert rec["scope"] == "project"
|
|
726
|
+
assert rec["base"] == str(repo)
|
|
727
|
+
|
|
728
|
+
def test_workflow_project_scope_under_base(self, tap, tmp_path):
|
|
729
|
+
repo = tmp_path / "repo2"
|
|
730
|
+
repo.mkdir()
|
|
731
|
+
store.install(_workflow_entry(tap), scope="project", base=str(repo))
|
|
732
|
+
assert (repo / ".claude" / "commands" / "ship-it.md").is_file()
|
|
733
|
+
assert not (paths.home() / ".claude" / "commands" / "ship-it.md").exists()
|
|
734
|
+
assert lockfile.get_workflow("ship-it")["base"] == str(repo)
|
|
735
|
+
|
|
736
|
+
def test_user_scope_is_default_and_global(self, tap):
|
|
737
|
+
res = store.install(_rule_entry(tap))
|
|
738
|
+
assert res.scope == "user"
|
|
739
|
+
assert (paths.home() / ".claude" / "CLAUDE.md").is_file()
|
|
740
|
+
rec = lockfile.get_rule("team-conventions")
|
|
741
|
+
assert rec["scope"] == "user" and rec["base"] is None
|
|
742
|
+
|
|
743
|
+
def test_project_uninstall_reverses_repo_files(self, tap, tmp_path):
|
|
744
|
+
repo = tmp_path / "repo3"
|
|
745
|
+
repo.mkdir()
|
|
746
|
+
store.install(_rule_entry(tap), scope="project", base=str(repo))
|
|
747
|
+
store.uninstall("team-conventions")
|
|
748
|
+
assert not (repo / ".cursor" / "rules" / "team-conventions.mdc").exists()
|
|
749
|
+
assert not (repo / "CLAUDE.local.md").exists() # held only our block
|
|
750
|
+
assert lockfile.get_rule("team-conventions") is None
|
|
@@ -47,3 +47,13 @@ class TestWorkflowTarget:
|
|
|
47
47
|
def test_uses_configured_skills_dir_parent(self):
|
|
48
48
|
p = workflows.workflow_target(Path("/opt/cursor/skills"), "commands", "x")
|
|
49
49
|
assert p == Path("/opt/cursor/commands/x.md")
|
|
50
|
+
|
|
51
|
+
def test_project_scope_lands_under_repo_dotdir(self):
|
|
52
|
+
p = workflows.workflow_target(Path("/h/.claude/skills"), "commands",
|
|
53
|
+
"ship", base=Path("/repo"))
|
|
54
|
+
assert p == Path("/repo/.claude/commands/ship.md")
|
|
55
|
+
|
|
56
|
+
def test_project_scope_subagent_under_repo(self):
|
|
57
|
+
p = workflows.workflow_target(Path("/h/.claude/skills"), "agents",
|
|
58
|
+
"rev", base=Path("/repo"))
|
|
59
|
+
assert p == Path("/repo/.claude/agents/rev.md")
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|