boost-skill-cli 1.0.148__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.148 → boost_skill_cli-1.0.150}/Makefile +19 -1
  2. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/PKG-INFO +5 -1
  3. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/_version.py +2 -2
  4. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/discovery.py +170 -42
  5. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/PKG-INFO +5 -1
  6. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/SOURCES.txt +18 -0
  7. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/requires.txt +6 -0
  8. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/pyproject.toml +10 -0
  9. boost_skill_cli-1.0.150/scripts/bench_cli.sh +27 -0
  10. boost_skill_cli-1.0.150/tests/bdd/features/adapt.feature +46 -0
  11. boost_skill_cli-1.0.150/tests/bdd/features/browse.feature +26 -0
  12. boost_skill_cli-1.0.150/tests/bdd/features/count.feature +24 -0
  13. boost_skill_cli-1.0.150/tests/bdd/features/discover.feature +39 -0
  14. boost_skill_cli-1.0.150/tests/bdd/features/doctor.feature +30 -0
  15. boost_skill_cli-1.0.150/tests/bdd/features/environment.py +66 -0
  16. boost_skill_cli-1.0.150/tests/bdd/features/explain.feature +25 -0
  17. boost_skill_cli-1.0.150/tests/bdd/features/index.feature +25 -0
  18. boost_skill_cli-1.0.150/tests/bdd/features/install.feature +43 -0
  19. boost_skill_cli-1.0.150/tests/bdd/features/list.feature +42 -0
  20. boost_skill_cli-1.0.150/tests/bdd/features/mcp.feature +31 -0
  21. boost_skill_cli-1.0.150/tests/bdd/features/search.feature +37 -0
  22. boost_skill_cli-1.0.150/tests/bdd/features/steps/cli_steps.py +104 -0
  23. boost_skill_cli-1.0.150/tests/bdd/features/steps/discovery_steps.py +77 -0
  24. boost_skill_cli-1.0.150/tests/bdd/features/steps/mcp_steps.py +48 -0
  25. boost_skill_cli-1.0.150/tests/bdd/features/steps/quality_steps.py +12 -0
  26. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_discovery.py +142 -44
  27. boost_skill_cli-1.0.150/tests/perf/test_benchmarks.py +59 -0
  28. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/.gitattributes +0 -0
  29. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/.gitignore +0 -0
  30. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/CLAUDE.md +0 -0
  31. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/CONTRIBUTING.md +0 -0
  32. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/LICENSE +0 -0
  33. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/MANIFEST.in +0 -0
  34. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/README.md +0 -0
  35. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/SECURITY.md +0 -0
  36. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost +0 -0
  37. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/__init__.py +0 -0
  38. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/__main__.py +0 -0
  39. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/cli.py +0 -0
  40. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/cliparse.py +0 -0
  41. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/__init__.py +0 -0
  42. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/bmad.py +0 -0
  43. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/configuration.py +0 -0
  44. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/hooks.py +0 -0
  45. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/info.py +0 -0
  46. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/intelligence.py +0 -0
  47. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/pkg.py +0 -0
  48. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/quality.py +0 -0
  49. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/taps.py +0 -0
  50. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/commands/team.py +0 -0
  51. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/__init__.py +0 -0
  52. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/adapters.py +0 -0
  53. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/agents.py +0 -0
  54. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/ai.py +0 -0
  55. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/catalog.py +0 -0
  56. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/claude_settings.py +0 -0
  57. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/config.py +0 -0
  58. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/dense.py +0 -0
  59. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/embed.py +0 -0
  60. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/frontmatter.py +0 -0
  61. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/gitutil.py +0 -0
  62. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/imperative.py +0 -0
  63. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/injectscan.py +0 -0
  64. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/journal.py +0 -0
  65. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/lockfile.py +0 -0
  66. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/logs.py +0 -0
  67. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/mcp.py +0 -0
  68. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/nethttp.py +0 -0
  69. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/output.py +0 -0
  70. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/paths.py +0 -0
  71. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/policy.py +0 -0
  72. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/rag.py +0 -0
  73. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/registry.py +0 -0
  74. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/rules.py +0 -0
  75. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/secretscan.py +0 -0
  76. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/serve.py +0 -0
  77. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/stackprobe.py +0 -0
  78. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/staleness.py +0 -0
  79. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/store.py +0 -0
  80. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/typosquat.py +0 -0
  81. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/updatediff.py +0 -0
  82. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/util.py +0 -0
  83. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/core/workflows.py +0 -0
  84. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/data/registries.json +0 -0
  85. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/errors.py +0 -0
  86. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_cli/spin.py +0 -0
  87. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  88. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  89. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/boost_skill_cli.egg-info/top_level.txt +0 -0
  90. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/.claude/settings.local.json +0 -0
  91. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/DEBUGGING.md +0 -0
  92. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/adapters.html +0 -0
  93. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/demo.gif +0 -0
  94. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/demo.tape +0 -0
  95. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/design-roadmap.html +0 -0
  96. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/eval.html +0 -0
  97. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/index.html +0 -0
  98. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/mcp-hub.html +0 -0
  99. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/rag-architecture.md +0 -0
  100. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D01.md +0 -0
  101. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D02.md +0 -0
  102. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D03.md +0 -0
  103. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D04.md +0 -0
  104. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D05.md +0 -0
  105. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D06.md +0 -0
  106. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D07.md +0 -0
  107. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D08.md +0 -0
  108. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D09.md +0 -0
  109. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D10.md +0 -0
  110. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D11.md +0 -0
  111. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D12.md +0 -0
  112. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D13.md +0 -0
  113. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D14.md +0 -0
  114. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D15.md +0 -0
  115. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D16.md +0 -0
  116. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D17.md +0 -0
  117. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D18.md +0 -0
  118. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D19.md +0 -0
  119. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D20.md +0 -0
  120. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D21.md +0 -0
  121. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D22.md +0 -0
  122. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D23.md +0 -0
  123. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D24.md +0 -0
  124. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D25.md +0 -0
  125. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/BOOST-D26.md +0 -0
  126. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  127. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  128. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  129. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  130. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  131. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  132. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  133. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  134. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  135. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  136. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  137. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  138. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  139. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  140. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  141. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  142. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  143. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  144. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  145. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  146. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  147. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  148. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  149. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  150. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  151. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  152. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  153. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  154. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  155. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  156. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  157. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  158. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  159. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  160. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  161. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  162. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  163. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  164. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  165. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  166. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  167. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  168. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  169. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  170. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  171. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  172. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  173. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  174. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  175. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  176. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  177. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  178. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  179. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  180. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  181. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  182. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  183. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  184. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  185. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  186. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  187. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  188. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  189. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  190. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  191. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  192. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  193. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  194. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  195. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  196. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  197. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  198. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  199. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  200. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  201. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  202. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  203. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  204. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  205. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  206. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  207. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  208. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  209. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  210. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  211. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  212. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  213. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  214. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  215. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  216. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  217. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  218. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  219. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  220. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  221. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  222. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  223. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  224. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  225. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  226. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  227. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  228. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  229. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
  230. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  231. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  232. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  233. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap/items/workspace-scope-install.md +0 -0
  234. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/docs/roadmap.html +0 -0
  235. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/examples/README.md +0 -0
  236. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/examples/adapt-demo.sh +0 -0
  237. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/examples/boost-run-prototype.sh +0 -0
  238. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/index.html +0 -0
  239. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/build_registries.py +0 -0
  240. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/build_roadmap.py +0 -0
  241. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/ensure_eval_corpus.sh +0 -0
  242. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/eval_explain.py +0 -0
  243. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/eval_recommend.py +0 -0
  244. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/eval_retrieval.py +0 -0
  245. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/scripts/mutation_gate.py +0 -0
  246. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/setup.cfg +0 -0
  247. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/style/README.md +0 -0
  248. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/style/boost.css +0 -0
  249. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/style/boost.js +0 -0
  250. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/style/demo.html +0 -0
  251. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/conftest.py +0 -0
  252. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/eval/baseline.json +0 -0
  253. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/eval/explain.jsonl +0 -0
  254. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/eval/golden.jsonl +0 -0
  255. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/eval/recommend.jsonl +0 -0
  256. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/eval/taps.txt +0 -0
  257. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_adapt.py +0 -0
  258. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_bmad.py +0 -0
  259. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_configuration.py +0 -0
  260. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_hooks.py +0 -0
  261. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_info.py +0 -0
  262. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_intelligence.py +0 -0
  263. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_pkg.py +0 -0
  264. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_quality.py +0 -0
  265. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_taps.py +0 -0
  266. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_cli_team.py +0 -0
  267. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_everyday_loop.py +0 -0
  268. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/functional/test_property_parsers.py +0 -0
  269. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/make_fixture.py +0 -0
  270. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/smoke.sh +0 -0
  271. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_adapters.py +0 -0
  272. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_agents.py +0 -0
  273. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_ai.py +0 -0
  274. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_catalog.py +0 -0
  275. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_claude_settings.py +0 -0
  276. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_cliparse.py +0 -0
  277. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_config.py +0 -0
  278. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_dense.py +0 -0
  279. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_dense_fallback.py +0 -0
  280. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_docs_pages_linked.py +0 -0
  281. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_embed.py +0 -0
  282. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_errors_and_cli_table.py +0 -0
  283. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_frontmatter.py +0 -0
  284. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_gitutil.py +0 -0
  285. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_imperative.py +0 -0
  286. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_injectscan.py +0 -0
  287. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_journal.py +0 -0
  288. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_lockfile.py +0 -0
  289. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_logs.py +0 -0
  290. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_mcp.py +0 -0
  291. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_mutation_hardening.py +0 -0
  292. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_nethttp.py +0 -0
  293. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_output.py +0 -0
  294. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_paths.py +0 -0
  295. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_pkg_confusions.py +0 -0
  296. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_pkg_injection.py +0 -0
  297. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_pkg_secrets.py +0 -0
  298. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_policy.py +0 -0
  299. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_rag.py +0 -0
  300. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_registries_fresh.py +0 -0
  301. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_registry.py +0 -0
  302. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_roadmap_fresh.py +0 -0
  303. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_rules.py +0 -0
  304. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_secretscan.py +0 -0
  305. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_serve.py +0 -0
  306. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_spin.py +0 -0
  307. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_stackprobe.py +0 -0
  308. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_staleness.py +0 -0
  309. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_store.py +0 -0
  310. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_token_parity.py +0 -0
  311. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_typosquat.py +0 -0
  312. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_updatediff.py +0 -0
  313. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_util.py +0 -0
  314. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_version.py +0 -0
  315. {boost_skill_cli-1.0.148 → boost_skill_cli-1.0.150}/tests/unit/test_workflows.py +0 -0
@@ -10,7 +10,7 @@ PYTEST := $(VENV)/bin/pytest
10
10
  # pinned taps out of the developer's real ~/.boost.
11
11
  EVAL_HOME := $(CURDIR)/.eval-home
12
12
 
13
- .PHONY: venv test unit functional smoke coverage mutation lint check demo clean-test eval eval-ai eval-rec eval-stats eval-explain audit dist-check
13
+ .PHONY: venv test unit functional smoke coverage mutation lint check demo clean-test eval eval-ai eval-rec eval-stats eval-explain audit dist-check bdd bench bench-cli
14
14
 
15
15
  venv:
16
16
  python3 -m venv $(VENV)
@@ -96,6 +96,24 @@ eval-explain:
96
96
  PYTHON=$(PY) BOOST_HOME=$(EVAL_HOME) bash scripts/ensure_eval_corpus.sh
97
97
  BOOST_HOME=$(EVAL_HOME) $(PY) scripts/eval_explain.py --fail-under 0.80
98
98
 
99
+ # Opt-in Gherkin/BDD suite (tests/bdd) — additive to tests/functional, not a
100
+ # replacement. Needs the [bdd] extra (`pip install -e '.[bdd]'`); not part of
101
+ # `make check`.
102
+ bdd:
103
+ $(VENV)/bin/behave tests/bdd/features
104
+
105
+ # Opt-in micro-benchmarks (tests/perf) for perf-sensitive core paths. Needs
106
+ # the [perf] extra (`pip install -e '.[perf]'`); not part of `make check` — a
107
+ # timing regression is informational, not a merge gate.
108
+ bench:
109
+ $(PYTEST) tests/perf --benchmark-only -q
110
+
111
+ # Opt-in wall-clock benchmark of the real `boost` binary via hyperfine. Not
112
+ # part of `make check`; degrades cleanly (prints a skip message, exits 0) when
113
+ # hyperfine isn't installed (`brew install hyperfine`).
114
+ bench-cli:
115
+ @command -v hyperfine >/dev/null 2>&1 && bash scripts/bench_cli.sh || echo "hyperfine not on PATH — install via 'brew install hyperfine', skipping"
116
+
99
117
  # regenerate generated artifacts from their source (registries + roadmap boards)
100
118
  generate:
101
119
  $(PY) scripts/build_registries.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.148
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
@@ -708,6 +708,10 @@ Requires-Dist: ragas<0.3,>=0.2; extra == "eval"
708
708
  Requires-Dist: langchain-core<0.4; extra == "eval"
709
709
  Requires-Dist: langchain-community<0.4; extra == "eval"
710
710
  Requires-Dist: langchain-openai<1; extra == "eval"
711
+ Provides-Extra: bdd
712
+ Requires-Dist: behave>=1.2.6; extra == "bdd"
713
+ Provides-Extra: perf
714
+ Requires-Dist: pytest-benchmark>=4.0; extra == "perf"
711
715
  Dynamic: license-file
712
716
 
713
717
  # boost — a package manager for AI coding skills
@@ -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.148'
22
- __version_tuple__ = version_tuple = (1, 0, 148)
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.148
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
@@ -708,6 +708,10 @@ Requires-Dist: ragas<0.3,>=0.2; extra == "eval"
708
708
  Requires-Dist: langchain-core<0.4; extra == "eval"
709
709
  Requires-Dist: langchain-community<0.4; extra == "eval"
710
710
  Requires-Dist: langchain-openai<1; extra == "eval"
711
+ Provides-Extra: bdd
712
+ Requires-Dist: behave>=1.2.6; extra == "bdd"
713
+ Provides-Extra: perf
714
+ Requires-Dist: pytest-benchmark>=4.0; extra == "perf"
711
715
  Dynamic: license-file
712
716
 
713
717
  # boost — a package manager for AI coding skills
@@ -217,6 +217,7 @@ docs/roadmap/items/workspace-scope-install.md
217
217
  examples/README.md
218
218
  examples/adapt-demo.sh
219
219
  examples/boost-run-prototype.sh
220
+ scripts/bench_cli.sh
220
221
  scripts/build_registries.py
221
222
  scripts/build_roadmap.py
222
223
  scripts/ensure_eval_corpus.sh
@@ -231,6 +232,22 @@ style/demo.html
231
232
  tests/conftest.py
232
233
  tests/make_fixture.py
233
234
  tests/smoke.sh
235
+ tests/bdd/features/adapt.feature
236
+ tests/bdd/features/browse.feature
237
+ tests/bdd/features/count.feature
238
+ tests/bdd/features/discover.feature
239
+ tests/bdd/features/doctor.feature
240
+ tests/bdd/features/environment.py
241
+ tests/bdd/features/explain.feature
242
+ tests/bdd/features/index.feature
243
+ tests/bdd/features/install.feature
244
+ tests/bdd/features/list.feature
245
+ tests/bdd/features/mcp.feature
246
+ tests/bdd/features/search.feature
247
+ tests/bdd/features/steps/cli_steps.py
248
+ tests/bdd/features/steps/discovery_steps.py
249
+ tests/bdd/features/steps/mcp_steps.py
250
+ tests/bdd/features/steps/quality_steps.py
234
251
  tests/eval/baseline.json
235
252
  tests/eval/explain.jsonl
236
253
  tests/eval/golden.jsonl
@@ -249,6 +266,7 @@ tests/functional/test_cli_taps.py
249
266
  tests/functional/test_cli_team.py
250
267
  tests/functional/test_everyday_loop.py
251
268
  tests/functional/test_property_parsers.py
269
+ tests/perf/test_benchmarks.py
252
270
  tests/unit/test_adapters.py
253
271
  tests/unit/test_agents.py
254
272
  tests/unit/test_ai.py
@@ -1,4 +1,7 @@
1
1
 
2
+ [bdd]
3
+ behave>=1.2.6
4
+
2
5
  [eval]
3
6
  ranx>=0.3.20
4
7
  ragas<0.3,>=0.2
@@ -6,5 +9,8 @@ langchain-core<0.4
6
9
  langchain-community<0.4
7
10
  langchain-openai<1
8
11
 
12
+ [perf]
13
+ pytest-benchmark>=4.0
14
+
9
15
  [rag]
10
16
  sqlite-vec>=0.1.6
@@ -55,6 +55,16 @@ eval = [
55
55
  "langchain-community<0.4",
56
56
  "langchain-openai<1",
57
57
  ]
58
+ # Opt-in Gherkin/BDD functional suite (tests/bdd) — reusable Given/When/Then
59
+ # scenarios over the same in-process CLI entrypoint tests/functional drives.
60
+ # Additive to, not a replacement for, the pytest functional suite; not part of
61
+ # `make check`. `pip install -e '.[bdd]'`.
62
+ bdd = ["behave>=1.2.6"]
63
+ # Opt-in micro-benchmarks (tests/perf, `make bench`) for perf-sensitive core
64
+ # paths (BM25 indexing/search, catalog scan). Not part of `make check` — a
65
+ # timing regression shouldn't block a merge the way a correctness gate does.
66
+ # `pip install -e '.[perf]'`.
67
+ perf = ["pytest-benchmark>=4.0"]
58
68
 
59
69
  [project.scripts]
60
70
  boost = "boost_cli.cli:main"
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # Wall-clock benchmark of the real `boost` binary via hyperfine — the actual
3
+ # fork+exec CLI, not the in-process pytest-benchmark suite (tests/perf).
4
+ # Opt-in (`make bench-cli`); not part of `make check`. Degrades cleanly if
5
+ # hyperfine isn't installed.
6
+ set -uo pipefail
7
+
8
+ if ! command -v hyperfine >/dev/null 2>&1; then
9
+ echo "hyperfine not installed — install via 'brew install hyperfine', skipping"
10
+ exit 0
11
+ fi
12
+
13
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
14
+ SB="$(mktemp -d "${TMPDIR:-/tmp}/boost-bench-XXXXXX")"
15
+ trap 'rm -rf "$SB"' EXIT
16
+ export HOME="$SB" BOOST_NO_AI=1 BOOST_ASSUME_YES=1 NO_COLOR=1
17
+
18
+ FIX="$(python3 "$ROOT/tests/make_fixture.py" "$SB/fixture-tap")"
19
+ "$ROOT/boost" tap "$FIX" >/dev/null
20
+ "$ROOT/boost" install brainstorming >/dev/null
21
+
22
+ echo "== hyperfine: $(command -v hyperfine) over $ROOT/boost (sandbox: $SB)"
23
+ hyperfine --warmup 3 \
24
+ "$ROOT/boost --help" \
25
+ "$ROOT/boost list" \
26
+ "$ROOT/boost search brainstorming" \
27
+ "$ROOT/boost count"
@@ -0,0 +1,46 @@
1
+ Feature: boost adapt
2
+ Render a skill as another agent framework's native source (CrewAI, Agents SDK).
3
+
4
+ Background:
5
+ Given a fresh boost environment
6
+ And the fixture tap is added
7
+
8
+ Scenario: adapting an installed skill to CrewAI
9
+ Given the "brainstorming" skill is installed
10
+ When I run "boost adapt brainstorming --to crewai"
11
+ Then the exit code should be 0
12
+ And the output should contain "from crewai import Agent"
13
+ And the output should contain "brainstorming = Agent("
14
+
15
+ Scenario: adapting an installed skill to the Agents SDK
16
+ Given the "brainstorming" skill is installed
17
+ When I run "boost adapt brainstorming --to agents-sdk"
18
+ Then the exit code should be 0
19
+ And the output should contain "from agents import Agent"
20
+ And the output should match "name=\"brainstorming\""
21
+
22
+ Scenario: adapting a tapped-but-not-installed skill still resolves
23
+ When I run "boost adapt brainstorming --to crewai"
24
+ Then the exit code should be 0
25
+ And the output should contain "brainstorming = Agent("
26
+
27
+ Scenario: --model none opts out of pinning an LLM
28
+ Given the "brainstorming" skill is installed
29
+ When I run "boost adapt brainstorming --to crewai --model none"
30
+ Then the exit code should be 0
31
+ And the output should not contain "llm="
32
+
33
+ Scenario: an unknown framework is rejected
34
+ Given the "brainstorming" skill is installed
35
+ When I run "boost adapt brainstorming --to langchain"
36
+ Then the exit code should be 1
37
+ And the output should contain "unknown framework"
38
+
39
+ Scenario: --to is required
40
+ Given the "brainstorming" skill is installed
41
+ When I run "boost adapt brainstorming"
42
+ Then the exit code should be 2
43
+
44
+ Scenario: adapting an unknown skill fails
45
+ When I run "boost adapt does-not-exist --to crewai"
46
+ Then the exit code should be 1
@@ -0,0 +1,26 @@
1
+ Feature: boost browse
2
+ Interactive full-screen TUI with fuzzy search. There is no TTY in these
3
+ scenarios, so `browse` takes its documented non-interactive escape hatch
4
+ and prints the full catalog instead of launching curses.
5
+
6
+ Background:
7
+ Given a fresh boost environment
8
+
9
+ Scenario: no skills available to browse
10
+ When I run "boost browse"
11
+ Then the exit code should be 1
12
+ And the output should contain "no skills available to browse"
13
+
14
+ Scenario: without a TTY, prints the full catalog instead of the TUI
15
+ Given the fixture tap is added
16
+ When I run "boost browse"
17
+ Then the exit code should be 0
18
+ And the output should contain "interactive mode needs a TTY — showing the full catalog"
19
+ And the output should contain "brainstorming"
20
+ And the output should contain "commit-messages"
21
+ And the output should contain "5 skills · install with `boost install <name>`"
22
+
23
+ Scenario: --help documents the command without touching the catalog
24
+ When I run "boost browse --help"
25
+ Then the exit code should be 0
26
+ And the output should contain "Interactive full-screen TUI with fuzzy search"
@@ -0,0 +1,24 @@
1
+ Feature: boost count
2
+ A one-line inventory summary: installed / available / taps / discovery index.
3
+
4
+ Background:
5
+ Given a fresh boost environment
6
+
7
+ Scenario: an empty environment
8
+ When I run "boost count"
9
+ Then the exit code should be 0
10
+ And the output should contain "installed 0 · available 0 (across 0 taps) · discovery index not built"
11
+
12
+ Scenario: one tap with one installed skill
13
+ Given the "brainstorming" skill is installed
14
+ When I run "boost count"
15
+ Then the exit code should be 0
16
+ And the output should contain "installed 1 · available 5 (across 1 tap) · discovery index not built"
17
+ And the output should contain "╭─ inventory"
18
+
19
+ Scenario: --json is machine readable
20
+ Given the "brainstorming" skill is installed
21
+ When I run "boost count --json"
22
+ Then the exit code should be 0
23
+ And the output should match "\"installed\": 1"
24
+ And the output should match "\"taps\": 1"
@@ -0,0 +1,39 @@
1
+ Feature: boost discover
2
+ Browse and filter the GitHub-wide discovery index built by `boost index`.
3
+
4
+ Background:
5
+ Given a fresh boost environment
6
+
7
+ Scenario: without an index and without gh, hints to install gh
8
+ Given the GitHub CLI is not installed
9
+ When I run "boost discover"
10
+ Then the exit code should be 0
11
+ And the output should contain "the discovery index has not been built yet"
12
+ And the output should contain "install the GitHub CLI first"
13
+
14
+ Scenario: without an index but with gh, hints to run boost index
15
+ Given the GitHub CLI is installed and code search returns 2 sample skill files
16
+ When I run "boost discover"
17
+ Then the exit code should be 0
18
+ And the output should contain "build it with `boost index` (GitHub Code Search)"
19
+
20
+ Scenario: a query filters indexed entries
21
+ Given the discovery index is seeded with sample items
22
+ When I run "boost discover acme"
23
+ Then the exit code should be 0
24
+ And the output should contain "skills/web/SKILL.md"
25
+ And the output should not contain "octo/skills"
26
+ And the output should contain "2 of 3 indexed skills · GitHub reports ~42 total"
27
+
28
+ Scenario: a query with no matches reports the index size
29
+ Given the discovery index is seeded with sample items
30
+ When I run "boost discover zzz"
31
+ Then the exit code should be 0
32
+ And the output should contain "no indexed skills match 'zzz'"
33
+ And the output should contain "the index holds 3 entries — rebuild with `boost index`"
34
+
35
+ Scenario: a corrupt index fails cleanly
36
+ Given the discovery index is corrupt
37
+ When I run "boost discover"
38
+ Then the exit code should be 1
39
+ And the output should contain "the discovery index is corrupt"