boost-skill-cli 1.0.149__tar.gz → 1.0.150__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 (315) hide show
  1. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/_version.py +2 -2
  3. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/discovery.py +170 -42
  4. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/PKG-INFO +1 -1
  5. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_discovery.py +142 -44
  6. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/.gitattributes +0 -0
  7. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/.gitignore +0 -0
  8. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/CLAUDE.md +0 -0
  9. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/CONTRIBUTING.md +0 -0
  10. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/LICENSE +0 -0
  11. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/MANIFEST.in +0 -0
  12. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/Makefile +0 -0
  13. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/README.md +0 -0
  14. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/SECURITY.md +0 -0
  15. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost +0 -0
  16. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/__init__.py +0 -0
  17. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/__main__.py +0 -0
  18. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/cli.py +0 -0
  19. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/cliparse.py +0 -0
  20. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/__init__.py +0 -0
  21. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/bmad.py +0 -0
  22. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/configuration.py +0 -0
  23. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/hooks.py +0 -0
  24. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/info.py +0 -0
  25. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/intelligence.py +0 -0
  26. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/pkg.py +0 -0
  27. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/quality.py +0 -0
  28. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/taps.py +0 -0
  29. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/commands/team.py +0 -0
  30. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/__init__.py +0 -0
  31. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/adapters.py +0 -0
  32. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/agents.py +0 -0
  33. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/ai.py +0 -0
  34. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/catalog.py +0 -0
  35. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/claude_settings.py +0 -0
  36. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/config.py +0 -0
  37. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/dense.py +0 -0
  38. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/embed.py +0 -0
  39. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/frontmatter.py +0 -0
  40. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/gitutil.py +0 -0
  41. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/imperative.py +0 -0
  42. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/injectscan.py +0 -0
  43. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/journal.py +0 -0
  44. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/lockfile.py +0 -0
  45. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/logs.py +0 -0
  46. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/mcp.py +0 -0
  47. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/nethttp.py +0 -0
  48. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/output.py +0 -0
  49. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/paths.py +0 -0
  50. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/policy.py +0 -0
  51. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/rag.py +0 -0
  52. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/registry.py +0 -0
  53. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/rules.py +0 -0
  54. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/secretscan.py +0 -0
  55. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/serve.py +0 -0
  56. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/stackprobe.py +0 -0
  57. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/staleness.py +0 -0
  58. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/store.py +0 -0
  59. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/typosquat.py +0 -0
  60. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/updatediff.py +0 -0
  61. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/util.py +0 -0
  62. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/core/workflows.py +0 -0
  63. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/data/registries.json +0 -0
  64. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/errors.py +0 -0
  65. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_cli/spin.py +0 -0
  66. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/SOURCES.txt +0 -0
  67. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  68. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  69. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/requires.txt +0 -0
  70. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/top_level.txt +0 -0
  71. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/.claude/settings.local.json +0 -0
  72. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/DEBUGGING.md +0 -0
  73. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/adapters.html +0 -0
  74. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/demo.gif +0 -0
  75. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/demo.tape +0 -0
  76. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/design-roadmap.html +0 -0
  77. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/eval.html +0 -0
  78. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/index.html +0 -0
  79. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/mcp-hub.html +0 -0
  80. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/rag-architecture.md +0 -0
  81. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D01.md +0 -0
  82. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D02.md +0 -0
  83. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D03.md +0 -0
  84. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D04.md +0 -0
  85. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D05.md +0 -0
  86. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D06.md +0 -0
  87. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D07.md +0 -0
  88. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D08.md +0 -0
  89. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D09.md +0 -0
  90. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D10.md +0 -0
  91. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D11.md +0 -0
  92. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D12.md +0 -0
  93. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D13.md +0 -0
  94. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D14.md +0 -0
  95. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D15.md +0 -0
  96. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D16.md +0 -0
  97. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D17.md +0 -0
  98. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D18.md +0 -0
  99. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D19.md +0 -0
  100. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D20.md +0 -0
  101. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D21.md +0 -0
  102. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D22.md +0 -0
  103. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D23.md +0 -0
  104. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D24.md +0 -0
  105. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D25.md +0 -0
  106. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D26.md +0 -0
  107. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  108. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  109. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  110. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  111. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  112. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  113. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  114. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  115. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  116. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  117. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  118. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  119. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  120. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  121. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  122. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  123. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  124. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  125. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  126. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  127. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  128. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  129. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  130. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  131. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  132. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  133. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  134. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  135. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  136. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  137. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  138. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  139. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  140. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  141. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  142. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  143. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  144. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  145. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  146. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  147. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  148. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  149. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  150. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  151. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  152. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  153. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  154. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  155. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  156. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  157. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  158. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  159. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  160. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  161. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  162. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  163. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  164. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  165. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  166. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  167. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  168. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  169. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  170. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  171. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  172. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  173. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  174. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  175. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  176. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  177. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  178. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  179. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  180. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  181. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  182. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  183. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  184. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  185. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  186. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  187. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  188. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  189. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  190. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  191. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  192. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  193. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  194. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  195. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  196. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  197. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  198. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  199. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  200. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  201. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  202. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  203. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  204. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  205. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  206. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  207. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  208. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  209. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  210. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
  211. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  212. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  213. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  214. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap/items/workspace-scope-install.md +0 -0
  215. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/docs/roadmap.html +0 -0
  216. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/examples/README.md +0 -0
  217. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/examples/adapt-demo.sh +0 -0
  218. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/examples/boost-run-prototype.sh +0 -0
  219. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/index.html +0 -0
  220. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/pyproject.toml +0 -0
  221. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/bench_cli.sh +0 -0
  222. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/build_registries.py +0 -0
  223. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/build_roadmap.py +0 -0
  224. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/ensure_eval_corpus.sh +0 -0
  225. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/eval_explain.py +0 -0
  226. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/eval_recommend.py +0 -0
  227. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/eval_retrieval.py +0 -0
  228. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/scripts/mutation_gate.py +0 -0
  229. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/setup.cfg +0 -0
  230. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/style/README.md +0 -0
  231. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/style/boost.css +0 -0
  232. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/style/boost.js +0 -0
  233. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/style/demo.html +0 -0
  234. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/adapt.feature +0 -0
  235. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/browse.feature +0 -0
  236. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/count.feature +0 -0
  237. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/discover.feature +0 -0
  238. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/doctor.feature +0 -0
  239. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/environment.py +0 -0
  240. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/explain.feature +0 -0
  241. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/index.feature +0 -0
  242. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/install.feature +0 -0
  243. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/list.feature +0 -0
  244. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/mcp.feature +0 -0
  245. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/search.feature +0 -0
  246. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/steps/cli_steps.py +0 -0
  247. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/steps/discovery_steps.py +0 -0
  248. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/steps/mcp_steps.py +0 -0
  249. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/bdd/features/steps/quality_steps.py +0 -0
  250. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/conftest.py +0 -0
  251. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/eval/baseline.json +0 -0
  252. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/eval/explain.jsonl +0 -0
  253. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/eval/golden.jsonl +0 -0
  254. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/eval/recommend.jsonl +0 -0
  255. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/eval/taps.txt +0 -0
  256. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_adapt.py +0 -0
  257. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_bmad.py +0 -0
  258. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_configuration.py +0 -0
  259. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_hooks.py +0 -0
  260. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_info.py +0 -0
  261. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_intelligence.py +0 -0
  262. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_pkg.py +0 -0
  263. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_quality.py +0 -0
  264. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_taps.py +0 -0
  265. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_cli_team.py +0 -0
  266. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_everyday_loop.py +0 -0
  267. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/functional/test_property_parsers.py +0 -0
  268. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/make_fixture.py +0 -0
  269. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/perf/test_benchmarks.py +0 -0
  270. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/smoke.sh +0 -0
  271. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_adapters.py +0 -0
  272. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_agents.py +0 -0
  273. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_ai.py +0 -0
  274. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_catalog.py +0 -0
  275. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_claude_settings.py +0 -0
  276. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_cliparse.py +0 -0
  277. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_config.py +0 -0
  278. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_dense.py +0 -0
  279. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_dense_fallback.py +0 -0
  280. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_docs_pages_linked.py +0 -0
  281. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_embed.py +0 -0
  282. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_errors_and_cli_table.py +0 -0
  283. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_frontmatter.py +0 -0
  284. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_gitutil.py +0 -0
  285. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_imperative.py +0 -0
  286. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_injectscan.py +0 -0
  287. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_journal.py +0 -0
  288. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_lockfile.py +0 -0
  289. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_logs.py +0 -0
  290. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_mcp.py +0 -0
  291. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_mutation_hardening.py +0 -0
  292. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_nethttp.py +0 -0
  293. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_output.py +0 -0
  294. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_paths.py +0 -0
  295. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_pkg_confusions.py +0 -0
  296. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_pkg_injection.py +0 -0
  297. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_pkg_secrets.py +0 -0
  298. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_policy.py +0 -0
  299. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_rag.py +0 -0
  300. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_registries_fresh.py +0 -0
  301. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_registry.py +0 -0
  302. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_roadmap_fresh.py +0 -0
  303. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_rules.py +0 -0
  304. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_secretscan.py +0 -0
  305. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_serve.py +0 -0
  306. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_spin.py +0 -0
  307. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_stackprobe.py +0 -0
  308. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_staleness.py +0 -0
  309. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_store.py +0 -0
  310. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_token_parity.py +0 -0
  311. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_typosquat.py +0 -0
  312. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_updatediff.py +0 -0
  313. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_util.py +0 -0
  314. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_version.py +0 -0
  315. {boost_skill_cli-1.0.149 → boost_skill_cli-1.0.150}/tests/unit/test_workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.149
3
+ Version: 1.0.150
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.149'
22
- __version_tuple__ = version_tuple = (1, 0, 149)
21
+ __version__ = version = '1.0.150'
22
+ __version_tuple__ = version_tuple = (1, 0, 150)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -11,12 +11,14 @@ import re
11
11
  import shutil
12
12
  import subprocess
13
13
  import sys
14
+ import threading
14
15
  import time
15
16
  import urllib.parse
16
17
  from pathlib import Path
17
18
 
18
19
  from .. import cliparse, spin
19
- from ..core import agents, ai, catalog, dense, embed, gitutil, journal, lockfile, paths, rag, registry, store, util
20
+ from ..core import (agents, ai, catalog, config, dense, embed, gitutil,
21
+ journal, lockfile, paths, rag, registry, store, util)
20
22
  from ..core import output as out
21
23
  from ..core.stackprobe import detect_stack # re-exported: shared with Quality
22
24
  from ..errors import BoostError
@@ -451,6 +453,77 @@ def _browse_plain(entries, why: str):
451
453
  return 0
452
454
 
453
455
 
456
+ def _tap_categories() -> dict:
457
+ """tap name ('owner/repo') -> curated category, from the bundled registry
458
+ catalog (data/registries.json) — the only place "category" lives; catalog
459
+ entries themselves carry no category, only their tap does."""
460
+ return {e["name"]: e["category"] for e in config.load_registry_catalog()
461
+ if e.get("category")}
462
+
463
+
464
+ _KIND_BADGE_KEYS = {"skill": "badge_skill", "rule": "badge_rule",
465
+ "workflow": "badge_workflow"}
466
+
467
+
468
+ def _kind_theme_key(kind: str) -> str:
469
+ return _KIND_BADGE_KEYS.get(kind, "badge_skill")
470
+
471
+
472
+ def _row_badges(e: dict, categories: dict):
473
+ """Ordered (text, theme-key) badges for a browse row: kind, version, tap,
474
+ and — when the tap is one of the curated registries — its category.
475
+ Most-important-first, so a narrow terminal drops from the tail without
476
+ losing the essentials (kind and version survive; category goes first)."""
477
+ badges = [("[%s]" % e.get("kind", "skill"),
478
+ _kind_theme_key(e.get("kind", "skill"))),
479
+ ("v" + e["version"], "version"),
480
+ ("[%s]" % e["tap"], "tap")]
481
+ category = categories.get(e["tap"])
482
+ if category:
483
+ badges.append(("[%s]" % category, "badge_category"))
484
+ return badges
485
+
486
+
487
+ def _desc_key(e: dict):
488
+ return (e.get("tap", ""), e["name"])
489
+
490
+
491
+ def _entry_source_path(e: dict):
492
+ """On-disk path of the file backing a catalog entry, or None when its tap
493
+ isn't cloned/known — used to fall back to a `boost explain`-style summary
494
+ for entries whose frontmatter has no description."""
495
+ try:
496
+ tap = registry.get(e["tap"])
497
+ except BoostError:
498
+ return None
499
+ path = tap.path / e.get("skill_md", "")
500
+ return path if path.is_file() else None
501
+
502
+
503
+ def _lazy_description(e: dict) -> str:
504
+ """A one-line fallback description for an entry with no frontmatter
505
+ description — the `boost explain` idea, sized for a picker row. Meant to
506
+ run off the UI thread since it may shell out to `claude`/the Anthropic API."""
507
+ path = _entry_source_path(e)
508
+ text = ""
509
+ if path is not None:
510
+ try:
511
+ text = path.read_text(encoding="utf-8", errors="replace")
512
+ except OSError:
513
+ text = ""
514
+ if text and ai.available():
515
+ reply = ai.ask(
516
+ "In ONE short plain sentence (max 18 words, no markdown), describe "
517
+ "what this %s named %r does, based on its file:\n\n%s"
518
+ % (e.get("kind", "skill"), e["name"], text[:4000]),
519
+ system="You summarize AI coding skills, rules, and workflows in "
520
+ "one plain sentence for a picker UI.",
521
+ max_tokens=60)
522
+ if reply:
523
+ return reply.strip().splitlines()[0][:200]
524
+ return "no description available"
525
+
526
+
454
527
  # --- Aurora palette for the curses browser -----------------------------------
455
528
  # The full-screen browser paints itself in the same Aurora tokens as the rest
456
529
  # of the CLI. curses can't reuse the ANSI escapes from core.output, so these
@@ -561,7 +634,10 @@ def _aurora_theme(curses):
561
634
  "scroll": curses.A_DIM, "detail_name": curses.A_BOLD,
562
635
  "detail_meta": curses.A_DIM, "detail_tag": curses.A_NORMAL,
563
636
  "dot_r": curses.A_NORMAL, "dot_y": curses.A_NORMAL,
564
- "dot_g": curses.A_NORMAL}
637
+ "dot_g": curses.A_NORMAL, "check": curses.A_BOLD,
638
+ "badge_skill": curses.A_NORMAL, "badge_rule": curses.A_NORMAL,
639
+ "badge_workflow": curses.A_NORMAL, "badge_category": curses.A_NORMAL,
640
+ "desc": curses.A_DIM}
565
641
  if not hasattr(curses, "start_color"):
566
642
  return t
567
643
  try:
@@ -581,19 +657,49 @@ def _aurora_theme(curses):
581
657
  "tap": P("violet"), "rule": [P("cyan"), P("violet"), P("pink")],
582
658
  "scroll": P("cyan"), "detail_name": P("cyan", True),
583
659
  "detail_tag": P("pink"), "dot_r": P("red"), "dot_y": P("yellow"),
584
- "dot_g": P("green")})
660
+ "dot_g": P("green"), "check": P("green", True),
661
+ "badge_skill": P("cyan"), "badge_rule": P("violet"),
662
+ "badge_workflow": P("pink"), "badge_category": P("yellow")})
585
663
  return t
586
664
 
587
665
 
666
+ _NAME_X = 4 # column the name (and everything under it) starts at
667
+
668
+
588
669
  def _browse_tui(curses, entries):
589
- """Run the curses UI. Returns the entry picked for install, or None."""
590
- state = {"pick": None}
670
+ """Run the curses UI. Returns the list of entries picked for install
671
+ (one or more, via multi-select), or None if the user quit without picking.
672
+ """
673
+ state = {"picks": None}
674
+ categories = _tap_categories()
675
+ desc_cache: dict = {}
676
+ loading: set = set()
677
+
678
+ def describe(e):
679
+ """Description text for a row, kicking off a background lazy-load
680
+ (the `boost explain` fallback) the first time an entry with no
681
+ frontmatter description is shown — never blocks the draw loop."""
682
+ if e.get("description"):
683
+ return e["description"]
684
+ key = _desc_key(e)
685
+ if key in desc_cache:
686
+ return desc_cache[key]
687
+ if key not in loading:
688
+ loading.add(key)
689
+
690
+ def worker(entry=e, cache_key=key):
691
+ desc_cache[cache_key] = _lazy_description(entry)
692
+ loading.discard(cache_key)
693
+
694
+ threading.Thread(target=worker, daemon=True).start()
695
+ return "loading description…"
591
696
 
592
697
  def ui(scr):
593
698
  try:
594
699
  curses.curs_set(0)
595
700
  except curses.error:
596
701
  pass
702
+ scr.timeout(80) # short poll so a finished lazy-load redraws promptly
597
703
  th = _aurora_theme(curses)
598
704
 
599
705
  def put(y, x, s, attr=0): # clip-safe cell writer
@@ -604,6 +710,7 @@ def _browse_tui(curses, entries):
604
710
  pass
605
711
 
606
712
  filt, sel, detail = "", 0, False
713
+ selected: set = set()
607
714
  while True:
608
715
  q = filt.lower()
609
716
  matches = [e for e in entries
@@ -627,35 +734,46 @@ def _browse_tui(curses, entries):
627
734
  put(1, 0, "❯", th["prompt"])
628
735
  put(1, 2, filt, th["query"])
629
736
  put(1, 2 + len(filt), "▏", th["prompt"])
630
- count = "%d/%d" % (len(matches), len(entries))
737
+ sel_txt = "%d selected · " % len(selected) if selected else ""
738
+ count = "%s%d/%d" % (sel_txt, len(matches), len(entries))
631
739
  put(1, max(2 + len(filt) + 2, _w - 1 - len(count)), count,
632
740
  th["count"])
633
- put(2, 0, "↑↓ move ↵ detail i install q quit",
741
+ put(2, 0, "↑↓ move space select ↵ detail i install q quit",
634
742
  th["help"])
635
- rows = max(1, _h - 3 - pane)
743
+ # each result is a two-row card: name/badges, then description
744
+ avail = max(0, _h - 3 - pane)
745
+ rows = max(1, avail // 2)
636
746
  top = max(0, sel - rows + 1)
637
747
  view = matches[top:top + rows]
638
748
  namew = min(max((len(e["name"]) for e in view), default=4),
639
- max(4, _w - 26))
749
+ max(8, _w - _NAME_X - 30))
640
750
  bar = _scrollbar(len(matches), rows, top)
641
751
  for i, e in enumerate(view):
642
- y = 3 + i
752
+ y = 3 + i * 2
643
753
  chosen = top + i == sel
754
+ checked = _desc_key(e) in selected
644
755
  put(y, 0, "▎" if chosen else " ", th["sel_bar"])
645
- put(y, 1, "" if e.get("curated") else " ", th["star"])
756
+ put(y, 1, "" if checked else " ", th["check"])
757
+ put(y, 2, "★" if e.get("curated") else " ", th["star"])
646
758
  nm = out.truncate(e["name"], namew)
647
- put(y, 3, nm.ljust(namew),
759
+ put(y, _NAME_X, nm.ljust(namew),
648
760
  th["sel_name"] if chosen else th["name"])
649
761
  for pos in _match_positions(q, nm.lower()):
650
- put(y, 3 + pos, nm[pos], th["match"])
651
- vx = 3 + namew + 2
652
- put(y, vx, "v" + e["version"], th["version"])
653
- put(y, vx + len(e["version"]) + 3, e["tap"], th["tap"])
762
+ put(y, _NAME_X + pos, nm[pos], th["match"])
763
+ bx = _NAME_X + namew + 2
764
+ for text, tkey in _row_badges(e, categories):
765
+ if bx + len(text) > _w - 2:
766
+ break
767
+ put(y, bx, text, th[tkey])
768
+ bx += len(text) + 1
769
+ put(y + 1, _NAME_X, out.truncate(describe(e), _w - 1 - _NAME_X),
770
+ th["desc"])
654
771
  if bar:
655
772
  bstart, blen = bar
656
- put(y, _w - 2,
657
- "█" if bstart <= i < bstart + blen else "│",
658
- th["scroll"])
773
+ thumb = bstart <= i < bstart + blen
774
+ ch = "█" if thumb else "│"
775
+ put(y, _w - 2, ch, th["scroll"])
776
+ put(y + 1, _w - 2, ch, th["scroll"])
659
777
  if pane and matches:
660
778
  e = matches[sel]
661
779
  y0 = _h - pane
@@ -666,7 +784,7 @@ def _browse_tui(curses, entries):
666
784
  (e.get("meta", {}).get("tags") or []))
667
785
  put(y0 + 1, 1, "%s v%s" % (e["name"], e["version"]),
668
786
  th["detail_name"])
669
- put(y0 + 2, 1, out.truncate(e["description"] or "", _w - 3),
787
+ put(y0 + 2, 1, out.truncate(describe(e), _w - 3),
670
788
  th["name"])
671
789
  put(y0 + 3, 1, "tap %s dir %s" % (e["tap"], e["rel_dir"]),
672
790
  th["detail_meta"])
@@ -675,12 +793,21 @@ def _browse_tui(curses, entries):
675
793
  except curses.error:
676
794
  pass # terminal too small mid-draw; retry on next key
677
795
  key = scr.getch()
796
+ if key == -1: # poll tick, no key
797
+ continue
678
798
  if key in (ord("q"), 27): # q / ESC
679
799
  return
680
800
  if key == ord("i") and matches:
681
- state["pick"] = matches[sel]
801
+ state["picks"] = ([e for e in entries if _desc_key(e) in selected]
802
+ if selected else [matches[sel]])
682
803
  return
683
- if key == curses.KEY_UP:
804
+ if key == ord(" ") and matches:
805
+ k = _desc_key(matches[sel])
806
+ if k in selected:
807
+ selected.discard(k)
808
+ else:
809
+ selected.add(k)
810
+ elif key == curses.KEY_UP:
684
811
  sel = max(0, sel - 1)
685
812
  elif key == curses.KEY_DOWN:
686
813
  sel = min(sel + 1, max(0, len(matches) - 1))
@@ -688,11 +815,11 @@ def _browse_tui(curses, entries):
688
815
  detail = not detail
689
816
  elif key in (curses.KEY_BACKSPACE, 127, 8):
690
817
  filt = filt[:-1]
691
- elif 32 <= key <= 126:
818
+ elif 33 <= key <= 126: # printable, not space
692
819
  filt += chr(key)
693
820
 
694
821
  curses.wrapper(ui) # wrapper guards drawing with try/finally endwin()
695
- return state["pick"]
822
+ return state["picks"]
696
823
 
697
824
 
698
825
  def cmd_browse(argv):
@@ -712,25 +839,26 @@ def cmd_browse(argv):
712
839
  except ImportError:
713
840
  return _browse_plain(entries, "curses is unavailable on this Python")
714
841
  picked = _browse_tui(curses, entries)
715
- if picked is None:
842
+ if not picked:
716
843
  return 0
717
- try:
718
- res = store.install(picked)
719
- except BoostError as e:
720
- # browse lists every catalog kind (skills, rules, workflows), but only
721
- # skills install; a rule/workflow pick or an already-installed/pinned
722
- # skill must not exit the interactive TUI with a fatal error.
723
- out.warn(e.message)
724
- if e.hint:
725
- out.info(out.c(e.hint, out.DIM))
726
- return 0
727
- out.ok("installed %s v%s → %s"
728
- % (picked["name"], picked["version"], _tilde(res.dest)))
729
- if res.linked:
730
- out.info("linked into: "
731
- + ", ".join(agents.display_name(a) for a in res.linked))
732
- for conflict in res.conflicts:
733
- out.warn("conflict: %s exists and is not a symlink" % _tilde(conflict))
844
+ for entry in picked:
845
+ try:
846
+ res = store.install(entry)
847
+ except BoostError as e:
848
+ # browse lists every catalog kind (skills, rules, workflows), but
849
+ # only skills install; a rule/workflow pick or an already-
850
+ # installed/pinned skill — must not abort the rest of the batch.
851
+ out.warn(e.message)
852
+ if e.hint:
853
+ out.info(out.c(e.hint, out.DIM))
854
+ continue
855
+ out.ok("installed %s v%s → %s"
856
+ % (entry["name"], entry["version"], _tilde(res.dest)))
857
+ if res.linked:
858
+ out.info("linked into: "
859
+ + ", ".join(agents.display_name(a) for a in res.linked))
860
+ for conflict in res.conflicts:
861
+ out.warn("conflict: %s exists and is not a symlink" % _tilde(conflict))
734
862
  return 0
735
863
 
736
864
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.149
3
+ Version: 1.0.150
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -569,6 +569,49 @@ class TestRecommend:
569
569
 
570
570
  # ---------------------------------------------------------------- browse
571
571
 
572
+ class _FakeCurses:
573
+ """Minimal curses stand-in for driving _browse_tui without a real TTY."""
574
+
575
+ A_BOLD, A_DIM, A_REVERSE, A_NORMAL = 1, 2, 4, 0
576
+ KEY_UP, KEY_DOWN, KEY_ENTER, KEY_BACKSPACE = 259, 258, 343, 263
577
+ ACS_HLINE = ord("-")
578
+
579
+ class error(Exception):
580
+ pass
581
+
582
+ def __init__(self, keys):
583
+ self.keys = list(keys)
584
+ self.drawn = []
585
+
586
+ def curs_set(self, n):
587
+ raise self.error("no cursor support")
588
+
589
+ def timeout(self, ms):
590
+ pass
591
+
592
+ def wrapper(self, fn):
593
+ fn(self)
594
+
595
+ # screen protocol
596
+ def getmaxyx(self):
597
+ return (24, 80)
598
+
599
+ def erase(self):
600
+ pass
601
+
602
+ def addnstr(self, y, x, s, n, attr=0):
603
+ self.drawn.append(s)
604
+
605
+ def hline(self, *a):
606
+ pass
607
+
608
+ def refresh(self):
609
+ pass
610
+
611
+ def getch(self):
612
+ return self.keys.pop(0) if self.keys else ord("q")
613
+
614
+
572
615
  class TestBrowse:
573
616
  def test_non_tty_prints_full_catalog(self, boost, tapped):
574
617
  r = boost("browse")
@@ -589,8 +632,8 @@ class TestBrowse:
589
632
  types.SimpleNamespace(stdin=tty, stdout=tty))
590
633
  monkeypatch.setattr(
591
634
  discovery, "_browse_tui",
592
- lambda curses, entries: next(e for e in entries
593
- if e["name"] == "brainstorming"))
635
+ lambda curses, entries: [next(e for e in entries
636
+ if e["name"] == "brainstorming")])
594
637
  r = boost("browse")
595
638
  assert "installed brainstorming v1.4.0" in r.out
596
639
  assert "linked into: Claude Code, Windsurf, Cursor" in r.out
@@ -618,7 +661,7 @@ class TestBrowse:
618
661
  workflow = {"name": "AGENT-playbook", "kind": "workflow",
619
662
  "version": "1.0", "tap": "fixture-tap"}
620
663
  monkeypatch.setattr(discovery, "_browse_tui",
621
- lambda curses, entries: workflow)
664
+ lambda curses, entries: [workflow])
622
665
  r = boost("browse") # default expect=0 → no crash
623
666
  assert "vanished from tap" in r.out # friendly message, not Error:
624
667
  assert "boost update" in r.out # the hint is shown
@@ -628,53 +671,45 @@ class TestBrowse:
628
671
  from boost_cli.commands import discovery
629
672
  from boost_cli.core import catalog
630
673
 
631
- class FakeCurses:
632
- A_BOLD, A_DIM, A_REVERSE, A_NORMAL = 1, 2, 4, 0
633
- KEY_UP, KEY_DOWN, KEY_ENTER, KEY_BACKSPACE = 259, 258, 343, 263
634
- ACS_HLINE = ord("-")
635
-
636
- class error(Exception):
637
- pass
638
-
639
- def __init__(self, keys):
640
- self.keys = list(keys)
641
- self.drawn = []
642
-
643
- def curs_set(self, n):
644
- raise self.error("no cursor support")
645
-
646
- def wrapper(self, fn):
647
- fn(self)
648
-
649
- # screen protocol
650
- def getmaxyx(self):
651
- return (24, 80)
652
-
653
- def erase(self):
654
- pass
655
-
656
- def addnstr(self, y, x, s, n, attr=0):
657
- self.drawn.append(s)
658
-
659
- def hline(self, *a):
660
- pass
661
-
662
- def refresh(self):
663
- pass
664
-
665
- def getch(self):
666
- return self.keys.pop(0) if self.keys else ord("q")
667
-
668
674
  entries = sorted(catalog.all_entries(), key=lambda e: e["name"])
669
- fake = FakeCurses([ord("t"), FakeCurses.KEY_DOWN, 10, FakeCurses.KEY_UP,
670
- FakeCurses.KEY_BACKSPACE, ord("i")])
675
+ fake = _FakeCurses([ord("t"), _FakeCurses.KEY_DOWN, 10, _FakeCurses.KEY_UP,
676
+ _FakeCurses.KEY_BACKSPACE, ord("i")])
671
677
  picked = discovery._browse_tui(fake, entries)
672
- assert picked["name"] == "brainstorming"
678
+ # a plain "i" with nothing space-selected installs just the highlighted pick
679
+ assert [e["name"] for e in picked] == ["brainstorming"]
673
680
  # the typed query is echoed after the "❯" prompt
674
681
  assert "t" in fake.drawn
675
682
  assert any("boost browse" in s for s in fake.drawn)
676
683
  # ESC quits without a pick
677
- assert discovery._browse_tui(FakeCurses([27]), entries) is None
684
+ assert discovery._browse_tui(_FakeCurses([27]), entries) is None
685
+
686
+ def test_tui_space_selects_multiple_for_batch_install(self, boost, tapped):
687
+ from boost_cli.commands import discovery
688
+ from boost_cli.core import catalog
689
+
690
+ # alphabetical: brainstorming, commit-messages, cowboy-coding, ...
691
+ entries = sorted(catalog.all_entries(), key=lambda e: e["name"])
692
+ keys = [ord(" "), # check brainstorming
693
+ _FakeCurses.KEY_DOWN, _FakeCurses.KEY_DOWN, # -> cowboy-coding
694
+ ord(" "), # check cowboy-coding
695
+ ord("i")] # install the batch
696
+ fake = _FakeCurses(keys)
697
+ picked = discovery._browse_tui(fake, entries)
698
+ assert [e["name"] for e in picked] == ["brainstorming", "cowboy-coding"]
699
+ assert any("2 selected" in s for s in fake.drawn)
700
+
701
+ def test_tui_cards_show_type_tap_badges_and_descriptions(self, boost, tapped):
702
+ from boost_cli.commands import discovery
703
+ from boost_cli.core import catalog
704
+
705
+ entries = sorted(catalog.all_entries(), key=lambda e: e["name"])
706
+ fake = _FakeCurses([]) # no keys queued -> one draw, then default "q" quits
707
+ picked = discovery._browse_tui(fake, entries)
708
+ assert picked is None
709
+ assert "[skill]" in fake.drawn
710
+ assert "[fixture-tap]" in fake.drawn
711
+ assert "v1.0.2" in fake.drawn # commit-messages' version
712
+ assert "Conventional, atomic commit message discipline" in fake.drawn
678
713
 
679
714
 
680
715
  class TestBrowseAurora:
@@ -781,6 +816,69 @@ class TestBrowseAurora:
781
816
  assert th["rule"] == [MonoCurses.A_NORMAL] * 3
782
817
 
783
818
 
819
+ class TestBrowseCards:
820
+ """Row badges, tap categories, and the boost-explain lazy description
821
+ fallback that power the browse card list."""
822
+
823
+ def test_tap_categories_reads_curated_registry_catalog(self, monkeypatch):
824
+ from boost_cli.commands import discovery
825
+ from boost_cli.core import config
826
+ monkeypatch.setattr(config, "load_registry_catalog", lambda: [
827
+ {"name": "a/b", "category": "framework"},
828
+ {"name": "c/d", "category": ""},
829
+ {"name": "e/f"},
830
+ ])
831
+ assert discovery._tap_categories() == {"a/b": "framework"}
832
+
833
+ def test_kind_theme_key_maps_known_kinds_and_falls_back(self):
834
+ from boost_cli.commands import discovery
835
+ assert discovery._kind_theme_key("skill") == "badge_skill"
836
+ assert discovery._kind_theme_key("rule") == "badge_rule"
837
+ assert discovery._kind_theme_key("workflow") == "badge_workflow"
838
+ assert discovery._kind_theme_key("whatever") == "badge_skill"
839
+
840
+ def test_row_badges_order_and_category_only_when_known(self):
841
+ from boost_cli.commands import discovery
842
+ e = {"name": "x", "version": "1.0.0", "tap": "a/b", "kind": "rule"}
843
+ with_category = discovery._row_badges(e, {"a/b": "framework"})
844
+ assert with_category == [
845
+ ("[rule]", "badge_rule"), ("v1.0.0", "version"),
846
+ ("[a/b]", "tap"), ("[framework]", "badge_category")]
847
+ without_category = discovery._row_badges(e, {})
848
+ assert without_category == with_category[:3] # category badge dropped
849
+
850
+ def test_entry_source_path_resolves_within_the_tap(self, boost, tapped):
851
+ from boost_cli.commands import discovery
852
+ from boost_cli.core import catalog
853
+ e = next(x for x in catalog.all_entries()
854
+ if x["name"] == "commit-messages")
855
+ path = discovery._entry_source_path(e)
856
+ assert path is not None and path.is_file()
857
+ assert path.name == "SKILL.md"
858
+
859
+ def test_entry_source_path_none_for_unknown_tap(self):
860
+ from boost_cli.commands import discovery
861
+ e = {"name": "x", "tap": "no/such-tap", "skill_md": "skills/x/SKILL.md"}
862
+ assert discovery._entry_source_path(e) is None
863
+
864
+ def test_lazy_description_falls_back_without_ai(self, boost, tapped):
865
+ # the `sandbox` fixture sets BOOST_NO_AI=1, so ai.available() is False.
866
+ from boost_cli.commands import discovery
867
+ from boost_cli.core import catalog
868
+ e = next(x for x in catalog.all_entries()
869
+ if x["name"] == "commit-messages")
870
+ assert discovery._lazy_description(e) == "no description available"
871
+
872
+ def test_lazy_description_uses_ai_when_available(self, boost, tapped, monkeypatch):
873
+ from boost_cli.commands import discovery
874
+ from boost_cli.core import ai, catalog
875
+ monkeypatch.setattr(ai, "available", lambda: True)
876
+ monkeypatch.setattr(ai, "ask", lambda *a, **k: "Writes tidy commits.\nextra")
877
+ e = next(x for x in catalog.all_entries()
878
+ if x["name"] == "commit-messages")
879
+ assert discovery._lazy_description(e) == "Writes tidy commits."
880
+
881
+
784
882
  # ---------------------------------------------------------------- trending
785
883
 
786
884
  class TestTrending: