boost-skill-cli 1.0.322__tar.gz → 1.0.324__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 (575) hide show
  1. {boost_skill_cli-1.0.322/boost_skill_cli.egg-info → boost_skill_cli-1.0.324}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/_version.py +2 -2
  3. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/rag.py +217 -19
  4. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324/boost_skill_cli.egg-info}/PKG-INFO +1 -1
  5. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_skill_cli.egg-info/SOURCES.txt +1 -0
  6. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/bm25-index-is-one-json-blob.md +32 -1
  7. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/near-duplicate-items-eat-the-result-slots.md +37 -1
  8. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap.html +69 -2
  9. boost_skill_cli-1.0.324/tests/unit/test_content_dedupe.py +144 -0
  10. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_rag.py +97 -12
  11. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.gitattributes +0 -0
  12. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.gitignore +0 -0
  13. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.gitleaks.toml +0 -0
  14. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.htmlvalidate.json +0 -0
  15. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.lighthouserc.json +0 -0
  16. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.lycheeignore +0 -0
  17. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.markdownlint-cli2.jsonc +0 -0
  18. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.pre-commit-config.yaml +0 -0
  19. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.stylelintrc.json +0 -0
  20. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.vale/styles/boost/Terminology.yml +0 -0
  21. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.vale/styles/config/vocabularies/boost/accept.txt +0 -0
  22. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/.vale.ini +0 -0
  23. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/CLAUDE.md +0 -0
  24. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/CODE_OF_CONDUCT.md +0 -0
  25. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/CONTRIBUTING.md +0 -0
  26. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/LICENSE +0 -0
  27. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/MANIFEST.in +0 -0
  28. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/Makefile +0 -0
  29. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/README.md +0 -0
  30. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/SECURITY.md +0 -0
  31. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost +0 -0
  32. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/__init__.py +0 -0
  33. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/__main__.py +0 -0
  34. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/cli.py +0 -0
  35. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/cliparse.py +0 -0
  36. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/__init__.py +0 -0
  37. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/_common.py +0 -0
  38. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/bmad.py +0 -0
  39. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/configuration.py +0 -0
  40. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/discovery.py +0 -0
  41. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/hooks.py +0 -0
  42. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/info.py +0 -0
  43. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/intelligence.py +0 -0
  44. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/pkg.py +0 -0
  45. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/quality.py +0 -0
  46. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/run.py +0 -0
  47. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/safety.py +0 -0
  48. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/taps.py +0 -0
  49. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/commands/team.py +0 -0
  50. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/__init__.py +0 -0
  51. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/adapters.py +0 -0
  52. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/agents.py +0 -0
  53. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/ai.py +0 -0
  54. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/capabilities.py +0 -0
  55. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/catalog.py +0 -0
  56. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/claude_settings.py +0 -0
  57. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/config.py +0 -0
  58. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/dense.py +0 -0
  59. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/ed25519.py +0 -0
  60. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/embed.py +0 -0
  61. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/faithfulness.py +0 -0
  62. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/frontmatter.py +0 -0
  63. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/gitutil.py +0 -0
  64. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/imperative.py +0 -0
  65. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/injectscan.py +0 -0
  66. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/installscan.py +0 -0
  67. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/integrity.py +0 -0
  68. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/journal.py +0 -0
  69. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/localembed.py +0 -0
  70. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/lockfile.py +0 -0
  71. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/logs.py +0 -0
  72. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/mcp.py +0 -0
  73. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/mcpdecl.py +0 -0
  74. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/mcphost.py +0 -0
  75. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/minisign.py +0 -0
  76. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/nethttp.py +0 -0
  77. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/output.py +0 -0
  78. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/paths.py +0 -0
  79. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/policy.py +0 -0
  80. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/projectlock.py +0 -0
  81. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/provenance.py +0 -0
  82. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/registry.py +0 -0
  83. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/resolve.py +0 -0
  84. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/rules.py +0 -0
  85. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/scopes.py +0 -0
  86. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/secretscan.py +0 -0
  87. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/selfupdate.py +0 -0
  88. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/serve.py +0 -0
  89. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/stackprobe.py +0 -0
  90. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/staleness.py +0 -0
  91. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/store.py +0 -0
  92. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/trustaudit.py +0 -0
  93. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/typosquat.py +0 -0
  94. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/updatediff.py +0 -0
  95. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/util.py +0 -0
  96. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/core/workflows.py +0 -0
  97. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/data/registries.json +0 -0
  98. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/errors.py +0 -0
  99. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_cli/spin.py +0 -0
  100. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  101. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  102. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_skill_cli.egg-info/requires.txt +0 -0
  103. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/boost_skill_cli.egg-info/top_level.txt +0 -0
  104. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/codecov.yml +0 -0
  105. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/.claude/settings.local.json +0 -0
  106. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/DEBUGGING.md +0 -0
  107. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/adapters.html +0 -0
  108. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/architecture/README.md +0 -0
  109. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/architecture/c4-components-core.md +0 -0
  110. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/architecture/c4-containers.md +0 -0
  111. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/architecture/c4-context.md +0 -0
  112. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/architecture/c4-dynamic-install.md +0 -0
  113. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/distill.gif +0 -0
  114. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/doctor.gif +0 -0
  115. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/explain.gif +0 -0
  116. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/hooks.gif +0 -0
  117. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/install.gif +0 -0
  118. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/pulse.gif +0 -0
  119. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/search.gif +0 -0
  120. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/gifs/tap.gif +0 -0
  121. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/distill.tape +0 -0
  122. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/doctor.tape +0 -0
  123. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/explain.tape +0 -0
  124. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/hooks.tape +0 -0
  125. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/install.tape +0 -0
  126. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/pulse.tape +0 -0
  127. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/search.tape +0 -0
  128. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel/tapes/tap.tape +0 -0
  129. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/carousel.html +0 -0
  130. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/commands.html +0 -0
  131. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/demo.gif +0 -0
  132. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/demo.tape +0 -0
  133. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/design-roadmap.html +0 -0
  134. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/eval.html +0 -0
  135. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/index.html +0 -0
  136. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/mcp-hub.html +0 -0
  137. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/rag-architecture.md +0 -0
  138. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D01.md +0 -0
  139. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D02.md +0 -0
  140. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D03.md +0 -0
  141. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D04.md +0 -0
  142. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D05.md +0 -0
  143. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D06.md +0 -0
  144. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D07.md +0 -0
  145. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D08.md +0 -0
  146. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D09.md +0 -0
  147. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D10.md +0 -0
  148. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D11.md +0 -0
  149. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D12.md +0 -0
  150. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D13.md +0 -0
  151. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D14.md +0 -0
  152. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D15.md +0 -0
  153. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D16.md +0 -0
  154. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D17.md +0 -0
  155. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D18.md +0 -0
  156. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D19.md +0 -0
  157. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D20.md +0 -0
  158. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D21.md +0 -0
  159. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D22.md +0 -0
  160. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D23.md +0 -0
  161. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D24.md +0 -0
  162. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D25.md +0 -0
  163. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/BOOST-D26.md +0 -0
  164. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  165. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/actionlint-skips-shellcheck-silently.md +0 -0
  166. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/adapter-conformance-langgraph-leg-never-ran.md +0 -0
  167. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ai-bridge-silent-failure-logging.md +0 -0
  168. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ambiguous-tap-short-name-resolution.md +0 -0
  169. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  170. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  171. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  172. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  173. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  174. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  175. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  176. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  177. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  178. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  179. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ci-job-timeout-minutes.md +0 -0
  180. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ci-summary-step-fails-itself.md +0 -0
  181. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ci-time-is-now-runner-queueing.md +0 -0
  182. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  183. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/code-scanning-rule-can-be-restored.md +0 -0
  184. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/codeql-job-rename-stranded-merge-protection.md +0 -0
  185. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/command-carousel-page.md +0 -0
  186. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  187. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  188. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  189. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  190. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  191. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/crash-listing-branch-untested.md +0 -0
  192. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  193. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/demo-cannot-open-its-own-pr.md +0 -0
  194. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/demo-gif-workflow-has-never-succeeded.md +0 -0
  195. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/dense-search-fallback-and-stale-tap-pruning.md +0 -0
  196. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/dependabot-missing-pip-ecosystem.md +0 -0
  197. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/dependabot-regeneration-drops-platform-pins.md +0 -0
  198. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/dependabot-root-pip-entry-duplicates-requirements.md +0 -0
  199. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  200. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
  201. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  202. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  203. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  204. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/eval-corpus-cannot-see-real-retrieval.md +0 -0
  205. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  206. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  207. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  208. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  209. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  210. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  211. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  212. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  213. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  214. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/gemini-cli-agent-target.md +0 -0
  215. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/github-community-health-files.md +0 -0
  216. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  217. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  218. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  219. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  220. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/install-docs-never-mentioned-upgrading.md +0 -0
  221. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/install-from-path-bypasses-policy-and-pin-checks.md +0 -0
  222. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
  223. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  224. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/installed-skill-trust-audit.md +0 -0
  225. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  226. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/internal-architecture-diagrams.md +0 -0
  227. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  228. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/journal-rotation-race-and-handle-leak.md +0 -0
  229. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/keyless-dense-tier-local-static-embeddings.md +0 -0
  230. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/keyless-semantic-search-for-everyone.md +0 -0
  231. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  232. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/license-compliance-scanning.md +0 -0
  233. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  234. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/lint-tap-misscopes-rule-workflow-entries.md +0 -0
  235. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  236. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  237. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/lock-invariant-cannot-parse-extras-pins.md +0 -0
  238. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  239. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  240. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  241. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/main-has-no-branch-protection.md +0 -0
  242. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/main-ruleset-ref-pattern-has-literal-quotes.md +0 -0
  243. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/make-lint-masks-actionlint-failures.md +0 -0
  244. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  245. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  246. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/markdownlint-lints-the-fuzz-corpus.md +0 -0
  247. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-aware-skills.md +0 -0
  248. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-check-skills-before-starting-a-task.md +0 -0
  249. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  250. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-install-skips-the-injection-scan.md +0 -0
  251. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  252. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-one-benefit-nameable-task.md +0 -0
  253. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  254. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
  255. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-servers-ignore-install-scope.md +0 -0
  256. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  257. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  258. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/merge-queue-would-deadlock-most-required-checks.md +0 -0
  259. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/merged-loop-branches-are-never-pruned.md +0 -0
  260. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  261. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mutation-gate-was-the-whole-critical-path.md +0 -0
  262. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  263. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  264. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  265. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mutation-shard-floor-is-one-file.md +0 -0
  266. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/mypy-strict-mode-for-commands.md +0 -0
  267. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/negative-limit-inverts-log-pulse-output.md +0 -0
  268. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/onboard-overwrites-generated-files-without-confirm.md +0 -0
  269. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  270. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/one-commit-can-cut-two-releases.md +0 -0
  271. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  272. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  273. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  274. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/path-traversal-unsanitized-rule-workflow-name.md +0 -0
  275. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  276. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
  277. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/post-deploy-always-defeated-by-skipped-setup.md +0 -0
  278. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  279. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  280. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/prerequisites-and-semantic-search-setup.md +0 -0
  281. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
  282. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
  283. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  284. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  285. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  286. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  287. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  288. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/publish-gate-ignores-pip-audit-and-metadata.md +0 -0
  289. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/publish-trigger-was-reachable-from-a-fork.md +0 -0
  290. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/pytest-tmpdir-cve-blocked-by-py39-floor.md +0 -0
  291. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  292. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  293. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  294. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  295. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/rel-time-tests-race-the-wall-clock.md +0 -0
  296. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/release-verifies-the-wrong-commit.md +0 -0
  297. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/required-checks-can-declare-a-check-that-deadlocks-prs.md +0 -0
  298. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/required-checks-config-drift.md +0 -0
  299. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  300. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  301. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/roadmap-html-goes-stale-on-every-rebase.md +0 -0
  302. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  303. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/ruff-016-widens-the-default-rule-set.md +0 -0
  304. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  305. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  306. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  307. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  308. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/sbom-declares-the-wrong-version.md +0 -0
  309. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  310. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/sbom-release-event-never-fires.md +0 -0
  311. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  312. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/scheduled-toolchain-lock-regeneration.md +0 -0
  313. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/scorecard-findings-triage.md +0 -0
  314. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  315. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  316. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  317. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  318. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/self-update-broken-for-pip-pipx-installs.md +0 -0
  319. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/serve-py-traversal-guards-untested.md +0 -0
  320. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  321. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  322. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  323. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  324. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  325. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  326. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/structured-json-log-output-mode.md +0 -0
  327. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  328. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  329. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/sync-deletes-unowned-broken-symlinks.md +0 -0
  330. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/sync-relinks-into-every-agent-ignoring-scope.md +0 -0
  331. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  332. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  333. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  334. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  335. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  336. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/uninstall-has-no-confirmation-prompt.md +0 -0
  337. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  338. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  339. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  340. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/update-reinstall-widens-agent-scope.md +0 -0
  341. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  342. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  343. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
  344. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/wire-bdd-suite-into-ci.md +0 -0
  345. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  346. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  347. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  348. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/workspace-scope-install.md +0 -0
  349. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/docs/roadmap/items/zizmor-pin-is-a-yanked-release.md +0 -0
  350. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/eslint.config.mjs +0 -0
  351. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/README.md +0 -0
  352. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/__init__.py +0 -0
  353. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/baseline.json +0 -0
  354. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/faithfulness.py +0 -0
  355. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/golden_set.json +0 -0
  356. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/make_corpus.py +0 -0
  357. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/make_golden.py +0 -0
  358. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/metrics.py +0 -0
  359. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/evals/run_evals.py +0 -0
  360. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/examples/README.md +0 -0
  361. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/examples/adapt-demo.sh +0 -0
  362. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/examples/boost-run-prototype.sh +0 -0
  363. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/index.html +0 -0
  364. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/noxfile.py +0 -0
  365. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/pyproject.toml +0 -0
  366. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/coverage-tools.in +0 -0
  367. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/coverage-tools.txt +0 -0
  368. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/lint-tools.in +0 -0
  369. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/lint-tools.txt +0 -0
  370. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/mutation-tools.in +0 -0
  371. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/mutation-tools.txt +0 -0
  372. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/platform-pins.lock +0 -0
  373. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/release-tools.in +0 -0
  374. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/release-tools.txt +0 -0
  375. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/test-tools.in +0 -0
  376. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/requirements/test-tools.txt +0 -0
  377. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/a11y_check.py +0 -0
  378. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/bench_cli.sh +0 -0
  379. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/build_command_reference.py +0 -0
  380. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/build_registries.py +0 -0
  381. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/build_roadmap.py +0 -0
  382. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/check_anchors.py +0 -0
  383. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/check_licenses.py +0 -0
  384. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/check_required_checks.py +0 -0
  385. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/ensure_eval_corpus.sh +0 -0
  386. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/eval_explain.py +0 -0
  387. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/eval_gate.py +0 -0
  388. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/eval_recommend.py +0 -0
  389. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/eval_retrieval.py +0 -0
  390. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/eval_stats_summary.py +0 -0
  391. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/import_budget.py +0 -0
  392. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/lock_toolchain.py +0 -0
  393. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/mutation_gate.py +0 -0
  394. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/mutation_shards.py +0 -0
  395. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/mutation_weights.json +0 -0
  396. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/perf_gate.py +0 -0
  397. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/post_deploy_smoke.py +0 -0
  398. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/release_guard.py +0 -0
  399. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/scripts/release_preflight.py +0 -0
  400. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/setup.cfg +0 -0
  401. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/sonar-project.properties +0 -0
  402. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/style/README.md +0 -0
  403. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/style/boost.css +0 -0
  404. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/style/boost.js +0 -0
  405. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/style/demo.html +0 -0
  406. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/adapt.feature +0 -0
  407. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/browse.feature +0 -0
  408. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/count.feature +0 -0
  409. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/discover.feature +0 -0
  410. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/doctor.feature +0 -0
  411. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/environment.py +0 -0
  412. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/explain.feature +0 -0
  413. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/index.feature +0 -0
  414. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/install.feature +0 -0
  415. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/list.feature +0 -0
  416. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/mcp.feature +0 -0
  417. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/search.feature +0 -0
  418. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/steps/cli_steps.py +0 -0
  419. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/steps/discovery_steps.py +0 -0
  420. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/steps/mcp_steps.py +0 -0
  421. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/bdd/features/steps/quality_steps.py +0 -0
  422. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/conftest.py +0 -0
  423. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/eval/baseline.json +0 -0
  424. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/eval/explain.jsonl +0 -0
  425. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/eval/golden-natural.jsonl +0 -0
  426. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/eval/golden.jsonl +0 -0
  427. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/eval/recommend.jsonl +0 -0
  428. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/eval/taps.txt +0 -0
  429. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_capabilities_policy.py +0 -0
  430. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_adapt.py +0 -0
  431. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_bmad.py +0 -0
  432. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_configuration.py +0 -0
  433. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_discovery.py +0 -0
  434. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_hooks.py +0 -0
  435. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_info.py +0 -0
  436. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_intelligence.py +0 -0
  437. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_pkg.py +0 -0
  438. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_quality.py +0 -0
  439. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_taps.py +0 -0
  440. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_cli_team.py +0 -0
  441. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_everyday_loop.py +0 -0
  442. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_integrity_enforce.py +0 -0
  443. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_property_parsers.py +0 -0
  444. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_run.py +0 -0
  445. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_tap_signing.py +0 -0
  446. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/functional/test_workspace_scope.py +0 -0
  447. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/README.md +0 -0
  448. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/01-typical.md +0 -0
  449. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/02-lossy-version.md +0 -0
  450. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/03-lossy-numbers.md +0 -0
  451. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/04-lists.md +0 -0
  452. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/05-nested.md +0 -0
  453. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/06-empty-block.md +0 -0
  454. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/07-unterminated.md +0 -0
  455. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/08-none.md +0 -0
  456. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/09-unbalanced-quote.md +0 -0
  457. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/10-keywords.md +0 -0
  458. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/11-crlf.md +0 -0
  459. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/frontmatter/12-bom.md +0 -0
  460. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/01-shorthand.txt +0 -0
  461. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/02-https.txt +0 -0
  462. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/03-ssh.txt +0 -0
  463. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/04-traversal.txt +0 -0
  464. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/05-empty.txt +0 -0
  465. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/06-space.txt +0 -0
  466. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/07-slash.txt +0 -0
  467. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/corpus/registry/08-bare-host.txt +0 -0
  468. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/fuzz_frontmatter.py +0 -0
  469. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/fuzz/fuzz_registry.py +0 -0
  470. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/make_fixture.py +0 -0
  471. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/perf/test_benchmarks.py +0 -0
  472. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/smoke.sh +0 -0
  473. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_a11y_check.py +0 -0
  474. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_adapters.py +0 -0
  475. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_adapters_multi.py +0 -0
  476. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_adapters_runner.py +0 -0
  477. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_agents.py +0 -0
  478. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_ai.py +0 -0
  479. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_capabilities.py +0 -0
  480. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_catalog.py +0 -0
  481. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_check_anchors.py +0 -0
  482. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_check_licenses.py +0 -0
  483. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_claude_settings.py +0 -0
  484. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_cliparse.py +0 -0
  485. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_codeql_analysis_key.py +0 -0
  486. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_command_reference_fresh.py +0 -0
  487. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_community_health_files.py +0 -0
  488. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_config.py +0 -0
  489. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_dense.py +0 -0
  490. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_dense_fallback.py +0 -0
  491. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_dense_fix_hint.py +0 -0
  492. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_dense_status.py +0 -0
  493. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_dependabot_config.py +0 -0
  494. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_docs_pages_linked.py +0 -0
  495. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_docsite_chrome.py +0 -0
  496. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_ed25519.py +0 -0
  497. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_embed.py +0 -0
  498. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_embed_local.py +0 -0
  499. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_entry_key.py +0 -0
  500. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_errors_and_cli_table.py +0 -0
  501. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_eval_baseline.py +0 -0
  502. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_eval_faithfulness.py +0 -0
  503. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_eval_gate.py +0 -0
  504. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_eval_metrics.py +0 -0
  505. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_eval_stats_summary.py +0 -0
  506. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_faithfulness.py +0 -0
  507. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_frontmatter.py +0 -0
  508. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_fuzz_targets.py +0 -0
  509. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_gitleaks_config.py +0 -0
  510. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_gitutil.py +0 -0
  511. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_imperative.py +0 -0
  512. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_import_budget.py +0 -0
  513. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_injectscan.py +0 -0
  514. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_installscan.py +0 -0
  515. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_integrity.py +0 -0
  516. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_journal.py +0 -0
  517. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_localembed.py +0 -0
  518. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_lockfile.py +0 -0
  519. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_logs.py +0 -0
  520. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_marketing_counts.py +0 -0
  521. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mcp.py +0 -0
  522. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mcp_install_scan.py +0 -0
  523. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mcpdecl.py +0 -0
  524. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mcphost.py +0 -0
  525. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_minisign.py +0 -0
  526. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mutation_hardening.py +0 -0
  527. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mutation_shards.py +0 -0
  528. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mutation_subfile_shards.py +0 -0
  529. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_mutmut_source_paths.py +0 -0
  530. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_nethttp.py +0 -0
  531. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_output.py +0 -0
  532. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_paths.py +0 -0
  533. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_perf_gate.py +0 -0
  534. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_pkg_confusions.py +0 -0
  535. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_pkg_injection.py +0 -0
  536. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_pkg_secrets.py +0 -0
  537. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_platform_pins.py +0 -0
  538. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_policy.py +0 -0
  539. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_post_deploy_smoke.py +0 -0
  540. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_project_mcp_sidecar.py +0 -0
  541. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_projectlock.py +0 -0
  542. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_provenance.py +0 -0
  543. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_rag_fusion.py +0 -0
  544. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_registries_fresh.py +0 -0
  545. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_registry.py +0 -0
  546. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_registry_categories.py +0 -0
  547. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_release_guard.py +0 -0
  548. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_release_preflight.py +0 -0
  549. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_required_checks.py +0 -0
  550. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_resolve.py +0 -0
  551. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_roadmap_fresh.py +0 -0
  552. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_rules.py +0 -0
  553. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_scopes.py +0 -0
  554. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_secretscan.py +0 -0
  555. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_selfupdate.py +0 -0
  556. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_serve.py +0 -0
  557. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_spin.py +0 -0
  558. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_stackprobe.py +0 -0
  559. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_staleness.py +0 -0
  560. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_store.py +0 -0
  561. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_token_parity.py +0 -0
  562. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_toolchain_lock.py +0 -0
  563. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_trustaudit.py +0 -0
  564. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_typosquat.py +0 -0
  565. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_updatediff.py +0 -0
  566. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_util.py +0 -0
  567. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_version.py +0 -0
  568. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_workflow_concurrency.py +0 -0
  569. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_workflow_timeouts.py +0 -0
  570. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/unit/test_workflows.py +0 -0
  571. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/visual/a11y_check.mjs +0 -0
  572. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/visual/console_check.mjs +0 -0
  573. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/visual/package-lock.json +0 -0
  574. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/visual/package.json +0 -0
  575. {boost_skill_cli-1.0.322 → boost_skill_cli-1.0.324}/tests/visual/visual_check.mjs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.322
3
+ Version: 1.0.324
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.322'
22
- __version_tuple__ = version_tuple = (1, 0, 322)
21
+ __version__ = version = '1.0.324'
22
+ __version_tuple__ = version_tuple = (1, 0, 324)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -15,13 +15,16 @@ no AI -> the BM25 order is returned as-is.
15
15
  """
16
16
  from __future__ import annotations
17
17
 
18
+ import contextlib
19
+ import hashlib
18
20
  import json
19
21
  import math
20
22
  import re
23
+ import sqlite3
21
24
  from collections import defaultdict
22
25
  from collections.abc import Sequence
23
26
  from pathlib import Path
24
- from typing import Dict, List, Optional, Tuple
27
+ from typing import Dict, List, Optional, Tuple, cast
25
28
 
26
29
  try: # TypedDict lives in typing on 3.9+, kept optional for safety
27
30
  from typing import TypedDict
@@ -33,7 +36,7 @@ from . import ai, catalog, frontmatter, gitutil, paths, registry, util
33
36
  # v2: `snip` stores a larger head of the matched chunk (not just SNIP_WIDTH
34
37
  # chars) so `retrieve` can window it onto the query terms; bump forces a
35
38
  # one-time reindex.
36
- INDEX_VERSION = 4
39
+ INDEX_VERSION = 6
37
40
  ENGINE = "bm25"
38
41
 
39
42
  # Chunking defaults (documented in docs/rag-architecture.md §4).
@@ -62,10 +65,13 @@ _STOPWORDS = frozenset({
62
65
  })
63
66
 
64
67
 
65
- class Hit(TypedDict): # type: ignore[misc]
68
+ class Hit(TypedDict, total=False): # type: ignore[misc]
66
69
  entry: dict # the live catalog entry (name, tap, kind, skill_md, ...)
67
70
  score: float # BM25 relevance (higher = better)
68
71
  snippet: str # best-matching passage, for display + rerank context
72
+ content: str # digest of the body, for collapsing byte-identical copies.
73
+ # Optional: dense hits arrive without one and have it
74
+ # attached from the BM25 index (see `_with_content`).
69
75
 
70
76
 
71
77
  # --------------------------------------------------------------- tokenizing
@@ -193,13 +199,16 @@ def _make_docs(entries: List[dict], tap_paths: Dict[str, Path]) -> List[dict]:
193
199
  docs: List[dict] = []
194
200
  for e in entries:
195
201
  body = read_body(e, tap_paths)
202
+ # Hashed here because the body is already in hand; doing it at query
203
+ # time would mean re-reading ~30k files to answer one search.
204
+ digest = hashlib.sha256(body.strip().encode("utf-8", "replace")).hexdigest()[:16]
196
205
  tf: Dict[str, int] = defaultdict(int)
197
206
  for tok in tokenize(surface(e) + "\n" + body):
198
207
  tf[tok] += 1
199
208
  if not tf:
200
209
  continue
201
210
  docs.append({
202
- "n": e["name"], "t": e["tap"], "f": e["skill_md"],
211
+ "n": e["name"], "t": e["tap"], "f": e["skill_md"], "h": digest,
203
212
  "k": e.get("kind", "skill"),
204
213
  "c": 0, "l": sum(tf.values()),
205
214
  "snip": body[:SNIP_STORE].strip(), # windowed at retrieve time
@@ -211,7 +220,7 @@ def _make_docs(entries: List[dict], tap_paths: Dict[str, Path]) -> List[dict]:
211
220
  def _postings_to_doc_tf(raw: dict) -> Dict[int, Dict[str, int]]:
212
221
  """Invert persisted postings back to per-doc term frequencies."""
213
222
  doc_tf: Dict[int, Dict[str, int]] = defaultdict(dict)
214
- for term, plist in raw.get("postings", {}).items():
223
+ for term, plist in (raw.get("postings") or _all_postings()).items():
215
224
  for doc_id, tf in plist:
216
225
  doc_tf[doc_id][term] = tf
217
226
  return doc_tf
@@ -259,6 +268,94 @@ def build(entries: Optional[List[dict]] = None, force: bool = False) -> dict:
259
268
  }
260
269
 
261
270
 
271
+ def postings_path() -> Path:
272
+ return paths.cache_dir() / "rag_postings.sqlite"
273
+
274
+
275
+ def _write_postings(postings: Dict[str, List[List[int]]]) -> None:
276
+ """Persist the term -> [(doc, tf)] map to SQLite, replacing any existing one.
277
+
278
+ Postings were the largest part of the JSON index — measured 64% of it after
279
+ unchunking — and `json.loads` had to materialise every one of them to answer
280
+ a query that touches a handful of terms. On a real 83-tap install that was
281
+ 8-13 s and multiple GB resident per cold `boost search`, against 31-70 ms of
282
+ actual scoring.
283
+
284
+ SQLite is used purely as an indexed key-value store here, not as a search
285
+ engine: BM25 scoring stays in `_bm25` byte for byte, so this changes cold
286
+ start cost and nothing about which results come back.
287
+ """
288
+ paths.ensure_dirs()
289
+ final = postings_path()
290
+ tmp = final.with_suffix(".sqlite.tmp")
291
+ with contextlib.suppress(OSError):
292
+ tmp.unlink()
293
+ con = sqlite3.connect(str(tmp))
294
+ try:
295
+ # No durability needed: this file is a derived cache, and a crash
296
+ # mid-build leaves the .tmp behind rather than a torn index.
297
+ con.execute("PRAGMA journal_mode=OFF")
298
+ con.execute("PRAGMA synchronous=OFF")
299
+ con.execute("CREATE TABLE postings (term TEXT, doc INTEGER, tf INTEGER)")
300
+ con.executemany(
301
+ "INSERT INTO postings (term, doc, tf) VALUES (?, ?, ?)",
302
+ ((term, doc, tf) for term, plist in postings.items()
303
+ for doc, tf in plist))
304
+ # Built after the insert: maintaining it per-row is far slower.
305
+ con.execute("CREATE INDEX postings_term ON postings(term)")
306
+ con.commit()
307
+ finally:
308
+ con.close()
309
+ tmp.replace(final) # atomic swap, same as the JSON half
310
+
311
+
312
+ def read_postings(terms: Sequence[str]) -> Dict[str, List[List[int]]]:
313
+ """Postings for just these terms — the whole point of the SQLite store.
314
+
315
+ A query touches a handful of terms out of tens of thousands, so this reads
316
+ kilobytes where the JSON index read the entire file.
317
+ """
318
+ uniq = sorted(set(terms))
319
+ if not uniq:
320
+ return {}
321
+ try:
322
+ con = sqlite3.connect("file:%s?mode=ro" % postings_path(), uri=True)
323
+ except sqlite3.Error:
324
+ return {}
325
+ out: Dict[str, List[List[int]]] = defaultdict(list)
326
+ try:
327
+ # Chunked: SQLite caps host parameters (999 on older builds), and a
328
+ # long query can exceed it.
329
+ for i in range(0, len(uniq), 500):
330
+ batch = uniq[i:i + 500]
331
+ q = ("SELECT term, doc, tf FROM postings WHERE term IN (%s)" # noqa: S608 interpolates only `?` placeholders; terms are bound params
332
+ % ",".join("?" * len(batch)))
333
+ for term, doc, tf in con.execute(q, batch):
334
+ out[term].append([doc, tf])
335
+ except sqlite3.Error:
336
+ return {}
337
+ finally:
338
+ con.close()
339
+ return dict(out) # noqa: FURB123 out is a defaultdict; .copy() would keep the factory
340
+
341
+
342
+ def _all_postings() -> Dict[str, List[List[int]]]:
343
+ """Every posting — only the incremental rebuild path needs this."""
344
+ try:
345
+ con = sqlite3.connect("file:%s?mode=ro" % postings_path(), uri=True)
346
+ except sqlite3.Error:
347
+ return {}
348
+ out: Dict[str, List[List[int]]] = defaultdict(list)
349
+ try:
350
+ for term, doc, tf in con.execute("SELECT term, doc, tf FROM postings"):
351
+ out[term].append([doc, tf])
352
+ except sqlite3.Error:
353
+ return {}
354
+ finally:
355
+ con.close()
356
+ return dict(out) # noqa: FURB123 out is a defaultdict; .copy() would keep the factory
357
+
358
+
262
359
  def _save(docs: List[dict], commits: Dict[str, str]) -> None:
263
360
  postings: Dict[str, List[List[int]]] = defaultdict(list)
264
361
  meta_docs: List[dict] = []
@@ -268,6 +365,7 @@ def _save(docs: List[dict], commits: Dict[str, str]) -> None:
268
365
  postings[term].append([doc_id, tf])
269
366
  total_len += d["l"]
270
367
  meta_docs.append({"n": d["n"], "t": d["t"], "f": d["f"], "k": d["k"],
368
+ "h": d.get("h", ""),
271
369
  "c": d["c"], "l": d["l"], "snip": d["snip"]})
272
370
  payload = {
273
371
  "version": INDEX_VERSION,
@@ -279,12 +377,12 @@ def _save(docs: List[dict], commits: Dict[str, str]) -> None:
279
377
  "stats": {"docs": len(docs),
280
378
  "avg_len": (total_len / len(docs)) if docs else 0.0},
281
379
  "docs": meta_docs,
282
- "postings": dict(postings.items()),
283
380
  }
284
381
  paths.ensure_dirs()
285
382
  p = index_path()
286
383
  # Atomic swap: a bare write_text here can leave a truncated/corrupt index
287
384
  # on disk if the process dies mid-write or a query reads concurrently.
385
+ _write_postings(postings)
288
386
  util.atomic_write_text(p, json.dumps(payload))
289
387
  # Drop the mtime-keyed cache so a reindex is visible to an immediately
290
388
  # following query even when the filesystem mtime granularity is coarse.
@@ -322,9 +420,14 @@ def _load_raw() -> Optional[dict]:
322
420
 
323
421
 
324
422
  def ready() -> bool:
325
- """True when a usable index exists on disk."""
423
+ """True when a usable index exists on disk.
424
+
425
+ Both halves are required: the JSON carries the documents, the SQLite file
426
+ carries the postings, and scoring needs both. A docs-only index reports not
427
+ ready rather than silently returning zero hits for every query.
428
+ """
326
429
  raw = _load_raw()
327
- return bool(raw and raw.get("docs"))
430
+ return bool(raw and raw.get("docs") and postings_path().exists())
328
431
 
329
432
 
330
433
  def ensure() -> bool:
@@ -346,11 +449,17 @@ def ensure() -> bool:
346
449
  return ready()
347
450
 
348
451
 
349
- def _bm25(query_terms: List[str], raw: dict) -> Dict[int, float]:
452
+ def _bm25(query_terms: List[str], raw: dict,
453
+ postings: Dict[str, List[List[int]]]) -> Dict[int, float]:
454
+ """Score documents for ``query_terms``. Pure: postings are passed in.
455
+
456
+ Reading them inside would hide I/O in the one function whose arithmetic is
457
+ pinned exactly (`TestBm25Math`), and would tie the scorer to where postings
458
+ happen to live — which is the thing this change moved.
459
+ """
350
460
  docs = raw["docs"]
351
461
  n = len(docs)
352
462
  avg = raw.get("stats", {}).get("avg_len") or 1.0
353
- postings = raw["postings"]
354
463
  scores: Dict[int, float] = defaultdict(float)
355
464
  for term in set(query_terms):
356
465
  plist = postings.get(term)
@@ -418,6 +527,54 @@ def entry_key(entry: dict) -> Tuple[str, str]:
418
527
  return (entry["tap"], entry["skill_md"])
419
528
 
420
529
 
530
+ def dedupe_by_content(hits: List[Hit], limit: int) -> List[Hit]:
531
+ """Collapse byte-identical copies, then take ``limit``.
532
+
533
+ Measured over 77 tapped registries: 78.3% of 29,938 entries are
534
+ byte-identical duplicates (largest cluster 40 copies), and the
535
+ natural-language query set averaged 4.94 of 10 result slots consumed by a
536
+ repeat — worst case 9 of 10. Registries mirror each other, so N copies of one
537
+ rule across N taps ate N of the ten slots a user sees.
538
+
539
+ Clustering is on the **body**, never the name. 82.7% of entries share a name
540
+ but only 78.3% share a body, and that ~4.4% gap is real distinct content —
541
+ the ``admin-interface-rule`` pair fixed in #366 is two different rules under
542
+ one name. Clustering on content cannot make that mistake: of 14,153 distinct
543
+ bodies, the number of clusters spanning more than one name is zero.
544
+
545
+ Within a cluster every copy is byte-identical, so *which* one survives is
546
+ purely about where the user would rather install from: a ``curated`` tap wins
547
+ over a better-ranked uncurated one. The cluster keeps its best score either
548
+ way, so promoting the trusted copy never demotes the result.
549
+
550
+ A hit with no recorded content hash is always kept — two unknowns must not
551
+ collapse into each other, which would hide real results.
552
+ """
553
+ best: Dict[str, int] = {} # content hash -> index into `out`
554
+ out: List[Hit] = []
555
+ for hit in hits:
556
+ digest = hit.get("content")
557
+ if not digest:
558
+ out.append(hit) # unknown content is never a match
559
+ continue
560
+ seen = best.get(digest)
561
+ if seen is None:
562
+ best[digest] = len(out)
563
+ out.append(hit)
564
+ continue
565
+ # Same body already shown. Keep its rank, but prefer a curated source.
566
+ kept = out[seen]
567
+ if hit["entry"].get("curated") and not kept["entry"].get("curated"):
568
+ # cast, not a literal annotation: `Hit` is total=False for the
569
+ # optional `content` key, and NotRequired needs 3.11 while boost
570
+ # targets 3.9.
571
+ out[seen] = cast(Hit, {"entry": hit["entry"],
572
+ "score": kept["score"],
573
+ "snippet": kept["snippet"],
574
+ "content": digest})
575
+ return out[:limit]
576
+
577
+
421
578
  def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
422
579
  entries: Optional[List[dict]] = None) -> List[Hit]:
423
580
  """Top-k full-content hits for ``query``. ``[]`` if the index is empty."""
@@ -430,8 +587,8 @@ def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
430
587
  entries = catalog.all_entries() if entries is None else entries
431
588
  live = {entry_key(e): e for e in entries}
432
589
  docs = raw["docs"]
433
- best: Dict[Tuple[str, str], Tuple[float, str]] = {}
434
- for doc_id, score in _bm25(terms, raw).items():
590
+ best: Dict[Tuple[str, str], Tuple[float, str, str]] = {}
591
+ for doc_id, score in _bm25(terms, raw, read_postings(terms)).items():
435
592
  d = docs[doc_id]
436
593
  if kind is not None and d["k"] != kind:
437
594
  continue
@@ -440,17 +597,20 @@ def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
440
597
  continue
441
598
  prev = best.get(key)
442
599
  if prev is None or score > prev[0]:
443
- best[key] = (score, d["snip"])
600
+ best[key] = (score, d["snip"], d.get("h", ""))
444
601
  # Tie-break on the displayed name, not the identity key: the key is now
445
602
  # (tap, path), and ordering by it would silently reshuffle equal-scoring
446
603
  # results relative to every previous release.
447
604
  ranked = sorted(best.items(),
448
605
  key=lambda kv: (-kv[1][0], live[kv[0]]["name"], kv[0]))
606
+ # Over-fetch, then collapse byte-identical copies before taking k — the
607
+ # duplicates have to be removed from the pool, not from the visible page,
608
+ # or they still consume the slots they were removed from.
449
609
  hits: List[Hit] = [
450
- {"entry": live[key], "score": score,
610
+ {"entry": live[key], "score": score, "content": digest,
451
611
  "snippet": _passage(snip, terms)} # type: ignore[typeddict-item]
452
- for key, (score, snip) in ranked[:k]]
453
- return hits
612
+ for key, (score, snip, digest) in ranked]
613
+ return dedupe_by_content(hits, k)
454
614
 
455
615
 
456
616
  def rerank(query: str, hits: List[Hit], limit: int = 10) -> Tuple[List[Hit], str]:
@@ -516,6 +676,39 @@ def rrf_fuse(rankings: List[List[Hit]], limit: int = 60) -> List[Hit]:
516
676
  return fused
517
677
 
518
678
 
679
+ def content_hashes() -> Dict[Tuple[str, str], str]:
680
+ """``entry_key`` -> content hash, read from the BM25 index.
681
+
682
+ The dense store does not carry the hash and does not need to: BM25 indexes
683
+ the same entries and auto-builds on first search, so one map serves every
684
+ engine. Keeping it in one place also means the two engines can never
685
+ disagree about which copies are identical.
686
+ """
687
+ raw = _load_raw()
688
+ if not raw:
689
+ return {}
690
+ return {(d["t"], d["f"]): d["h"]
691
+ for d in raw.get("docs", []) if d.get("h")}
692
+
693
+
694
+ def _with_content(hits: List[Hit]) -> List[Hit]:
695
+ """Attach content hashes to hits that lack them (dense, and fused output)."""
696
+ if all(h.get("content") for h in hits):
697
+ return hits
698
+ hashes = content_hashes()
699
+ if not hashes:
700
+ return hits
701
+ filled: List[Hit] = []
702
+ for h in hits:
703
+ if h.get("content"):
704
+ filled.append(h)
705
+ continue
706
+ filled.append(cast(Hit, {
707
+ "entry": h["entry"], "score": h["score"], "snippet": h["snippet"],
708
+ "content": hashes.get(entry_key(h["entry"]), "")}))
709
+ return filled
710
+
711
+
519
712
  def retrieve_any(query: str, k: int = 60, kind: Optional[str] = None,
520
713
  entries: Optional[List[dict]] = None
521
714
  ) -> Tuple[Optional[List[Hit]], str]:
@@ -551,13 +744,18 @@ def retrieve_any(query: str, k: int = 60, kind: Optional[str] = None,
551
744
  # counts as a ranking to fuse.
552
745
  bm25_hits = retrieve(query, k=pool, kind=kind,
553
746
  entries=entries) if ready() else None
747
+ # Dedupe at this seam as well as inside `retrieve`, because dense hits carry
748
+ # no hash of their own and fusion can reintroduce a duplicate that BM25
749
+ # already dropped — the copies are distinct `(tap, path)` keys, so RRF has
750
+ # no reason to treat them as one.
554
751
  if dense_hits and bm25_hits:
555
- return (rrf_fuse([bm25_hits, dense_hits], limit=k),
752
+ fused = rrf_fuse([bm25_hits, dense_hits], limit=max(k, RRF_K))
753
+ return (dedupe_by_content(_with_content(fused), k),
556
754
  "hybrid RRF (BM25 + dense)")
557
755
  if dense_hits:
558
- return dense_hits[:k], "dense vectors"
756
+ return dedupe_by_content(_with_content(dense_hits), k), "dense vectors"
559
757
  if bm25_hits is not None:
560
- return bm25_hits[:k], "BM25 full-content"
758
+ return bm25_hits[:k], "BM25 full-content" # already deduped
561
759
  return None, ""
562
760
 
563
761
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.322
3
+ Version: 1.0.324
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -483,6 +483,7 @@ tests/unit/test_codeql_analysis_key.py
483
483
  tests/unit/test_command_reference_fresh.py
484
484
  tests/unit/test_community_health_files.py
485
485
  tests/unit/test_config.py
486
+ tests/unit/test_content_dedupe.py
486
487
  tests/unit/test_dense.py
487
488
  tests/unit/test_dense_fallback.py
488
489
  tests/unit/test_dense_fix_hint.py
@@ -9,7 +9,7 @@ impact: High
9
9
  wow: 5
10
10
  note: 2.5 GB RSS per search at 50k items
11
11
  order: 72
12
- owner: loop/unchunked-bm25-index
12
+ owner: loop/postings-out-of-json
13
13
  pr:
14
14
  title: The BM25 index is one JSON blob, and it stops working between 10k and 50k items
15
15
  ---
@@ -99,3 +99,34 @@ memory but not the scan, and in the common case (one tap of many changed) nearly
99
99
  kept, so it saves little of that either. Denormalising <code>tf</code> back into the documents would
100
100
  fix it directly but roughly duplicates the 64% that postings already occupy. The real fix is the
101
101
  storage format, which is win (2) &mdash; worth treating them as one item rather than two.
102
+
103
+ <b>Win (2) is shipped: postings now live in SQLite, and a query reads its terms rather than the whole
104
+ index.</b> Measured on the pinned corpus &mdash; JSON 2.05 MB &rarr; <b>0.76 MB</b>, load
105
+ 0.015 s &rarr; <b>0.0013 s</b>, cold retrieve <b>0.0003 s</b>.
106
+
107
+ <b>SQLite as a key-value store, not as FTS5.</b> The card offered FTS5 or a binary format, and FTS5
108
+ was the tempting one &mdash; it is compiled in, verified on CPython 3.14 (SQLite 3.53.4) and system
109
+ 3.9 (3.54.0). But FTS5 replaces BM25 with <em>its own</em> ranking: different tokenizer, different
110
+ parameters, different results. The measured problem is cold-start cost, not scoring quality, so
111
+ adopting FTS5 would risk what works to fix what does not. Here <code>_bm25</code> is unchanged byte
112
+ for byte, and the eval gate proves the change is behaviour-neutral: 0.978 / 0.791 / 0.854 / 0.882,
113
+ identical before and after. <code>_bm25</code> also now takes postings as an <em>argument</em>, so
114
+ the one function whose arithmetic is pinned exactly contains no I/O and is not coupled to where
115
+ postings are stored.
116
+
117
+ <b>The honest cost: total disk goes up.</b> The store is 3.89 MB against 1.31 MB of JSON postings,
118
+ because SQLite keeps the term string on every row plus an index over it. Total on-disk is 4.65 MB
119
+ versus 2.05 MB. This card says <b>&ldquo;target RSS, not bytes&rdquo;</b> and that is why the trade
120
+ is the right way round &mdash; the hot path now reads kilobytes instead of materialising megabytes of
121
+ Python objects &mdash; but it is a real cost, not a free win. Interning terms into a
122
+ <code>term_id</code> table would recover most of it and is the obvious follow-up if the size matters.
123
+
124
+ <b>Both halves are now required.</b> <code>ready()</code> checks for the postings store as well as
125
+ the documents, so a docs-only index reports not-ready instead of silently scoring every query to
126
+ zero hits. <code>INDEX_VERSION</code> 5 &rarr; 6 forces the one-time rebuild.
127
+
128
+ <b>Still open:</b> <code>snip</code> text off the hot path &mdash; now only 14% of the JSON half, so
129
+ much smaller than this card assumed &mdash; and the O(entire-corpus) incremental reindex. The latter
130
+ is closer than it was: per-document term frequencies are now queryable by document rather than only
131
+ recoverable by inverting every posting, so scoping the rebuild to the changed taps is a tractable
132
+ follow-up rather than a format change.
@@ -9,7 +9,7 @@ impact: High
9
9
  wow: 4
10
10
  note: 56.3% of entries share a name
11
11
  order: 71
12
- owner: loop/entry-key-collision
12
+ owner: loop/content-hash-dedup
13
13
  pr:
14
14
  title: Near-duplicate items consume the top-10, and it gets worse with every tap
15
15
  ---
@@ -123,3 +123,39 @@ genuinely differ (the <code>admin-interface-rule</code> pair fixed in <code>#366
123
123
  here as duplicate slots but must <em>not</em> be merged. That is precisely why the card asks for
124
124
  clustering on content hash rather than on name, and this curve is an upper bound on what dedup could
125
125
  reclaim, not a target.
126
+
127
+ <b>Shipped: content-hash dedup, measured 4.94 &rarr; 0.60 duplicate slots per query</b> over the
128
+ 77-tap corpus (worst case 9 of 10 &rarr; 4). The residual 0.60 is correct rather than leftover: those
129
+ are entries sharing a <em>name</em> whose bodies genuinely differ, which must stay separate.
130
+
131
+ Two measurements settled the design the card left open (&ldquo;content hash <em>or</em> canonical
132
+ name&rdquo;). Of 29,938 entries, <b>78.3% are byte-identical duplicates</b> &mdash; 14,153 distinct
133
+ bodies, largest cluster <b>40 copies</b> &mdash; while 82.7% share a name. That ~4.4% gap is real
134
+ distinct content, so name clustering would merge exactly the pairs <code>#366</code> proved must not
135
+ be merged. And clustering on content cannot make the opposite mistake here: the number of content
136
+ clusters spanning more than one name is <b>0</b>, so collapsing by body never merges two
137
+ differently-named items. Content hashing is strictly the safer of the two options the card offered,
138
+ which was not obvious before measuring.
139
+
140
+ The quality prior is <code>curated</code>: inside a cluster every copy is byte-identical, so the one
141
+ worth surfacing is the one from a tap the user marked trusted. The cluster keeps its best score when
142
+ that swap happens, so promoting the trusted copy never demotes the result.
143
+
144
+ Mechanically, the body hash is computed in <code>_make_docs</code> where the body is already read
145
+ &mdash; doing it at query time would mean re-reading ~30k files to answer one search &mdash; and
146
+ persisted per document (<code>INDEX_VERSION</code> 4 &rarr; 5). Dense hits carry no hash of their own
147
+ and do not need a second schema: <code>content_hashes()</code> serves one map to every engine from
148
+ the BM25 index, so the engines can never disagree about which copies are identical. Dedup runs inside
149
+ <code>retrieve</code> <em>and</em> at the <code>retrieve_any</code> seam, because fusion can
150
+ reintroduce a copy BM25 already dropped &mdash; the copies are distinct <code>(tap, path)</code>
151
+ keys, so RRF has no reason to treat them as one. Collapsing happens before <code>k</code> is applied,
152
+ or the duplicates would still consume the slots they were removed from.
153
+
154
+ Cost on the pinned eval corpus: <b>none</b> &mdash; 0.978 / 0.791 / 0.854 / 0.882, identical to
155
+ before, all four floors passing. That corpus has little content duplication, which is exactly why the
156
+ 83-tap measurement had to exist.
157
+
158
+ <b>Still open:</b> a richer quality prior than <code>curated</code> (the card names stars, recency,
159
+ maintenance; only <code>curated</code> is available on an entry today), and
160
+ <code>core/typosquat.py</code>'s confusion machinery for near-identical rather than byte-identical
161
+ bodies. Both are refinements &mdash; the byte-identical case is 78.3% of the problem.
@@ -3472,7 +3472,43 @@ symptom the card names, not content-identical bodies &mdash; two rules that shar
3472
3472
  genuinely differ (the <code>admin-interface-rule</code> pair fixed in <code>#366</code>) are counted
3473
3473
  here as duplicate slots but must <em>not</em> be merged. That is precisely why the card asks for
3474
3474
  clustering on content hash rather than on name, and this curve is an upper bound on what dedup could
3475
- reclaim, not a target.</p>
3475
+ reclaim, not a target.
3476
+
3477
+ <b>Shipped: content-hash dedup, measured 4.94 &rarr; 0.60 duplicate slots per query</b> over the
3478
+ 77-tap corpus (worst case 9 of 10 &rarr; 4). The residual 0.60 is correct rather than leftover: those
3479
+ are entries sharing a <em>name</em> whose bodies genuinely differ, which must stay separate.
3480
+
3481
+ Two measurements settled the design the card left open (&ldquo;content hash <em>or</em> canonical
3482
+ name&rdquo;). Of 29,938 entries, <b>78.3% are byte-identical duplicates</b> &mdash; 14,153 distinct
3483
+ bodies, largest cluster <b>40 copies</b> &mdash; while 82.7% share a name. That ~4.4% gap is real
3484
+ distinct content, so name clustering would merge exactly the pairs <code>#366</code> proved must not
3485
+ be merged. And clustering on content cannot make the opposite mistake here: the number of content
3486
+ clusters spanning more than one name is <b>0</b>, so collapsing by body never merges two
3487
+ differently-named items. Content hashing is strictly the safer of the two options the card offered,
3488
+ which was not obvious before measuring.
3489
+
3490
+ The quality prior is <code>curated</code>: inside a cluster every copy is byte-identical, so the one
3491
+ worth surfacing is the one from a tap the user marked trusted. The cluster keeps its best score when
3492
+ that swap happens, so promoting the trusted copy never demotes the result.
3493
+
3494
+ Mechanically, the body hash is computed in <code>_make_docs</code> where the body is already read
3495
+ &mdash; doing it at query time would mean re-reading ~30k files to answer one search &mdash; and
3496
+ persisted per document (<code>INDEX_VERSION</code> 4 &rarr; 5). Dense hits carry no hash of their own
3497
+ and do not need a second schema: <code>content_hashes()</code> serves one map to every engine from
3498
+ the BM25 index, so the engines can never disagree about which copies are identical. Dedup runs inside
3499
+ <code>retrieve</code> <em>and</em> at the <code>retrieve_any</code> seam, because fusion can
3500
+ reintroduce a copy BM25 already dropped &mdash; the copies are distinct <code>(tap, path)</code>
3501
+ keys, so RRF has no reason to treat them as one. Collapsing happens before <code>k</code> is applied,
3502
+ or the duplicates would still consume the slots they were removed from.
3503
+
3504
+ Cost on the pinned eval corpus: <b>none</b> &mdash; 0.978 / 0.791 / 0.854 / 0.882, identical to
3505
+ before, all four floors passing. That corpus has little content duplication, which is exactly why the
3506
+ 83-tap measurement had to exist.
3507
+
3508
+ <b>Still open:</b> a richer quality prior than <code>curated</code> (the card names stars, recency,
3509
+ maintenance; only <code>curated</code> is available on an entry today), and
3510
+ <code>core/typosquat.py</code>'s confusion machinery for near-identical rather than byte-identical
3511
+ bodies. Both are refinements &mdash; the byte-identical case is 78.3% of the problem.</p>
3476
3512
  <div class="meta">
3477
3513
  <span class="m">Complexity <b>M</b></span>
3478
3514
  <span class="m hi">Impact <b>High</b></span>
@@ -3569,7 +3605,38 @@ taps means scanning every posting whatever the filter. Narrowing the inversion t
3569
3605
  memory but not the scan, and in the common case (one tap of many changed) nearly every document is
3570
3606
  kept, so it saves little of that either. Denormalising <code>tf</code> back into the documents would
3571
3607
  fix it directly but roughly duplicates the 64% that postings already occupy. The real fix is the
3572
- storage format, which is win (2) &mdash; worth treating them as one item rather than two.</p>
3608
+ storage format, which is win (2) &mdash; worth treating them as one item rather than two.
3609
+
3610
+ <b>Win (2) is shipped: postings now live in SQLite, and a query reads its terms rather than the whole
3611
+ index.</b> Measured on the pinned corpus &mdash; JSON 2.05 MB &rarr; <b>0.76 MB</b>, load
3612
+ 0.015 s &rarr; <b>0.0013 s</b>, cold retrieve <b>0.0003 s</b>.
3613
+
3614
+ <b>SQLite as a key-value store, not as FTS5.</b> The card offered FTS5 or a binary format, and FTS5
3615
+ was the tempting one &mdash; it is compiled in, verified on CPython 3.14 (SQLite 3.53.4) and system
3616
+ 3.9 (3.54.0). But FTS5 replaces BM25 with <em>its own</em> ranking: different tokenizer, different
3617
+ parameters, different results. The measured problem is cold-start cost, not scoring quality, so
3618
+ adopting FTS5 would risk what works to fix what does not. Here <code>_bm25</code> is unchanged byte
3619
+ for byte, and the eval gate proves the change is behaviour-neutral: 0.978 / 0.791 / 0.854 / 0.882,
3620
+ identical before and after. <code>_bm25</code> also now takes postings as an <em>argument</em>, so
3621
+ the one function whose arithmetic is pinned exactly contains no I/O and is not coupled to where
3622
+ postings are stored.
3623
+
3624
+ <b>The honest cost: total disk goes up.</b> The store is 3.89 MB against 1.31 MB of JSON postings,
3625
+ because SQLite keeps the term string on every row plus an index over it. Total on-disk is 4.65 MB
3626
+ versus 2.05 MB. This card says <b>&ldquo;target RSS, not bytes&rdquo;</b> and that is why the trade
3627
+ is the right way round &mdash; the hot path now reads kilobytes instead of materialising megabytes of
3628
+ Python objects &mdash; but it is a real cost, not a free win. Interning terms into a
3629
+ <code>term_id</code> table would recover most of it and is the obvious follow-up if the size matters.
3630
+
3631
+ <b>Both halves are now required.</b> <code>ready()</code> checks for the postings store as well as
3632
+ the documents, so a docs-only index reports not-ready instead of silently scoring every query to
3633
+ zero hits. <code>INDEX_VERSION</code> 5 &rarr; 6 forces the one-time rebuild.
3634
+
3635
+ <b>Still open:</b> <code>snip</code> text off the hot path &mdash; now only 14% of the JSON half, so
3636
+ much smaller than this card assumed &mdash; and the O(entire-corpus) incremental reindex. The latter
3637
+ is closer than it was: per-document term frequencies are now queryable by document rather than only
3638
+ recoverable by inverting every posting, so scoping the rebuild to the changed taps is a tractable
3639
+ follow-up rather than a format change.</p>
3573
3640
  <div class="meta">
3574
3641
  <span class="m">Complexity <b>L</b></span>
3575
3642
  <span class="m hi">Impact <b>High</b></span>