codebase-index 2.1.0__tar.gz → 2.1.2__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.2/.claude/skills/codebase-index/.skill_version +1 -0
  2. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude-plugin/plugin.json +1 -1
  3. codebase_index-2.1.2/.codex/skills/codebase-index/.skill_version +1 -0
  4. codebase_index-2.1.2/.opencode/skills/codebase-index/.skill_version +1 -0
  5. {codebase_index-2.1.0 → codebase_index-2.1.2}/CHANGELOG.md +37 -1
  6. {codebase_index-2.1.0 → codebase_index-2.1.2}/PKG-INFO +5 -2
  7. {codebase_index-2.1.0 → codebase_index-2.1.2}/README.md +4 -1
  8. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/installer.md +1 -1
  9. codebase_index-2.1.2/hooks/hooks.json +31 -0
  10. {codebase_index-2.1.0 → codebase_index-2.1.2}/pyproject.toml +1 -0
  11. {codebase_index-2.1.0 → codebase_index-2.1.2}/requirements.lock +1 -1
  12. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/__init__.py +1 -1
  13. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/cli.py +35 -0
  14. codebase_index-2.1.2/src/codebase_index/hooks.py +272 -0
  15. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/scaffold.py +61 -0
  16. codebase_index-2.1.2/tests/test_hooks_guard.py +157 -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.2}/.claude/skills/codebase-index/SKILL.md +0 -0
  22. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude/skills/codebase-index/examples/hooks/settings.json +0 -0
  23. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude/skills/codebase-index/references/commands.md +0 -0
  24. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude/skills/codebase-index/references/memory.md +0 -0
  25. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude/skills/codebase-index/references/response-contract.md +0 -0
  26. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude/skills/codebase-index/scripts/cbx +0 -0
  27. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude/skills/codebase-index/scripts/cbx.ps1 +0 -0
  28. {codebase_index-2.1.0 → codebase_index-2.1.2}/.claude-plugin/marketplace.json +0 -0
  29. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/SKILL.md +0 -0
  30. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/examples/hooks/settings.json +0 -0
  31. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/references/commands.md +0 -0
  32. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/references/memory.md +0 -0
  33. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/references/response-contract.md +0 -0
  34. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/scripts/cbx +0 -0
  35. {codebase_index-2.1.0 → codebase_index-2.1.2}/.codex/skills/codebase-index/scripts/cbx.ps1 +0 -0
  36. {codebase_index-2.1.0 → codebase_index-2.1.2}/.editorconfig +0 -0
  37. {codebase_index-2.1.0 → codebase_index-2.1.2}/.gitattributes +0 -0
  38. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/ISSUE_TEMPLATE/benchmark_report.yml +0 -0
  39. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  40. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  41. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  42. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/ISSUE_TEMPLATE/language_support.yml +0 -0
  43. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/ISSUE_TEMPLATE/skill_listing_request.yml +0 -0
  44. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  45. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/SUPPORT.md +0 -0
  46. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/labels.yml +0 -0
  47. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/workflows/ci.yml +0 -0
  48. {codebase_index-2.1.0 → codebase_index-2.1.2}/.github/workflows/release.yml +0 -0
  49. {codebase_index-2.1.0 → codebase_index-2.1.2}/.gitignore +0 -0
  50. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/agents/codebase-index.md +0 -0
  51. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/commands/codebase-index.md +0 -0
  52. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/SKILL.md +0 -0
  53. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/examples/hooks/settings.json +0 -0
  54. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/references/commands.md +0 -0
  55. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/references/memory.md +0 -0
  56. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/references/response-contract.md +0 -0
  57. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/scripts/cbx +0 -0
  58. {codebase_index-2.1.0 → codebase_index-2.1.2}/.opencode/skills/codebase-index/scripts/cbx.ps1 +0 -0
  59. {codebase_index-2.1.0 → codebase_index-2.1.2}/AGENTS.md +0 -0
  60. {codebase_index-2.1.0 → codebase_index-2.1.2}/CODE_OF_CONDUCT.md +0 -0
  61. {codebase_index-2.1.0 → codebase_index-2.1.2}/CONTRIBUTING.md +0 -0
  62. {codebase_index-2.1.0 → codebase_index-2.1.2}/LICENSE +0 -0
  63. {codebase_index-2.1.0 → codebase_index-2.1.2}/ROADMAP.md +0 -0
  64. {codebase_index-2.1.0 → codebase_index-2.1.2}/SECURITY.md +0 -0
  65. {codebase_index-2.1.0 → codebase_index-2.1.2}/adapters/claude.ps1 +0 -0
  66. {codebase_index-2.1.0 → codebase_index-2.1.2}/adapters/claude.sh +0 -0
  67. {codebase_index-2.1.0 → codebase_index-2.1.2}/adapters/codex.ps1 +0 -0
  68. {codebase_index-2.1.0 → codebase_index-2.1.2}/adapters/codex.sh +0 -0
  69. {codebase_index-2.1.0 → codebase_index-2.1.2}/adapters/opencode.ps1 +0 -0
  70. {codebase_index-2.1.0 → codebase_index-2.1.2}/adapters/opencode.sh +0 -0
  71. {codebase_index-2.1.0 → codebase_index-2.1.2}/assets/architecture.svg +0 -0
  72. {codebase_index-2.1.0 → codebase_index-2.1.2}/assets/benchmark.svg +0 -0
  73. {codebase_index-2.1.0 → codebase_index-2.1.2}/assets/demo-terminal.svg +0 -0
  74. {codebase_index-2.1.0 → codebase_index-2.1.2}/assets/mark.png +0 -0
  75. {codebase_index-2.1.0 → codebase_index-2.1.2}/assets/social-preview.png +0 -0
  76. {codebase_index-2.1.0 → codebase_index-2.1.2}/bin/cbx +0 -0
  77. {codebase_index-2.1.0 → codebase_index-2.1.2}/bin/cbx.ps1 +0 -0
  78. {codebase_index-2.1.0 → codebase_index-2.1.2}/bin/codebase-index +0 -0
  79. {codebase_index-2.1.0 → codebase_index-2.1.2}/bin/codebase-index.ps1 +0 -0
  80. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/ARCHITECTURE.md +0 -0
  81. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/BENCHMARKS.md +0 -0
  82. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/COMMUNITY_AUDIT.md +0 -0
  83. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/COMMUNITY_LAUNCH.md +0 -0
  84. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/COMPARISON.md +0 -0
  85. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/DATABASE_SCHEMA.md +0 -0
  86. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/DEMO.md +0 -0
  87. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/DEVELOPMENT.md +0 -0
  88. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/FAQ.md +0 -0
  89. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/INSTALLATION.md +0 -0
  90. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/LANGUAGES.md +0 -0
  91. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/MCP.md +0 -0
  92. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/MEMORY.md +0 -0
  93. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/PRODUCT_UPGRADE_PLAN.md +0 -0
  94. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/QUICKSTART.md +0 -0
  95. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/RELEASE_CHECKLIST.md +0 -0
  96. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/RETRIEVAL.md +0 -0
  97. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/RETRIEVAL_PIPELINE.md +0 -0
  98. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/ROADMAP.md +0 -0
  99. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/SCHEMA.md +0 -0
  100. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/SECURITY.md +0 -0
  101. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/SECURITY_MODEL.md +0 -0
  102. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/SKILL_DESIGN.md +0 -0
  103. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/demo.tape +0 -0
  104. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-benchmark-index-improvements.md +0 -0
  105. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-generalize-skill-research-discipline.md +0 -0
  106. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m1-storage-discovery.md +0 -0
  107. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m2-fts-lexical.md +0 -0
  108. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m3-treesitter-symbols.md +0 -0
  109. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m4-hybrid-search.md +0 -0
  110. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m5-graph-impact.md +0 -0
  111. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m6-embeddings-vector.md +0 -0
  112. {codebase_index-2.1.0 → codebase_index-2.1.2}/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.2}/docs/superpowers/plans/2026-05-29-m7-skill-packaging.md +0 -0
  114. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m8-hooks-watch.md +0 -0
  115. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-m9-release.md +0 -0
  116. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-05-29-multilang-symbols-make-skill-win.md +0 -0
  117. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/plans/2026-06-24-snippet-skeletonization.md +0 -0
  118. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/specs/2026-05-29-plugin-one-command-install-design.md +0 -0
  119. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/specs/2026-06-14-typed-framework-edges-design.md +0 -0
  120. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/specs/2026-06-24-snippet-skeletonization-design.md +0 -0
  121. {codebase_index-2.1.0 → codebase_index-2.1.2}/docs/superpowers/specs/2026-09-10-evidence-memory-design.md +0 -0
  122. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/config.example.json +0 -0
  123. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/demo/EXPECTED_OUTPUT.md +0 -0
  124. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/demo/README.md +0 -0
  125. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/demo/run_demo.ps1 +0 -0
  126. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/demo/run_demo.sh +0 -0
  127. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/hooks/settings.json +0 -0
  128. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/queries.md +0 -0
  129. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/sample-queries.md +0 -0
  130. {codebase_index-2.1.0 → codebase_index-2.1.2}/examples/sample-retrieval-output.md +0 -0
  131. {codebase_index-2.1.0 → codebase_index-2.1.2}/install.ps1 +0 -0
  132. {codebase_index-2.1.0 → codebase_index-2.1.2}/install.sh +0 -0
  133. {codebase_index-2.1.0 → codebase_index-2.1.2}/lib/common.ps1 +0 -0
  134. {codebase_index-2.1.0 → codebase_index-2.1.2}/lib/common.sh +0 -0
  135. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/2.0-audit.md +0 -0
  136. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/README.md +0 -0
  137. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/__init__.py +0 -0
  138. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/architecture.md +0 -0
  139. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/build_indexes.py +0 -0
  140. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/data/.gitignore +0 -0
  141. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/diagnose_accretion.py +0 -0
  142. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/diagnose_calibration.py +0 -0
  143. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/diagnose_slots.py +0 -0
  144. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/diagnose_structure.py +0 -0
  145. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/experiment_memory.py +0 -0
  146. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/experiments.md +0 -0
  147. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/fit_gate.py +0 -0
  148. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/fit_weights.py +0 -0
  149. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/hypotheses.md +0 -0
  150. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/novelty.md +0 -0
  151. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/nucleus/__init__.py +0 -0
  152. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/nucleus/baselines.py +0 -0
  153. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/nucleus/evalrun.py +0 -0
  154. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/nucleus/memory.py +0 -0
  155. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/nucleus/relations.py +0 -0
  156. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/nucleus/search.py +0 -0
  157. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/related-work.md +0 -0
  158. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/selection.md +0 -0
  159. {codebase_index-2.1.0 → codebase_index-2.1.2}/research/test_nucleus.py +0 -0
  160. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/apply_labels.sh +0 -0
  161. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/bootstrap.ps1 +0 -0
  162. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/bootstrap.sh +0 -0
  163. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/check_links.py +0 -0
  164. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/check_versions.py +0 -0
  165. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/gen_assets.py +0 -0
  166. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/gen_benchmark_chart.py +0 -0
  167. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/gen_terminal_svg.py +0 -0
  168. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/release_notes.py +0 -0
  169. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/release_smoke.py +0 -0
  170. {codebase_index-2.1.0 → codebase_index-2.1.2}/scripts/sync_skill_copies.py +0 -0
  171. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/SKILL.md +0 -0
  172. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/examples/basic-usage.md +0 -0
  173. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/examples/claude-md-example.md +0 -0
  174. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/examples/hooks-example.json +0 -0
  175. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/references/commands.md +0 -0
  176. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/references/memory.md +0 -0
  177. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/references/response-contract.md +0 -0
  178. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/scripts/bootstrap.py +0 -0
  179. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/scripts/cbx +0 -0
  180. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/scripts/cbx.ps1 +0 -0
  181. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/scripts/doctor.py +0 -0
  182. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/scripts/install.py +0 -0
  183. {codebase_index-2.1.0 → codebase_index-2.1.2}/skill/scripts/smoke_test.py +0 -0
  184. {codebase_index-2.1.0 → codebase_index-2.1.2}/skills/codebase-index/SKILL.md +0 -0
  185. {codebase_index-2.1.0 → codebase_index-2.1.2}/skills/codebase-index/references/commands.md +0 -0
  186. {codebase_index-2.1.0 → codebase_index-2.1.2}/skills/codebase-index/references/memory.md +0 -0
  187. {codebase_index-2.1.0 → codebase_index-2.1.2}/skills/codebase-index/references/response-contract.md +0 -0
  188. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/__main__.py +0 -0
  189. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/config.py +0 -0
  190. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/discovery/__init__.py +0 -0
  191. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/discovery/classify.py +0 -0
  192. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/discovery/gates.py +0 -0
  193. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/discovery/ignore.py +0 -0
  194. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/discovery/walker.py +0 -0
  195. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/doctor.py +0 -0
  196. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/embeddings/__init__.py +0 -0
  197. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/embeddings/backend.py +0 -0
  198. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/embeddings/external.py +0 -0
  199. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/embeddings/local.py +0 -0
  200. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/embeddings/noop.py +0 -0
  201. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/__init__.py +0 -0
  202. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/analysis.py +0 -0
  203. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/builder.py +0 -0
  204. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/expand.py +0 -0
  205. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/export.py +0 -0
  206. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/modules.py +0 -0
  207. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/navigate.py +0 -0
  208. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/graph/retrieval.py +0 -0
  209. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/indexer/__init__.py +0 -0
  210. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/indexer/doc_chunks.py +0 -0
  211. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/indexer/freshness.py +0 -0
  212. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/indexer/pipeline.py +0 -0
  213. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/mcp/__init__.py +0 -0
  214. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/mcp/server.py +0 -0
  215. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/memory/__init__.py +0 -0
  216. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/memory/identity.py +0 -0
  217. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/memory/session.py +0 -0
  218. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/memory/store.py +0 -0
  219. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/memory/validate.py +0 -0
  220. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/models.py +0 -0
  221. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/output/__init__.py +0 -0
  222. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/output/json.py +0 -0
  223. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/output/markdown.py +0 -0
  224. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/output/redact.py +0 -0
  225. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/parsers/__init__.py +0 -0
  226. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/parsers/base.py +0 -0
  227. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/parsers/languages.py +0 -0
  228. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/parsers/line_chunker.py +0 -0
  229. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/parsers/symbol_chunks.py +0 -0
  230. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/parsers/treesitter.py +0 -0
  231. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/__init__.py +0 -0
  232. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/budget.py +0 -0
  233. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/diversity.py +0 -0
  234. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/features.py +0 -0
  235. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/fusion.py +0 -0
  236. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/fuzzy.py +0 -0
  237. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/intent.py +0 -0
  238. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/lexical.py +0 -0
  239. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/pipeline.py +0 -0
  240. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/priors.py +0 -0
  241. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/rerank.py +0 -0
  242. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/searchers.py +0 -0
  243. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/skeleton.py +0 -0
  244. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/tuning.py +0 -0
  245. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/retrieval/types.py +0 -0
  246. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/service.py +0 -0
  247. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/SKILL.md +0 -0
  248. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/examples/hooks/settings.json +0 -0
  249. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/references/commands.md +0 -0
  250. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/references/memory.md +0 -0
  251. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/references/response-contract.md +0 -0
  252. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/scripts/cbx +0 -0
  253. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_template/scripts/cbx.ps1 +0 -0
  254. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/skill_update.py +0 -0
  255. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/storage/__init__.py +0 -0
  256. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/storage/db.py +0 -0
  257. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/storage/repo.py +0 -0
  258. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/storage/schema.sql +0 -0
  259. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/watch/__init__.py +0 -0
  260. {codebase_index-2.1.0 → codebase_index-2.1.2}/src/codebase_index/watch/watcher.py +0 -0
  261. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/__init__.py +0 -0
  262. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/benchmark_honest.py +0 -0
  263. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/benchmark_honest_RESULTS.md +0 -0
  264. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/benchmark_honest_newtowny.txt +0 -0
  265. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/benchmark_public.py +0 -0
  266. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/benchmark_public_RESULTS.md +0 -0
  267. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/benchmark_real_repo.py +0 -0
  268. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/conftest.py +0 -0
  269. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/README.md +0 -0
  270. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/__init__.py +0 -0
  271. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/baselines.py +0 -0
  272. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/gen_queries.py +0 -0
  273. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/harness.py +0 -0
  274. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/memory_eval.py +0 -0
  275. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/metrics.py +0 -0
  276. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/queries/self_repo.yml +0 -0
  277. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/queries/self_repo_git.yml +0 -0
  278. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/1.10.0-perf.json +0 -0
  279. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/1.10.0-retrieval-8corpus.txt +0 -0
  280. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-04-public-baselines.json +0 -0
  281. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-04-public-baselines.md +0 -0
  282. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-14-evidence-memory.json +0 -0
  283. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-14-evidence-memory.md +0 -0
  284. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-24-agent-pilot.md +0 -0
  285. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-24-retrieval-2.1.0.txt +0 -0
  286. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/results/2026-09-24-terra-nl-queries.yml +0 -0
  287. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/run_baselines.py +0 -0
  288. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/eval/run_eval.py +0 -0
  289. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/README.md +0 -0
  290. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/expected_answers.yml +0 -0
  291. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/index-1.10.0/index.sqlite +0 -0
  292. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/multilang/sample.go +0 -0
  293. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/multilang/sample.java +0 -0
  294. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/multilang/sample.rs +0 -0
  295. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/.env +0 -0
  296. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/.gitignore +0 -0
  297. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/huge.json +0 -0
  298. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/logo.png +0 -0
  299. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/node_modules/leftpad/index.js +0 -0
  300. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/secrets.pem +0 -0
  301. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/src/api/service.py +0 -0
  302. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/src/auth/token.py +0 -0
  303. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/src/models/user.py +0 -0
  304. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/src/schema.generated.ts +0 -0
  305. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/fixtures/sample_repo/web/app.ts +0 -0
  306. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/architecture.json +0 -0
  307. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/describe_refresh.json +0 -0
  308. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/explain_auth.json +0 -0
  309. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/impact_user_model.json +0 -0
  310. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_architecture.json +0 -0
  311. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_describe_symbol.json +0 -0
  312. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_explain_code.json +0 -0
  313. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_find_refs.json +0 -0
  314. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_find_symbol.json +0 -0
  315. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_healthcheck.json +0 -0
  316. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_impact_of.json +0 -0
  317. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_impact_of_diff.json +0 -0
  318. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_index_stats.json +0 -0
  319. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_path_between.json +0 -0
  320. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/mcp_search_code.json +0 -0
  321. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/path_renew_to_token.json +0 -0
  322. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/refs_refresh_access_token.json +0 -0
  323. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/search_token.json +0 -0
  324. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/stats.json +0 -0
  325. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden/symbol_user.json +0 -0
  326. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/golden_utils.py +0 -0
  327. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/installer/smoke.ps1 +0 -0
  328. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/installer/smoke.sh +0 -0
  329. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_agent_ergonomics.py +0 -0
  330. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_analysis.py +0 -0
  331. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_benchmark_comparison.py +0 -0
  332. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_bootstrap.py +0 -0
  333. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_budget.py +0 -0
  334. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_chunker.py +0 -0
  335. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_classify.py +0 -0
  336. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_clean_cli.py +0 -0
  337. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_cli.py +0 -0
  338. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_cli_golden.py +0 -0
  339. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_compact_output.py +0 -0
  340. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_config.py +0 -0
  341. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_diff_impact.py +0 -0
  342. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_discovery.py +0 -0
  343. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_diversity.py +0 -0
  344. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_doc_chunks.py +0 -0
  345. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_doctor.py +0 -0
  346. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_embeddings_backend.py +0 -0
  347. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_embeddings_external.py +0 -0
  348. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_embeddings_local.py +0 -0
  349. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_eval_baselines.py +0 -0
  350. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_freshness.py +0 -0
  351. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_fusion.py +0 -0
  352. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_fuzzy.py +0 -0
  353. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_gen_queries.py +0 -0
  354. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_graph.py +0 -0
  355. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_graph_coverage.py +0 -0
  356. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_graph_export_cli.py +0 -0
  357. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_graph_retrieval.py +0 -0
  358. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_hooks_merge.py +0 -0
  359. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_hybrid_ranking.py +0 -0
  360. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_ignore.py +0 -0
  361. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_impact_cli.py +0 -0
  362. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_impact_decay.py +0 -0
  363. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_index_cli.py +0 -0
  364. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_init_cli.py +0 -0
  365. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_intent.py +0 -0
  366. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_languages.py +0 -0
  367. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_lexical.py +0 -0
  368. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_mcp_golden.py +0 -0
  369. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_mcp_server.py +0 -0
  370. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_eval_smoke.py +0 -0
  371. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_identity.py +0 -0
  372. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_lifecycle.py +0 -0
  373. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_session.py +0 -0
  374. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_stale_context.py +0 -0
  375. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_store.py +0 -0
  376. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_surfaces.py +0 -0
  377. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_memory_validate.py +0 -0
  378. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_migration_1x.py +0 -0
  379. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_multilang_symbols.py +0 -0
  380. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_navigate.py +0 -0
  381. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_output.py +0 -0
  382. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_packaging.py +0 -0
  383. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_parse_guardrails.py +0 -0
  384. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_perf_smoke.py +0 -0
  385. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_pipeline.py +0 -0
  386. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_pipeline_search.py +0 -0
  387. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_pipeline_vectors.py +0 -0
  388. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_plugin_manifest.py +0 -0
  389. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_plugin_skill_parity.py +0 -0
  390. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_plugin_wrappers.py +0 -0
  391. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_priors.py +0 -0
  392. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_public_benchmark.py +0 -0
  393. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_qualified_refs.py +0 -0
  394. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_quality_benchmark.py +0 -0
  395. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_ranking_features.py +0 -0
  396. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_read_cap.py +0 -0
  397. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_redact.py +0 -0
  398. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_release_scripts.py +0 -0
  399. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_repo_search.py +0 -0
  400. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_rerank.py +0 -0
  401. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_retrieval_types.py +0 -0
  402. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_scaffold.py +0 -0
  403. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_search_cli.py +0 -0
  404. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_searchers.py +0 -0
  405. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_seeded_index.py +0 -0
  406. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_service.py +0 -0
  407. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_skeleton.py +0 -0
  408. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_skill_update.py +0 -0
  409. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_storage.py +0 -0
  410. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_symbol_cli.py +0 -0
  411. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_symbol_ranking.py +0 -0
  412. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_symbol_refs.py +0 -0
  413. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_sync_skill_copies.py +0 -0
  414. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_treesitter.py +0 -0
  415. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_tuning_contract.py +0 -0
  416. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_update.py +0 -0
  417. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_update_cli.py +0 -0
  418. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_vector_search.py +0 -0
  419. {codebase_index-2.1.0 → codebase_index-2.1.2}/tests/test_vectors_storage.py +0 -0
  420. {codebase_index-2.1.0 → codebase_index-2.1.2}/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.2",
7
7
  "author": {
8
8
  "name": "codebase-index contributors"
9
9
  },
@@ -6,6 +6,40 @@ All notable changes to this project are documented here. The format is based on
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.1.2] - 2026-09-25
10
+
11
+ ### Fixed
12
+
13
+ - The `PreToolUse` guard no longer blocks work it should let through:
14
+ - the first `codebase-index` command of a session turns the guard off even when it
15
+ is the command that builds the index; before, a repository without an index kept
16
+ the guard on for the whole session. Session state now lives in the system temp
17
+ directory (`CBX_HOOK_STATE` overrides it) instead of inside the index;
18
+ - heredoc bodies (`cat > script.ps1 <<'EOF'`, `python - <<EOF`) are file content, not
19
+ commands, and are no longer scanned for `grep`/`Select-String`;
20
+ - a retry is matched by its search term, not by the whole tool call, so a rewritten
21
+ command or a new description for the same search goes through;
22
+ - a quoted alternation such as `grep "a\|b"` is read whole, and a search on its own
23
+ line of a multi-line command is recognised.
24
+
25
+ ## [2.1.1] - 2026-09-25
26
+
27
+ ### Added
28
+
29
+ - `codebase-index install-hooks [--global]` makes Claude Code search through the index
30
+ by default. It merges two hooks into `.claude/settings.json` (or
31
+ `~/.claude/settings.json`), keeping every other hook:
32
+ - `SessionStart`: in an indexed repository, one line of context naming the commands
33
+ that answer code questions;
34
+ - `PreToolUse` on `Grep|Bash`: the first code search of a session, before any
35
+ `codebase-index` command has run, is sent back with the index command to use
36
+ instead. Repeating the same call lets it through, the first index command turns the
37
+ guard off for the session, and searches over docs, logs or config, pipelines such
38
+ as `ps | grep`, and directories without an index are never intercepted.
39
+ `CBX_GUARD=0` disables it; `install-hooks --uninstall` removes both hooks.
40
+ The hooks run as `codebase-index-hook`, a standard-library-only entry point (about
41
+ 0.1 s per call). The Claude Code plugin registers the same hooks.
42
+
9
43
  ## [2.1.0] - 2026-09-24
10
44
 
11
45
  Agent-efficiency release. An agent answering five code questions on a 5.9k-file
@@ -868,7 +902,9 @@ Pooled over 305 queries (Python, Java, TypeScript), v1.8.0 → 1.9.0:
868
902
  - Hooks example + `watch` mode for keeping the index fresh without blocking the edit loop (M8).
869
903
  - `doctor`, `stats`, `clean` diagnostics/maintenance commands.
870
904
 
871
- [Unreleased]: https://github.com/denfry/codebase-index/compare/v2.1.0...HEAD
905
+ [Unreleased]: https://github.com/denfry/codebase-index/compare/v2.1.2...HEAD
906
+ [2.1.2]: https://github.com/denfry/codebase-index/compare/v2.1.1...v2.1.2
907
+ [2.1.1]: https://github.com/denfry/codebase-index/compare/v2.1.0...v2.1.1
872
908
  [2.1.0]: https://github.com/denfry/codebase-index/compare/v2.0.0...v2.1.0
873
909
  [2.0.0]: https://github.com/denfry/codebase-index/compare/v1.9.0...v2.0.0
874
910
  [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.2
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.2
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.2.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.2"
@@ -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,272 @@
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: searching for the
10
+ same term again lets it through, the first ``codebase-index`` command in the
11
+ session turns the guard off (even one that has yet to build the index), and
12
+ searches over docs, logs or config, or inside heredoc bodies, are never
13
+ intercepted.
14
+
15
+ Measured on a 5.9k-file monorepo, an agent spent 19% fewer tokens with the index
16
+ than with grep (tests/eval/results/2026-09-24-agent-pilot.md), but agents reach
17
+ for Grep out of habit. The guard runs on every Bash call, so this module imports
18
+ only the standard library and does no index work beyond one small SQLite read.
19
+
20
+ Entry point: ``codebase-index-hook <event>`` reads the hook JSON on stdin and
21
+ prints the hook JSON reply (or nothing). ``CBX_GUARD=0`` disables the guard.
22
+ Per-session state lives in the system temp directory (``CBX_HOOK_STATE``
23
+ overrides it), so it exists before the repository has an index.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import hashlib
29
+ import json
30
+ import os
31
+ import re
32
+ import sqlite3
33
+ import sys
34
+ import tempfile
35
+ import time
36
+ from pathlib import Path
37
+ from typing import Any, Optional
38
+
39
+ INDEX_REL = Path(".claude") / "cache" / "codebase-index" / "index.sqlite"
40
+ _STATE_DIR = "codebase-index-hooks"
41
+ _STATE_TTL_S = 2 * 24 * 3600
42
+
43
+ # A shell segment that searches file contents: the first command of the segment
44
+ # (after `&&`, `||`, `;`, a newline or the start), so `ps aux | grep x` is not a
45
+ # repo search. Quoted arguments may hold `|`, as in grep "a\|b".
46
+ _SEARCH_CMD = re.compile(
47
+ r"(?:^|&&|\|\||;|\n)[ \t]*(?:cd\s+\S+\s*&&\s*)?"
48
+ r"(?P<cmd>git\s+grep|grep|egrep|fgrep|rg|ag|ack|findstr|Select-String)\b"
49
+ r"""(?P<args>(?:"[^"\n]*"|'[^'\n]*'|[^|;&\n"'])*)""",
50
+ re.IGNORECASE,
51
+ )
52
+ # A heredoc body is file content (a script being written, stdin for python), not
53
+ # commands; an unterminated one runs to the end of the command.
54
+ _HEREDOC = re.compile(
55
+ r"""(?<!<)<<-?(?!<)[ \t]*(['"]?)([A-Za-z_][\w-]*)\1(?P<rest>[^\n]*)\n"""
56
+ r".*?(?:^[ \t]*\2[ \t]*$|\Z)",
57
+ re.DOTALL | re.MULTILINE,
58
+ )
59
+ _INDEX_CMD = re.compile(r"(?:^|[\s;&|/\\\"'])(?:codebase-index|cbx)(?:\.exe|\.ps1)?\s+\w")
60
+ # Searches over prose, logs and config are what grep is for; leave them alone.
61
+ _NON_CODE = re.compile(
62
+ r"\.(?:md|mdx|rst|txt|log|ya?ml|json|toml|ini|cfg|conf|csv|xml|properties|lock|env|html?)\b"
63
+ r"|--include=\*?\.(?:md|txt|log|ya?ml|json|toml)",
64
+ re.IGNORECASE,
65
+ )
66
+ _NON_CODE_TYPES = {"md", "markdown", "txt", "log", "yaml", "yml", "json", "toml", "xml",
67
+ "csv", "ini", "config"}
68
+
69
+
70
+ def main(argv: Optional[list[str]] = None) -> int:
71
+ args = sys.argv[1:] if argv is None else argv
72
+ event = args[0] if args else ""
73
+ try:
74
+ payload = json.loads(sys.stdin.read() or "{}")
75
+ except (ValueError, OSError):
76
+ return 0
77
+ try:
78
+ reply = {"guard": guard, "session-start": session_start}.get(event, _none)(payload)
79
+ except Exception: # a hook must never break the session it runs in
80
+ return 0
81
+ if reply:
82
+ sys.stdout.write(json.dumps(reply))
83
+ return 0
84
+
85
+
86
+ def _none(_payload: dict) -> Optional[dict]:
87
+ return None
88
+
89
+
90
+ def find_index_root(start: Path) -> Optional[Path]:
91
+ """The nearest directory at or above `start` that holds an index."""
92
+ for path in (start, *start.parents):
93
+ if (path / INDEX_REL).is_file():
94
+ return path
95
+ return None
96
+
97
+
98
+ def _cwd(payload: dict) -> Path:
99
+ raw = str(payload.get("cwd") or os.getcwd())
100
+ # Git Bash hands out MSYS paths (/c/Projects/x); Python on Windows needs C:/.
101
+ msys = re.match(r"^/([a-zA-Z])(/.*)?$", raw)
102
+ if os.name == "nt" and msys:
103
+ raw = f"{msys.group(1)}:{msys.group(2) or '/'}"
104
+ return Path(raw)
105
+
106
+
107
+ # --- session-start ------------------------------------------------------------------
108
+
109
+
110
+ def session_start(payload: dict) -> Optional[dict]:
111
+ root = find_index_root(_cwd(payload))
112
+ if root is None:
113
+ return None
114
+ files = _indexed_files(root)
115
+ size = f" ({files} files)" if files else ""
116
+ note = (
117
+ f"This repository has a codebase-index index{size}. For questions about the "
118
+ "code (where something is, how it works, who calls it, what a change breaks) "
119
+ "use the codebase-index skill before Grep or Read: "
120
+ '`codebase-index search "<question>" --compact`, '
121
+ '`codebase-index refs "Owner.member" --compact`, '
122
+ '`codebase-index impact "Owner.member" --compact`. '
123
+ "Output lines are numbered; cite them as file:line."
124
+ )
125
+ return {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": note}}
126
+
127
+
128
+ def _indexed_files(root: Path) -> Optional[int]:
129
+ try:
130
+ conn = sqlite3.connect(f"file:{root / INDEX_REL}?mode=ro", uri=True, timeout=0.5)
131
+ try:
132
+ return int(conn.execute("SELECT COUNT(*) FROM files").fetchone()[0])
133
+ finally:
134
+ conn.close()
135
+ except sqlite3.Error:
136
+ return None
137
+
138
+
139
+ # --- guard ------------------------------------------------------------------------------
140
+
141
+
142
+ def guard(payload: dict) -> Optional[dict]:
143
+ if os.environ.get("CBX_GUARD", "").strip() == "0":
144
+ return None
145
+ tool = payload.get("tool_name") or ""
146
+ tool_input = payload.get("tool_input") or {}
147
+ if tool not in ("Grep", "Bash"):
148
+ return None
149
+ state = _State(str(payload.get("session_id") or "default"))
150
+
151
+ if tool == "Bash":
152
+ command = _HEREDOC.sub(lambda m: "<<" + m.group("rest"), str(tool_input.get("command") or ""))
153
+ if _INDEX_CMD.search(command):
154
+ # Checked before the index exists: the first search is what builds it.
155
+ state.mark_used()
156
+ return None
157
+ pattern = _shell_search_pattern(command)
158
+ if pattern is None:
159
+ return None
160
+ else:
161
+ if _grep_targets_non_code(tool_input):
162
+ return None
163
+ pattern = str(tool_input.get("pattern") or "")
164
+
165
+ if state.used or find_index_root(_cwd(payload)) is None:
166
+ return None
167
+ # Keyed by the search term, not the whole call: a retry rarely repeats the
168
+ # command byte for byte (a new description, another pipe, a regenerated script).
169
+ fingerprint = hashlib.sha1(_term(pattern).lower().encode("utf-8")).hexdigest()
170
+ if state.was_denied(fingerprint):
171
+ return None # the retry after a nudge: the agent decided grep is right
172
+ state.deny(fingerprint)
173
+ return {
174
+ "hookSpecificOutput": {
175
+ "hookEventName": "PreToolUse",
176
+ "permissionDecision": "deny",
177
+ "permissionDecisionReason": _reason(pattern),
178
+ }
179
+ }
180
+
181
+
182
+ def _shell_search_pattern(command: str) -> Optional[str]:
183
+ """The search term of a content search in `command`, or None if there is none."""
184
+ for match in _SEARCH_CMD.finditer(command):
185
+ args = match.group("args")
186
+ if _NON_CODE.search(args):
187
+ continue
188
+ quoted = re.search(r"""(['"])(.+?)\1""", args)
189
+ if quoted:
190
+ return quoted.group(2)
191
+ words = [w for w in args.split() if not w.startswith("-")]
192
+ return words[0] if words else ""
193
+ return None
194
+
195
+
196
+ def _grep_targets_non_code(tool_input: dict) -> bool:
197
+ kind = str(tool_input.get("type") or "").lower()
198
+ if kind in _NON_CODE_TYPES:
199
+ return True
200
+ target = f"{tool_input.get('glob') or ''} {tool_input.get('path') or ''}"
201
+ return bool(_NON_CODE.search(target))
202
+
203
+
204
+ def _term(pattern: str) -> str:
205
+ """A regex search pattern as plain query words."""
206
+ return " ".join(re.sub(r"""[\\^$()\[\]{}|*+?"']""", " ", pattern).split())[:80]
207
+
208
+
209
+ def _reason(pattern: str) -> str:
210
+ term = _term(pattern) or "<what you are looking for>"
211
+ return (
212
+ "codebase-index: this repository is indexed, and the index answers code "
213
+ "searches with ranked, numbered lines in fewer tokens than grep. Run it first:\n"
214
+ f' codebase-index search "{term}" --compact # where / how\n'
215
+ ' codebase-index refs "Owner.member" --compact # every call site, with callers\n'
216
+ ' codebase-index symbol "Name" # a definition\n'
217
+ "If the index does not answer, search for the same term again and it will run. "
218
+ "Once any codebase-index command has run in this session, searches are not "
219
+ "intercepted."
220
+ )
221
+
222
+
223
+ class _State:
224
+ """Per-session guard state: whether the index was used, which calls were nudged."""
225
+
226
+ def __init__(self, session: str) -> None:
227
+ safe = re.sub(r"[^A-Za-z0-9_.-]", "_", session)[:80] or "default"
228
+ self.dir = Path(os.environ.get("CBX_HOOK_STATE") or Path(tempfile.gettempdir()) / _STATE_DIR)
229
+ self.path = self.dir / f"{safe}.json"
230
+ self.data: dict[str, Any] = {"used": False, "denied": []}
231
+ try:
232
+ self.data.update(json.loads(self.path.read_text(encoding="utf-8")))
233
+ except (OSError, ValueError):
234
+ pass
235
+
236
+ @property
237
+ def used(self) -> bool:
238
+ return bool(self.data.get("used"))
239
+
240
+ def was_denied(self, fingerprint: str) -> bool:
241
+ return fingerprint in self.data.get("denied", [])
242
+
243
+ def mark_used(self) -> None:
244
+ if not self.used:
245
+ self.data["used"] = True
246
+ self._save()
247
+
248
+ def deny(self, fingerprint: str) -> None:
249
+ self.data.setdefault("denied", []).append(fingerprint)
250
+ self._save()
251
+
252
+ def _save(self) -> None:
253
+ try:
254
+ self.dir.mkdir(parents=True, exist_ok=True)
255
+ self.path.write_text(json.dumps(self.data), encoding="utf-8")
256
+ _prune(self.dir)
257
+ except OSError:
258
+ pass
259
+
260
+
261
+ def _prune(directory: Path) -> None:
262
+ cutoff = time.time() - _STATE_TTL_S
263
+ for item in directory.glob("*.json"):
264
+ try:
265
+ if item.stat().st_mtime < cutoff:
266
+ item.unlink()
267
+ except OSError:
268
+ pass
269
+
270
+
271
+ if __name__ == "__main__":
272
+ 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"