codebase-index 2.1.0__tar.gz → 2.1.1__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 (420) hide show
  1. codebase_index-2.1.1/.claude/skills/codebase-index/.skill_version +1 -0
  2. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude-plugin/plugin.json +1 -1
  3. codebase_index-2.1.1/.codex/skills/codebase-index/.skill_version +1 -0
  4. codebase_index-2.1.1/.opencode/skills/codebase-index/.skill_version +1 -0
  5. {codebase_index-2.1.0 → codebase_index-2.1.1}/CHANGELOG.md +20 -1
  6. {codebase_index-2.1.0 → codebase_index-2.1.1}/PKG-INFO +5 -2
  7. {codebase_index-2.1.0 → codebase_index-2.1.1}/README.md +4 -1
  8. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/installer.md +1 -1
  9. codebase_index-2.1.1/hooks/hooks.json +31 -0
  10. {codebase_index-2.1.0 → codebase_index-2.1.1}/pyproject.toml +1 -0
  11. {codebase_index-2.1.0 → codebase_index-2.1.1}/requirements.lock +1 -1
  12. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/__init__.py +1 -1
  13. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/cli.py +35 -0
  14. codebase_index-2.1.1/src/codebase_index/hooks.py +256 -0
  15. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/scaffold.py +61 -0
  16. codebase_index-2.1.1/tests/test_hooks_guard.py +120 -0
  17. codebase_index-2.1.0/.claude/skills/codebase-index/.skill_version +0 -1
  18. codebase_index-2.1.0/.codex/skills/codebase-index/.skill_version +0 -1
  19. codebase_index-2.1.0/.opencode/skills/codebase-index/.skill_version +0 -1
  20. codebase_index-2.1.0/hooks/hooks.json +0 -14
  21. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/SKILL.md +0 -0
  22. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/examples/hooks/settings.json +0 -0
  23. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/references/commands.md +0 -0
  24. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/references/memory.md +0 -0
  25. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/references/response-contract.md +0 -0
  26. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/scripts/cbx +0 -0
  27. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/scripts/cbx.ps1 +0 -0
  28. {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude-plugin/marketplace.json +0 -0
  29. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/SKILL.md +0 -0
  30. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/examples/hooks/settings.json +0 -0
  31. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/references/commands.md +0 -0
  32. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/references/memory.md +0 -0
  33. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/references/response-contract.md +0 -0
  34. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/scripts/cbx +0 -0
  35. {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/scripts/cbx.ps1 +0 -0
  36. {codebase_index-2.1.0 → codebase_index-2.1.1}/.editorconfig +0 -0
  37. {codebase_index-2.1.0 → codebase_index-2.1.1}/.gitattributes +0 -0
  38. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/benchmark_report.yml +0 -0
  39. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  40. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  41. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  42. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/language_support.yml +0 -0
  43. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/skill_listing_request.yml +0 -0
  44. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  45. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/SUPPORT.md +0 -0
  46. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/labels.yml +0 -0
  47. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/workflows/ci.yml +0 -0
  48. {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/workflows/release.yml +0 -0
  49. {codebase_index-2.1.0 → codebase_index-2.1.1}/.gitignore +0 -0
  50. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/agents/codebase-index.md +0 -0
  51. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/commands/codebase-index.md +0 -0
  52. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/SKILL.md +0 -0
  53. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/examples/hooks/settings.json +0 -0
  54. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/references/commands.md +0 -0
  55. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/references/memory.md +0 -0
  56. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/references/response-contract.md +0 -0
  57. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/scripts/cbx +0 -0
  58. {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/scripts/cbx.ps1 +0 -0
  59. {codebase_index-2.1.0 → codebase_index-2.1.1}/AGENTS.md +0 -0
  60. {codebase_index-2.1.0 → codebase_index-2.1.1}/CODE_OF_CONDUCT.md +0 -0
  61. {codebase_index-2.1.0 → codebase_index-2.1.1}/CONTRIBUTING.md +0 -0
  62. {codebase_index-2.1.0 → codebase_index-2.1.1}/LICENSE +0 -0
  63. {codebase_index-2.1.0 → codebase_index-2.1.1}/ROADMAP.md +0 -0
  64. {codebase_index-2.1.0 → codebase_index-2.1.1}/SECURITY.md +0 -0
  65. {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/claude.ps1 +0 -0
  66. {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/claude.sh +0 -0
  67. {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/codex.ps1 +0 -0
  68. {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/codex.sh +0 -0
  69. {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/opencode.ps1 +0 -0
  70. {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/opencode.sh +0 -0
  71. {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/architecture.svg +0 -0
  72. {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/benchmark.svg +0 -0
  73. {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/demo-terminal.svg +0 -0
  74. {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/mark.png +0 -0
  75. {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/social-preview.png +0 -0
  76. {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/cbx +0 -0
  77. {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/cbx.ps1 +0 -0
  78. {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/codebase-index +0 -0
  79. {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/codebase-index.ps1 +0 -0
  80. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/ARCHITECTURE.md +0 -0
  81. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/BENCHMARKS.md +0 -0
  82. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/COMMUNITY_AUDIT.md +0 -0
  83. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/COMMUNITY_LAUNCH.md +0 -0
  84. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/COMPARISON.md +0 -0
  85. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/DATABASE_SCHEMA.md +0 -0
  86. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/DEMO.md +0 -0
  87. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/DEVELOPMENT.md +0 -0
  88. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/FAQ.md +0 -0
  89. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/INSTALLATION.md +0 -0
  90. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/LANGUAGES.md +0 -0
  91. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/MCP.md +0 -0
  92. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/MEMORY.md +0 -0
  93. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/PRODUCT_UPGRADE_PLAN.md +0 -0
  94. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/QUICKSTART.md +0 -0
  95. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/RELEASE_CHECKLIST.md +0 -0
  96. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/RETRIEVAL.md +0 -0
  97. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/RETRIEVAL_PIPELINE.md +0 -0
  98. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/ROADMAP.md +0 -0
  99. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SCHEMA.md +0 -0
  100. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SECURITY.md +0 -0
  101. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SECURITY_MODEL.md +0 -0
  102. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SKILL_DESIGN.md +0 -0
  103. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/demo.tape +0 -0
  104. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-benchmark-index-improvements.md +0 -0
  105. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-generalize-skill-research-discipline.md +0 -0
  106. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m1-storage-discovery.md +0 -0
  107. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m2-fts-lexical.md +0 -0
  108. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m3-treesitter-symbols.md +0 -0
  109. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m4-hybrid-search.md +0 -0
  110. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m5-graph-impact.md +0 -0
  111. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m6-embeddings-vector.md +0 -0
  112. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m7-5-plugin-one-command-install.md +0 -0
  113. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m7-skill-packaging.md +0 -0
  114. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m8-hooks-watch.md +0 -0
  115. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m9-release.md +0 -0
  116. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-multilang-symbols-make-skill-win.md +0 -0
  117. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-06-24-snippet-skeletonization.md +0 -0
  118. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-05-29-plugin-one-command-install-design.md +0 -0
  119. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-06-14-typed-framework-edges-design.md +0 -0
  120. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-06-24-snippet-skeletonization-design.md +0 -0
  121. {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-09-10-evidence-memory-design.md +0 -0
  122. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/config.example.json +0 -0
  123. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/EXPECTED_OUTPUT.md +0 -0
  124. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/README.md +0 -0
  125. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/run_demo.ps1 +0 -0
  126. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/run_demo.sh +0 -0
  127. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/hooks/settings.json +0 -0
  128. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/queries.md +0 -0
  129. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/sample-queries.md +0 -0
  130. {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/sample-retrieval-output.md +0 -0
  131. {codebase_index-2.1.0 → codebase_index-2.1.1}/install.ps1 +0 -0
  132. {codebase_index-2.1.0 → codebase_index-2.1.1}/install.sh +0 -0
  133. {codebase_index-2.1.0 → codebase_index-2.1.1}/lib/common.ps1 +0 -0
  134. {codebase_index-2.1.0 → codebase_index-2.1.1}/lib/common.sh +0 -0
  135. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/2.0-audit.md +0 -0
  136. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/README.md +0 -0
  137. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/__init__.py +0 -0
  138. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/architecture.md +0 -0
  139. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/build_indexes.py +0 -0
  140. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/data/.gitignore +0 -0
  141. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_accretion.py +0 -0
  142. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_calibration.py +0 -0
  143. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_slots.py +0 -0
  144. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_structure.py +0 -0
  145. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/experiment_memory.py +0 -0
  146. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/experiments.md +0 -0
  147. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/fit_gate.py +0 -0
  148. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/fit_weights.py +0 -0
  149. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/hypotheses.md +0 -0
  150. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/novelty.md +0 -0
  151. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/__init__.py +0 -0
  152. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/baselines.py +0 -0
  153. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/evalrun.py +0 -0
  154. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/memory.py +0 -0
  155. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/relations.py +0 -0
  156. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/search.py +0 -0
  157. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/related-work.md +0 -0
  158. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/selection.md +0 -0
  159. {codebase_index-2.1.0 → codebase_index-2.1.1}/research/test_nucleus.py +0 -0
  160. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/apply_labels.sh +0 -0
  161. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/bootstrap.ps1 +0 -0
  162. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/bootstrap.sh +0 -0
  163. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/check_links.py +0 -0
  164. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/check_versions.py +0 -0
  165. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/gen_assets.py +0 -0
  166. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/gen_benchmark_chart.py +0 -0
  167. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/gen_terminal_svg.py +0 -0
  168. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/release_notes.py +0 -0
  169. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/release_smoke.py +0 -0
  170. {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/sync_skill_copies.py +0 -0
  171. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/SKILL.md +0 -0
  172. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/examples/basic-usage.md +0 -0
  173. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/examples/claude-md-example.md +0 -0
  174. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/examples/hooks-example.json +0 -0
  175. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/references/commands.md +0 -0
  176. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/references/memory.md +0 -0
  177. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/references/response-contract.md +0 -0
  178. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/bootstrap.py +0 -0
  179. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/cbx +0 -0
  180. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/cbx.ps1 +0 -0
  181. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/doctor.py +0 -0
  182. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/install.py +0 -0
  183. {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/smoke_test.py +0 -0
  184. {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/SKILL.md +0 -0
  185. {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/references/commands.md +0 -0
  186. {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/references/memory.md +0 -0
  187. {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/references/response-contract.md +0 -0
  188. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/__main__.py +0 -0
  189. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/config.py +0 -0
  190. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/__init__.py +0 -0
  191. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/classify.py +0 -0
  192. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/gates.py +0 -0
  193. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/ignore.py +0 -0
  194. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/walker.py +0 -0
  195. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/doctor.py +0 -0
  196. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/__init__.py +0 -0
  197. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/backend.py +0 -0
  198. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/external.py +0 -0
  199. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/local.py +0 -0
  200. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/noop.py +0 -0
  201. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/__init__.py +0 -0
  202. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/analysis.py +0 -0
  203. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/builder.py +0 -0
  204. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/expand.py +0 -0
  205. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/export.py +0 -0
  206. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/modules.py +0 -0
  207. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/navigate.py +0 -0
  208. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/retrieval.py +0 -0
  209. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/__init__.py +0 -0
  210. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/doc_chunks.py +0 -0
  211. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/freshness.py +0 -0
  212. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/pipeline.py +0 -0
  213. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/mcp/__init__.py +0 -0
  214. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/mcp/server.py +0 -0
  215. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/__init__.py +0 -0
  216. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/identity.py +0 -0
  217. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/session.py +0 -0
  218. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/store.py +0 -0
  219. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/validate.py +0 -0
  220. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/models.py +0 -0
  221. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/__init__.py +0 -0
  222. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/json.py +0 -0
  223. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/markdown.py +0 -0
  224. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/redact.py +0 -0
  225. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/__init__.py +0 -0
  226. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/base.py +0 -0
  227. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/languages.py +0 -0
  228. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/line_chunker.py +0 -0
  229. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/symbol_chunks.py +0 -0
  230. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/treesitter.py +0 -0
  231. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/__init__.py +0 -0
  232. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/budget.py +0 -0
  233. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/diversity.py +0 -0
  234. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/features.py +0 -0
  235. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/fusion.py +0 -0
  236. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/fuzzy.py +0 -0
  237. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/intent.py +0 -0
  238. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/lexical.py +0 -0
  239. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/pipeline.py +0 -0
  240. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/priors.py +0 -0
  241. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/rerank.py +0 -0
  242. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/searchers.py +0 -0
  243. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/skeleton.py +0 -0
  244. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/tuning.py +0 -0
  245. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/types.py +0 -0
  246. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/service.py +0 -0
  247. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/SKILL.md +0 -0
  248. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/examples/hooks/settings.json +0 -0
  249. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/references/commands.md +0 -0
  250. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/references/memory.md +0 -0
  251. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/references/response-contract.md +0 -0
  252. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/scripts/cbx +0 -0
  253. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/scripts/cbx.ps1 +0 -0
  254. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_update.py +0 -0
  255. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/__init__.py +0 -0
  256. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/db.py +0 -0
  257. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/repo.py +0 -0
  258. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/schema.sql +0 -0
  259. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/watch/__init__.py +0 -0
  260. {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/watch/watcher.py +0 -0
  261. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/__init__.py +0 -0
  262. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_honest.py +0 -0
  263. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_honest_RESULTS.md +0 -0
  264. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_honest_newtowny.txt +0 -0
  265. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_public.py +0 -0
  266. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_public_RESULTS.md +0 -0
  267. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_real_repo.py +0 -0
  268. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/conftest.py +0 -0
  269. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/README.md +0 -0
  270. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/__init__.py +0 -0
  271. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/baselines.py +0 -0
  272. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/gen_queries.py +0 -0
  273. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/harness.py +0 -0
  274. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/memory_eval.py +0 -0
  275. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/metrics.py +0 -0
  276. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/queries/self_repo.yml +0 -0
  277. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/queries/self_repo_git.yml +0 -0
  278. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/1.10.0-perf.json +0 -0
  279. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/1.10.0-retrieval-8corpus.txt +0 -0
  280. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-04-public-baselines.json +0 -0
  281. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-04-public-baselines.md +0 -0
  282. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-14-evidence-memory.json +0 -0
  283. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-14-evidence-memory.md +0 -0
  284. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-24-agent-pilot.md +0 -0
  285. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-24-retrieval-2.1.0.txt +0 -0
  286. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-24-terra-nl-queries.yml +0 -0
  287. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/run_baselines.py +0 -0
  288. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/run_eval.py +0 -0
  289. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/README.md +0 -0
  290. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/expected_answers.yml +0 -0
  291. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/index-1.10.0/index.sqlite +0 -0
  292. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/multilang/sample.go +0 -0
  293. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/multilang/sample.java +0 -0
  294. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/multilang/sample.rs +0 -0
  295. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/.env +0 -0
  296. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/.gitignore +0 -0
  297. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/huge.json +0 -0
  298. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/logo.png +0 -0
  299. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/node_modules/leftpad/index.js +0 -0
  300. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/secrets.pem +0 -0
  301. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/api/service.py +0 -0
  302. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/auth/token.py +0 -0
  303. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/models/user.py +0 -0
  304. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/schema.generated.ts +0 -0
  305. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/web/app.ts +0 -0
  306. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/architecture.json +0 -0
  307. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/describe_refresh.json +0 -0
  308. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/explain_auth.json +0 -0
  309. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/impact_user_model.json +0 -0
  310. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_architecture.json +0 -0
  311. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_describe_symbol.json +0 -0
  312. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_explain_code.json +0 -0
  313. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_find_refs.json +0 -0
  314. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_find_symbol.json +0 -0
  315. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_healthcheck.json +0 -0
  316. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_impact_of.json +0 -0
  317. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_impact_of_diff.json +0 -0
  318. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_index_stats.json +0 -0
  319. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_path_between.json +0 -0
  320. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_search_code.json +0 -0
  321. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/path_renew_to_token.json +0 -0
  322. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/refs_refresh_access_token.json +0 -0
  323. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/search_token.json +0 -0
  324. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/stats.json +0 -0
  325. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/symbol_user.json +0 -0
  326. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden_utils.py +0 -0
  327. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/installer/smoke.ps1 +0 -0
  328. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/installer/smoke.sh +0 -0
  329. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_agent_ergonomics.py +0 -0
  330. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_analysis.py +0 -0
  331. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_benchmark_comparison.py +0 -0
  332. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_bootstrap.py +0 -0
  333. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_budget.py +0 -0
  334. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_chunker.py +0 -0
  335. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_classify.py +0 -0
  336. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_clean_cli.py +0 -0
  337. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_cli.py +0 -0
  338. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_cli_golden.py +0 -0
  339. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_compact_output.py +0 -0
  340. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_config.py +0 -0
  341. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_diff_impact.py +0 -0
  342. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_discovery.py +0 -0
  343. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_diversity.py +0 -0
  344. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_doc_chunks.py +0 -0
  345. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_doctor.py +0 -0
  346. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_embeddings_backend.py +0 -0
  347. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_embeddings_external.py +0 -0
  348. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_embeddings_local.py +0 -0
  349. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_eval_baselines.py +0 -0
  350. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_freshness.py +0 -0
  351. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_fusion.py +0 -0
  352. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_fuzzy.py +0 -0
  353. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_gen_queries.py +0 -0
  354. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph.py +0 -0
  355. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph_coverage.py +0 -0
  356. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph_export_cli.py +0 -0
  357. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph_retrieval.py +0 -0
  358. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_hooks_merge.py +0 -0
  359. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_hybrid_ranking.py +0 -0
  360. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_ignore.py +0 -0
  361. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_impact_cli.py +0 -0
  362. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_impact_decay.py +0 -0
  363. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_index_cli.py +0 -0
  364. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_init_cli.py +0 -0
  365. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_intent.py +0 -0
  366. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_languages.py +0 -0
  367. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_lexical.py +0 -0
  368. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_mcp_golden.py +0 -0
  369. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_mcp_server.py +0 -0
  370. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_eval_smoke.py +0 -0
  371. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_identity.py +0 -0
  372. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_lifecycle.py +0 -0
  373. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_session.py +0 -0
  374. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_stale_context.py +0 -0
  375. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_store.py +0 -0
  376. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_surfaces.py +0 -0
  377. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_validate.py +0 -0
  378. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_migration_1x.py +0 -0
  379. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_multilang_symbols.py +0 -0
  380. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_navigate.py +0 -0
  381. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_output.py +0 -0
  382. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_packaging.py +0 -0
  383. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_parse_guardrails.py +0 -0
  384. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_perf_smoke.py +0 -0
  385. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_pipeline.py +0 -0
  386. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_pipeline_search.py +0 -0
  387. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_pipeline_vectors.py +0 -0
  388. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_plugin_manifest.py +0 -0
  389. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_plugin_skill_parity.py +0 -0
  390. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_plugin_wrappers.py +0 -0
  391. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_priors.py +0 -0
  392. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_public_benchmark.py +0 -0
  393. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_qualified_refs.py +0 -0
  394. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_quality_benchmark.py +0 -0
  395. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_ranking_features.py +0 -0
  396. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_read_cap.py +0 -0
  397. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_redact.py +0 -0
  398. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_release_scripts.py +0 -0
  399. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_repo_search.py +0 -0
  400. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_rerank.py +0 -0
  401. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_retrieval_types.py +0 -0
  402. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_scaffold.py +0 -0
  403. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_search_cli.py +0 -0
  404. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_searchers.py +0 -0
  405. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_seeded_index.py +0 -0
  406. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_service.py +0 -0
  407. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_skeleton.py +0 -0
  408. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_skill_update.py +0 -0
  409. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_storage.py +0 -0
  410. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_symbol_cli.py +0 -0
  411. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_symbol_ranking.py +0 -0
  412. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_symbol_refs.py +0 -0
  413. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_sync_skill_copies.py +0 -0
  414. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_treesitter.py +0 -0
  415. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_tuning_contract.py +0 -0
  416. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_update.py +0 -0
  417. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_update_cli.py +0 -0
  418. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_vector_search.py +0 -0
  419. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_vectors_storage.py +0 -0
  420. {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_watcher.py +0 -0
@@ -3,7 +3,7 @@
3
3
  "name": "codebase-index",
4
4
  "displayName": "Codebase Index",
5
5
  "description": "Give Claude a precise local map of your codebase: find implementations, trace behavior, and predict change impact with file-line evidence.",
6
- "version": "2.1.0",
6
+ "version": "2.1.1",
7
7
  "author": {
8
8
  "name": "codebase-index contributors"
9
9
  },
@@ -6,6 +6,24 @@ All notable changes to this project are documented here. The format is based on
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.1.1] - 2026-09-25
10
+
11
+ ### Added
12
+
13
+ - `codebase-index install-hooks [--global]` makes Claude Code search through the index
14
+ by default. It merges two hooks into `.claude/settings.json` (or
15
+ `~/.claude/settings.json`), keeping every other hook:
16
+ - `SessionStart`: in an indexed repository, one line of context naming the commands
17
+ that answer code questions;
18
+ - `PreToolUse` on `Grep|Bash`: the first code search of a session, before any
19
+ `codebase-index` command has run, is sent back with the index command to use
20
+ instead. Repeating the same call lets it through, the first index command turns the
21
+ guard off for the session, and searches over docs, logs or config, pipelines such
22
+ as `ps | grep`, and directories without an index are never intercepted.
23
+ `CBX_GUARD=0` disables it; `install-hooks --uninstall` removes both hooks.
24
+ The hooks run as `codebase-index-hook`, a standard-library-only entry point (about
25
+ 0.1 s per call). The Claude Code plugin registers the same hooks.
26
+
9
27
  ## [2.1.0] - 2026-09-24
10
28
 
11
29
  Agent-efficiency release. An agent answering five code questions on a 5.9k-file
@@ -868,7 +886,8 @@ Pooled over 305 queries (Python, Java, TypeScript), v1.8.0 → 1.9.0:
868
886
  - Hooks example + `watch` mode for keeping the index fresh without blocking the edit loop (M8).
869
887
  - `doctor`, `stats`, `clean` diagnostics/maintenance commands.
870
888
 
871
- [Unreleased]: https://github.com/denfry/codebase-index/compare/v2.1.0...HEAD
889
+ [Unreleased]: https://github.com/denfry/codebase-index/compare/v2.1.1...HEAD
890
+ [2.1.1]: https://github.com/denfry/codebase-index/compare/v2.1.0...v2.1.1
872
891
  [2.1.0]: https://github.com/denfry/codebase-index/compare/v2.0.0...v2.1.0
873
892
  [2.0.0]: https://github.com/denfry/codebase-index/compare/v1.9.0...v2.0.0
874
893
  [1.10.0]: https://github.com/denfry/codebase-index/compare/v1.9.0...abb67df
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codebase-index
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: A precise local codebase map for AI coding agents: find, trace, and predict with file-line evidence.
5
5
  Project-URL: Homepage, https://github.com/denfry/codebase-index
6
6
  Project-URL: Documentation, https://github.com/denfry/codebase-index/tree/main/docs
@@ -195,7 +195,10 @@ agent format: `path:start-end symbol` with the matching lines numbered underneat
195
195
  | **Any MCP client** (Claude Desktop, Cursor, VS Code, Zed, Windsurf, ...) | `pip install "codebase-index[mcp]"` then `codebase-index mcp --root /path/to/repo` | 11 tools (`search_code`, `find_refs`, `impact_of`, `impact_of_diff`, `path_between`, ...) with a versioned JSON envelope |
196
196
  | **Anything with a shell** | `codebase-index --json ...` | The same payloads as plain JSON, plus a local SQLite database you can query yourself |
197
197
 
198
- `init --target auto` detects which of these are present. See
198
+ `init --target auto` detects which of these are present. To make Claude Code reach for
199
+ the index before Grep in every indexed repository, run
200
+ `codebase-index install-hooks --global`: a session-start note plus a guard that sends the
201
+ first code search of a session to the index (repeat the call to run it anyway). See
199
202
  [INSTALLATION.md](docs/INSTALLATION.md) and [MCP.md](docs/MCP.md).
200
203
 
201
204
  ## Evidence
@@ -145,7 +145,10 @@ agent format: `path:start-end symbol` with the matching lines numbered underneat
145
145
  | **Any MCP client** (Claude Desktop, Cursor, VS Code, Zed, Windsurf, ...) | `pip install "codebase-index[mcp]"` then `codebase-index mcp --root /path/to/repo` | 11 tools (`search_code`, `find_refs`, `impact_of`, `impact_of_diff`, `path_between`, ...) with a versioned JSON envelope |
146
146
  | **Anything with a shell** | `codebase-index --json ...` | The same payloads as plain JSON, plus a local SQLite database you can query yourself |
147
147
 
148
- `init --target auto` detects which of these are present. See
148
+ `init --target auto` detects which of these are present. To make Claude Code reach for
149
+ the index before Grep in every indexed repository, run
150
+ `codebase-index install-hooks --global`: a session-start note plus a guard that sends the
151
+ first code search of a session to the index (repeat the call to run it anyway). See
149
152
  [INSTALLATION.md](docs/INSTALLATION.md) and [MCP.md](docs/MCP.md).
150
153
 
151
154
  ## Evidence
@@ -110,7 +110,7 @@ pwsh ./install.ps1 -Target claude -InstallDir "D:\skills\codebase-index"
110
110
  **Pin to a branch or tag** (reproducibility and safety):
111
111
 
112
112
  ```sh
113
- sh install.sh --branch v2.1.0
113
+ sh install.sh --branch v2.1.1
114
114
  ```
115
115
 
116
116
  ---
@@ -0,0 +1,31 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.sh\""
9
+ },
10
+ {
11
+ "type": "command",
12
+ "command": "codebase-index-hook session-start 2>/dev/null || true",
13
+ "timeout": 5
14
+ }
15
+ ]
16
+ }
17
+ ],
18
+ "PreToolUse": [
19
+ {
20
+ "matcher": "Grep|Bash",
21
+ "hooks": [
22
+ {
23
+ "type": "command",
24
+ "command": "codebase-index-hook guard 2>/dev/null || true",
25
+ "timeout": 5
26
+ }
27
+ ]
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -57,6 +57,7 @@ build = ["build>=1.2", "twine>=5.0"]
57
57
  codebase-index = "codebase_index.cli:app"
58
58
  cbx = "codebase_index.cli:app" # short alias
59
59
  codebase-index-mcp = "codebase_index.mcp.server:run" # standalone MCP entry point
60
+ codebase-index-hook = "codebase_index.hooks:main" # Claude Code hooks (stdlib only, fast)
60
61
 
61
62
  [project.urls]
62
63
  Homepage = "https://github.com/denfry/codebase-index"
@@ -1,3 +1,3 @@
1
- codebase-index @ https://github.com/denfry/codebase-index/archive/refs/tags/v2.1.0.tar.gz
1
+ codebase-index @ https://github.com/denfry/codebase-index/archive/refs/tags/v2.1.1.tar.gz
2
2
  tree-sitter==0.25.2
3
3
  tree-sitter-language-pack==1.8.1
@@ -4,4 +4,4 @@ Public surface is intentionally small; the CLI (`codebase_index.cli:app`) is the
4
4
  See docs/ARCHITECTURE.md for the module map.
5
5
  """
6
6
 
7
- __version__ = "2.1.0"
7
+ __version__ = "2.1.1"
@@ -1064,6 +1064,41 @@ def watch(
1064
1064
  raise typer.Exit(code=1)
1065
1065
 
1066
1066
 
1067
+ @app.command("install-hooks")
1068
+ def install_hooks(
1069
+ ctx: typer.Context,
1070
+ global_: bool = typer.Option(
1071
+ False, "--global", help="Install into ~/.claude/settings.json (every project)."
1072
+ ),
1073
+ uninstall: bool = typer.Option(False, "--uninstall", help="Remove the hooks instead."),
1074
+ ) -> None:
1075
+ """Make Claude Code search through the index by default.
1076
+
1077
+ Adds a SessionStart note and a PreToolUse guard on Grep/Bash: the first code
1078
+ search of a session, before any codebase-index command, is sent back with the
1079
+ index command to run instead; repeating it lets it through. Both do nothing in
1080
+ a directory without an index. Other hooks in the settings file are kept.
1081
+ """
1082
+ from .config import find_root
1083
+ from . import scaffold
1084
+
1085
+ if global_:
1086
+ path = Path.home() / ".claude" / "settings.json"
1087
+ else:
1088
+ root_opt = ctx.obj.get("root") if ctx.obj else None
1089
+ path = (Path(root_opt).resolve() if root_opt else find_root()) / scaffold.SETTINGS_REL
1090
+ if uninstall:
1091
+ changed = scaffold.uninstall_guard_hooks(path)
1092
+ verb = "removed from"
1093
+ else:
1094
+ changed = scaffold.install_guard_hooks(path)
1095
+ verb = "added to"
1096
+ if changed:
1097
+ typer.echo(f"[install-hooks] {', '.join(changed)} {verb} {path}")
1098
+ else:
1099
+ typer.echo(f"[install-hooks] nothing to change in {path}")
1100
+
1101
+
1067
1102
  @app.command("skill-update")
1068
1103
  def skill_update(
1069
1104
  ctx: typer.Context,
@@ -0,0 +1,256 @@
1
+ """Claude Code hooks that make the index the default way to search code.
2
+
3
+ Two events, both no-ops outside a repository that has an index:
4
+
5
+ - ``session-start`` adds one short note to the session context: this repository
6
+ is indexed, and which commands answer code questions.
7
+ - ``guard`` (PreToolUse on Grep and Bash) intercepts a code search the first time
8
+ in a session, before any ``codebase-index`` command has run, and names the
9
+ index command to use instead. It is a nudge, not a wall: repeating the same
10
+ call lets it through, the first ``codebase-index`` command in the session turns
11
+ the guard off, and searches over docs, logs or config are never intercepted.
12
+
13
+ Measured on a 5.9k-file monorepo, an agent spent 19% fewer tokens with the index
14
+ than with grep (tests/eval/results/2026-09-24-agent-pilot.md), but agents reach
15
+ for Grep out of habit. The guard runs on every Bash call, so this module imports
16
+ only the standard library and does no index work beyond one small SQLite read.
17
+
18
+ Entry point: ``codebase-index-hook <event>`` reads the hook JSON on stdin and
19
+ prints the hook JSON reply (or nothing). ``CBX_GUARD=0`` disables the guard.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import hashlib
25
+ import json
26
+ import os
27
+ import re
28
+ import sqlite3
29
+ import sys
30
+ import time
31
+ from pathlib import Path
32
+ from typing import Any, Optional
33
+
34
+ INDEX_REL = Path(".claude") / "cache" / "codebase-index" / "index.sqlite"
35
+ _STATE_DIR = "hook-sessions"
36
+ _STATE_TTL_S = 2 * 24 * 3600
37
+
38
+ # A shell segment that searches file contents: the first command of the segment
39
+ # (after `&&`, `||`, `;` or the start), so `ps aux | grep x` is not a repo search.
40
+ _SEARCH_CMD = re.compile(
41
+ r"(?:^|&&|\|\||;)\s*(?:cd\s+\S+\s*&&\s*)?"
42
+ r"(?P<cmd>git\s+grep|grep|egrep|fgrep|rg|ag|ack|findstr|Select-String)\b(?P<args>[^|;&]*)",
43
+ re.IGNORECASE,
44
+ )
45
+ _INDEX_CMD = re.compile(r"(?:^|[\s;&|/\\\"'])(?:codebase-index|cbx)(?:\.exe|\.ps1)?\s+\w")
46
+ # Searches over prose, logs and config are what grep is for; leave them alone.
47
+ _NON_CODE = re.compile(
48
+ r"\.(?:md|mdx|rst|txt|log|ya?ml|json|toml|ini|cfg|conf|csv|xml|properties|lock|env|html?)\b"
49
+ r"|--include=\*?\.(?:md|txt|log|ya?ml|json|toml)",
50
+ re.IGNORECASE,
51
+ )
52
+ _NON_CODE_TYPES = {"md", "markdown", "txt", "log", "yaml", "yml", "json", "toml", "xml",
53
+ "csv", "ini", "config"}
54
+
55
+
56
+ def main(argv: Optional[list[str]] = None) -> int:
57
+ args = sys.argv[1:] if argv is None else argv
58
+ event = args[0] if args else ""
59
+ try:
60
+ payload = json.loads(sys.stdin.read() or "{}")
61
+ except (ValueError, OSError):
62
+ return 0
63
+ try:
64
+ reply = {"guard": guard, "session-start": session_start}.get(event, _none)(payload)
65
+ except Exception: # a hook must never break the session it runs in
66
+ return 0
67
+ if reply:
68
+ sys.stdout.write(json.dumps(reply))
69
+ return 0
70
+
71
+
72
+ def _none(_payload: dict) -> Optional[dict]:
73
+ return None
74
+
75
+
76
+ def find_index_root(start: Path) -> Optional[Path]:
77
+ """The nearest directory at or above `start` that holds an index."""
78
+ for path in (start, *start.parents):
79
+ if (path / INDEX_REL).is_file():
80
+ return path
81
+ return None
82
+
83
+
84
+ def _cwd(payload: dict) -> Path:
85
+ raw = str(payload.get("cwd") or os.getcwd())
86
+ # Git Bash hands out MSYS paths (/c/Projects/x); Python on Windows needs C:/.
87
+ msys = re.match(r"^/([a-zA-Z])(/.*)?$", raw)
88
+ if os.name == "nt" and msys:
89
+ raw = f"{msys.group(1)}:{msys.group(2) or '/'}"
90
+ return Path(raw)
91
+
92
+
93
+ # --- session-start ------------------------------------------------------------------
94
+
95
+
96
+ def session_start(payload: dict) -> Optional[dict]:
97
+ root = find_index_root(_cwd(payload))
98
+ if root is None:
99
+ return None
100
+ files = _indexed_files(root)
101
+ size = f" ({files} files)" if files else ""
102
+ note = (
103
+ f"This repository has a codebase-index index{size}. For questions about the "
104
+ "code (where something is, how it works, who calls it, what a change breaks) "
105
+ "use the codebase-index skill before Grep or Read: "
106
+ '`codebase-index search "<question>" --compact`, '
107
+ '`codebase-index refs "Owner.member" --compact`, '
108
+ '`codebase-index impact "Owner.member" --compact`. '
109
+ "Output lines are numbered; cite them as file:line."
110
+ )
111
+ return {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": note}}
112
+
113
+
114
+ def _indexed_files(root: Path) -> Optional[int]:
115
+ try:
116
+ conn = sqlite3.connect(f"file:{root / INDEX_REL}?mode=ro", uri=True, timeout=0.5)
117
+ try:
118
+ return int(conn.execute("SELECT COUNT(*) FROM files").fetchone()[0])
119
+ finally:
120
+ conn.close()
121
+ except sqlite3.Error:
122
+ return None
123
+
124
+
125
+ # --- guard ------------------------------------------------------------------------------
126
+
127
+
128
+ def guard(payload: dict) -> Optional[dict]:
129
+ if os.environ.get("CBX_GUARD", "").strip() == "0":
130
+ return None
131
+ tool = payload.get("tool_name") or ""
132
+ tool_input = payload.get("tool_input") or {}
133
+ if tool not in ("Grep", "Bash"):
134
+ return None
135
+ root = find_index_root(_cwd(payload))
136
+ if root is None:
137
+ return None
138
+ session = str(payload.get("session_id") or "default")
139
+ state = _State(root, session)
140
+
141
+ if tool == "Bash":
142
+ command = str(tool_input.get("command") or "")
143
+ if _INDEX_CMD.search(command):
144
+ state.mark_used()
145
+ return None
146
+ pattern = _shell_search_pattern(command)
147
+ if pattern is None:
148
+ return None
149
+ else:
150
+ if _grep_targets_non_code(tool_input):
151
+ return None
152
+ pattern = str(tool_input.get("pattern") or "")
153
+
154
+ if state.used:
155
+ return None
156
+ fingerprint = hashlib.sha1(
157
+ (tool + json.dumps(tool_input, sort_keys=True, default=str)).encode("utf-8")
158
+ ).hexdigest()
159
+ if state.was_denied(fingerprint):
160
+ return None # the retry after a nudge: the agent decided grep is right
161
+ state.deny(fingerprint)
162
+ return {
163
+ "hookSpecificOutput": {
164
+ "hookEventName": "PreToolUse",
165
+ "permissionDecision": "deny",
166
+ "permissionDecisionReason": _reason(pattern),
167
+ }
168
+ }
169
+
170
+
171
+ def _shell_search_pattern(command: str) -> Optional[str]:
172
+ """The search term of a content search in `command`, or None if there is none."""
173
+ for match in _SEARCH_CMD.finditer(command):
174
+ args = match.group("args")
175
+ if _NON_CODE.search(args):
176
+ continue
177
+ quoted = re.search(r"""(['"])(.+?)\1""", args)
178
+ if quoted:
179
+ return quoted.group(2)
180
+ words = [w for w in args.split() if not w.startswith("-")]
181
+ return words[0] if words else ""
182
+ return None
183
+
184
+
185
+ def _grep_targets_non_code(tool_input: dict) -> bool:
186
+ kind = str(tool_input.get("type") or "").lower()
187
+ if kind in _NON_CODE_TYPES:
188
+ return True
189
+ target = f"{tool_input.get('glob') or ''} {tool_input.get('path') or ''}"
190
+ return bool(_NON_CODE.search(target))
191
+
192
+
193
+ def _reason(pattern: str) -> str:
194
+ term = re.sub(r"[\\^$()\[\]{}|*+?]", " ", pattern).strip() or "<what you are looking for>"
195
+ term = " ".join(term.split())[:80]
196
+ return (
197
+ "codebase-index: this repository is indexed, and the index answers code "
198
+ "searches with ranked, numbered lines in fewer tokens than grep. Run it first:\n"
199
+ f' codebase-index search "{term}" --compact # where / how\n'
200
+ ' codebase-index refs "Owner.member" --compact # every call site, with callers\n'
201
+ ' codebase-index symbol "Name" # a definition\n'
202
+ "If the index does not answer, repeat this exact call and it will run. Once any "
203
+ "codebase-index command has run in this session, searches are not intercepted."
204
+ )
205
+
206
+
207
+ class _State:
208
+ """Per-session guard state: whether the index was used, which calls were nudged."""
209
+
210
+ def __init__(self, root: Path, session: str) -> None:
211
+ safe = re.sub(r"[^A-Za-z0-9_.-]", "_", session)[:80] or "default"
212
+ self.dir = root / INDEX_REL.parent / _STATE_DIR
213
+ self.path = self.dir / f"{safe}.json"
214
+ self.data: dict[str, Any] = {"used": False, "denied": []}
215
+ try:
216
+ self.data.update(json.loads(self.path.read_text(encoding="utf-8")))
217
+ except (OSError, ValueError):
218
+ pass
219
+
220
+ @property
221
+ def used(self) -> bool:
222
+ return bool(self.data.get("used"))
223
+
224
+ def was_denied(self, fingerprint: str) -> bool:
225
+ return fingerprint in self.data.get("denied", [])
226
+
227
+ def mark_used(self) -> None:
228
+ if not self.used:
229
+ self.data["used"] = True
230
+ self._save()
231
+
232
+ def deny(self, fingerprint: str) -> None:
233
+ self.data.setdefault("denied", []).append(fingerprint)
234
+ self._save()
235
+
236
+ def _save(self) -> None:
237
+ try:
238
+ self.dir.mkdir(parents=True, exist_ok=True)
239
+ self.path.write_text(json.dumps(self.data), encoding="utf-8")
240
+ _prune(self.dir)
241
+ except OSError:
242
+ pass
243
+
244
+
245
+ def _prune(directory: Path) -> None:
246
+ cutoff = time.time() - _STATE_TTL_S
247
+ for item in directory.glob("*.json"):
248
+ try:
249
+ if item.stat().st_mtime < cutoff:
250
+ item.unlink()
251
+ except OSError:
252
+ pass
253
+
254
+
255
+ if __name__ == "__main__":
256
+ raise SystemExit(main())
@@ -283,6 +283,67 @@ def merge_hook_settings(root: Path) -> bool:
283
283
  return True
284
284
 
285
285
 
286
+ # Index-first hooks (hooks.py): a SessionStart note and a PreToolUse guard that
287
+ # steers the first code search of a session to the index.
288
+ _GUARD_MARKER = "codebase-index-hook"
289
+ GUARD_HOOKS: dict[str, dict] = {
290
+ "SessionStart": {
291
+ "hooks": [{"type": "command", "command": "codebase-index-hook session-start",
292
+ "timeout": 5}],
293
+ },
294
+ "PreToolUse": {
295
+ "matcher": "Grep|Bash",
296
+ "hooks": [{"type": "command", "command": "codebase-index-hook guard", "timeout": 5}],
297
+ },
298
+ }
299
+
300
+
301
+ def _is_guard_entry(entry: dict) -> bool:
302
+ return any(_GUARD_MARKER in str(h.get("command", "")) for h in entry.get("hooks", []))
303
+
304
+
305
+ def install_guard_hooks(settings_path: Path) -> list[str]:
306
+ """Merge the index-first hooks into a Claude Code settings file.
307
+
308
+ Idempotent, and leaves every other hook in place. Returns the events added.
309
+ """
310
+ settings: dict = {}
311
+ if settings_path.exists():
312
+ settings = json.loads(settings_path.read_text(encoding="utf-8") or "{}")
313
+ hooks = settings.setdefault("hooks", {})
314
+ added = []
315
+ for event, entry in GUARD_HOOKS.items():
316
+ entries = hooks.setdefault(event, [])
317
+ if not any(_is_guard_entry(e) for e in entries):
318
+ entries.append(json.loads(json.dumps(entry)))
319
+ added.append(event)
320
+ if added:
321
+ settings_path.parent.mkdir(parents=True, exist_ok=True)
322
+ settings_path.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
323
+ return added
324
+
325
+
326
+ def uninstall_guard_hooks(settings_path: Path) -> list[str]:
327
+ """Remove the index-first hooks; other hooks are kept. Returns the events changed."""
328
+ if not settings_path.exists():
329
+ return []
330
+ settings = json.loads(settings_path.read_text(encoding="utf-8") or "{}")
331
+ hooks = settings.get("hooks", {})
332
+ removed = []
333
+ for event in GUARD_HOOKS:
334
+ entries = hooks.get(event, [])
335
+ kept = [e for e in entries if not _is_guard_entry(e)]
336
+ if len(kept) != len(entries):
337
+ removed.append(event)
338
+ if kept:
339
+ hooks[event] = kept
340
+ else:
341
+ hooks.pop(event, None)
342
+ if removed:
343
+ settings_path.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
344
+ return removed
345
+
346
+
286
347
  # ── MCP client config helpers ──────────────────────────────────────────────────────────────────
287
348
 
288
349
  _MCP_SERVER_NAME = "codebase-index"
@@ -0,0 +1,120 @@
1
+ """Index-first hooks: a SessionStart note and a PreToolUse guard (hooks.py)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ from codebase_index import hooks, scaffold
12
+
13
+
14
+ @pytest.fixture
15
+ def repo(tmp_path: Path) -> Path:
16
+ db = tmp_path / "repo" / hooks.INDEX_REL
17
+ db.parent.mkdir(parents=True)
18
+ import sqlite3
19
+
20
+ conn = sqlite3.connect(db)
21
+ conn.execute("CREATE TABLE files (id INTEGER PRIMARY KEY)")
22
+ conn.executemany("INSERT INTO files VALUES (?)", [(i,) for i in range(3)])
23
+ conn.commit()
24
+ conn.close()
25
+ return tmp_path / "repo"
26
+
27
+
28
+ def _call(root: Path, tool: str, session: str = "s1", **tool_input):
29
+ return hooks.guard({"session_id": session, "cwd": str(root / "src"),
30
+ "tool_name": tool, "tool_input": tool_input})
31
+
32
+
33
+ def _denied(reply) -> bool:
34
+ return bool(reply) and reply["hookSpecificOutput"]["permissionDecision"] == "deny"
35
+
36
+
37
+ def test_session_start_notes_the_index_only_where_one_exists(repo: Path, tmp_path: Path):
38
+ reply = hooks.session_start({"cwd": str(repo)})
39
+ context = reply["hookSpecificOutput"]["additionalContext"]
40
+ assert "(3 files)" in context and "--compact" in context
41
+ assert hooks.session_start({"cwd": str(tmp_path)}) is None
42
+
43
+
44
+ def test_first_code_search_is_sent_to_the_index_and_its_retry_runs(repo: Path):
45
+ first = _call(repo, "Grep", pattern="TownService.refresh")
46
+ assert _denied(first)
47
+ reason = first["hookSpecificOutput"]["permissionDecisionReason"]
48
+ assert 'codebase-index search "TownService.refresh" --compact' in reason
49
+ assert _call(repo, "Grep", pattern="TownService.refresh") is None # the retry
50
+ assert _denied(_call(repo, "Grep", pattern="somethingElse")) # a new search
51
+
52
+
53
+ def test_using_the_index_turns_the_guard_off_for_the_session(repo: Path):
54
+ assert _call(repo, "Bash", command="cd x && codebase-index refs A.b --compact") is None
55
+ assert _call(repo, "Grep", pattern="anything") is None
56
+ assert _call(repo, "Bash", command="rg -n foo src") is None
57
+ assert _denied(_call(repo, "Grep", session="other", pattern="anything"))
58
+
59
+
60
+ @pytest.mark.parametrize("command", [
61
+ "grep -rn TODO docs/*.md",
62
+ "ps aux | grep java",
63
+ "git log --oneline | grep fix",
64
+ "ls -la",
65
+ "rg -n error build.log",
66
+ ])
67
+ def test_non_code_and_non_search_shell_commands_pass(repo: Path, command: str):
68
+ assert _call(repo, "Bash", command=command) is None
69
+
70
+
71
+ @pytest.mark.parametrize("command", [
72
+ "grep -rn treasurerDeparted --include=*.java .",
73
+ "cd /c/Projects/x && rg -n 'TownService.refresh' realism-polity",
74
+ "git grep -n place",
75
+ ])
76
+ def test_code_searches_in_the_shell_are_guarded(repo: Path, command: str):
77
+ assert _denied(_call(repo, "Bash", command=command))
78
+
79
+
80
+ def test_grep_over_docs_or_config_passes(repo: Path):
81
+ assert _call(repo, "Grep", pattern="x", glob="**/*.yml") is None
82
+ assert _call(repo, "Grep", pattern="x", type="md") is None
83
+
84
+
85
+ def test_no_index_or_disabled_guard_never_intercepts(repo: Path, tmp_path: Path, monkeypatch):
86
+ assert _call(tmp_path, "Grep", pattern="x") is None
87
+ monkeypatch.setenv("CBX_GUARD", "0")
88
+ assert _call(repo, "Grep", pattern="x") is None
89
+
90
+
91
+ def test_msys_cwd_is_understood(repo: Path, monkeypatch):
92
+ monkeypatch.setattr(hooks.os, "name", "nt")
93
+ drive, rest = str(repo).replace("\\", "/").split(":", 1) if ":" in str(repo) else ("", "")
94
+ if not drive:
95
+ pytest.skip("POSIX path has no drive letter")
96
+ assert hooks._cwd({"cwd": f"/{drive.lower()}{rest}"}) == Path(f"{drive}:{rest}")
97
+
98
+
99
+ def test_main_reads_stdin_and_writes_the_reply(repo: Path, monkeypatch, capsys):
100
+ payload = {"session_id": "m", "cwd": str(repo), "tool_name": "Grep",
101
+ "tool_input": {"pattern": "x"}}
102
+ monkeypatch.setattr("sys.stdin", io.StringIO(json.dumps(payload)))
103
+ assert hooks.main(["guard"]) == 0
104
+ assert json.loads(capsys.readouterr().out)["hookSpecificOutput"]["permissionDecision"] == "deny"
105
+ monkeypatch.setattr("sys.stdin", io.StringIO("not json"))
106
+ assert hooks.main(["guard"]) == 0 and capsys.readouterr().out == ""
107
+
108
+
109
+ def test_install_merges_idempotently_and_uninstall_keeps_other_hooks(tmp_path: Path):
110
+ path = tmp_path / "settings.json"
111
+ other = {"type": "command", "command": "my-other-hook"}
112
+ path.write_text(json.dumps({"model": "x", "hooks": {
113
+ "PreToolUse": [{"matcher": "Bash", "hooks": [other]}]}}), encoding="utf-8")
114
+ assert scaffold.install_guard_hooks(path) == ["SessionStart", "PreToolUse"]
115
+ assert scaffold.install_guard_hooks(path) == []
116
+ data = json.loads(path.read_text(encoding="utf-8"))
117
+ assert data["model"] == "x" and len(data["hooks"]["PreToolUse"]) == 2
118
+ assert scaffold.uninstall_guard_hooks(path) == ["SessionStart", "PreToolUse"]
119
+ data = json.loads(path.read_text(encoding="utf-8"))
120
+ assert data["hooks"] == {"PreToolUse": [{"matcher": "Bash", "hooks": [other]}]}
@@ -1,14 +0,0 @@
1
- {
2
- "hooks": {
3
- "SessionStart": [
4
- {
5
- "hooks": [
6
- {
7
- "type": "command",
8
- "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.sh\""
9
- }
10
- ]
11
- }
12
- ]
13
- }
14
- }