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.
- codebase_index-2.1.1/.claude/skills/codebase-index/.skill_version +1 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude-plugin/plugin.json +1 -1
- codebase_index-2.1.1/.codex/skills/codebase-index/.skill_version +1 -0
- codebase_index-2.1.1/.opencode/skills/codebase-index/.skill_version +1 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/CHANGELOG.md +20 -1
- {codebase_index-2.1.0 → codebase_index-2.1.1}/PKG-INFO +5 -2
- {codebase_index-2.1.0 → codebase_index-2.1.1}/README.md +4 -1
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/installer.md +1 -1
- codebase_index-2.1.1/hooks/hooks.json +31 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/pyproject.toml +1 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/requirements.lock +1 -1
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/__init__.py +1 -1
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/cli.py +35 -0
- codebase_index-2.1.1/src/codebase_index/hooks.py +256 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/scaffold.py +61 -0
- codebase_index-2.1.1/tests/test_hooks_guard.py +120 -0
- codebase_index-2.1.0/.claude/skills/codebase-index/.skill_version +0 -1
- codebase_index-2.1.0/.codex/skills/codebase-index/.skill_version +0 -1
- codebase_index-2.1.0/.opencode/skills/codebase-index/.skill_version +0 -1
- codebase_index-2.1.0/hooks/hooks.json +0 -14
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/SKILL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/examples/hooks/settings.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/references/commands.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/references/memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/references/response-contract.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/scripts/cbx +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude/skills/codebase-index/scripts/cbx.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.claude-plugin/marketplace.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/SKILL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/examples/hooks/settings.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/references/commands.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/references/memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/references/response-contract.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/scripts/cbx +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.codex/skills/codebase-index/scripts/cbx.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.editorconfig +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.gitattributes +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/benchmark_report.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/language_support.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/ISSUE_TEMPLATE/skill_listing_request.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/SUPPORT.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/labels.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/workflows/ci.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.github/workflows/release.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.gitignore +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/agents/codebase-index.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/commands/codebase-index.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/SKILL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/examples/hooks/settings.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/references/commands.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/references/memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/references/response-contract.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/scripts/cbx +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/.opencode/skills/codebase-index/scripts/cbx.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/AGENTS.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/CODE_OF_CONDUCT.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/CONTRIBUTING.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/LICENSE +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/ROADMAP.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/SECURITY.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/claude.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/claude.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/codex.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/codex.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/opencode.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/adapters/opencode.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/architecture.svg +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/benchmark.svg +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/demo-terminal.svg +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/mark.png +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/assets/social-preview.png +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/cbx +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/cbx.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/codebase-index +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/bin/codebase-index.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/ARCHITECTURE.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/BENCHMARKS.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/COMMUNITY_AUDIT.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/COMMUNITY_LAUNCH.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/COMPARISON.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/DATABASE_SCHEMA.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/DEMO.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/DEVELOPMENT.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/FAQ.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/INSTALLATION.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/LANGUAGES.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/MCP.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/MEMORY.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/PRODUCT_UPGRADE_PLAN.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/QUICKSTART.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/RELEASE_CHECKLIST.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/RETRIEVAL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/RETRIEVAL_PIPELINE.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/ROADMAP.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SCHEMA.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SECURITY.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SECURITY_MODEL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/SKILL_DESIGN.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/demo.tape +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-benchmark-index-improvements.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-generalize-skill-research-discipline.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m1-storage-discovery.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m2-fts-lexical.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m3-treesitter-symbols.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m4-hybrid-search.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m5-graph-impact.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m6-embeddings-vector.md +0 -0
- {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
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m7-skill-packaging.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m8-hooks-watch.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-m9-release.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-05-29-multilang-symbols-make-skill-win.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/plans/2026-06-24-snippet-skeletonization.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-05-29-plugin-one-command-install-design.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-06-14-typed-framework-edges-design.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-06-24-snippet-skeletonization-design.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/docs/superpowers/specs/2026-09-10-evidence-memory-design.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/config.example.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/EXPECTED_OUTPUT.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/README.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/run_demo.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/demo/run_demo.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/hooks/settings.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/queries.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/sample-queries.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/examples/sample-retrieval-output.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/install.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/install.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/lib/common.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/lib/common.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/2.0-audit.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/README.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/architecture.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/build_indexes.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/data/.gitignore +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_accretion.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_calibration.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_slots.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/diagnose_structure.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/experiment_memory.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/experiments.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/fit_gate.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/fit_weights.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/hypotheses.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/novelty.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/baselines.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/evalrun.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/memory.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/relations.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/nucleus/search.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/related-work.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/selection.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/research/test_nucleus.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/apply_labels.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/bootstrap.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/bootstrap.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/check_links.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/check_versions.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/gen_assets.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/gen_benchmark_chart.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/gen_terminal_svg.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/release_notes.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/release_smoke.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/scripts/sync_skill_copies.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/SKILL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/examples/basic-usage.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/examples/claude-md-example.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/examples/hooks-example.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/references/commands.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/references/memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/references/response-contract.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/bootstrap.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/cbx +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/cbx.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/doctor.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/install.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skill/scripts/smoke_test.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/SKILL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/references/commands.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/references/memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/skills/codebase-index/references/response-contract.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/__main__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/config.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/classify.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/gates.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/ignore.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/discovery/walker.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/doctor.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/backend.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/external.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/local.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/embeddings/noop.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/analysis.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/builder.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/expand.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/export.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/modules.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/navigate.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/graph/retrieval.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/doc_chunks.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/freshness.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/indexer/pipeline.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/mcp/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/mcp/server.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/identity.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/session.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/store.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/memory/validate.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/models.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/json.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/markdown.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/output/redact.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/base.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/languages.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/line_chunker.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/symbol_chunks.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/parsers/treesitter.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/budget.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/diversity.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/features.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/fusion.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/fuzzy.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/intent.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/lexical.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/pipeline.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/priors.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/rerank.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/searchers.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/skeleton.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/tuning.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/retrieval/types.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/service.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/SKILL.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/examples/hooks/settings.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/references/commands.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/references/memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/references/response-contract.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/scripts/cbx +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_template/scripts/cbx.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/skill_update.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/db.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/repo.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/storage/schema.sql +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/watch/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/src/codebase_index/watch/watcher.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_honest.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_honest_RESULTS.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_honest_newtowny.txt +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_public.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_public_RESULTS.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/benchmark_real_repo.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/conftest.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/README.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/__init__.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/baselines.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/gen_queries.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/harness.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/memory_eval.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/metrics.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/queries/self_repo.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/queries/self_repo_git.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/1.10.0-perf.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/1.10.0-retrieval-8corpus.txt +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-04-public-baselines.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-04-public-baselines.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-14-evidence-memory.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-14-evidence-memory.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-24-agent-pilot.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-24-retrieval-2.1.0.txt +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/results/2026-09-24-terra-nl-queries.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/run_baselines.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/eval/run_eval.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/README.md +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/expected_answers.yml +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/index-1.10.0/index.sqlite +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/multilang/sample.go +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/multilang/sample.java +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/multilang/sample.rs +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/.env +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/.gitignore +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/huge.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/logo.png +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/node_modules/leftpad/index.js +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/secrets.pem +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/api/service.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/auth/token.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/models/user.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/src/schema.generated.ts +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/fixtures/sample_repo/web/app.ts +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/architecture.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/describe_refresh.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/explain_auth.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/impact_user_model.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_architecture.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_describe_symbol.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_explain_code.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_find_refs.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_find_symbol.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_healthcheck.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_impact_of.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_impact_of_diff.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_index_stats.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_path_between.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/mcp_search_code.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/path_renew_to_token.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/refs_refresh_access_token.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/search_token.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/stats.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden/symbol_user.json +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/golden_utils.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/installer/smoke.ps1 +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/installer/smoke.sh +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_agent_ergonomics.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_analysis.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_benchmark_comparison.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_bootstrap.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_budget.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_chunker.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_classify.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_clean_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_cli_golden.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_compact_output.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_config.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_diff_impact.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_discovery.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_diversity.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_doc_chunks.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_doctor.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_embeddings_backend.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_embeddings_external.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_embeddings_local.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_eval_baselines.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_freshness.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_fusion.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_fuzzy.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_gen_queries.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph_coverage.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph_export_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_graph_retrieval.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_hooks_merge.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_hybrid_ranking.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_ignore.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_impact_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_impact_decay.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_index_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_init_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_intent.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_languages.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_lexical.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_mcp_golden.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_mcp_server.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_eval_smoke.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_identity.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_lifecycle.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_session.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_stale_context.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_store.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_surfaces.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_memory_validate.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_migration_1x.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_multilang_symbols.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_navigate.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_output.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_packaging.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_parse_guardrails.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_perf_smoke.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_pipeline.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_pipeline_search.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_pipeline_vectors.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_plugin_manifest.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_plugin_skill_parity.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_plugin_wrappers.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_priors.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_public_benchmark.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_qualified_refs.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_quality_benchmark.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_ranking_features.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_read_cap.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_redact.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_release_scripts.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_repo_search.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_rerank.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_retrieval_types.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_scaffold.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_search_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_searchers.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_seeded_index.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_service.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_skeleton.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_skill_update.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_storage.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_symbol_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_symbol_ranking.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_symbol_refs.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_sync_skill_copies.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_treesitter.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_tuning_contract.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_update.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_update_cli.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_vector_search.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_vectors_storage.py +0 -0
- {codebase_index-2.1.0 → codebase_index-2.1.1}/tests/test_watcher.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.1
|
|
@@ -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.
|
|
6
|
+
"version": "2.1.1",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "codebase-index contributors"
|
|
9
9
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.1
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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"
|
|
@@ -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 +0,0 @@
|
|
|
1
|
-
2.1.0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.1.0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.1.0
|
|
File without changes
|