boost-skill-cli 1.0.206__tar.gz → 1.0.207__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.
Files changed (382) hide show
  1. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/CONTRIBUTING.md +1 -1
  2. {boost_skill_cli-1.0.206/boost_skill_cli.egg-info → boost_skill_cli-1.0.207}/PKG-INFO +1 -1
  3. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/_version.py +2 -2
  4. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/quality.py +4 -1
  5. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/safety.py +158 -2
  6. boost_skill_cli-1.0.207/boost_cli/core/trustaudit.py +224 -0
  7. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207/boost_skill_cli.egg-info}/PKG-INFO +1 -1
  8. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_skill_cli.egg-info/SOURCES.txt +2 -0
  9. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/commands.html +4 -3
  10. boost_skill_cli-1.0.207/docs/roadmap/items/installed-skill-trust-audit.md +29 -0
  11. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap.html +15 -11
  12. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_quality.py +65 -0
  13. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/smoke.sh +8 -0
  14. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_output.py +30 -0
  15. boost_skill_cli-1.0.207/tests/unit/test_trustaudit.py +369 -0
  16. boost_skill_cli-1.0.206/docs/roadmap/items/installed-skill-trust-audit.md +0 -25
  17. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.gitattributes +0 -0
  18. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.gitignore +0 -0
  19. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.gitleaks.toml +0 -0
  20. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.htmlvalidate.json +0 -0
  21. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.lighthouserc.json +0 -0
  22. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.lycheeignore +0 -0
  23. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.markdownlint-cli2.jsonc +0 -0
  24. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.pre-commit-config.yaml +0 -0
  25. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.stylelintrc.json +0 -0
  26. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.vale/styles/boost/Terminology.yml +0 -0
  27. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.vale/styles/config/vocabularies/boost/accept.txt +0 -0
  28. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/.vale.ini +0 -0
  29. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/CLAUDE.md +0 -0
  30. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/LICENSE +0 -0
  31. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/MANIFEST.in +0 -0
  32. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/Makefile +0 -0
  33. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/README.md +0 -0
  34. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/SECURITY.md +0 -0
  35. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost +0 -0
  36. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/__init__.py +0 -0
  37. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/__main__.py +0 -0
  38. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/cli.py +0 -0
  39. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/cliparse.py +0 -0
  40. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/__init__.py +0 -0
  41. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/_common.py +0 -0
  42. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/bmad.py +0 -0
  43. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/configuration.py +0 -0
  44. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/discovery.py +0 -0
  45. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/hooks.py +0 -0
  46. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/info.py +0 -0
  47. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/intelligence.py +0 -0
  48. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/pkg.py +0 -0
  49. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/run.py +0 -0
  50. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/taps.py +0 -0
  51. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/commands/team.py +0 -0
  52. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/__init__.py +0 -0
  53. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/adapters.py +0 -0
  54. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/agents.py +0 -0
  55. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/ai.py +0 -0
  56. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/capabilities.py +0 -0
  57. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/catalog.py +0 -0
  58. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/claude_settings.py +0 -0
  59. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/config.py +0 -0
  60. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/dense.py +0 -0
  61. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/ed25519.py +0 -0
  62. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/embed.py +0 -0
  63. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/faithfulness.py +0 -0
  64. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/frontmatter.py +0 -0
  65. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/gitutil.py +0 -0
  66. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/imperative.py +0 -0
  67. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/injectscan.py +0 -0
  68. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/integrity.py +0 -0
  69. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/journal.py +0 -0
  70. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/lockfile.py +0 -0
  71. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/logs.py +0 -0
  72. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/mcp.py +0 -0
  73. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/minisign.py +0 -0
  74. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/nethttp.py +0 -0
  75. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/output.py +0 -0
  76. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/paths.py +0 -0
  77. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/policy.py +0 -0
  78. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/projectlock.py +0 -0
  79. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/provenance.py +0 -0
  80. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/rag.py +0 -0
  81. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/registry.py +0 -0
  82. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/resolve.py +0 -0
  83. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/rules.py +0 -0
  84. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/scopes.py +0 -0
  85. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/secretscan.py +0 -0
  86. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/serve.py +0 -0
  87. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/stackprobe.py +0 -0
  88. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/staleness.py +0 -0
  89. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/store.py +0 -0
  90. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/typosquat.py +0 -0
  91. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/updatediff.py +0 -0
  92. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/util.py +0 -0
  93. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/core/workflows.py +0 -0
  94. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/data/registries.json +0 -0
  95. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/errors.py +0 -0
  96. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_cli/spin.py +0 -0
  97. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  98. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  99. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_skill_cli.egg-info/requires.txt +0 -0
  100. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/boost_skill_cli.egg-info/top_level.txt +0 -0
  101. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/.claude/settings.local.json +0 -0
  102. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/DEBUGGING.md +0 -0
  103. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/adapters.html +0 -0
  104. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/demo.gif +0 -0
  105. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/demo.tape +0 -0
  106. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/design-roadmap.html +0 -0
  107. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/eval.html +0 -0
  108. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/index.html +0 -0
  109. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/mcp-hub.html +0 -0
  110. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/rag-architecture.md +0 -0
  111. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D01.md +0 -0
  112. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D02.md +0 -0
  113. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D03.md +0 -0
  114. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D04.md +0 -0
  115. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D05.md +0 -0
  116. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D06.md +0 -0
  117. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D07.md +0 -0
  118. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D08.md +0 -0
  119. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D09.md +0 -0
  120. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D10.md +0 -0
  121. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D11.md +0 -0
  122. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D12.md +0 -0
  123. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D13.md +0 -0
  124. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D14.md +0 -0
  125. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D15.md +0 -0
  126. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D16.md +0 -0
  127. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D17.md +0 -0
  128. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D18.md +0 -0
  129. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D19.md +0 -0
  130. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D20.md +0 -0
  131. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D21.md +0 -0
  132. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D22.md +0 -0
  133. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D23.md +0 -0
  134. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D24.md +0 -0
  135. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D25.md +0 -0
  136. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/BOOST-D26.md +0 -0
  137. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  138. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  139. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  140. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  141. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  142. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  143. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  144. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  145. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  146. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  147. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  148. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  149. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  150. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  151. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  152. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  153. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  154. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  155. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  156. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
  157. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  158. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  159. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  160. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  161. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  162. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  163. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  164. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  165. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  166. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  167. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  168. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  169. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  170. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  171. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  172. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  173. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
  174. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  175. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  176. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  177. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  178. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  179. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  180. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  181. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  182. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  183. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  184. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  185. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  186. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mcp-aware-skills.md +0 -0
  187. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  188. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  189. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  190. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
  191. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  192. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  193. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  194. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  195. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  196. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  197. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  198. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  199. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  200. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  201. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  202. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
  203. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  204. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  205. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
  206. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
  207. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  208. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  209. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  210. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  211. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  212. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  213. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  214. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  215. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  216. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  217. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  218. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  219. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  220. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  221. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  222. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  223. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  224. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  225. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  226. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  227. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  228. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  229. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  230. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  231. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  232. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  233. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  234. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  235. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  236. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  237. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  238. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  239. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  240. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  241. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  242. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  243. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  244. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  245. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  246. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  247. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
  248. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  249. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  250. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  251. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/docs/roadmap/items/workspace-scope-install.md +0 -0
  252. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/eslint.config.mjs +0 -0
  253. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/examples/README.md +0 -0
  254. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/examples/adapt-demo.sh +0 -0
  255. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/examples/boost-run-prototype.sh +0 -0
  256. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/index.html +0 -0
  257. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/noxfile.py +0 -0
  258. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/pyproject.toml +0 -0
  259. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/requirements/lint-tools.txt +0 -0
  260. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/bench_cli.sh +0 -0
  261. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/build_command_reference.py +0 -0
  262. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/build_registries.py +0 -0
  263. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/build_roadmap.py +0 -0
  264. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/check_anchors.py +0 -0
  265. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/ensure_eval_corpus.sh +0 -0
  266. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/eval_explain.py +0 -0
  267. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/eval_recommend.py +0 -0
  268. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/eval_retrieval.py +0 -0
  269. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/eval_stats_summary.py +0 -0
  270. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/import_budget.py +0 -0
  271. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/mutation_gate.py +0 -0
  272. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/scripts/perf_gate.py +0 -0
  273. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/setup.cfg +0 -0
  274. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/style/README.md +0 -0
  275. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/style/boost.css +0 -0
  276. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/style/boost.js +0 -0
  277. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/style/demo.html +0 -0
  278. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/adapt.feature +0 -0
  279. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/browse.feature +0 -0
  280. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/count.feature +0 -0
  281. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/discover.feature +0 -0
  282. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/doctor.feature +0 -0
  283. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/environment.py +0 -0
  284. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/explain.feature +0 -0
  285. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/index.feature +0 -0
  286. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/install.feature +0 -0
  287. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/list.feature +0 -0
  288. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/mcp.feature +0 -0
  289. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/search.feature +0 -0
  290. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/steps/cli_steps.py +0 -0
  291. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/steps/discovery_steps.py +0 -0
  292. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/steps/mcp_steps.py +0 -0
  293. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/bdd/features/steps/quality_steps.py +0 -0
  294. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/conftest.py +0 -0
  295. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/eval/baseline.json +0 -0
  296. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/eval/explain.jsonl +0 -0
  297. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/eval/golden.jsonl +0 -0
  298. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/eval/recommend.jsonl +0 -0
  299. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/eval/taps.txt +0 -0
  300. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_capabilities_policy.py +0 -0
  301. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_adapt.py +0 -0
  302. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_bmad.py +0 -0
  303. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_configuration.py +0 -0
  304. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_discovery.py +0 -0
  305. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_hooks.py +0 -0
  306. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_info.py +0 -0
  307. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_intelligence.py +0 -0
  308. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_pkg.py +0 -0
  309. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_taps.py +0 -0
  310. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_cli_team.py +0 -0
  311. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_everyday_loop.py +0 -0
  312. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_integrity_enforce.py +0 -0
  313. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_property_parsers.py +0 -0
  314. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_run.py +0 -0
  315. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_tap_signing.py +0 -0
  316. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/functional/test_workspace_scope.py +0 -0
  317. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/make_fixture.py +0 -0
  318. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/perf/test_benchmarks.py +0 -0
  319. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_adapters.py +0 -0
  320. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_adapters_multi.py +0 -0
  321. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_adapters_runner.py +0 -0
  322. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_agents.py +0 -0
  323. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_ai.py +0 -0
  324. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_capabilities.py +0 -0
  325. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_catalog.py +0 -0
  326. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_check_anchors.py +0 -0
  327. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_claude_settings.py +0 -0
  328. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_cliparse.py +0 -0
  329. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_command_reference_fresh.py +0 -0
  330. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_config.py +0 -0
  331. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_dense.py +0 -0
  332. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_dense_fallback.py +0 -0
  333. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_docs_pages_linked.py +0 -0
  334. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_docsite_chrome.py +0 -0
  335. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_ed25519.py +0 -0
  336. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_embed.py +0 -0
  337. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_errors_and_cli_table.py +0 -0
  338. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_eval_stats_summary.py +0 -0
  339. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_faithfulness.py +0 -0
  340. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_frontmatter.py +0 -0
  341. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_gitleaks_config.py +0 -0
  342. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_gitutil.py +0 -0
  343. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_imperative.py +0 -0
  344. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_import_budget.py +0 -0
  345. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_injectscan.py +0 -0
  346. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_integrity.py +0 -0
  347. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_journal.py +0 -0
  348. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_lockfile.py +0 -0
  349. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_logs.py +0 -0
  350. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_mcp.py +0 -0
  351. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_minisign.py +0 -0
  352. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_mutation_hardening.py +0 -0
  353. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_nethttp.py +0 -0
  354. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_paths.py +0 -0
  355. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_perf_gate.py +0 -0
  356. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_pkg_confusions.py +0 -0
  357. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_pkg_injection.py +0 -0
  358. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_pkg_secrets.py +0 -0
  359. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_policy.py +0 -0
  360. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_projectlock.py +0 -0
  361. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_provenance.py +0 -0
  362. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_rag.py +0 -0
  363. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_registries_fresh.py +0 -0
  364. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_registry.py +0 -0
  365. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_resolve.py +0 -0
  366. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_roadmap_fresh.py +0 -0
  367. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_rules.py +0 -0
  368. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_scopes.py +0 -0
  369. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_secretscan.py +0 -0
  370. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_serve.py +0 -0
  371. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_spin.py +0 -0
  372. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_stackprobe.py +0 -0
  373. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_staleness.py +0 -0
  374. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_store.py +0 -0
  375. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_token_parity.py +0 -0
  376. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_typosquat.py +0 -0
  377. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_updatediff.py +0 -0
  378. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_util.py +0 -0
  379. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_version.py +0 -0
  380. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/unit/test_workflows.py +0 -0
  381. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/visual/package.json +0 -0
  382. {boost_skill_cli-1.0.206 → boost_skill_cli-1.0.207}/tests/visual/visual_check.mjs +0 -0
@@ -50,7 +50,7 @@ auto-fixes straight back onto the branch.
50
50
  |---|---|
51
51
  | `make test` | unit + functional suites, **≥80% line coverage** |
52
52
  | `make patch-coverage` | changed-line coverage vs `main`, **≥80% of the diff** (PRs) |
53
- | `bash tests/smoke.sh` | 152 end-to-end checks through the real `./boost` shim |
53
+ | `bash tests/smoke.sh` | 170 end-to-end checks through the real `./boost` shim |
54
54
  | `make mutation` | mutmut over `boost_cli/core`, **≥80% mutants killed** |
55
55
  | `ruff check boost_cli tests` | lint, zero findings |
56
56
  | `mypy` | type check, zero errors |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.206
3
+ Version: 1.0.207
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -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.206'
22
- __version_tuple__ = version_tuple = (1, 0, 206)
21
+ __version__ = version = '1.0.207'
22
+ __version_tuple__ = version_tuple = (1, 0, 207)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -848,7 +848,10 @@ def cmd_health(argv):
848
848
  _PROVENANCE_STYLE = {
849
849
  provenance.VERIFIED: "success",
850
850
  provenance.UNTRUSTED: "warn",
851
- provenance.INVALID: "err",
851
+ # "danger", not "err": out.role() looks the name up in output.ROLES, which
852
+ # has no "err" key — so an invalid tap signature raised KeyError on any
853
+ # color terminal. The suite never caught it because conftest sets NO_COLOR.
854
+ provenance.INVALID: "danger",
852
855
  provenance.UNSIGNED: "muted",
853
856
  }
854
857
 
@@ -10,11 +10,13 @@ from __future__ import annotations
10
10
 
11
11
  import json
12
12
  import re
13
+ import time
14
+ from contextlib import suppress
13
15
 
14
16
  from .. import cliparse
15
- from ..core import integrity, journal, lockfile
17
+ from ..core import catalog, frontmatter, gitutil, integrity, journal, lockfile
16
18
  from ..core import output as out
17
- from ..core import policy, store, util
19
+ from ..core import policy, provenance, registry, staleness, store, trustaudit, util
18
20
  from ..errors import BoostError
19
21
  from ._common import _iter_installed, _s
20
22
 
@@ -42,8 +44,14 @@ def cmd_audit(argv):
42
44
  prog="boost audit",
43
45
  description="Check installed skills against a safety blocklist")
44
46
  ap.add_argument("--json", action="store_true")
47
+ ap.add_argument("--skills", action="store_true",
48
+ help="trust/staleness report for installed skills instead "
49
+ "of the content scan")
45
50
  args = ap.parse_args(argv)
46
51
 
52
+ if args.skills:
53
+ return _trust_audit(args.json)
54
+
47
55
  pol = policy.load()
48
56
  installed = _iter_installed()
49
57
  findings: dict = {}
@@ -110,6 +118,154 @@ def cmd_audit(argv):
110
118
  return 1 if counts["HIGH"] or counts["MED"] else 0
111
119
 
112
120
 
121
+ # --- audit --skills: trust & staleness across the whole installed set ------
122
+ #
123
+ # The signals already existed, one per command: `trust` reports tap signing,
124
+ # `outdated` reports drift vs the tap, `deps` shows the conflicts: graph. This
125
+ # gathers all three over the installed set and hands the decision to
126
+ # core/trustaudit.py, which is where every branch of it is unit-tested.
127
+
128
+ _TRUST_ROLE = {trustaudit.HIGH: "danger", trustaudit.MED: "warn",
129
+ trustaudit.LOW: "muted"}
130
+
131
+
132
+ def _tap_age_days(tap):
133
+ """Days since a tap clone's last commit, or ``None`` when unknowable."""
134
+ try:
135
+ proc = gitutil.run(["-C", str(tap.path), "log", "-1", "--format=%ct"],
136
+ check=False)
137
+ except BoostError:
138
+ return None
139
+ if proc.returncode != 0 or not proc.stdout.strip():
140
+ return None
141
+ try:
142
+ committed = int(proc.stdout.strip())
143
+ except ValueError:
144
+ return None
145
+ return max(0, int((time.time() - committed) // 86400))
146
+
147
+
148
+ def _tap_signals(tap_name, cache):
149
+ """``(provenance status, age in days, HEAD commit)`` for a tap, memoized.
150
+
151
+ A tap boost cannot read — unknown name, never cloned — yields
152
+ ``(None, None, "")``, which ``trustaudit`` renders as "signature cannot be
153
+ checked" rather than silently passing.
154
+ """
155
+ if tap_name not in cache:
156
+ try:
157
+ tap = registry.get(tap_name)
158
+ except BoostError:
159
+ cache[tap_name] = (None, None, "")
160
+ else:
161
+ cache[tap_name] = ((provenance.verify_dir(tap.path).status,
162
+ _tap_age_days(tap), gitutil.head_commit(tap.path))
163
+ if tap.is_cloned else (None, None, ""))
164
+ return cache[tap_name]
165
+
166
+
167
+ def _upstream_reason(name, lk, tap_name, head):
168
+ """``staleness.upstream_reason`` for one installed skill, or ``None``.
169
+
170
+ Same signal-gathering order as ``cmd_outdated`` so the two commands can
171
+ never disagree about whether a skill is behind its tap.
172
+ """
173
+ matches = [e for e in catalog.find(name) if e["tap"] == tap_name]
174
+ if not matches:
175
+ return None
176
+ entry = matches[0]
177
+ latest = str(entry.get("version") or "0.0.0")
178
+ installed_v = str(lk.get("version") or "0.0.0")
179
+ src_sha = None
180
+ if not util.semver_gt(latest, installed_v) and head and head != lk.get("commit"):
181
+ try:
182
+ src_sha = util.sha256_dir(store.source_dir_for(entry))
183
+ except BoostError:
184
+ return None # source checkout is gone; `verify` owns that finding
185
+ return staleness.upstream_reason(installed_v, latest, lk.get("commit", ""),
186
+ head, lk.get("sha256", ""), src_sha)
187
+
188
+
189
+ def _installed_conflicts(names):
190
+ """A ``conflicts_of`` callable reading each installed skill's own copy.
191
+
192
+ Reads the store copy rather than the catalog on purpose: the audit is about
193
+ what is *installed*, which can be an older revision than the tap now
194
+ advertises.
195
+ """
196
+ cache: dict = {}
197
+
198
+ def conflicts_of(name):
199
+ if name not in cache:
200
+ meta: dict = {}
201
+ # unreadable store copy: `verify` owns that finding, not this
202
+ with suppress(OSError, BoostError):
203
+ text = (store.skill_store_dir(name) / "SKILL.md").read_text(
204
+ encoding="utf-8", errors="replace")
205
+ meta, _body = frontmatter.parse(text)
206
+ cache[name] = trustaudit.relation_list(meta, "conflicts")
207
+ return cache[name]
208
+
209
+ return conflicts_of
210
+
211
+
212
+ def _trust_audit(as_json: bool) -> int:
213
+ """`boost audit --skills` — one trust-health answer for the installed set."""
214
+ installed = dict(_iter_installed())
215
+ peers: dict = {}
216
+ for name, peer in trustaudit.conflict_pairs(list(installed),
217
+ _installed_conflicts(installed)):
218
+ peers.setdefault(name, []).append(peer)
219
+
220
+ taps: dict = {}
221
+ findings: dict = {}
222
+ for name, lk in sorted(installed.items()):
223
+ tap_name = str(lk.get("tap") or "local")
224
+ is_local = tap_name == "local"
225
+ status, age, head = ((None, None, "") if is_local
226
+ else _tap_signals(tap_name, taps))
227
+ rows = trustaudit.skill_findings(
228
+ is_local=is_local, provenance_status=status, tap_age_days=age,
229
+ upstream_reason=(None if is_local
230
+ else _upstream_reason(name, lk, tap_name, head)),
231
+ conflicts_with=peers.get(name, ()))
232
+ if rows:
233
+ findings[name] = rows
234
+
235
+ counts = trustaudit.count_severities(findings)
236
+ rc = trustaudit.exit_code(counts)
237
+ if as_json:
238
+ print(json.dumps({"skills_scanned": len(installed),
239
+ "findings": findings, "counts": counts}, indent=2))
240
+ return rc
241
+ return _render_trust_audit(installed, findings, counts, rc)
242
+
243
+
244
+ def _render_trust_audit(installed, findings, counts, rc: int) -> int:
245
+ """Human rendering for `boost audit --skills`."""
246
+ total = len(installed)
247
+ out.heading("trust audit — %d skill%s" % (total, _s(total)))
248
+ if not total:
249
+ out.info("nothing installed yet — `boost install <skill>` to start")
250
+ return rc
251
+ if not findings:
252
+ out.ok("all %d installed skill%s signed, current and conflict-free"
253
+ % (total, _s(total)))
254
+ return rc
255
+ for name in sorted(findings):
256
+ print(" " + out.c(name, out.BOLD))
257
+ for f in findings[name]:
258
+ print(" %s %s %s"
259
+ % (out.role(f["severity"].ljust(4), _TRUST_ROLE[f["severity"]]),
260
+ f["label"], out.role(f["detail"], "muted")))
261
+ print()
262
+ out.verdict(trustaudit.is_healthy(counts),
263
+ "%d high · %d medium · %d low across %d of %d skill%s"
264
+ % (counts[trustaudit.HIGH], counts[trustaudit.MED],
265
+ counts[trustaudit.LOW], len(findings), total, _s(total)))
266
+ return rc
267
+
268
+
113
269
  def cmd_verify(argv):
114
270
  ap = cliparse.parser(
115
271
  prog="boost verify",
@@ -0,0 +1,224 @@
1
+ """Single source of truth for "is the set of skills I actually run healthy?".
2
+
3
+ boost already computes every individual trust signal, each in its own command:
4
+ ``verify`` checks lock-file integrity, ``outdated`` compares an installed skill
5
+ against its tap, ``trust`` reports tap-level signing provenance, ``deps`` shows
6
+ the ``requires:``/``conflicts:`` graph. What nothing answered is the aggregate
7
+ question — *of the skills I have installed, which ones should I stop trusting?*
8
+
9
+ These functions are the decision layer for ``boost audit --skills``. They are
10
+ pure and I/O-free on purpose, exactly like :mod:`boost_cli.core.staleness`: the
11
+ command gathers the raw signals (``provenance.verify_dir``, a tap's last-sync
12
+ timestamp, ``staleness.upstream_reason``, ``resolve.resolve``) and passes them
13
+ in, so every branch of the *decision* is trivially testable and stays reachable
14
+ by the mutation gate.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ from typing import Dict, List, Optional, Sequence, Tuple
19
+
20
+ from . import provenance
21
+
22
+ # ── severity vocabulary ────────────────────────────────────────────────────
23
+ # Mirrors cmd_audit's content scan so one `boost audit` mental model covers
24
+ # both halves: HIGH means stop and look, MED means decide, LOW is context.
25
+ HIGH = "HIGH"
26
+ MED = "MED"
27
+ LOW = "LOW"
28
+
29
+ # ── finding labels ─────────────────────────────────────────────────────────
30
+ INVALID_SIGNATURE = "invalid-signature" # signed, but the signature fails crypto
31
+ UNTRUSTED_TAP = "untrusted-tap" # signed by a key you have not trusted
32
+ UNSIGNED_TAP = "unsigned-tap" # the tap publishes no signature at all
33
+ LOCAL_SOURCE = "local-source" # imported from a path; no tap to trust
34
+ STALE_TAP = "stale-tap" # the tap clone has not been synced
35
+ BEHIND_TAP = "behind-tap" # the tap has a newer copy than yours
36
+ CONFLICT = "conflict" # declares conflicts: against a peer
37
+
38
+ # A tap clone older than this many days is reported as stale. Chosen to be
39
+ # quiet: a month of no upstream sync is long enough to be a real signal and
40
+ # long enough that a healthy weekly-ish `boost update` never trips it.
41
+ STALE_TAP_DAYS = 30
42
+
43
+ # label -> severity. A malformed signature is the only HIGH: it means the tap
44
+ # is actively lying about its provenance, as opposed to merely not claiming any.
45
+ SEVERITY = {
46
+ INVALID_SIGNATURE: HIGH,
47
+ UNTRUSTED_TAP: MED,
48
+ CONFLICT: MED,
49
+ UNSIGNED_TAP: LOW,
50
+ LOCAL_SOURCE: LOW,
51
+ STALE_TAP: LOW,
52
+ BEHIND_TAP: LOW,
53
+ }
54
+
55
+ # provenance status -> our label. VERIFIED maps to nothing: a trusted signature
56
+ # is the healthy case and must not produce a finding.
57
+ _PROVENANCE_LABEL = {
58
+ provenance.INVALID: INVALID_SIGNATURE,
59
+ provenance.UNTRUSTED: UNTRUSTED_TAP,
60
+ provenance.UNSIGNED: UNSIGNED_TAP,
61
+ }
62
+
63
+
64
+ def signing_label(status: Optional[str], is_local: bool = False) -> Optional[str]:
65
+ """The trust finding for a tap's signing ``status``, or ``None`` if healthy.
66
+
67
+ ``is_local`` marks a skill imported from a path rather than tapped: there is
68
+ no tap to sign it, so it reports :data:`LOCAL_SOURCE` rather than being
69
+ silently treated as unsigned upstream content. A ``status`` of ``None``
70
+ (tap recorded but its clone is gone) reports as unsigned — boost cannot
71
+ show a signature it cannot read.
72
+ """
73
+ if is_local:
74
+ return LOCAL_SOURCE
75
+ if status == provenance.VERIFIED:
76
+ return None
77
+ if status is None:
78
+ return UNSIGNED_TAP
79
+ return _PROVENANCE_LABEL.get(status, UNSIGNED_TAP)
80
+
81
+
82
+ def stale_tap_label(age_days: Optional[int],
83
+ limit: int = STALE_TAP_DAYS) -> Optional[str]:
84
+ """:data:`STALE_TAP` when a tap clone's last sync is older than ``limit``.
85
+
86
+ ``age_days`` is ``None`` when the age could not be determined (no clone, no
87
+ cache timestamp); that is treated as "no signal", not as stale, so a
88
+ missing timestamp never manufactures a finding. The comparison is strictly
89
+ greater-than, so a tap synced exactly ``limit`` days ago is still fresh.
90
+ """
91
+ if age_days is None:
92
+ return None
93
+ return STALE_TAP if age_days > limit else None
94
+
95
+
96
+ def skill_findings(*, is_local: bool, provenance_status: Optional[str],
97
+ tap_age_days: Optional[int],
98
+ upstream_reason: Optional[str],
99
+ conflicts_with: Sequence[str] = (),
100
+ stale_after: int = STALE_TAP_DAYS) -> List[dict]:
101
+ """Every trust finding for one installed skill, worst severity first.
102
+
103
+ Each finding is ``{"severity", "label", "detail"}``. The caller supplies
104
+ pre-computed signals:
105
+
106
+ * ``is_local`` — imported from a path (no tap).
107
+ * ``provenance_status`` — a :mod:`boost_cli.core.provenance` status for the
108
+ skill's tap clone, or ``None`` when it could not be read.
109
+ * ``tap_age_days`` — days since the tap clone last synced, or ``None``.
110
+ * ``upstream_reason`` — :func:`staleness.upstream_reason`'s verdict.
111
+ * ``conflicts_with`` — peer skills this one declares a ``conflicts:``
112
+ against, already filtered to skills that are actually installed.
113
+
114
+ A locally-imported skill is deliberately not checked for tap staleness or
115
+ upstream drift: it has no upstream, so those axes are not signals about it.
116
+ """
117
+ found: List[dict] = []
118
+ label = signing_label(provenance_status, is_local=is_local)
119
+ if label:
120
+ found.append({"severity": SEVERITY[label], "label": label,
121
+ "detail": _signing_detail(label, provenance_status)})
122
+ if not is_local:
123
+ # The `is not None` guard is what stale_tap_label already encodes, but
124
+ # spelling it here keeps the %d interpolation below provably non-None.
125
+ if tap_age_days is not None and stale_tap_label(tap_age_days, stale_after):
126
+ found.append({"severity": SEVERITY[STALE_TAP], "label": STALE_TAP,
127
+ "detail": "tap last synced %d days ago" % tap_age_days})
128
+ if upstream_reason:
129
+ found.append({"severity": SEVERITY[BEHIND_TAP], "label": BEHIND_TAP,
130
+ "detail": "tap has a newer copy (%s)" % upstream_reason})
131
+ found.extend({"severity": SEVERITY[CONFLICT], "label": CONFLICT,
132
+ "detail": "declares a conflict with %s" % peer}
133
+ for peer in conflicts_with)
134
+ return sort_findings(found)
135
+
136
+
137
+ def _signing_detail(label: str, status: Optional[str]) -> str:
138
+ """Human detail for a signing finding."""
139
+ if label == LOCAL_SOURCE:
140
+ return "imported from a path — no tap signature to check"
141
+ if label == UNSIGNED_TAP and status is None:
142
+ return "tap clone is missing — signature cannot be checked"
143
+ if label == UNSIGNED_TAP:
144
+ return "tap publishes no signature"
145
+ if label == UNTRUSTED_TAP:
146
+ return "signed, but by a key that is not in your trusted set"
147
+ return "signature is present but does not validate"
148
+
149
+
150
+ # Sort order for rendering: severity descending, then label, then detail, so
151
+ # output is deterministic (two runs of the same state print byte-identically).
152
+ _SEVERITY_RANK = {HIGH: 0, MED: 1, LOW: 2}
153
+
154
+
155
+ def sort_findings(findings: Sequence[dict]) -> List[dict]:
156
+ """Findings ordered worst-first, then alphabetically for determinism."""
157
+ return sorted(findings, key=lambda f: (_SEVERITY_RANK[f["severity"]],
158
+ f["label"], f["detail"]))
159
+
160
+
161
+ def count_severities(findings_by_skill: Dict[str, List[dict]]) -> Dict[str, int]:
162
+ """Total findings per severity across every skill."""
163
+ counts = {HIGH: 0, MED: 0, LOW: 0}
164
+ for findings in findings_by_skill.values():
165
+ for finding in findings:
166
+ counts[finding["severity"]] += 1
167
+ return counts
168
+
169
+
170
+ def is_healthy(counts: Dict[str, int]) -> bool:
171
+ """True when nothing needs a decision — no HIGH and no MED findings.
172
+
173
+ LOW findings alone stay healthy: an unsigned tap is the norm for most of
174
+ the catalog today, so treating it as unhealthy would make the command cry
175
+ wolf on a perfectly ordinary install and train users to ignore it.
176
+ """
177
+ return not counts[HIGH] and not counts[MED]
178
+
179
+
180
+ def exit_code(counts: Dict[str, int]) -> int:
181
+ """``0`` when healthy, ``1`` when a HIGH or MED finding needs attention.
182
+
183
+ Matches ``cmd_audit``'s content-scan contract, so ``--skills`` composes in
184
+ a CI pipeline the same way the default scan already does.
185
+ """
186
+ return 0 if is_healthy(counts) else 1
187
+
188
+
189
+ def relation_list(meta: Optional[dict], key: str) -> List[str]:
190
+ """A skill's ``requires:``/``conflicts:`` frontmatter as a clean name list.
191
+
192
+ Accepts a YAML list or a comma-separated string, drops blanks, and treats a
193
+ missing/empty/``False`` value as "no relations" — an author writing
194
+ ``conflicts:`` with nothing after it must not read as a conflict with "".
195
+
196
+ ``pkg._rel_list`` and ``info._as_list`` are older command-layer copies of
197
+ this that disagree on edge cases; folding them onto this one is a
198
+ worthwhile follow-up, but retargeting three call sites is out of scope here.
199
+ """
200
+ val = (meta or {}).get(key)
201
+ if val in (None, "", False):
202
+ return []
203
+ if isinstance(val, list):
204
+ return [str(x).strip() for x in val if str(x).strip()]
205
+ return [s.strip() for s in str(val).split(",") if s.strip()]
206
+
207
+
208
+ def conflict_pairs(installed: Sequence[str],
209
+ conflicts_of) -> List[Tuple[str, str]]:
210
+ """``(skill, peer)`` pairs where both sides are installed.
211
+
212
+ Deliberately independent of :func:`resolve.resolve`: that answers "what
213
+ would break if I installed this", keyed off an install plan, while this
214
+ audits a set that is *already* installed — every member is both a root and
215
+ already present, which ``resolve`` prunes. Self-conflicts and conflicts
216
+ against something not installed are dropped; a mutual conflict is reported
217
+ from both sides, because both skills are equally implicated.
218
+ """
219
+ present = set(installed)
220
+ pairs: List[Tuple[str, str]] = []
221
+ for name in sorted(present):
222
+ pairs.extend((name, peer) for peer in conflicts_of(name)
223
+ if peer and peer != name and peer in present)
224
+ return sorted(set(pairs))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.206
3
+ Version: 1.0.207
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -82,6 +82,7 @@ boost_cli/core/serve.py
82
82
  boost_cli/core/stackprobe.py
83
83
  boost_cli/core/staleness.py
84
84
  boost_cli/core/store.py
85
+ boost_cli/core/trustaudit.py
85
86
  boost_cli/core/typosquat.py
86
87
  boost_cli/core/updatediff.py
87
88
  boost_cli/core/util.py
@@ -369,6 +370,7 @@ tests/unit/test_stackprobe.py
369
370
  tests/unit/test_staleness.py
370
371
  tests/unit/test_store.py
371
372
  tests/unit/test_token_parity.py
373
+ tests/unit/test_trustaudit.py
372
374
  tests/unit/test_typosquat.py
373
375
  tests/unit/test_updatediff.py
374
376
  tests/unit/test_util.py
@@ -161,7 +161,7 @@
161
161
  <div class="group-h">Health &amp; integrity</div>
162
162
  <a class="cmd-link" href="#cmd-doctor" data-search="doctor check installation health &amp; report issues ">doctor</a>
163
163
  <a class="cmd-link" href="#cmd-lint" data-search="lint validate skill.md frontmatter &amp; quality --tap tap --min n --json name">lint</a>
164
- <a class="cmd-link" href="#cmd-audit" data-search="audit check installed skills against a safety blocklist --json">audit</a>
164
+ <a class="cmd-link" href="#cmd-audit" data-search="audit check installed skills against a safety blocklist --json --skills">audit</a>
165
165
  <a class="cmd-link" href="#cmd-verify" data-search="verify validate skill quality &amp; lock-file integrity --json name">verify</a>
166
166
  <a class="cmd-link" href="#cmd-drift" data-search="drift detect installed skills diverging from source --json name">drift</a>
167
167
  <a class="cmd-link" href="#cmd-test" data-search="test validate installed skills against quality checks name">test</a>
@@ -790,13 +790,14 @@
790
790
  <div class="arg"><code>--json</code><span></span></div>
791
791
  </div>
792
792
  </section>
793
- <section class="cmd" id="cmd-audit" data-search="audit check installed skills against a safety blocklist --json">
793
+ <section class="cmd" id="cmd-audit" data-search="audit check installed skills against a safety blocklist --json --skills">
794
794
  <h3><span class="cname">audit</span><span class="gtag">chk</span></h3>
795
795
  <p class="summary">Check installed skills against a safety blocklist</p>
796
- <pre class="syn">boost audit [--json]</pre>
796
+ <pre class="syn">boost audit [--json] [--skills]</pre>
797
797
  <div class="args-h">Options</div>
798
798
  <div class="args">
799
799
  <div class="arg"><code>--json</code><span></span></div>
800
+ <div class="arg"><code>--skills</code><span>trust/staleness report for installed skills instead of the content scan</span></div>
800
801
  </div>
801
802
  </section>
802
803
  <section class="cmd" id="cmd-verify" data-search="verify validate skill quality &amp; lock-file integrity --json name">
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: installed-skill-trust-audit
3
+ board: code
4
+ section: trust
5
+ status: shipped
6
+ category: Trust · Health
7
+ complexity: M
8
+ impact: Med
9
+ wow: 3
10
+ note: audit what you run
11
+ order: 47
12
+ owner: loop/trust-audit
13
+ pr: 233
14
+ title: <code>boost audit --skills</code> — a trust/staleness report for installed skills
15
+ ---
16
+ boost already computes every individual trust signal, each in its own command:
17
+ <code>verify</code> checks lock-file integrity, <code>outdated</code> compares an
18
+ installed skill against its tap, <code>trust</code> reports tap-level signing
19
+ provenance, <code>deps</code> shows the <code>requires:</code>/<code>conflicts:</code>
20
+ graph. What nothing answered is the aggregate question — <em>of the skills I
21
+ actually run, which ones should I stop trusting?</em> <code>boost audit --skills</code>
22
+ gathers all of it into one report: every installed skill that is unsigned, signed
23
+ by an untrusted key, signed but failing verification, sitting on a tap nobody has
24
+ synced in a month, behind its tap, or conflicting with another installed skill.
25
+ The decision layer is <code>core/trustaudit.py</code> — pure and I/O-free like
26
+ <code>core/staleness.py</code>, so every branch is unit-tested and reachable by
27
+ the mutation gate. Only a malformed signature is HIGH; an unsigned tap is the norm
28
+ for most of the catalog today and stays LOW, so the command never cries wolf on an
29
+ ordinary install.
@@ -2300,18 +2300,22 @@ paths.</p>
2300
2300
  </article>
2301
2301
 
2302
2302
  <article class="cap rcard" id="installed-skill-trust-audit">
2303
- <div class="head"><span class="pill planned">Planned</span><span class="cat">Trust · Health</span></div>
2303
+ <div class="head"><span class="pill shipped">Shipped</span><span class="cat">Trust · Health</span></div>
2304
2304
  <h3><code>boost audit --skills</code> — a trust/staleness report for installed skills</h3>
2305
- <p><code>boost audit</code> today gates CVEs in boost's <em>own</em> Python dependencies,
2306
- and <code>boost outdated</code> compares an installed skill's version against
2307
- its tap but nothing gives a single trust-health report for the set of skills
2308
- you actually run. The catalog's large supply-chain / dependency-audit cluster
2309
- is the tell: users want to know what they're trusting. Add
2310
- <code>boost audit --skills</code> — flag each installed skill that is unsigned,
2311
- came from an untrusted or now-stale tap, is behind its tap, or newly conflicts
2312
- with another installed skill. Composes with the provenance work already shipped
2313
- (signing, trusted keys, <code>verify</code>) and the <code>requires:</code>/<code>conflicts:</code>
2314
- graph, turning scattered signals into one "is my installed set healthy?" answer.</p>
2305
+ <p>boost already computes every individual trust signal, each in its own command:
2306
+ <code>verify</code> checks lock-file integrity, <code>outdated</code> compares an
2307
+ installed skill against its tap, <code>trust</code> reports tap-level signing
2308
+ provenance, <code>deps</code> shows the <code>requires:</code>/<code>conflicts:</code>
2309
+ graph. What nothing answered is the aggregate question <em>of the skills I
2310
+ actually run, which ones should I stop trusting?</em> <code>boost audit --skills</code>
2311
+ gathers all of it into one report: every installed skill that is unsigned, signed
2312
+ by an untrusted key, signed but failing verification, sitting on a tap nobody has
2313
+ synced in a month, behind its tap, or conflicting with another installed skill.
2314
+ The decision layer is <code>core/trustaudit.py</code> pure and I/O-free like
2315
+ <code>core/staleness.py</code>, so every branch is unit-tested and reachable by
2316
+ the mutation gate. Only a malformed signature is HIGH; an unsigned tap is the norm
2317
+ for most of the catalog today and stays LOW, so the command never cries wolf on an
2318
+ ordinary install.</p>
2315
2319
  <div class="meta">
2316
2320
  <span class="m">Complexity <b>M</b></span>
2317
2321
  <span class="m">Impact <b>Med</b></span>
@@ -217,6 +217,71 @@ class TestAudit:
217
217
  assert all(f["severity"] == "HIGH"
218
218
  for f in data["findings"]["danger-skill"])
219
219
 
220
+ def test_skills_flag_reports_the_unsigned_fixture_tap(self, boost, installed):
221
+ # the fixture tap publishes no signature: one LOW finding, still rc 0 —
222
+ # LOW alone must not fail, or the command cries wolf on every install.
223
+ r = boost("audit", "--skills")
224
+ assert "trust audit — 1 skill" in r.out
225
+ assert "brainstorming" in r.out
226
+ assert "LOW" in r.out
227
+ assert "unsigned-tap" in r.out
228
+ assert "tap publishes no signature" in r.out
229
+ assert "0 high · 0 medium · 1 low across 1 of 1 skill" in r.out
230
+
231
+ def test_skills_json_shape(self, boost, installed):
232
+ data = json.loads(boost("audit", "--skills", "--json").out)
233
+ assert data["skills_scanned"] == 1
234
+ assert data["counts"] == {"HIGH": 0, "MED": 0, "LOW": 1}
235
+ assert [f["label"] for f in data["findings"]["brainstorming"]] \
236
+ == ["unsigned-tap"]
237
+
238
+ def test_skills_nothing_installed(self, boost, sandbox):
239
+ r = boost("audit", "--skills")
240
+ assert "nothing installed yet" in r.out
241
+
242
+ def test_skills_local_import_reports_local_source(self, boost, tmp_path):
243
+ _import_skill(boost, tmp_path, "local-skill", "# Local\n\nNothing here.\n")
244
+ data = json.loads(boost("audit", "--skills", "--json").out)
245
+ assert [f["label"] for f in data["findings"]["local-skill"]] \
246
+ == ["local-source"]
247
+ assert data["counts"]["LOW"] == 1
248
+
249
+ def test_skills_conflict_between_installed_is_medium_rc1(self, boost, tmp_path):
250
+ _import_skill(boost, tmp_path, "alpha", "# Alpha\n",
251
+ extra_fm="conflicts: beta\n")
252
+ _import_skill(boost, tmp_path, "beta", "# Beta\n")
253
+ r = boost("audit", "--skills", expect=1)
254
+ assert "conflict" in r.out
255
+ assert "declares a conflict with beta" in r.out
256
+ assert "1 high · 0 medium" not in r.out # a conflict is MED, not HIGH
257
+
258
+ data = json.loads(boost("audit", "--skills", "--json", expect=1).out)
259
+ assert data["counts"]["MED"] == 1
260
+ labels = [f["label"] for f in data["findings"]["alpha"]]
261
+ assert "conflict" in labels
262
+ # beta never declared anything, so it gets no conflict finding
263
+ assert "conflict" not in [f["label"] for f in data["findings"]["beta"]]
264
+
265
+ def test_skills_conflict_against_uninstalled_peer_is_silent(self, boost, tmp_path):
266
+ _import_skill(boost, tmp_path, "alpha", "# Alpha\n",
267
+ extra_fm="conflicts: never-installed\n")
268
+ data = json.loads(boost("audit", "--skills", "--json").out)
269
+ assert [f["label"] for f in data["findings"]["alpha"]] == ["local-source"]
270
+
271
+ def test_skills_behind_tap(self, boost, fixture_tap_src, tmp_path):
272
+ tap_dir = _copy_tap(fixture_tap_src, tmp_path / "bumped-tap")
273
+ boost("tap", tap_dir)
274
+ boost("install", "brainstorming")
275
+ _bump(tap_dir, "brainstorming", "1.4.0", "1.5.0")
276
+ boost("update", "--taps-only")
277
+
278
+ data = json.loads(boost("audit", "--skills", "--json").out)
279
+ labels = [f["label"] for f in data["findings"]["brainstorming"]]
280
+ assert "behind-tap" in labels
281
+ detail = next(f["detail"] for f in data["findings"]["brainstorming"]
282
+ if f["label"] == "behind-tap")
283
+ assert detail == "tap has a newer copy (version)"
284
+
220
285
  def test_blocked_skills_policy_hit(self, boost, installed):
221
286
  (paths.state_dir() / "policy.json").write_text(
222
287
  json.dumps({"blocked_skills": ["brainstorming"]}), encoding="utf-8")