From dc713daa10743002d826c3f4118ad1f20b0965e3 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 03:24:19 +0000 Subject: [PATCH 01/13] find: an exact-text tool that replaces grep over the index The MCP surface had no answer to grep's question. The guard pointed agents at bm25_search for exact identifiers, but that is ranked and cut at k: 'every place Foo appears' came back as the ten chunks most about Foo. Agents kept a reason to reach for grep. Add a fourth tool, find: every line containing an exact string, cited path:line like grep -n, complete and unranked. The engine's token match narrows to the chunks that contain every token of the query (an inverted-list intersection, no scoring), then the literal is verified line by line inside those chunks. The analyzer splits identifiers (parse_config indexes as parse and config), so the first step alone would over-match; the second makes every hit a real occurrence, and grep's line-based, case-sensitive semantics fall out of it (ignoreCase opts out). Results are capped (default 100, max 500) and always carry the repo-wide total, so a cut list still says how many matches exist. The same door serves the CLI as cx find, prints path:line: text, and records a receipt and ledger entry like search and sql. Docs, the skill, and the tool descriptions now describe four tools, one per question: find is unranked and complete where search is ranked and top-k, so it is not the near-duplicate the three-tool rule guards against. --- .devin/wiki.json | 6 +- AGENTS.md | 13 +- README.md | 45 ++--- context7.json | 2 +- .../concepts/code-search-for-coding-agents.md | 12 +- docs/faq.md | 21 ++- docs/tradeoffs.md | 16 +- llms.txt | 30 ++-- skills/code-context/SKILL.md | 55 ++++-- src/cli.ts | 18 +- src/commands/query-cmds.ts | 55 +++++- src/core/searcher.ts | 159 +++++++++++++++++- src/core/usage.ts | 29 +++- src/mcp/server.ts | 108 +++++++++++- test/integration.test.ts | 44 ++++- test/searcher.test.ts | 42 ++++- test/usage.test.ts | 31 ++++ 17 files changed, 585 insertions(+), 101 deletions(-) diff --git a/.devin/wiki.json b/.devin/wiki.json index d17f1ca..0dfead7 100644 --- a/.devin/wiki.json +++ b/.devin/wiki.json @@ -1,13 +1,13 @@ { "repo_notes": [ { - "content": "code-context is local code search for AI coding agents: a CLI (bins `code-context` and `cx`) and an MCP server over a ranked index that lives in plain files inside the repo, under `.infino/`. It fuses exact keyword matching (BM25) with semantic similarity (vectors) into one ranked pass, and exposes read-only SQL over the index so search composes with aggregation (`GROUP BY`). Drop it into Claude Code with `claude mcp add code-context -- npx -y @infino-ai/code-context mcp`, or install the CLI with `npm install -g @infino-ai/code-context` then `cx index` in a repo. Document it as a tool developers and coding agents use to answer questions about a codebase without crawling files into the context window: lead with the MCP setup, the three MCP tools (search, sql, reindex), and how the index stays fresh." + "content": "code-context is local code search for AI coding agents: a CLI (bins `code-context` and `cx`) and an MCP server over a ranked index that lives in plain files inside the repo, under `.infino/`. It fuses exact keyword matching (BM25) with semantic similarity (vectors) into one ranked pass, and exposes read-only SQL over the index so search composes with aggregation (`GROUP BY`). Drop it into Claude Code with `claude mcp add code-context -- npx -y @infino-ai/code-context mcp`, or install the CLI with `npm install -g @infino-ai/code-context` then `cx index` in a repo. Document it as a tool developers and coding agents use to answer questions about a codebase without crawling files into the context window: lead with the MCP setup, the four MCP tools (find, search, sql, reindex), and how the index stays fresh." }, { - "content": "Common misconceptions to avoid when documenting code-context, with the corrected statements: (1) It is NOT a cloud service and needs NO account, API key, or database server. It runs locally; embedding uses a small local model downloaded once, and code never leaves the machine. (2) It is NOT semantic/vector search only. Search is hybrid: BM25 and vector rankings fuse in one pass, and until vectors finish backfilling it degrades to keyword-only rather than failing. (3) The index is NOT a proprietary database tier. It is plain files in `.infino/` that you can copy, cache in CI, or gitignore. (4) It is NOT a structural code-intelligence tool. It does ranked content retrieval and content-relevance aggregation, not call-graph tracing, dead-code detection, or type resolution; graph/LSP tools are complementary and stack alongside it. (5) It is for questions that span the repo (understanding a subsystem, ranking or aggregating across files); for jumping to one known symbol a plain grep is already cheap." + "content": "Common misconceptions to avoid when documenting code-context, with the corrected statements: (1) It is NOT a cloud service and needs NO account, API key, or database server. It runs locally; embedding uses a small local model downloaded once, and code never leaves the machine. (2) It is NOT semantic/vector search only. Search is hybrid: BM25 and vector rankings fuse in one pass, and until vectors finish backfilling it degrades to keyword-only rather than failing. (3) The index is NOT a proprietary database tier. It is plain files in `.infino/` that you can copy, cache in CI, or gitignore. (4) It is NOT a structural code-intelligence tool. It does ranked content retrieval and content-relevance aggregation, not call-graph tracing, dead-code detection, or type resolution; graph/LSP tools are complementary and stack alongside it. (5) It is NOT only for questions that span the repo. Those (understanding a subsystem, ranking or aggregating across files) are where it saves the most, but the grep case - every occurrence of a known symbol or literal - is served by the find tool from the same index, complete and unranked, with no file scanned." }, { - "content": "How it works: files are chunked at definition boundaries with tree-sitter (WASM, no native compile) for common languages, with a fixed-window fallback for the rest; every chunk carries path, start_line, end_line, lang, and content. Chunks are indexed into one table named `chunks` in `.infino/`, with a BM25 full-text index and an IVF vector index, queried in-process through the infino engine's Node binding (no server). Readiness is staged: the keyword index commits in seconds so search is live immediately, while vectors backfill in the background and hybrid ranking unlocks automatically when they land. Sync is incremental: a per-file state map (size/mtime prefilter, then content hash) re-chunks and re-embeds only changed files, and the MCP server auto-syncs in the background as queries arrive. The MCP surface is exactly three tools by design: search (hybrid ranked retrieval), sql (read-only SELECT/WITH, with the ranked search functions bm25_search/hybrid_search usable as table-valued relations so search composes with GROUP BY aggregation), and reindex (incremental sync). code-context is built on the infino engine (https://github.com/infino-ai/infino), whose same index format also serves logs, docs, and agent memory." + "content": "How it works: files are chunked at definition boundaries with tree-sitter (WASM, no native compile) for common languages, with a fixed-window fallback for the rest; every chunk carries path, start_line, end_line, lang, and content. Chunks are indexed into one table named `chunks` in `.infino/`, with a BM25 full-text index and an IVF vector index, queried in-process through the infino engine's Node binding (no server). Readiness is staged: the keyword index commits in seconds so search is live immediately, while vectors backfill in the background and hybrid ranking unlocks automatically when they land. Sync is incremental: a per-file state map (size/mtime prefilter, then content hash) re-chunks and re-embeds only changed files, and the MCP server auto-syncs in the background as queries arrive. The MCP surface is exactly four tools by design: find (every line containing an exact string, cited path:line; the index's token match picks candidate chunks and each line is checked for the literal, so it is complete and unranked like grep -n), search (hybrid ranked retrieval), sql (read-only SELECT/WITH, with the ranked search functions bm25_search/hybrid_search usable as table-valued relations so search composes with GROUP BY aggregation), and reindex (incremental sync). code-context is built on the infino engine (https://github.com/infino-ai/infino), whose same index format also serves logs, docs, and agent memory." } ] } diff --git a/AGENTS.md b/AGENTS.md index 5b02689..f24248f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,7 @@ the honest limits in [docs/tradeoffs.md](docs/tradeoffs.md). ## Repo map - `src/cli.ts`: the `cx` / `code-context` command entry (commander). -- `src/mcp/server.ts`: the MCP server, three tools (`search`, `sql`, +- `src/mcp/server.ts`: the MCP server, four tools (`find`, `search`, `sql`, `reindex`). Each takes an optional `path` (repo root) so one server serves multiple repos in a session, defaulting to the startup root. - `src/mcp/repos.ts`: the per-repo registry - resolves and validates a @@ -34,7 +34,7 @@ the honest limits in [docs/tradeoffs.md](docs/tradeoffs.md). (`CX_AUTO_INDEX=0` restores the strict "index it first" error). - `src/core/`: the engine-facing core. `chunker` (tree-sitter chunking), `indexer` (build + staged readiness + incremental sync), `searcher` - (hybrid search + SQL), `embedder` (local model), `filestate` (incremental + (find, hybrid search, SQL), `embedder` (local model), `filestate` (incremental sync state), `walker`, `manifest`, `config`, `context`, `output`. - `src/commands/`: CLI command implementations (`index-cmd`, `query-cmds`). - `test/`: vitest suites. `bench/`: the benchmark harness. `docs/`: docs. @@ -53,9 +53,12 @@ before opening a PR. ## Conventions - TypeScript, ES modules. Every source file carries an SPDX header. -- The MCP surface is deliberately three tools: one way to find (`search`), - one way to count (`sql`), one way to stay fresh (`reindex`). Adding - near-duplicate retrieval tools worsens an agent's tool selection; resist it. +- The MCP surface is deliberately four tools, one per question: where does + this exact text occur (`find`, unranked and complete - the grep + replacement), what is most relevant (`search`, ranked top-k), how much of + what is where (`sql`), stay fresh (`reindex`). Adding near-duplicate + retrieval tools worsens an agent's tool selection; resist it. A new tool + must answer a question none of these four does. - Search results carry chunk content plus `path:line` ranges so answers cite code; keep that contract when touching `searcher` or the tool descriptions. diff --git a/README.md b/README.md index f44baa3..7e60fd2 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Install the Claude Code plugin - nothing to paste into a config: /plugin install code-context@infino-ai ``` -It registers code-context's three tools with `alwaysLoad` already set, so the +It registers code-context's four tools with `alwaysLoad` already set, so the agent keeps them in view and reaches for the index directly instead of falling back to plain file search. @@ -75,8 +75,8 @@ servers - where clients defer tool definitions behind a tool-search step - the agent doesn't miss the index and fall back to plain file search. (Use *either* the plugin or this command, not both.) -Then just ask a question about the code. The first `search` or `sql` on an -unindexed repo builds the index inline and answers on the same call: keyword +Then just ask a question about the code. The first `find`, `search`, or `sql` +on an unindexed repo builds the index inline and answers on the same call: keyword search is live in seconds, and vectors backfill in the background. (Prefer to kick it off yourself? The `reindex` tool does the same build on demand.) @@ -118,14 +118,17 @@ One index and a deliberately small tool surface for agents: | Tool | What it does | When agents use it | |---|---|---| +| `find` | Every line containing an exact string, cited `path:line` like `grep -n`. Complete and unranked: the index's token match picks the candidate chunks, then each line is checked for the literal, so no file is scanned and every hit is a real occurrence. | Where an agent would grep: every use or definition of an identifier, an error message, a config key. | | `search` | One ranked pass fusing exact keyword matching (BM25) with semantic similarity (reciprocal-rank fusion). Hits carry the chunk content, so answers come straight from results. | A strong default for finding and understanding code: how a subsystem works, code by meaning or exact term, context before a change, similar implementations - exact identifiers and paraphrases in the same call. | | `sql` | Read-only SQL over the index, with the ranked search functions (`bm25_search`/`hybrid_search`) usable as table-valued relations. | Counts, rankings, aggregates over the whole repo in one query. | | `reindex` | Incremental sync (the server also auto-syncs in the background). | After significant edits. | -Three tools is a deliberate design: one way to find, one way to count, one -way to stay fresh. Every additional near-duplicate retrieval tool worsens an -agent's tool selection, and hybrid search's keyword half already ranks -exact identifier terms highly, so a separate lexical tool has no job left. +Four tools, each a different question: where does this exact text occur, +what is most relevant to this, how much of what is where, and stay fresh. +There are still no near-duplicate retrieval tools, because those worsen an +agent's tool selection: `find` is unranked and complete where `search` is +ranked and top-k, and hybrid search's keyword half already ranks exact +identifier terms highly, so no separate lexical *ranking* tool exists. ### The SQL move @@ -164,8 +167,8 @@ export and pass around. ## Setup for agents code-context is an MCP server over stdio, so any MCP client works. Register -it once and the tools (`search`, `sql`, `reindex`) become available to the -agent. +it once and the tools (`find`, `search`, `sql`, `reindex`) become available to +the agent.
Claude Code @@ -188,7 +191,7 @@ claude mcp add-json code-context -s user '{"command":"npx","args":["-y","@infino for the index directly. In sessions with many MCP servers Claude Code defers tool definitions behind a tool-search step; without `alwaysLoad` the agent can miss code-context and fall back to grep/read. It's a small, always-loaded set -(three tools). Omit it (or use the shorter `claude mcp add code-context -- npx +(four tools). Omit it (or use the shorter `claude mcp add code-context -- npx -y @infino-ai/code-context mcp`) if you'd rather leave the tools deferred. Use *either* the plugin or the `add-json` command, not both. They register the @@ -252,9 +255,9 @@ when the client's working directory is not the repo.
-Tools: `search`, `sql`, `reindex` (incremental sync: an unchanged repo is -a fast no-op, and the server also auto-syncs in the background as queries -arrive, so results track your edits without anyone asking). +Tools: `find`, `search`, `sql`, `reindex` (incremental sync: an unchanged +repo is a fast no-op, and the server also auto-syncs in the background as +queries arrive, so results track your edits without anyone asking). **Multiple repos in one session.** Each tool takes an optional `path` (an absolute repo root). Omit it and the server uses its startup root; set it to @@ -268,15 +271,15 @@ no restart, no per-repo config. |---|---|---| | `CX_INDEX_DIR` | `/.infino` | where the index lives | | `CX_SEARCH_K` | 10 | default number of hits `search` returns (also settable per call and via the CLI `-k` flag) | -| `CX_MAX_FILES` / `CX_MAX_FILE_BYTES` | 20000 / 1MB | indexing caps (files over the file cap are left out; `search`/`sql` then flag the index as partial so an absence isn't read as proof) | +| `CX_MAX_FILES` / `CX_MAX_FILE_BYTES` | 20000 / 1MB | indexing caps (files over the file cap are left out; `find`/`search`/`sql` then flag the index as partial so an absence isn't read as proof) | | `CX_ROOT` | current directory | default repo root for the MCP server / CLI when not run from the repo (each tool call can override it with a `path` argument) | -| `CX_AUTO_INDEX` | on | `0` makes a query on an unindexed repo error instead of building the index inline on the first `search`/`sql` | +| `CX_AUTO_INDEX` | on | `0` makes a query on an unindexed repo error instead of building the index inline on the first `find`/`search`/`sql` | | `CX_AUTO_SYNC` | on | `0` disables the MCP server's background staleness sync | | `CX_SYNC_INTERVAL_SECS` | 30 | auto-sync debounce between staleness checks | | `CX_NO_EMBED` | off | keyword-only mode for the MCP server (skip the vector stage) | | `CX_NO_RECEIPT` | off | `1` turns off usage accounting - the per-call receipt on results and the `cx usage` ledger | -Every `search` / `sql` result carries a **usage receipt** - a terse, local line +Every `find` / `search` / `sql` result carries a **usage receipt** - a terse, local line showing the tokens it returned, the files it spanned, and a running session total (e.g. `returned ~1.2k tokens | 4 chunks / 3 files | session ~8.4k over 7 queries`). Every figure is a `~` estimate, computed in-process - nothing about @@ -294,6 +297,7 @@ npm install -g @infino-ai/code-context ``` cx index [path] sync the index (incremental; --full rebuilds, --watch follows edits) +cx find every line containing the exact text, path:line (-i, --limit) cx search exact terms + meaning, one ranked pass (-k hits) cx sql read-only SQL; --embed q="text" fills {{q}} cx status what the index holds, how fresh, vector readiness @@ -301,10 +305,11 @@ cx usage ledger of queries run and what each returned (-n, --a cx mcp serve the MCP tools over stdio ``` -`cx usage` reads the local ledger at `.infino/usage.jsonl` - every `search` / -`sql` (from the CLI or the MCP server) appends one line recording the query and -a compact summary of what came back (paths and line ranges for search, row -count for sql), plus the token figures from the receipt. It's a deterministic, +`cx usage` reads the local ledger at `.infino/usage.jsonl` - every `find` / +`search` / `sql` (from the CLI or the MCP server) appends one line recording the +query and a compact summary of what came back (`path:line` for find, paths and +line ranges for search, row count for sql), plus the token figures from the +receipt. It's a deterministic, model-independent view of what went through the index - no running server or agent needed to read it back. `CX_NO_RECEIPT=1` turns off both the inline receipt and this ledger. diff --git a/context7.json b/context7.json index bf12e6a..8b5b6e7 100644 --- a/context7.json +++ b/context7.json @@ -15,7 +15,7 @@ "code-context runs locally: install with 'npm install -g @infino-ai/code-context' (bins: code-context and cx), or zero-install into Claude Code with 'claude mcp add code-context -- npx -y @infino-ai/code-context mcp'.", "Index a repo with 'cx index' from the repo root. Keyword (BM25) search is live seconds after indexing starts; vectors backfill in the background and hybrid ranking unlocks automatically when they land.", "'cx index' is incremental by default: only files that changed since the last index re-chunk and re-embed. Use --full to rebuild from scratch and --watch to sync on file events.", - "The MCP server ('cx mcp', stdio) exposes exactly three tools: search (exact terms AND meaning in one ranked pass, hits carry chunk content with path:line ranges), sql (read-only SELECT/WITH over the index), and reindex (incremental sync).", + "The MCP server ('cx mcp', stdio) exposes exactly four tools: find (every line containing an exact string, cited path:line like grep -n; complete and unranked - the grep replacement), search (exact terms AND meaning in one ranked pass, hits carry chunk content with path:line ranges), sql (read-only SELECT/WITH over the index), and reindex (incremental sync).", "The index is one table named chunks(path, start_line, end_line, lang, content[, embedding]) living in .infino/ inside the repo - plain files you can copy, cache in CI, or gitignore (add .infino/ to your .gitignore).", "The engine's search functions are SQL table functions, so one query can rank AND aggregate: SELECT path, SUM(end_line - start_line + 1) AS lines FROM bm25_search('chunks','content','', 300) GROUP BY path ORDER BY lines DESC.", "vector_search and hybrid_search table functions take a query vector via a {{name}} placeholder: pass an embed map like {\"q\": \"query text\"} and the server embeds it locally and substitutes the vector.", diff --git a/docs/concepts/code-search-for-coding-agents.md b/docs/concepts/code-search-for-coding-agents.md index 1e94107..8fa06fd 100644 --- a/docs/concepts/code-search-for-coding-agents.md +++ b/docs/concepts/code-search-for-coding-agents.md @@ -33,13 +33,15 @@ files read one at a time. paraphrases, so "where is auth handled" works without knowing the exact identifier. -## Where crawling still wins +## Pinpoint lookups Jumping to one known symbol or literal string is a single grep's job, and -there an index does not save tokens: the grep returns one line, while ranked -search returns content the agent did not need for a path. code-context routes -this correctly (its tool descriptions tell an agent to prefer native grep for -pinpoint lookups) and reaches for the index when a question spans files. +ranked search does not save tokens there: it returns content the agent did +not need for a path. code-context answers that question with `find` instead: +every line containing the exact text, cited `path:line`, from the index's +token match plus a per-line check, so it returns grep's one-line-per-match +shape without scanning a file. The ranked tools are reached for when a +question spans files. ## Hybrid, not just semantic diff --git a/docs/faq.md b/docs/faq.md index 6a42a0e..bd6537f 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -13,8 +13,9 @@ file by file. The rule of thumb: the more a question spans the repo, the more the index saves. Use it for understanding how a subsystem works, finding code by meaning when you do not know the identifier, and ranking or aggregating -across the whole repo. For jumping to one known symbol or literal string, a -plain grep is already cheap and there is no need for an index. +across the whole repo. For the grep case itself - every occurrence of a known +symbol or literal string - `find` answers from the same index: every matching +line, cited `path:line`, complete and unranked, with no file scanned. ### Does my code leave the machine? @@ -32,8 +33,8 @@ reports that honestly rather than failing. ### Do I have to index before I can search? -No. The first `search` or `sql` on a repo that has never been indexed builds -the index inline and answers on that same call - keyword search is live in +No. The first `find`, `search`, or `sql` on a repo that has never been indexed +builds the index inline and answers on that same call - keyword search is live in seconds, vectors backfill behind it. Call `reindex` first if you'd rather kick the build off explicitly, or set `CX_AUTO_INDEX=0` to make an unindexed query return a "index it first" error instead of building. @@ -63,7 +64,7 @@ no-op. The MCP server also auto-syncs in the background as queries arrive. Indexing caps how many files it takes (`CX_MAX_FILES`, default 20,000); files past the cap are left out. When that happens the index is marked partial: -every `search` and `sql` result carries a `partial` note with how many files +every `find`, `search`, and `sql` result carries a `partial` note with how many files were skipped and the cap in effect, so an agent treats a missing match as "maybe not indexed" rather than "not in the repo." `cx status` shows the same, and `cx search` prints a warning. Raise `CX_MAX_FILES` (CLI: `--max-files`) @@ -71,12 +72,16 @@ and re-index for full coverage. ### What tools does the MCP server expose? -Three, by design: `search` (hybrid keyword + semantic retrieval, one ranked -pass, hits carry chunk content with `path:line` ranges), `sql` (read-only +Four, by design, one per question: `find` (every line containing an exact +string, cited `path:line` like `grep -n`; complete and unranked, the grep +replacement), `search` (hybrid keyword + semantic retrieval, one ranked pass, +hits carry chunk content with `path:line` ranges), `sql` (read-only `SELECT`/`WITH` over the index, with the ranked search functions usable as table-valued relations so search composes with `GROUP BY`), and `reindex` (incremental sync). Every additional near-duplicate retrieval tool worsens an -agent's tool selection, so the surface is kept deliberately small. +agent's tool selection, so the surface is kept deliberately small: `find` and +`search` are not duplicates, one is complete and unranked, the other ranked +and top-k. ### How is SQL over code useful? diff --git a/docs/tradeoffs.md b/docs/tradeoffs.md index 38d992f..f112912 100644 --- a/docs/tradeoffs.md +++ b/docs/tradeoffs.md @@ -10,13 +10,15 @@ symbol-precise references. It ranks and retrieves content and aggregates by relevance. Tools that resolve structure (LSP servers, graph indexes) are complementary: MCP servers stack, so run both when you need both. -### It does not beat grep on pinpoint lookups - -Naming the one file a known symbol lives in is a single grep's job. There the -index does not save tokens: a grep returns one matching line, while ranked -search returns chunks that carry their content. That content is what pays off -on "how does X work" and whole-repo questions, and it is dead weight when all -you need is a path. Adding code-context does not reduce accuracy on +### Pinpoint lookups are not where the token win is + +Naming the one file a known symbol lives in is a single grep's job. `find` +does that job from the index - every matching line as `path:line`, no file +scanned - and returns the same one-line-per-match shape grep does, so it +matches grep's cost rather than beating it. Ranked `search` is the wrong tool +there: it returns chunks that carry their content, which is what pays off on +"how does X work" and whole-repo questions and is dead weight when all you +need is a path. Adding code-context does not reduce accuracy on localization; it just does not win on cost there. Both are measured in the [benchmark](benchmark.md). diff --git a/llms.txt b/llms.txt index 8aef780..ea2ce3a 100644 --- a/llms.txt +++ b/llms.txt @@ -26,19 +26,21 @@ logs, docs, and agent memory. view when many MCP servers are configured): `claude mcp add-json code-context -s user '{"command":"npx","args":["-y","@infino-ai/code-context","mcp"],"alwaysLoad":true}'`. A Claude Code plugin (`/plugin marketplace add infino-ai/code-context`) bakes the same config in. -- MCP tools (stdio): `search` (exact terms AND meaning, one ranked pass, - hits carry chunk content with path:line ranges), `sql` (read-only - SELECT/WITH over `chunks(path, start_line, end_line, lang, content)`, - ranked search functions (bm25_search/hybrid_search) usable as table-valued - relations so search composes with GROUP BY), `reindex` (incremental sync; - the server also auto-syncs in the - background). A `search`/`sql` on a never-indexed repo builds the index - inline and answers on the same call (keyword live in seconds, vectors - backfilling); `CX_AUTO_INDEX=0` restores a strict "index it first" error. - Each tool takes an optional `path` (absolute repo root) so one server serves - multiple repos in a session; omit it for the startup root. -- CLI: `cx index` (incremental; `--full`, `--watch`), `cx search`, - `cx sql`, `cx status`, `cx mcp`. +- MCP tools (stdio): `find` (every line containing an exact string, cited + path:line like grep -n; complete and unranked - the grep replacement), + `search` (exact terms AND meaning, one ranked pass, hits carry chunk + content with path:line ranges), `sql` (read-only SELECT/WITH over + `chunks(path, start_line, end_line, lang, content)`, ranked search + functions (bm25_search/hybrid_search) usable as table-valued relations so + search composes with GROUP BY), `reindex` (incremental sync; the server + also auto-syncs in the background). A `find`/`search`/`sql` on a + never-indexed repo builds the index inline and answers on the same call + (keyword live in seconds, vectors backfilling); `CX_AUTO_INDEX=0` restores + a strict "index it first" error. Each tool takes an optional `path` + (absolute repo root) so one server serves multiple repos in a session; + omit it for the startup root. +- CLI: `cx index` (incremental; `--full`, `--watch`), `cx find`, + `cx search`, `cx sql`, `cx status`, `cx mcp`. ## Evidence @@ -60,7 +62,7 @@ logs, docs, and agent memory. the crawl-vs-retrieve model, when an index saves tokens, and where a plain grep is still the right tool. - [FAQ](docs/faq.md): what it is, when an agent should use it, local-only - guarantees, freshness, the three-tool MCP surface. + guarantees, freshness, the four-tool MCP surface. - [Tradeoffs](docs/tradeoffs.md): honest limits (no structural code intelligence; no token win on pinpoint lookups; one-time vector cost). diff --git a/skills/code-context/SKILL.md b/skills/code-context/SKILL.md index d8da38c..3b1dcf8 100644 --- a/skills/code-context/SKILL.md +++ b/skills/code-context/SKILL.md @@ -1,37 +1,58 @@ --- name: code-context description: > - How to answer codebase questions with the code-context MCP tools (search, - sql, reindex): ranked hybrid keyword+semantic search, relevance-ranked SQL - aggregation over the index, and index lifecycle. Use when a question spans - many files ("how does X work", "where is Y handled"), when ranking or - counting code by topic across a repo, or when the code-context tools are - present but deferred and need loading before use. Not needed for jumping - to one known identifier - plain grep is fine there. + How to answer codebase questions with the code-context MCP tools (find, + search, sql, reindex): exact-text lookup that replaces grep, ranked hybrid + keyword+semantic search, relevance-ranked SQL aggregation over the index, + and index lifecycle. Use when you would grep for an identifier or literal, + when a question spans many files ("how does X work", "where is Y + handled"), when ranking or counting code by topic across a repo, or when + the code-context tools are present but deferred and need loading before + use. --- -# code-context: ranked search over the repository +# code-context: search over the repository code-context maintains a local index of the repository (in `.infino/` at the -repo root) and exposes three MCP tools. The more a question spans the repo, -the more one ranked pass beats crawling files into context. +repo root) and exposes four MCP tools. Every lookup an agent would otherwise +do with grep or by crawling files runs against the index instead: `find` for +the exact-text case, one ranked pass for everything that spans the repo. ## If the tools are deferred When the tool names appear in a deferred-tools listing but their schemas are -not loaded, load all three in ONE ToolSearch call before the first use, e.g. -query `+code-context search sql reindex` (or `select:` with the exact +not loaded, load all four in ONE ToolSearch call before the first use, e.g. +query `+code-context find search sql reindex` (or `select:` with the exact listed names, comma-separated). Never load them one call at a time. ## Choosing the right tool | Situation | Use | | --- | --- | -| One known identifier, literal string, or file | plain grep / file tools | +| Every occurrence of an exact identifier, string, or key (where you would grep) | `find` | +| A file you already know the path of | Read | | "How does X work", "where is Y handled", concept without exact name | `search` | | Counts, rankings, GROUP BY across the repo ("which files have the most code about X") | `sql` | | Working tree changed a lot mid-session | `reindex` (usually unnecessary - see lifecycle) | +## find + +- Pass the exact text as it appears in the code: an identifier, an error + message, a config key. Literal, not a regex; within one line; + case-sensitive unless `ignoreCase`. +- Complete, not ranked: every matching line comes back as `path`, `line`, + and the line's `text` (plus the enclosing definition's `symbol` when + known), in path order, up to `limit` (default 100, max 500). `total` is + the repo-wide count either way, and `truncated` says when the list was + cut - raise `limit` or narrow the text. +- The index's token match picks the candidate chunks and each line is then + checked for the literal, so a hit is always a real occurrence and no file + is scanned. The index stores identifiers as tokens (`parse_config` is + `parse` and `config`), but that only widens the candidates: `find` returns + only lines containing the exact text you gave. +- Read `path:line` (a few lines around it) when you need the surrounding + code; most grep-shaped questions are answered by the list itself. + ## search - Pass terms, a phrase, or a plain-language description; one pass fuses BM25 @@ -88,9 +109,9 @@ GROUP BY path ORDER BY lines DESC LIMIT 15 - A result carrying a `partial` marker means the repo exceeded the index's file cap and some files were left out: treat a missing match as possibly-unindexed, not as proof the code doesn't exist. -- Search and sql results carry a one-line `usage` receipt (tokens returned, - chunks/files, session running total), computed locally. End your reply by - showing that line to the user verbatim. +- Find, search, and sql results carry a one-line `usage` receipt (tokens + returned, matches or chunks / files, session running total), computed + locally. End your reply by showing that line to the user verbatim. ## Multi-repo sessions @@ -99,7 +120,7 @@ different repository than the one the server started in. ## Cost awareness -- `search`/`sql` calls are cheap (local, milliseconds). +- `find`/`search`/`sql` calls are cheap (local, milliseconds). - The first index of a repo and the vector backfill are the expensive part (CPU for the local embedding model, proportional to repo size). Avoid forcing `full: true` rebuilds unless the index is actually wrong, and diff --git a/src/cli.ts b/src/cli.ts index e51eb45..7c053ba 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -6,8 +6,9 @@ import { Command } from "commander"; import { indexCmd } from "./commands/index-cmd.js"; -import { searchCmd, sqlCmd, statusCmd, usageCmd } from "./commands/query-cmds.js"; +import { findCmd, searchCmd, sqlCmd, statusCmd, usageCmd } from "./commands/query-cmds.js"; import { DEFAULT_SEARCH_K } from "./core/config.js"; +import { DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT } from "./core/searcher.js"; const program = new Command(); @@ -24,14 +25,25 @@ program ` Examples: cx index index the current repo (keyword search is live in seconds) + cx find "parse_config" every line containing it, path:line - like grep -n cx search "parse_config" exact terms and meaning, one ranked pass cx search "where is auth handled" works when you don't know the words cx sql "SELECT path, SUM(end_line - start_line + 1) AS lines \\ FROM bm25_search('chunks','content','vector index', 300) \\ GROUP BY path ORDER BY lines DESC LIMIT 10" - cx mcp serve the MCP tools (search/sql/reindex) over stdio`, + cx mcp serve the MCP tools (find/search/sql/reindex) over stdio`, ); +program + .command("find") + .description("every line containing an exact string, like grep -n: complete and unranked") + .argument("", "the exact text to find, as it appears in the code") + .option("-i, --ignore-case", "match regardless of letter case") + .option("--limit ", `maximum matching lines to print (default ${DEFAULT_FIND_LIMIT}, max ${MAX_FIND_LIMIT})`) + .option("--json", "machine-readable output") + .option("-C, --path ", "repo root (default: current directory)") + .action(findCmd); + program .command("index") .description("bring the index up to date (incremental; full build on first run)") @@ -87,7 +99,7 @@ program program .command("mcp") - .description("serve the MCP tools (search / sql / reindex) over stdio") + .description("serve the MCP tools (find / search / sql / reindex) over stdio") .option("-C, --path ", "repo root (default: current directory)") .action(async (opts: { path?: string }) => { const { serveMcp } = await import("./mcp/server.js"); diff --git a/src/commands/query-cmds.ts b/src/commands/query-cmds.ts index fe72707..a38af83 100644 --- a/src/commands/query-cmds.ts +++ b/src/commands/query-cmds.ts @@ -1,14 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Infino Authors // -// `cx search` / `cx sql` / `cx status` - the query commands. +// `cx find` / `cx search` / `cx sql` / `cx status` / `cx usage` - the query commands. import { openIndex, NoIndexError } from "../core/context.js"; import { indexDir, resolveRoot } from "../core/config.js"; import { createEmbedder, embedderInfo } from "../core/embedder.js"; -import { search, runSql, jsonify } from "../core/searcher.js"; +import { find, search, runSql, jsonify } from "../core/searcher.js"; import { receiptEnabled, + findEntry, searchEntry, sqlEntry, formatReceipt, @@ -27,6 +28,42 @@ function die(err: unknown): never { process.exit(1); } +export interface FindCmdOptions { + ignoreCase?: boolean; + limit?: string; + json?: boolean; + path?: string; +} + +/** `cx find` - every line containing the exact text, printed `path:line: text` + * the way `grep -n` does, so it drops into the same habits and pipelines. */ +export function findCmd(text: string, opts: FindCmdOptions): void { + try { + const handle = openIndex(opts.path); + const result = find(handle, text, { + ignoreCase: opts.ignoreCase, + limit: opts.limit === undefined ? undefined : Number(opts.limit), + }); + if (receiptEnabled()) { + const entry = findEntry(result); + recordUsage(handle.dir, entry); + console.error(dim(formatReceipt(entry))); + } + if (opts.json) { + console.log(jsonify(result, true)); + return; + } + if (result.partial) console.error(yellow(`warning: ${result.partial.note}`)); + for (const m of result.matches) console.log(`${cyan(m.path)}${dim(`:${m.line}:`)} ${m.text}`); + if (result.truncated) { + console.error(yellow(`showing ${result.matches.length} of ${result.total} matches - raise --limit to see more`)); + } + if (result.total === 0) console.error(yellow("no matches")); + } catch (err) { + die(err); + } +} + export interface SearchCmdOptions { k: string; json?: boolean; @@ -111,7 +148,7 @@ export function statusCmd(opts: StatusCmdOptions): void { console.log( `code-context index: ${fmtCount(m.chunks)} chunks from ${fmtCount(m.files)} files, ` + `vectors ${m.vectors}, indexed ${fmtAge(m.indexedAt)}. ` + - `MCP tools: search (terms + meaning), sql (aggregation), reindex (after big edits).`, + `MCP tools: find (exact text, every occurrence), search (terms + meaning), sql (aggregation), reindex (after big edits).`, ); return; } @@ -187,7 +224,7 @@ export async function usageCmd(opts: UsageCmdOptions): Promise { return; } if (entries.length === 0 && (!session || session.prompts === 0)) { - console.error(yellow("no usage recorded yet - run `cx search`/`cx sql` here, or query via the MCP server")); + console.error(yellow("no usage recorded yet - run `cx find`/`cx search`/`cx sql` here, or query via the MCP server")); return; } @@ -218,6 +255,16 @@ export async function usageCmd(opts: UsageCmdOptions): Promise { ); const locs = hits.slice(0, 5).map((h) => `${h.path}:${h.startLine}-${h.endLine}`); if (locs.length) console.log(green(` ${locs.join(" ")}${hits.length > 5 ? dim(` (+${hits.length - 5} more)`) : ""}`)); + } else if (e.tool === "find") { + // A find match is one line, so cite it as path:line; the count is the + // repo-wide total, which can exceed the lines that were returned. + const hits = e.hits ?? []; + const files = new Set(hits.map((h) => h.path)).size; + console.log( + `${dim(clock)} ${bold(tool)} ${q} ${dim(`-> ${e.matches ?? hits.length} matches / ${files} files | ~${fmtTokens(e.returnedTokens)} tok`)}`, + ); + const locs = hits.slice(0, 5).map((h) => `${h.path}:${h.startLine}`); + if (locs.length) console.log(green(` ${locs.join(" ")}${hits.length > 5 ? dim(` (+${hits.length - 5} more)`) : ""}`)); } else { console.log( `${dim(clock)} ${bold(tool)} ${q} ${dim(`-> ${e.rows ?? 0} rows | ~${fmtTokens(e.returnedTokens)} tok`)}`, diff --git a/src/core/searcher.ts b/src/core/searcher.ts index 0b8f9b7..0e04e6e 100644 --- a/src/core/searcher.ts +++ b/src/core/searcher.ts @@ -1,8 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Infino Authors // -// The two retrieval doors, shared by the CLI and the MCP server: +// The three retrieval doors, shared by the CLI and the MCP server: // +// find - the grep door: every line containing an exact string, cited +// path:line. Complete and unranked - "every place this appears" +// is a different question from "the chunks most about it". // search - the finding door: one ranked pass fuses exact keyword matching // (BM25) with semantic similarity (vectors, RRF) once vectors are // ready; ranked keyword search until then. Hits carry chunk @@ -131,6 +134,160 @@ export async function search( }; } +// --- find ------------------------------------------------------------------- +// +// Two steps. The index's token match narrows to the chunks that contain every +// token of the query - an inverted-list intersection, no scoring, no top-k - +// then the literal is verified line by line inside those chunks. The analyzer +// splits identifiers (`parse_config` indexes as `parse` and `config`), so the +// first step alone would over-match; the second makes every hit a real +// occurrence of the exact text, and grep's line-based, case-sensitive +// semantics fall out of it. + +export interface FindMatch { + path: string; + /** 1-based line number of the matching line. */ + line: number; + /** The matching line, capped at FIND_LINE_CAP characters. */ + text: string; + /** Definition name(s) of the enclosing chunk (e.g. "parseConfig"), when known. */ + symbol?: string; +} + +export interface FindResult { + query: string; + ignoreCase: boolean; + /** Matching lines in path then line order, cut at the limit. */ + matches: FindMatch[]; + /** Matching lines across the repo before the limit was applied. */ + total: number; + /** Distinct files with at least one match, before the limit. */ + files: number; + /** Set when `total` exceeded the limit and `matches` was cut. */ + truncated?: boolean; + /** Present when the index omitted files over the cap - results may be incomplete. */ + partial?: PartialIndex; +} + +export interface FindOptions { + /** Match regardless of letter case. Default false: case-sensitive, like grep. */ + ignoreCase?: boolean; + /** Maximum matches returned; clamped to [1, MAX_FIND_LIMIT]. */ + limit?: number; +} + +/** Default and hard cap on matches returned. A grep over a large repo can hit + * thousands of lines; past this the agent needs the count, not every line. */ +export const DEFAULT_FIND_LIMIT = 100; +export const MAX_FIND_LIMIT = 500; + +/** Per-line cap so one minified or generated line cannot flood the result. */ +const FIND_LINE_CAP = 240; + +/** Columns a find reads: no `end_line` (each match cites its own line) and no + * `score` (there is none - matches are unranked). */ +const FIND_PROJECTION = ["path", "start_line", "symbol", "content"]; + +/** First code point outside ASCII. The default analyzer extends a token run + * across such characters and then drops the whole run. */ +const NON_ASCII_MIN = 0x80; + +/** The tokens the index's default analyzer (`ascii_lower`) produces for a + * string: runs of `[A-Za-z0-9]`, lowercased, and a run that touches non-ASCII + * text is dropped whole. Mirrored here so the candidate lookup asks the index + * for exactly the tokens it holds - a different split would miss chunks that + * do contain the literal. Duplicates are dropped; the intersection is the same. */ +export function analyzerTokens(text: string): string[] { + const out = new Set(); + let run = ""; + let nonAscii = false; + const flush = () => { + if (run && !nonAscii) out.add(run.toLowerCase()); + run = ""; + nonAscii = false; + }; + for (const ch of text) { + if ((ch.codePointAt(0) ?? 0) >= NON_ASCII_MIN) { + run += ch; + nonAscii = true; + } else if (/[A-Za-z0-9]/.test(ch)) { + run += ch; + } else { + flush(); + } + } + flush(); + return [...out]; +} + +/** The lines of `content` (whose first line is 1-based `startLine`) that + * contain `query` literally, each with its repo line number. */ +export function matchLines( + content: string, + startLine: number, + query: string, + ignoreCase: boolean, +): Array<{ line: number; text: string }> { + const needle = ignoreCase ? query.toLowerCase() : query; + const out: Array<{ line: number; text: string }> = []; + const lines = content.split("\n"); + for (let i = 0; i < lines.length; i++) { + const haystack = ignoreCase ? lines[i].toLowerCase() : lines[i]; + if (haystack.includes(needle)) out.push({ line: startLine + i, text: lines[i].replace(/\r$/, "") }); + } + return out; +} + +export function find(handle: IndexHandle, query: string, opts: FindOptions = {}): FindResult { + if (query.length === 0) throw new Error("find needs a non-empty string to look for"); + if (/[\r\n]/.test(query)) { + throw new Error("find matches within a single line - the query must not contain a newline"); + } + const tokens = analyzerTokens(query); + if (tokens.length === 0) { + throw new Error( + "find needs at least one run of ASCII letters or digits to look up in the index; a query of " + + "only punctuation or non-ASCII text cannot use it - try search, or sql with regexp_like(content, ...)", + ); + } + const ignoreCase = opts.ignoreCase ?? false; + const limit = Math.min(Math.max(1, opts.limit ?? DEFAULT_FIND_LIMIT), MAX_FIND_LIMIT); + + const table = handle.db.openTable(TABLE); + const candidates = table.tokenMatch("content", tokens.join(" "), { mode: "and", projection: FIND_PROJECTION }); + + // Fixed-window chunks overlap, so one line can arrive in two chunks; key by path:line. + const seen = new Set(); + const all: FindMatch[] = []; + for (const row of candidates) { + const path = String(row.path); + const symbol = row.symbol ? String(row.symbol) : undefined; + for (const m of matchLines(String(row.content), Number(row.start_line), query, ignoreCase)) { + const key = `${path}${m.line}`; + if (seen.has(key)) continue; + seen.add(key); + all.push({ + path, + line: m.line, + text: m.text.length > FIND_LINE_CAP ? m.text.slice(0, FIND_LINE_CAP) + "..." : m.text, + ...(symbol ? { symbol } : {}), + }); + } + } + all.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : a.line - b.line)); + + const partial = partialIndex(handle.manifest); + return { + query, + ignoreCase, + matches: all.slice(0, limit), + total: all.length, + files: new Set(all.map((m) => m.path)).size, + ...(all.length > limit ? { truncated: true } : {}), + ...(partial ? { partial } : {}), + }; +} + // --- sql -------------------------------------------------------------------- const PLACEHOLDER = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g; diff --git a/src/core/usage.ts b/src/core/usage.ts index 4d0b67c..30aeba0 100644 --- a/src/core/usage.ts +++ b/src/core/usage.ts @@ -9,7 +9,7 @@ import { appendFileSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"; import { join, resolve } from "node:path"; -import { jsonify, type SearchResult } from "./searcher.js"; +import { jsonify, type FindResult, type SearchResult } from "./searcher.js"; /** Rough tokens-per-char - the standard heuristic for English + code. Kept * deliberately simple: usage reports `~` figures, not a billed count. */ @@ -38,14 +38,17 @@ export const receiptEnabled = (): boolean => * it doesn't duplicate the repo. */ export interface UsageEntry { ts: string; - tool: "search" | "sql"; + tool: "find" | "search" | "sql"; query: string; returnedTokens: number; /** search only: whole-file size of the distinct files the hits came from. */ wholeFileTokens?: number | null; ranking?: "hybrid" | "keyword"; - /** search only: the response, as the regions you'd jump to. */ + /** search and find: the response, as the regions you'd jump to (a find + * match is a single line, so its start and end are the same). */ hits?: Array<{ path: string; startLine: number; endLine: number }>; + /** find only: matching lines across the repo, before the limit. */ + matches?: number; /** sql only. */ rows?: number; /** sql only: a truncated preview of the returned rows (the answer itself). */ @@ -84,6 +87,20 @@ export function searchEntry(result: SearchResult, root: string): UsageEntry { }; } +/** A find returns one line per match, so what it cost is the serialized + * matches themselves; the whole-file counterfactual is search's and does not + * apply - grep never read the files whole either. */ +export function findEntry(result: FindResult): UsageEntry { + return { + ts: new Date().toISOString(), + tool: "find", + query: result.query, + returnedTokens: estTokens(jsonify(result.matches)), + hits: result.matches.map((m) => ({ path: m.path, startLine: m.line, endLine: m.line })), + matches: result.total, + }; +} + const ROWS_PREVIEW_CAP = 2000; export function sqlEntry(query: string, rows: Array>): UsageEntry { @@ -119,6 +136,12 @@ export function formatReceipt(entry: UsageEntry, session?: SessionUsage): string // it after every response. The raw wholeFileTokens still lives in the entry // for anyone who wants to reason about it from the ledger. parts.push(`returned ~${fmtTokens(entry.returnedTokens)} tokens | ${plural(hits.length, "chunk", "chunks")} / ${plural(files, "file", "files")}`); + } else if (entry.tool === "find") { + const hits = entry.hits ?? []; + const files = new Set(hits.map((h) => h.path)).size; + // The repo-wide count, not just the lines returned: a cut result still + // tells the reader how many matches exist. + parts.push(`returned ~${fmtTokens(entry.returnedTokens)} tokens | ${plural(entry.matches ?? hits.length, "match", "matches")} / ${plural(files, "file", "files")}`); } else { parts.push(`returned ~${fmtTokens(entry.returnedTokens)} tokens | ${plural(entry.rows ?? 0, "row", "rows")}`); } diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 01f7f72..282d4b0 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -1,18 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Infino Authors // -// The dedicated MCP server: three tools over one code index. +// The dedicated MCP server: four tools over one code index. // +// find - the grep door: every line containing an exact string, cited +// path:line - complete and unranked // search - find code: exact terms AND meaning in one ranked pass // sql - the power door: relevance-ranked aggregation over the search // table functions (bm25_search / hybrid_search + GROUP BY) // reindex - sync from the working tree; replies the moment keyword // search is live and backfills vectors in-process // -// Three tools, deliberately: one way to find, one way to count, one way to -// stay fresh - every additional near-duplicate retrieval tool worsens the -// agent's tool selection. Results carry took_ms - server-side time for -// the call (query embedding included where one happens; no transport). +// Four tools, each a different question: where does this exact text occur, +// what is most relevant to this, how much of what is where, and stay fresh. +// No near-duplicate retrieval tools - those worsen the agent's tool +// selection - so find is unranked and complete where search is ranked and +// top-k, and hybrid search's keyword half already ranks exact identifiers. +// Results carry took_ms - server-side time for the call (query embedding +// included where one happens; no transport). import { existsSync } from "node:fs"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; @@ -22,8 +27,16 @@ import { connect } from "@infino-ai/infino"; import { indexDir, resolveRoot, TABLE, DEFAULT_CAPS, DEFAULT_SEARCH_K } from "../core/config.js"; import { readManifest, type Manifest } from "../core/manifest.js"; import type { IndexHandle } from "../core/context.js"; -import { search, runSql, jsonify, partialIndex } from "../core/searcher.js"; -import { newSession, receiptEnabled, searchEntry, sqlEntry, formatReceipt, recordUsage } from "../core/usage.js"; +import { find, search, runSql, jsonify, partialIndex, DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT } from "../core/searcher.js"; +import { + newSession, + receiptEnabled, + findEntry, + searchEntry, + sqlEntry, + formatReceipt, + recordUsage, +} from "../core/usage.js"; import { indexRepoStaged, syncRepo, @@ -184,7 +197,9 @@ export async function serveMcp(rootPath?: string): Promise { "context before an edit, locating a bug or the code behind a behaviour, reviewing existing " + "patterns, planning a refactor, understanding the architecture for feature work, or spotting " + "similar/duplicate implementations. It is the primary tool for finding and understanding " + - "code here, for almost any question about this codebase. Three tools:\n" + + "code here, for almost any question about this codebase. Four tools:\n" + + "- find - every line containing an exact string, cited path:line - the grep replacement: " + + "complete and unranked, for every use or definition of an identifier or literal.\n" + "- search - find code by meaning or terms across files and understand how something works, " + "in one ranked pass.\n" + "- sql - counts, rankings, and aggregates over the whole repo in one query, including " + @@ -277,6 +292,83 @@ export async function serveMcp(rootPath?: string): Promise { }, ); + server.registerTool( + "find", + { + title: "Find exact text (every occurrence, like grep -n)", + description: + "Every line in the repository that contains an exact string - the grep replacement. Use it " + + "where you would reach for grep or rg: every use or definition of an identifier, an error " + + "message, a config key, a literal. Complete, not ranked: it returns every matching line (up " + + "to `limit`, and always the repo-wide total) as path, line number, and the line's text, plus " + + "the enclosing definition name when known. Literal text, not a regex; within a single line; " + + "case-sensitive unless ignoreCase. It reads only the chunks the index says contain the " + + "query's tokens and then checks each line, so every hit is a real occurrence and no file is " + + "scanned. Read path:line for the surrounding code when you need it. For 'how does X work' or " + + "when you don't know the exact words, use search; for counts and rankings, sql. The result " + + "includes a 'usage' field - a one-line receipt (tokens returned, matches/files, session " + + "total). After you answer, end your reply by showing that 'usage' line to the user verbatim.", + inputSchema: { + query: z + .string() + .min(1) + .describe("The exact text to find, as it appears in the code - an identifier, a string, a key."), + ignoreCase: z + .boolean() + .optional() + .describe("Match regardless of letter case. Default false: case-sensitive, like grep."), + limit: z + .number() + .int() + .positive() + .max(MAX_FIND_LIMIT) + .default(DEFAULT_FIND_LIMIT) + .describe("Maximum matching lines to return; the result reports the total either way."), + path: z + .string() + .optional() + .describe( + "Absolute path to the repository root to search. Defaults to the server's configured root; " + + "set it to target a specific repo when a session spans more than one.", + ), + }, + }, + async ({ query, ignoreCase, limit, path }) => { + let ctx: RepoCtx; + try { + ctx = repoFor(path); + } catch (err) { + return fail((err as Error).message); + } + let ensured: EnsureResult; + try { + ensured = await ensureIndexed(ctx, { autoIndexEnabled, getHandle, build: buildIndex }); + } catch (err) { + return fail(`indexing failed: ${(err as Error).message}`); + } + if ("needsIndex" in ensured) return noIndex(ctx.root); + const { handle, autoIndexed } = ensured; + if (!autoIndexed) maybeAutoSync(ctx); // a fresh build is already current + try { + const { value: result, tookMs } = timed(() => find(handle, query, { ignoreCase, limit })); + let usage: string | undefined; + if (receiptOn) { + const entry = findEntry(result); + recordUsage(ctx.dir, entry); + usage = formatReceipt(entry, session); + } + return ok({ + ...result, + ...(autoIndexed ? { auto_indexed: autoIndexNote(autoIndexed) } : {}), + took_ms: tookMs, + ...(usage ? { usage } : {}), + }); + } catch (err) { + return fail(`find failed: ${(err as Error).message}`); + } + }, + ); + server.registerTool( "sql", { diff --git a/test/integration.test.ts b/test/integration.test.ts index fc54725..8f93796 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -7,7 +7,7 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { connect } from "@infino-ai/infino"; import { indexRepo, indexRepoStaged } from "../src/core/indexer.js"; import { readManifest } from "../src/core/manifest.js"; -import { runSql, search } from "../src/core/searcher.js"; +import { find, runSql, search } from "../src/core/searcher.js"; import type { IndexHandle } from "../src/core/context.js"; import type { Embedder } from "../src/core/embedder.js"; @@ -106,6 +106,48 @@ describe("search", () => { }); }); +describe("find", () => { + it("returns every line containing the literal, cited path:line, in file order", () => { + // `verifySession(` on line 2 and `revokeSession(` on line 5 of auth.ts; the + // header comment's "Session tokens" has no "(" and must not match. + const r = find(handle, "Session("); + expect(r.matches.map((m) => `${m.path}:${m.line}`)).toEqual(["src/auth.ts:2", "src/auth.ts:5"]); + expect(r.matches[0].text).toContain("export function verifySession(token: string)"); + expect(r.matches[0].symbol).toContain("verifySession"); + expect(r.total).toBe(2); + expect(r.files).toBe(1); + expect(r.truncated).toBeUndefined(); + expect(r.ignoreCase).toBe(false); + }); + + it("matches the literal, not just its tokens", () => { + // The comment says "session record"; "record session" has the same tokens + // in the same chunk and occurs nowhere. + expect(find(handle, "session record").total).toBe(1); + expect(find(handle, "record session").total).toBe(0); + }); + + it("is case-sensitive unless asked otherwise", () => { + expect(find(handle, "verifysession").total).toBe(0); + const r = find(handle, "verifysession", { ignoreCase: true }); + expect(r.matches.map((m) => m.line)).toEqual([2]); + expect(r.ignoreCase).toBe(true); + }); + + it("caps the matches and still reports the repo-wide total", () => { + const r = find(handle, "Session(", { limit: 1 }); + expect(r.matches.length).toBe(1); + expect(r.total).toBe(2); + expect(r.truncated).toBe(true); + }); + + it("refuses a query the index cannot look up", () => { + expect(() => find(handle, "->")).toThrow(/ASCII/); + expect(() => find(handle, "a\nb")).toThrow(/newline/); + expect(() => find(handle, "")).toThrow(/non-empty/); + }); +}); + describe("sql", () => { it("ranked aggregation through the search table function", async () => { const rows = await runSql( diff --git a/test/searcher.test.ts b/test/searcher.test.ts index 6ae073c..7cd43e6 100644 --- a/test/searcher.test.ts +++ b/test/searcher.test.ts @@ -1,7 +1,47 @@ import { describe, expect, it } from "vitest"; -import { applyEmbeds, guardSql } from "../src/core/searcher.js"; +import { analyzerTokens, applyEmbeds, guardSql, matchLines } from "../src/core/searcher.js"; import type { Embedder } from "../src/core/embedder.js"; +describe("analyzerTokens", () => { + it("splits on anything outside [A-Za-z0-9] and lowercases, like the index analyzer", () => { + // `parse_config` indexes as two tokens: the underscore is a separator. + expect(analyzerTokens("parse_config(Path)")).toEqual(["parse", "config", "path"]); + }); + + it("dedupes repeated tokens", () => { + expect(analyzerTokens("a.a A")).toEqual(["a"]); + }); + + it("drops a run that touches non-ASCII text, as the analyzer does", () => { + expect(analyzerTokens("Süd ok")).toEqual(["ok"]); + // The non-ASCII character extends the run rather than splitting it, so + // the ASCII neighbours go with it. + expect(analyzerTokens("abcédef ghi")).toEqual(["ghi"]); + }); + + it("yields nothing for punctuation-only text", () => { + expect(analyzerTokens("->")).toEqual([]); + expect(analyzerTokens("")).toEqual([]); + }); +}); + +describe("matchLines", () => { + const content = "let parse_config = 1;\nparse config\nPARSE_CONFIG"; + + it("cites 1-based lines offset from the chunk start and matches the literal, not its tokens", () => { + // Line 2 has both tokens but not the literal. + expect(matchLines(content, 10, "parse_config", false)).toEqual([{ line: 10, text: "let parse_config = 1;" }]); + }); + + it("is case-sensitive unless asked otherwise", () => { + expect(matchLines(content, 10, "parse_config", true).map((m) => m.line)).toEqual([10, 12]); + }); + + it("strips a CRLF file's carriage return from the cited text", () => { + expect(matchLines("x = 1;\r\ny = 2;\r\n", 1, "y =", false)).toEqual([{ line: 2, text: "y = 2;" }]); + }); +}); + describe("guardSql", () => { it("accepts a single SELECT / WITH statement and strips the trailing semicolon", () => { expect(guardSql("SELECT 1;")).toBe("SELECT 1"); diff --git a/test/usage.test.ts b/test/usage.test.ts index 2d50e87..c8f72c7 100644 --- a/test/usage.test.ts +++ b/test/usage.test.ts @@ -5,6 +5,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from import { estTokens, newSession, + findEntry, searchEntry, sqlEntry, formatReceipt, @@ -76,6 +77,36 @@ describe("search receipt", () => { }); }); +describe("find receipt", () => { + const found = { + query: "x", + ignoreCase: false, + total: 3, + files: 1, + truncated: true, + matches: [ + { path: "a.ts", line: 3, text: "x = 1" }, + { path: "a.ts", line: 9, text: "x = 2" }, + ], + }; + + it("reports the repo-wide match count and the files, not just the lines returned", () => { + const line = formatReceipt(findEntry(found)); + expect(line).toMatch(/^returned ~\d+ tokens \| 3 matches \/ 1 file$/); + }); + + it("records each match as a one-line region in the ledger", () => { + const entry = findEntry(found); + expect(entry.tool).toBe("find"); + expect(entry.hits).toEqual([ + { path: "a.ts", startLine: 3, endLine: 3 }, + { path: "a.ts", startLine: 9, endLine: 9 }, + ]); + expect(entry.matches).toBe(3); + expect(entry.wholeFileTokens).toBeUndefined(); + }); +}); + describe("sql receipt", () => { it("reports row count and token estimate of the rows", () => { const line = formatReceipt(sqlEntry("SELECT 1", [{ path: "a.ts", lines: 12 }, { path: "b.ts", lines: 8 }])); From dbf6c8db32f0a89f894d8c045177d312e4eb8df0 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 10:37:13 +0000 Subject: [PATCH 02/13] find: per-file counts, match windows, limit validation, sharper description Review follow-ups on the find tool. byFile on the result: matching lines per file over every match, most first, computed before the limit is applied so it stays whole when the line list is cut. It is the grep -c answer, the one grep-shaped question the tool could not answer; cx find gets -c/--count for the same view. A long line is now cut to a window around the match instead of its head: head-truncation at the cap could return a hit whose text did not contain the query, which reads as the tool being wrong. Both cut ends are marked. A malformed limit is rejected: Number("abc") is NaN, and the old clamp passed it through so cx find printed nothing and reported nothing. The check lives in find itself so the CLI and MCP paths share it. The tool description says what find is not for - a file you already know is a Read - which is where a mis-selection showed up in review testing. Tests: excerpt windows; per-file counts on a cut list; a line in two overlapping fixed-window chunks is reported once (and the overlap is asserted to exist); the client-side analyzer mirror agrees with the engine on which chunks are candidates for five edge-case strings; the partial-index marker propagates; malformed limits throw. The tradeoffs page names the saving on exact lookups instead of calling it a wash. --- README.md | 4 +- docs/tradeoffs.md | 22 +++++----- skills/code-context/SKILL.md | 8 +++- src/cli.ts | 1 + src/commands/query-cmds.ts | 14 +++++-- src/core/searcher.ts | 62 +++++++++++++++++++++++----- src/mcp/server.ts | 19 +++++---- test/integration.test.ts | 79 +++++++++++++++++++++++++++++++++++- test/searcher.test.ts | 34 ++++++++++++++-- test/usage.test.ts | 1 + 10 files changed, 203 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 7e60fd2..73bde75 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ One index and a deliberately small tool surface for agents: | Tool | What it does | When agents use it | |---|---|---| -| `find` | Every line containing an exact string, cited `path:line` like `grep -n`. Complete and unranked: the index's token match picks the candidate chunks, then each line is checked for the literal, so no file is scanned and every hit is a real occurrence. | Where an agent would grep: every use or definition of an identifier, an error message, a config key. | +| `find` | Every line containing an exact string, cited `path:line` like `grep -n`, plus matching lines per file like `grep -c`. Complete and unranked: the index's token match picks the candidate chunks, then each line is checked for the literal, so no file is scanned and every hit is a real occurrence. | Where an agent would grep: every use or definition of an identifier, an error message, a config key. | | `search` | One ranked pass fusing exact keyword matching (BM25) with semantic similarity (reciprocal-rank fusion). Hits carry the chunk content, so answers come straight from results. | A strong default for finding and understanding code: how a subsystem works, code by meaning or exact term, context before a change, similar implementations - exact identifiers and paraphrases in the same call. | | `sql` | Read-only SQL over the index, with the ranked search functions (`bm25_search`/`hybrid_search`) usable as table-valued relations. | Counts, rankings, aggregates over the whole repo in one query. | | `reindex` | Incremental sync (the server also auto-syncs in the background). | After significant edits. | @@ -297,7 +297,7 @@ npm install -g @infino-ai/code-context ``` cx index [path] sync the index (incremental; --full rebuilds, --watch follows edits) -cx find every line containing the exact text, path:line (-i, --limit) +cx find every line containing the exact text, path:line (-i, -c per-file counts, --limit) cx search exact terms + meaning, one ranked pass (-k hits) cx sql read-only SQL; --embed q="text" fills {{q}} cx status what the index holds, how fresh, vector readiness diff --git a/docs/tradeoffs.md b/docs/tradeoffs.md index f112912..3f89af2 100644 --- a/docs/tradeoffs.md +++ b/docs/tradeoffs.md @@ -10,17 +10,17 @@ symbol-precise references. It ranks and retrieves content and aggregates by relevance. Tools that resolve structure (LSP servers, graph indexes) are complementary: MCP servers stack, so run both when you need both. -### Pinpoint lookups are not where the token win is - -Naming the one file a known symbol lives in is a single grep's job. `find` -does that job from the index - every matching line as `path:line`, no file -scanned - and returns the same one-line-per-match shape grep does, so it -matches grep's cost rather than beating it. Ranked `search` is the wrong tool -there: it returns chunks that carry their content, which is what pays off on -"how does X work" and whole-repo questions and is dead weight when all you -need is a path. Adding code-context does not reduce accuracy on -localization; it just does not win on cost there. Both are measured in the -[benchmark](benchmark.md). +### Pinpoint lookups are the smaller win + +Naming the one file a known symbol lives in is a single grep's job, and +`find` does that job from the index: every matching line as `path:line`, per +file counts, no file scanned. A grep hit still needs a follow-up read before +it is a cited line; a `find` hit already is one, so on exact lookups the +saving is the reads that never happen, not a change in what gets found. +Ranked `search` is the wrong tool there: it returns chunks that carry their +content, which is what pays off on "how does X work" and whole-repo questions +and is dead weight when all you need is a path. The large savings are on +questions that span the repo; the [benchmark](benchmark.md) records both. ### The first index of a repo pays a one-time vector cost diff --git a/skills/code-context/SKILL.md b/skills/code-context/SKILL.md index 3b1dcf8..88f02fd 100644 --- a/skills/code-context/SKILL.md +++ b/skills/code-context/SKILL.md @@ -43,8 +43,12 @@ listed names, comma-separated). Never load them one call at a time. - Complete, not ranked: every matching line comes back as `path`, `line`, and the line's `text` (plus the enclosing definition's `symbol` when known), in path order, up to `limit` (default 100, max 500). `total` is - the repo-wide count either way, and `truncated` says when the list was - cut - raise `limit` or narrow the text. + the repo-wide count either way, `byFile` lists matching lines per file + over every match (the `grep -c` answer, never cut), and `truncated` says + when the line list was cut - raise `limit` or narrow the text. +- Not for a file you already know the path of: Read it. `find` locates + occurrences across the repo; pulling a few lines out of one known file + is a Read. - The index's token match picks the candidate chunks and each line is then checked for the literal, so a hit is always a real occurrence and no file is scanned. The index stores identifiers as tokens (`parse_config` is diff --git a/src/cli.ts b/src/cli.ts index 7c053ba..cb795e8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -39,6 +39,7 @@ program .description("every line containing an exact string, like grep -n: complete and unranked") .argument("", "the exact text to find, as it appears in the code") .option("-i, --ignore-case", "match regardless of letter case") + .option("-c, --count", "print matching lines per file instead of the lines, like grep -c") .option("--limit ", `maximum matching lines to print (default ${DEFAULT_FIND_LIMIT}, max ${MAX_FIND_LIMIT})`) .option("--json", "machine-readable output") .option("-C, --path ", "repo root (default: current directory)") diff --git a/src/commands/query-cmds.ts b/src/commands/query-cmds.ts index a38af83..02aeda8 100644 --- a/src/commands/query-cmds.ts +++ b/src/commands/query-cmds.ts @@ -30,6 +30,8 @@ function die(err: unknown): never { export interface FindCmdOptions { ignoreCase?: boolean; + /** Per-file counts instead of the matching lines, like `grep -c`. */ + count?: boolean; limit?: string; json?: boolean; path?: string; @@ -40,6 +42,8 @@ export interface FindCmdOptions { export function findCmd(text: string, opts: FindCmdOptions): void { try { const handle = openIndex(opts.path); + // `find` rejects a non-integer, so `--limit abc` is an error rather than an + // empty listing; the raw string is converted here and validated there. const result = find(handle, text, { ignoreCase: opts.ignoreCase, limit: opts.limit === undefined ? undefined : Number(opts.limit), @@ -54,9 +58,13 @@ export function findCmd(text: string, opts: FindCmdOptions): void { return; } if (result.partial) console.error(yellow(`warning: ${result.partial.note}`)); - for (const m of result.matches) console.log(`${cyan(m.path)}${dim(`:${m.line}:`)} ${m.text}`); - if (result.truncated) { - console.error(yellow(`showing ${result.matches.length} of ${result.total} matches - raise --limit to see more`)); + if (opts.count) { + for (const f of result.byFile) console.log(`${cyan(f.path)}${dim(":")} ${f.count}`); + } else { + for (const m of result.matches) console.log(`${cyan(m.path)}${dim(`:${m.line}:`)} ${m.text}`); + if (result.truncated) { + console.error(yellow(`showing ${result.matches.length} of ${result.total} matches - raise --limit to see more`)); + } } if (result.total === 0) console.error(yellow("no matches")); } catch (err) { diff --git a/src/core/searcher.ts b/src/core/searcher.ts index 0e04e6e..e5990f8 100644 --- a/src/core/searcher.ts +++ b/src/core/searcher.ts @@ -148,12 +148,19 @@ export interface FindMatch { path: string; /** 1-based line number of the matching line. */ line: number; - /** The matching line, capped at FIND_LINE_CAP characters. */ + /** The matching line; a line longer than FIND_LINE_CAP is cut to a window + * around the match, with `...` marking each cut end. */ text: string; /** Definition name(s) of the enclosing chunk (e.g. "parseConfig"), when known. */ symbol?: string; } +/** Matching lines in one file - the `grep -c` view. */ +export interface FindFileCount { + path: string; + count: number; +} + export interface FindResult { query: string; ignoreCase: boolean; @@ -163,6 +170,10 @@ export interface FindResult { total: number; /** Distinct files with at least one match, before the limit. */ files: number; + /** Matching lines per file, before the limit, most matches first. Always + * complete even when `matches` is cut, so "how many and where" never needs a + * second call. */ + byFile: FindFileCount[]; /** Set when `total` exceeded the limit and `matches` was cut. */ truncated?: boolean; /** Present when the index omitted files over the cap - results may be incomplete. */ @@ -172,7 +183,7 @@ export interface FindResult { export interface FindOptions { /** Match regardless of letter case. Default false: case-sensitive, like grep. */ ignoreCase?: boolean; - /** Maximum matches returned; clamped to [1, MAX_FIND_LIMIT]. */ + /** Maximum matches returned: a positive integer, clamped to MAX_FIND_LIMIT. */ limit?: number; } @@ -184,6 +195,10 @@ export const MAX_FIND_LIMIT = 500; /** Per-line cap so one minified or generated line cannot flood the result. */ const FIND_LINE_CAP = 240; +/** Characters kept ahead of the match when a long line is cut to a window, so + * the excerpt shows what leads into the match rather than starting on it. */ +const FIND_EXCERPT_LEAD = 60; + /** Columns a find reads: no `end_line` (each match cites its own line) and no * `score` (there is none - matches are unranked). */ const FIND_PROJECTION = ["path", "start_line", "symbol", "content"]; @@ -221,23 +236,37 @@ export function analyzerTokens(text: string): string[] { } /** The lines of `content` (whose first line is 1-based `startLine`) that - * contain `query` literally, each with its repo line number. */ + * contain `query` literally, each with its repo line number and the 0-based + * column of the first occurrence. */ export function matchLines( content: string, startLine: number, query: string, ignoreCase: boolean, -): Array<{ line: number; text: string }> { +): Array<{ line: number; text: string; at: number }> { const needle = ignoreCase ? query.toLowerCase() : query; - const out: Array<{ line: number; text: string }> = []; + const out: Array<{ line: number; text: string; at: number }> = []; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { - const haystack = ignoreCase ? lines[i].toLowerCase() : lines[i]; - if (haystack.includes(needle)) out.push({ line: startLine + i, text: lines[i].replace(/\r$/, "") }); + const text = lines[i].replace(/\r$/, ""); + const at = (ignoreCase ? text.toLowerCase() : text).indexOf(needle); + if (at >= 0) out.push({ line: startLine + i, text, at }); } return out; } +/** `text` cut to at most FIND_LINE_CAP characters around the match at `at` + * (of `needleLength` characters), with `...` on each end that was cut. A short + * line comes back whole. The match always survives the cut: a hit whose text + * did not contain the query would read as the tool being wrong. */ +export function excerpt(text: string, at: number, needleLength: number): string { + if (text.length <= FIND_LINE_CAP) return text; + const lead = Math.min(FIND_EXCERPT_LEAD, Math.max(0, FIND_LINE_CAP - needleLength)); + const start = Math.max(0, Math.min(at - lead, text.length - FIND_LINE_CAP)); + const end = Math.min(text.length, start + FIND_LINE_CAP); + return `${start > 0 ? "..." : ""}${text.slice(start, end)}${end < text.length ? "..." : ""}`; +} + export function find(handle: IndexHandle, query: string, opts: FindOptions = {}): FindResult { if (query.length === 0) throw new Error("find needs a non-empty string to look for"); if (/[\r\n]/.test(query)) { @@ -250,8 +279,13 @@ export function find(handle: IndexHandle, query: string, opts: FindOptions = {}) "only punctuation or non-ASCII text cannot use it - try search, or sql with regexp_like(content, ...)", ); } + // Reject rather than clamp a malformed limit: NaN would slice to nothing and + // report nothing, which reads as "no matches". + if (opts.limit !== undefined && (!Number.isInteger(opts.limit) || opts.limit < 1)) { + throw new Error(`limit must be a positive integer, got ${opts.limit}`); + } const ignoreCase = opts.ignoreCase ?? false; - const limit = Math.min(Math.max(1, opts.limit ?? DEFAULT_FIND_LIMIT), MAX_FIND_LIMIT); + const limit = Math.min(opts.limit ?? DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT); const table = handle.db.openTable(TABLE); const candidates = table.tokenMatch("content", tokens.join(" "), { mode: "and", projection: FIND_PROJECTION }); @@ -269,20 +303,28 @@ export function find(handle: IndexHandle, query: string, opts: FindOptions = {}) all.push({ path, line: m.line, - text: m.text.length > FIND_LINE_CAP ? m.text.slice(0, FIND_LINE_CAP) + "..." : m.text, + text: excerpt(m.text, m.at, query.length), ...(symbol ? { symbol } : {}), }); } } all.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : a.line - b.line)); + // Per-file counts over every match, not the cut list: `grep -c` in one call. + const counts = new Map(); + for (const m of all) counts.set(m.path, (counts.get(m.path) ?? 0) + 1); + const byFile: FindFileCount[] = [...counts] + .map(([path, count]) => ({ path, count })) + .sort((a, b) => b.count - a.count || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0)); + const partial = partialIndex(handle.manifest); return { query, ignoreCase, matches: all.slice(0, limit), total: all.length, - files: new Set(all.map((m) => m.path)).size, + files: byFile.length, + byFile, ...(all.length > limit ? { truncated: true } : {}), ...(partial ? { partial } : {}), }; diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 282d4b0..35e7d3d 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -300,14 +300,17 @@ export async function serveMcp(rootPath?: string): Promise { "Every line in the repository that contains an exact string - the grep replacement. Use it " + "where you would reach for grep or rg: every use or definition of an identifier, an error " + "message, a config key, a literal. Complete, not ranked: it returns every matching line (up " + - "to `limit`, and always the repo-wide total) as path, line number, and the line's text, plus " + - "the enclosing definition name when known. Literal text, not a regex; within a single line; " + - "case-sensitive unless ignoreCase. It reads only the chunks the index says contain the " + - "query's tokens and then checks each line, so every hit is a real occurrence and no file is " + - "scanned. Read path:line for the surrounding code when you need it. For 'how does X work' or " + - "when you don't know the exact words, use search; for counts and rankings, sql. The result " + - "includes a 'usage' field - a one-line receipt (tokens returned, matches/files, session " + - "total). After you answer, end your reply by showing that 'usage' line to the user verbatim.", + "to `limit`, and always the repo-wide `total`) as path, line number, and the line's text, " + + "plus the enclosing definition name when known, and `byFile` - matching lines per file over " + + "every match, the `grep -c` answer in the same call. Literal text, not a regex; within a " + + "single line; case-sensitive unless ignoreCase. It reads only the chunks the index says " + + "contain the query's tokens and then checks each line, so every hit is a real occurrence " + + "and no file is scanned. Not for a file you already know: Read that file directly - find is " + + "for locating occurrences across the repository, and Read path:line when you need the code " + + "around a hit. For 'how does X work' or when you don't know the exact words, use search; " + + "for rankings and aggregates beyond per-file counts, sql. The result includes a 'usage' " + + "field - a one-line receipt (tokens returned, matches/files, session total). After you " + + "answer, end your reply by showing that 'usage' line to the user verbatim.", inputSchema: { query: z .string() diff --git a/test/integration.test.ts b/test/integration.test.ts index 8f93796..069c05b 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -7,7 +7,8 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { connect } from "@infino-ai/infino"; import { indexRepo, indexRepoStaged } from "../src/core/indexer.js"; import { readManifest } from "../src/core/manifest.js"; -import { find, runSql, search } from "../src/core/searcher.js"; +import { analyzerTokens, find, runSql, search } from "../src/core/searcher.js"; +import { TABLE } from "../src/core/config.js"; import type { IndexHandle } from "../src/core/context.js"; import type { Embedder } from "../src/core/embedder.js"; @@ -51,6 +52,15 @@ export function replayLog(): number { return 42; } `, ); writeFileSync(join(root, "README.md"), "# Fixture\n\nA tiny repo about sessions and commit logs.\n"); + // A plain-text file long enough to chunk as fixed windows (60 lines, 10 + // overlapping), so a line in the overlap lives in two chunks. Line 55 is in + // windows 1-60 and 51-110; line 20 carries analyzer edge cases; line 130 is + // longer than the excerpt cap with its marker past the cap. + const notes = Array.from({ length: 130 }, (_, i) => `filler ${i + 1}`); + notes[19] = "parse_config(Path) ABC-123 x.y Süd ok"; + notes[54] = "OVERLAP_MARK sits in two windows"; + notes[129] = "z".repeat(600) + " FAR_MARK " + "z".repeat(300); + writeFileSync(join(root, "notes.txt"), notes.join("\n") + "\n"); writeFileSync(join(root, ".gitignore"), "ignored.ts\n"); writeFileSync(join(root, "ignored.ts"), "export const SHOULD_NOT_APPEAR = 1;\n"); @@ -67,7 +77,7 @@ afterAll(() => { describe("indexing", () => { it("indexes the fixture and honors .gitignore", () => { const m = handle.manifest; - expect(m.files).toBe(3); // auth.ts, storage.ts, README.md (.gitignore is not indexable) + expect(m.files).toBe(4); // auth.ts, storage.ts, README.md, notes.txt (.gitignore is not indexable) expect(m.vectors).toBe("ready"); expect(m.embedder?.dim).toBe(16); const rows = handle.db.querySql("SELECT DISTINCT path FROM chunks ORDER BY path") as Array<{ path: string }>; @@ -116,10 +126,75 @@ describe("find", () => { expect(r.matches[0].symbol).toContain("verifySession"); expect(r.total).toBe(2); expect(r.files).toBe(1); + expect(r.byFile).toEqual([{ path: "src/auth.ts", count: 2 }]); expect(r.truncated).toBeUndefined(); expect(r.ignoreCase).toBe(false); }); + it("counts matches per file over every match, most first, even when the list is cut", () => { + // `export function` twice in each of auth.ts and storage.ts; the tie + // breaks on path. The cut list is one line, the counts are still whole. + const r = find(handle, "export function", { limit: 1 }); + expect(r.matches.length).toBe(1); + expect(r.total).toBe(4); + expect(r.byFile).toEqual([ + { path: "src/auth.ts", count: 2 }, + { path: "src/storage.ts", count: 2 }, + ]); + }); + + it("reports a line that lives in two overlapping chunks once", () => { + const r = find(handle, "OVERLAP_MARK"); + expect(r.matches.map((m) => `${m.path}:${m.line}`)).toEqual(["notes.txt:55"]); + expect(r.total).toBe(1); + // The overlap is real: the line is in two indexed chunks. + const rows = handle.db.querySql( + `SELECT start_line FROM ${TABLE} WHERE path = 'notes.txt' AND start_line <= 55 AND end_line >= 55`, + ); + expect(rows.length).toBe(2); + }); + + it("windows a long line around the match so the cited text contains it", () => { + const r = find(handle, "FAR_MARK"); + expect(r.matches.length).toBe(1); + expect(r.matches[0].line).toBe(130); + expect(r.matches[0].text).toContain("FAR_MARK"); + expect(r.matches[0].text.startsWith("...")).toBe(true); + }); + + it("agrees with the engine's analyzer on which chunks are candidates", () => { + // The client-side token mirror must produce the same candidate set as + // handing the raw text to the engine, or a literal the repo does contain + // could be missed. Checked on strings with the analyzer's edge cases: + // underscores and punctuation as separators, digits, a dropped non-ASCII run. + const table = handle.db.openTable(TABLE); + const chunks = (rows: Array>) => + rows.map((r) => `${r.path}:${r.start_line}`).sort(); + for (const text of ["parse_config(Path)", "ABC-123 x.y", "Süd ok", "Session(", "session record"]) { + const viaEngine = table.tokenMatch("content", text, { mode: "and", projection: ["path", "start_line"] }); + const viaMirror = table.tokenMatch("content", analyzerTokens(text).join(" "), { + mode: "and", + projection: ["path", "start_line"], + }); + expect(viaEngine.length, text).toBeGreaterThan(0); + expect(chunks(viaMirror), text).toEqual(chunks(viaEngine)); + } + }); + + it("carries the partial-index marker like search does", () => { + const partial = { ...handle, manifest: { ...handle.manifest, truncatedFiles: 3, maxFiles: 10 } }; + expect(find(partial, "Session(").partial?.filesSkipped).toBe(3); + expect(find(handle, "Session(").partial).toBeUndefined(); + }); + + it("rejects a malformed limit instead of returning nothing", () => { + expect(() => find(handle, "Session(", { limit: Number.NaN })).toThrow(/positive integer/); + expect(() => find(handle, "Session(", { limit: 0 })).toThrow(/positive integer/); + expect(() => find(handle, "Session(", { limit: 2.5 })).toThrow(/positive integer/); + // Over the hard cap clamps rather than errors: a big number is a valid wish. + expect(find(handle, "Session(", { limit: 10_000 }).total).toBe(2); + }); + it("matches the literal, not just its tokens", () => { // The comment says "session record"; "record session" has the same tokens // in the same chunk and occurs nowhere. diff --git a/test/searcher.test.ts b/test/searcher.test.ts index 7cd43e6..4bb9554 100644 --- a/test/searcher.test.ts +++ b/test/searcher.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { analyzerTokens, applyEmbeds, guardSql, matchLines } from "../src/core/searcher.js"; +import { analyzerTokens, applyEmbeds, excerpt, guardSql, matchLines } from "../src/core/searcher.js"; import type { Embedder } from "../src/core/embedder.js"; describe("analyzerTokens", () => { @@ -30,7 +30,7 @@ describe("matchLines", () => { it("cites 1-based lines offset from the chunk start and matches the literal, not its tokens", () => { // Line 2 has both tokens but not the literal. - expect(matchLines(content, 10, "parse_config", false)).toEqual([{ line: 10, text: "let parse_config = 1;" }]); + expect(matchLines(content, 10, "parse_config", false)).toEqual([{ line: 10, text: "let parse_config = 1;", at: 4 }]); }); it("is case-sensitive unless asked otherwise", () => { @@ -38,7 +38,35 @@ describe("matchLines", () => { }); it("strips a CRLF file's carriage return from the cited text", () => { - expect(matchLines("x = 1;\r\ny = 2;\r\n", 1, "y =", false)).toEqual([{ line: 2, text: "y = 2;" }]); + expect(matchLines("x = 1;\r\ny = 2;\r\n", 1, "y =", false)).toEqual([{ line: 2, text: "y = 2;", at: 0 }]); + }); +}); + +describe("excerpt", () => { + it("returns a short line whole", () => { + expect(excerpt("let x = needle;", 8, 6)).toBe("let x = needle;"); + }); + + it("windows a long line around the match and marks both cut ends", () => { + // A match past column 240 must still be in the cited text, or the hit + // reads as wrong. Some lead-in is kept so the excerpt shows what the match + // sits in, and both cuts are marked. + const line = "a".repeat(600) + "NEEDLE" + "b".repeat(300); + const out = excerpt(line, 600, "NEEDLE".length); + expect(out).toContain("NEEDLE"); + expect(out.startsWith("...")).toBe(true); + expect(out.endsWith("...")).toBe(true); + expect(out.length).toBeLessThanOrEqual(240 + "......".length); + expect(out.indexOf("NEEDLE")).toBeGreaterThan("...".length); + }); + + it("marks only the end that was cut", () => { + const head = excerpt("NEEDLE" + "b".repeat(600), 0, 6); + expect(head.startsWith("NEEDLE")).toBe(true); + expect(head.endsWith("...")).toBe(true); + const tail = excerpt("a".repeat(600) + "NEEDLE", 600, 6); + expect(tail.startsWith("...")).toBe(true); + expect(tail.endsWith("NEEDLE")).toBe(true); }); }); diff --git a/test/usage.test.ts b/test/usage.test.ts index c8f72c7..cbecb17 100644 --- a/test/usage.test.ts +++ b/test/usage.test.ts @@ -83,6 +83,7 @@ describe("find receipt", () => { ignoreCase: false, total: 3, files: 1, + byFile: [{ path: "a.ts", count: 3 }], truncated: true, matches: [ { path: "a.ts", line: 3, text: "x = 1" }, From 78b36f829b88635b86a3f3fe6efd3497b4159a91 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 11:12:35 +0000 Subject: [PATCH 03/13] find: record the head-to-head measurement; CX_FIND_LIMIT The review asked the docs to cite the measurement rather than assert the four-tool design. docs/benchmark.md now carries the 2026-09-04 run of feat/find-tool against main: same repo (infino @ ed4e020, 402 files, 5,528 chunks), questions, model (claude-sonnet-4-6) and a blind judge (claude-opus-5), 128 agent runs over 64 judged pairs. Answer quality is level (29 / 22 / 13, no out-of-bounds citation); exact lookups are -35% tokens, -17% dollars, -38% tool calls; the shipped set is flat; a fourth tool costs about a thousand prompt tokens per turn. The section also records what the run found and the commit after it changed (byFile for the per-file count gap, the known-file steer), and the discarded first run whose index had drifted from the tree. README and tradeoffs cite the numbers instead of asserting. The eight pinpoint questions ship as bench/questions/infino-pinpoint.json so the comparison can be re-run. The default find limit moves to config beside DEFAULT_SEARCH_K and reads CX_FIND_LIMIT, so the fallback for an agent that passes no limit is a deployment setting rather than a constant; the hard cap stays the context-flood guard. --- README.md | 8 ++ bench/questions/infino-pinpoint.json | 10 +++ docs/benchmark.md | 128 +++++++++++++++++++++++++-- docs/tradeoffs.md | 13 ++- src/cli.ts | 3 +- src/core/config.ts | 10 +++ src/core/searcher.ts | 7 +- src/mcp/server.ts | 12 ++- 8 files changed, 172 insertions(+), 19 deletions(-) create mode 100644 bench/questions/infino-pinpoint.json diff --git a/README.md b/README.md index 73bde75..5d4d9b1 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,13 @@ explore less efficiently, so the savings tend to be **larger** there. On pinpoint symbol lookup, where a single grep is already cheap, an index matches file tools rather than beating them. +Adding `find` was measured the same way, against the three-tool build on the +same repo, questions, model, and a blind judge: answer quality level (judge +29 / 22 / 13 main / find / tie over 64 pairs, no out-of-bounds citation in +128 answers), exact-lookup questions **-35% tokens, -17% dollars, -38% tool +calls**, the shipped question set flat (-3% tokens, +1% dollars), and about a +thousand tokens per turn of added prompt for the fourth tool. + Full methodology and per-question tables are in [docs/benchmark.md](docs/benchmark.md), with the harness in [`bench/`](bench/) so you can run the same lanes on your own repo. @@ -271,6 +278,7 @@ no restart, no per-repo config. |---|---|---| | `CX_INDEX_DIR` | `/.infino` | where the index lives | | `CX_SEARCH_K` | 10 | default number of hits `search` returns (also settable per call and via the CLI `-k` flag) | +| `CX_FIND_LIMIT` | 100 | default number of matching lines `find` returns, hard cap 500 (also settable per call and via the CLI `--limit` flag); `total` and `byFile` are complete either way | | `CX_MAX_FILES` / `CX_MAX_FILE_BYTES` | 20000 / 1MB | indexing caps (files over the file cap are left out; `find`/`search`/`sql` then flag the index as partial so an absence isn't read as proof) | | `CX_ROOT` | current directory | default repo root for the MCP server / CLI when not run from the repo (each tool call can override it with a `path` argument) | | `CX_AUTO_INDEX` | on | `0` makes a query on an unindexed repo error instead of building the index inline on the first `find`/`search`/`sql` | diff --git a/bench/questions/infino-pinpoint.json b/bench/questions/infino-pinpoint.json new file mode 100644 index 0000000..9784c54 --- /dev/null +++ b/bench/questions/infino-pinpoint.json @@ -0,0 +1,10 @@ +[ + { "cat": "pinpoint", "q": "Where is the constant RRF_K defined, and what is its value?" }, + { "cat": "pinpoint", "q": "List every call site of reconcile_tombstone_seqs, with file and line." }, + { "cat": "pinpoint", "q": "Which file defines InfinoError, and how many variants does the enum have?" }, + { "cat": "pinpoint", "q": "List every place the code reads an environment variable through std::env::var, with the variable name and file:line for each." }, + { "cat": "pinpoint", "q": "Where is pointer_refresh_due defined, and where is it called?" }, + { "cat": "pinpoint", "q": "What are the crate version and the arrow and datafusion dependency versions in Cargo.toml?" }, + { "cat": "pinpoint", "q": "For each file under src/supertable/, how many #[tokio::test] tests does it contain?" }, + { "cat": "pinpoint", "q": "List the tracing call sites in src/compaction/ with the message each one logs." } +] diff --git a/docs/benchmark.md b/docs/benchmark.md index d37491d..54dcacc 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -88,16 +88,133 @@ time follows (13%). The biggest wins land on the questions where the baseline reads the most (one dropped from 861k tokens to 292k). Every answer carries `path:start-end` citations, since hits arrive as ranked chunks with content. -## Where it does not help +## Where ranked search does not help Pinpoint symbol lookup - "jump to this one known identifier" - is a single -grep's home turf, and an index does not beat it there: ranked search returns +grep's home turf, and ranked search does not beat it there: it returns chunks that carry their content, which is dead weight when all you need is one path. That same content is exactly what lets the comprehension answers quote code without opening the file. Same mechanism, opposite sign, depending on whether the question is "where is this exact name" or "how does this work". -The tool descriptions say as much, so the agent still uses a plain grep for -pinpoint lookups. +The `find` tool exists for that first question; the next section measures it. + +## `find`, the grep replacement + +Does adding `find` - every line containing an exact string, cited +`path:line` - change what an agent answers, or what it costs? Measured +2026-09-04 against the three-tool build, on the same repo, questions, model +and judge. + +**Setup.** Two hermetic lanes from `bench/run-questions.mjs`, differing only +in the server build: *main* (`search`, `sql`, `reindex`, from `main @ eec2fe7`) +and *find* (the same plus `find`, from `feat/find-tool @ dc713da`). Both +lanes keep the stock file tools including Grep; nothing is restricted. Repo +under test: [infino](https://github.com/infino-ai/infino) pinned at +`ed4e020` (402 files, 5,528 chunks), indexed once from that clone with +auto-sync off, so every run sees the same index. Agent `claude-sonnet-4-6`, +50-turn cap, fresh conversation per question. Two question sets: the shipped +16 (10 aggregation, 6 comprehension; 3 repeats), and 8 pinpoint lookups +written to be grep's home ground and phrased without naming a tool +(`bench/questions/infino-pinpoint.json`; 2 repeats). 128 agent runs, 64 +judged pairs. Quality was measured two ways: a mechanical citation check +(every cited file exists, every line range is in bounds, the identifier named +beside a citation appears within five lines of it), and a blind pairwise +judge (`claude-opus-5`, a different model from the agent) that sees both +answers in random order with Read/Grep/Glob on the clone and returns a +winner, a confidence, and unsupported-claim counts. Tokens are input + cache +writes + cache reads + output; cost is the API's accounting; per-question +figures are medians over repeats and totals sum the medians. + +### Answer quality: level + +| Blind judge | Pairs | main wins | find wins | Ties | Unsupported claims main / find | Median confidence | +|---|---|---|---|---|---|---| +| shipped, aggregation | 30 | 15 | 9 | 6 | 109 / 97 | 0.62 | +| shipped, comprehension | 18 | 7 | 8 | 3 | 37 / 29 | 0.68 | +| pinpoint | 16 | 7 | 5 | 4 | 13 / 17 | 0.84 | +| **all** | 64 | 29 | 22 | 13 | 159 / 143 | 0.70 | + +The signals point both ways, which is what no effect looks like: main takes +more wins, `find` has fewer unsupported claims on the shipped set and a few +more on the pinpoint set, and where the judge is confident (pinpoint, 0.84) +the split is 7 to 5 with 4 ties. No answer in either lane cited a line +outside its file. The one change is in the form of the answers: on the +pinpoint set, `find` answers carried 111 line-ranged citations across 16 +answers against main's 22, all 105 identifier-anchored ones anchored +correctly. Same correctness, more of it shown. + +### Cost: exact lookups a third cheaper, everything else flat + +Pinpoint set, medians over repeats: + +| Q | Question | main tokens | find tokens | Tokens | Calls main → find | How find answered | +|---|---|---|---|---|---|---| +| 1 | Where `RRF_K` is defined and its value | 49k | 17k | -66% | 2.5 → 1 | one find | +| 2 | Every call site of `reconcile_tombstone_seqs` | 20k | 17k | -14% | 1.5 → 1 | one find | +| 3 | File defining `InfinoError` and its variant count | 108k | 45k | -58% | 5.5 → 2.5 | find, then Read | +| 4 | Every `std::env::var` read, with name and file:line | 203k | 92k | -55% | 13.5 → 3 | two or three finds | +| 5 | Where `pointer_refresh_due` is defined and called | 24k | 17k | -30% | 2 → 1 | one find | +| 6 | Crate, arrow, datafusion versions in Cargo.toml | 22k | 49k | +121% | 1 → 3.5 | three finds, then Read; main did one Read | +| 7 | `#[tokio::test]` counts per file under src/supertable/ | 16k | 17k | +6% | 1 → 1 | Grep, same as main | +| 8 | tracing call sites in src/compaction/ with messages | 135k | 121k | -11% | 8.5 → 9 | mixed find, Grep, Read | +| | **pinpoint (8), tokens** | 578k | 374k | **-35%** | 35.5 → 22 (**-38%**) | | +| | **pinpoint (8), cost per pass** | $0.68 | $0.56 | **-17%** | | | + +Where it fits, it collapses the search: definitions, call sites and +repo-wide inventories (Q1 to Q5) go from a grep-then-read sequence to one to +three `find` calls. The env-var inventory is the clearest case - main's worst +run was 23 calls and 380k tokens, `find`'s best was 2 calls and 57k, with +every cited line verified by the judge. Where it does not fit, the agent +sometimes uses it anyway: Q6 asks for a few lines of one known file, main did +one Read, and the `find` lane ran three finds and then read the file (this is +what the tool description's "a known file is a Read" sentence is for). The +agent opened with `find` in 12 of 16 lookup runs; on main the first call was +Grep in 8 of 16. + +Shipped set, by category: + +| Category | main tokens | find tokens | Tokens | Cost main → find | Calls main → find | +|---|---|---|---|---|---| +| aggregation (10) | 186k | 244k | +31% | $0.28 → $0.33 (+20%) | 11 → 16 | +| comprehension (6) | 719k | 635k | -12% | $1.27 → $1.24 (-3%) | 30 → 27 | +| **blended (16)** | 904k | 879k | **-3%** | $1.55 → $1.57 (**+1%**) | 41 → 43 | + +Flat overall, with the two categories moving in opposite directions by +amounts inside the repeat-to-repeat spread. Two small effects are real. Seven +of the ten aggregation questions are one `sql` call in both lanes, and each +moved from 17k to 18k tokens uniformly: that is the fourth tool's schema and +description in every turn's prompt, roughly a thousand tokens. And on two +aggregation questions the agent tried `find` before falling back to `sql`. +The agent used `find` in 5 of 48 shipped-set runs; these are not the +questions it exists for. + +### What the run found, and what changed after it + +- Quality unchanged; cost down by a third in tokens and a sixth in dollars + where the tool applies; a standing cost of about a thousand prompt tokens + per turn plus occasional mis-selection. +- One grep-shaped question was not covered: per-file counts (Q7). `find` + returned lines with a total, capped at 100 by default, where the true answer + was about 300 lines across 27 files, so the agent used Grep instead. Both + lanes answered that question identically, and identically wrong (13 files + where the tree has 27). The build measured here was `dc713da`; the commit + after it added `byFile` - matching lines per file over every match, never + cut - and the "known file is a Read" steer, neither of which is measured + above. +- Steering is by description only and mostly works: nothing restricts Grep, + and the agent still preferred `find` on lookups. + +**Caveats.** Sonnet 4.6 is the bench default, and tool-selection behaviour - +which drives both the gains and the mis-selection cost - is model-dependent. +Repeats are 3 on the shipped set and 2 on pinpoint; per-question deltas under +about ±50% are noise, so read the totals (main's own pinpoint total varied by +50% between this run and one two days earlier on the same commit). A first +run was discarded because the working tree under test had moved two versions +ahead of its index, so `find` returned line numbers right for the indexed tree +and wrong for the live one: exact-looking, no signal. The MCP server's +auto-sync exists for exactly that; the bench disables it to keep the index +identical across runs, so it must pin the tree instead. Spend for the two +lanes: about $14 of agent runs and $14 of judging. ## Indexing at scale @@ -130,7 +247,8 @@ node bench/run-questions.mjs /path/to/repo # default question set ``` Question sets are `bench/questions/*.json` (`{cat, q}` arrays) - point it at -your own repo and questions with no code changes. Wall-clock is only clean +your own repo and questions with no code changes. `infino-pinpoint.json` is +the eight-lookup set from the `find` comparison above. Wall-clock is only clean run sequentially (`CX_BENCH_CONCURRENCY=1`); tool-call count is the concurrency-independent latency proxy. Expect the aggregation multiple to grow with repo size, and the whole gap to grow on weaker models. diff --git a/docs/tradeoffs.md b/docs/tradeoffs.md index 3f89af2..ef434a1 100644 --- a/docs/tradeoffs.md +++ b/docs/tradeoffs.md @@ -17,10 +17,15 @@ Naming the one file a known symbol lives in is a single grep's job, and file counts, no file scanned. A grep hit still needs a follow-up read before it is a cited line; a `find` hit already is one, so on exact lookups the saving is the reads that never happen, not a change in what gets found. -Ranked `search` is the wrong tool there: it returns chunks that carry their -content, which is what pays off on "how does X work" and whole-repo questions -and is dead weight when all you need is a path. The large savings are on -questions that span the repo; the [benchmark](benchmark.md) records both. +Measured against the grep path on eight such questions: -35% tokens, -17% +dollars, -38% tool calls, with answer quality level under a blind judge (see +the [benchmark](benchmark.md#find-the-grep-replacement)). Ranked `search` is +the wrong tool there: it returns chunks that carry their content, which is +what pays off on "how does X work" and whole-repo questions and is dead +weight when all you need is a path. The large savings are still on questions +that span the repo, and a fourth tool has a standing cost of about a thousand +prompt tokens per turn plus the occasional wrong pick; the benchmark records +both. ### The first index of a repo pays a one-time vector cost diff --git a/src/cli.ts b/src/cli.ts index cb795e8..9423336 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,8 +7,7 @@ import { Command } from "commander"; import { indexCmd } from "./commands/index-cmd.js"; import { findCmd, searchCmd, sqlCmd, statusCmd, usageCmd } from "./commands/query-cmds.js"; -import { DEFAULT_SEARCH_K } from "./core/config.js"; -import { DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT } from "./core/searcher.js"; +import { DEFAULT_SEARCH_K, DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT } from "./core/config.js"; const program = new Command(); diff --git a/src/core/config.ts b/src/core/config.ts index 810a4ab..7624806 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -57,3 +57,13 @@ export const EMBED_MAX_CHARS = Number(process.env.CX_EMBED_MAX_CHARS ?? 8000); /** Default number of search hits. Configurable per call (the `k` tool param / * CLI `-k`) and via CX_SEARCH_K for config/CI-level defaults. */ export const DEFAULT_SEARCH_K = Number(process.env.CX_SEARCH_K ?? 10); + +/** Default number of matching lines `find` returns; the result's `total` and + * `byFile` are complete either way. Configurable per call (the `limit` tool + * param / CLI `--limit`) and via CX_FIND_LIMIT. Bounded by MAX_FIND_LIMIT. */ +export const DEFAULT_FIND_LIMIT = Number(process.env.CX_FIND_LIMIT ?? 100); + +/** Hard cap on matching lines in one `find` result, so a ubiquitous term + * cannot flood the context; a cut list still carries the full total and the + * per-file counts. */ +export const MAX_FIND_LIMIT = 500; diff --git a/src/core/searcher.ts b/src/core/searcher.ts index e5990f8..dd6d059 100644 --- a/src/core/searcher.ts +++ b/src/core/searcher.ts @@ -16,7 +16,7 @@ // vector functions. import type { IndexHandle } from "./context.js"; -import { TABLE, DEFAULT_SEARCH_K } from "./config.js"; +import { TABLE, DEFAULT_SEARCH_K, DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT } from "./config.js"; import type { Embedder } from "./embedder.js"; import type { Manifest } from "./manifest.js"; @@ -187,11 +187,6 @@ export interface FindOptions { limit?: number; } -/** Default and hard cap on matches returned. A grep over a large repo can hit - * thousands of lines; past this the agent needs the count, not every line. */ -export const DEFAULT_FIND_LIMIT = 100; -export const MAX_FIND_LIMIT = 500; - /** Per-line cap so one minified or generated line cannot flood the result. */ const FIND_LINE_CAP = 240; diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 35e7d3d..bafb714 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -24,10 +24,18 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { connect } from "@infino-ai/infino"; -import { indexDir, resolveRoot, TABLE, DEFAULT_CAPS, DEFAULT_SEARCH_K } from "../core/config.js"; +import { + indexDir, + resolveRoot, + TABLE, + DEFAULT_CAPS, + DEFAULT_SEARCH_K, + DEFAULT_FIND_LIMIT, + MAX_FIND_LIMIT, +} from "../core/config.js"; import { readManifest, type Manifest } from "../core/manifest.js"; import type { IndexHandle } from "../core/context.js"; -import { find, search, runSql, jsonify, partialIndex, DEFAULT_FIND_LIMIT, MAX_FIND_LIMIT } from "../core/searcher.js"; +import { find, search, runSql, jsonify, partialIndex } from "../core/searcher.js"; import { newSession, receiptEnabled, From 2ac662860f079c539bb1b1e3f378488c7b5641b8 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 11:15:37 +0000 Subject: [PATCH 04/13] find: default limit is the cap The default number of matching lines find returns moves from 100 to 500, the hard cap, so the cut only ever lands on a flood - a ubiquitous term returning tens of thousands of lines - and never on a real answer: the largest lookup measured needed about 300 lines and the old default cut it. At roughly fifty tokens per returned line the cap is about 25k tokens of tool result, large but survivable. A caller that wants fewer passes a smaller limit; total and byFile are complete either way. --- README.md | 2 +- docs/benchmark.md | 14 +++++++------- skills/code-context/SKILL.md | 5 +++-- src/core/config.ts | 20 ++++++++++++-------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5d4d9b1..7f8ff9b 100644 --- a/README.md +++ b/README.md @@ -278,7 +278,7 @@ no restart, no per-repo config. |---|---|---| | `CX_INDEX_DIR` | `/.infino` | where the index lives | | `CX_SEARCH_K` | 10 | default number of hits `search` returns (also settable per call and via the CLI `-k` flag) | -| `CX_FIND_LIMIT` | 100 | default number of matching lines `find` returns, hard cap 500 (also settable per call and via the CLI `--limit` flag); `total` and `byFile` are complete either way | +| `CX_FIND_LIMIT` | 500 | default number of matching lines `find` returns, which is also the hard cap, so it only bites on a flood (also settable per call and via the CLI `--limit` flag); `total` and `byFile` are complete either way | | `CX_MAX_FILES` / `CX_MAX_FILE_BYTES` | 20000 / 1MB | indexing caps (files over the file cap are left out; `find`/`search`/`sql` then flag the index as partial so an absence isn't read as proof) | | `CX_ROOT` | current directory | default repo root for the MCP server / CLI when not run from the repo (each tool call can override it with a `path` argument) | | `CX_AUTO_INDEX` | on | `0` makes a query on an unindexed repo error instead of building the index inline on the first `find`/`search`/`sql` | diff --git a/docs/benchmark.md b/docs/benchmark.md index 54dcacc..265dbb5 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -194,13 +194,13 @@ questions it exists for. where the tool applies; a standing cost of about a thousand prompt tokens per turn plus occasional mis-selection. - One grep-shaped question was not covered: per-file counts (Q7). `find` - returned lines with a total, capped at 100 by default, where the true answer - was about 300 lines across 27 files, so the agent used Grep instead. Both - lanes answered that question identically, and identically wrong (13 files - where the tree has 27). The build measured here was `dc713da`; the commit - after it added `byFile` - matching lines per file over every match, never - cut - and the "known file is a Read" steer, neither of which is measured - above. + returned lines with a total, capped at 100 by default in that build, where + the true answer was about 300 lines across 27 files, so the agent used Grep + instead. Both lanes answered that question identically, and identically + wrong (13 files where the tree has 27). The build measured here was + `dc713da`; the commits after it added `byFile` - matching lines per file + over every match, never cut - the "known file is a Read" steer, and a + default limit of 500 (the cap), none of which is measured above. - Steering is by description only and mostly works: nothing restricts Grep, and the agent still preferred `find` on lookups. diff --git a/skills/code-context/SKILL.md b/skills/code-context/SKILL.md index 88f02fd..212be5a 100644 --- a/skills/code-context/SKILL.md +++ b/skills/code-context/SKILL.md @@ -42,10 +42,11 @@ listed names, comma-separated). Never load them one call at a time. case-sensitive unless `ignoreCase`. - Complete, not ranked: every matching line comes back as `path`, `line`, and the line's `text` (plus the enclosing definition's `symbol` when - known), in path order, up to `limit` (default 100, max 500). `total` is + known), in path order, up to `limit` (default and cap 500, so it only + bites on a flood; pass a smaller `limit` when you want fewer). `total` is the repo-wide count either way, `byFile` lists matching lines per file over every match (the `grep -c` answer, never cut), and `truncated` says - when the line list was cut - raise `limit` or narrow the text. + when the line list was cut - narrow the text. - Not for a file you already know the path of: Read it. `find` locates occurrences across the repo; pulling a few lines out of one known file is a Read. diff --git a/src/core/config.ts b/src/core/config.ts index 7624806..e48fca6 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -58,12 +58,16 @@ export const EMBED_MAX_CHARS = Number(process.env.CX_EMBED_MAX_CHARS ?? 8000); * CLI `-k`) and via CX_SEARCH_K for config/CI-level defaults. */ export const DEFAULT_SEARCH_K = Number(process.env.CX_SEARCH_K ?? 10); -/** Default number of matching lines `find` returns; the result's `total` and - * `byFile` are complete either way. Configurable per call (the `limit` tool - * param / CLI `--limit`) and via CX_FIND_LIMIT. Bounded by MAX_FIND_LIMIT. */ -export const DEFAULT_FIND_LIMIT = Number(process.env.CX_FIND_LIMIT ?? 100); - -/** Hard cap on matching lines in one `find` result, so a ubiquitous term - * cannot flood the context; a cut list still carries the full total and the - * per-file counts. */ +/** Hard cap on matching lines in one `find` result. At roughly fifty tokens + * per returned line (path, line number, text) this is about 25k tokens: a + * large tool result, but one a session survives, where an unbounded find of + * a ubiquitous term could return a hundred thousand lines. A cut list still + * carries the full total and the per-file counts. */ export const MAX_FIND_LIMIT = 500; + +/** Default number of matching lines `find` returns when the caller passes no + * limit: the cap itself, so the cut only ever lands on a flood, never on a + * real answer (the largest measured lookup needed about 300 lines). The + * result's `total` and `byFile` are complete either way. Configurable per call + * (the `limit` tool param / CLI `--limit`) and via CX_FIND_LIMIT. */ +export const DEFAULT_FIND_LIMIT = Number(process.env.CX_FIND_LIMIT ?? MAX_FIND_LIMIT); From 5770441bf06f86f9adc16edc053887ac8a43fc87 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 11:59:16 +0000 Subject: [PATCH 05/13] usage: per-tool calls and the first tool of each prompt The hook tally counted how many prompts used code-context; it could not say which tool, or what the agent reached for first. Both are the selection signal the tool-surface measurement needs: whether a grep-shaped prompt opens with find or with Grep, and which door each call goes through. PromptStats gains cxCallsByTool and firstToolByPrompt. Every PostToolUse event the hook forwards counts toward the first-tool tally (code-context tools by their short name, anything else by the name delivered); only code-context's own tools count as invocations, as before. With the documented matcher only code-context tools are forwarded; the README says how to widen it. Older stats files load and grow the fields on the next event. cx usage prints both lines. Two question sets for the surface comparison: infino-known-file.json, where the right first call is Read, and infino-by-meaning.json, where the question names no identifier. --- README.md | 8 ++++ bench/questions/infino-by-meaning.json | 8 ++++ bench/questions/infino-known-file.json | 8 ++++ docs/benchmark.md | 5 ++- src/commands/query-cmds.ts | 15 +++++++ src/core/usage.ts | 57 +++++++++++++++++++++++--- test/usage.test.ts | 55 +++++++++++++++++++++++++ 7 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 bench/questions/infino-by-meaning.json create mode 100644 bench/questions/infino-known-file.json diff --git a/README.md b/README.md index 7f8ff9b..87f64b9 100644 --- a/README.md +++ b/README.md @@ -348,6 +348,14 @@ to your Claude Code settings (`~/.claude/settings.json` or a project and prints nothing. If you run code-context via `npx`, use `npx -y @infino-ai/code-context usage --hook` as the command. +The same tally breaks the calls down by tool (`by tool: find 4 · search 2 · +sql 1`) and records which tool the agent reached for first in each prompt +(`first tool of a prompt: find 4 · Grep 2`), which is what tells you whether +the tool surface steers as intended. With the matcher above only +code-context's own tools are forwarded, so the first-tool line names them +alone; set the `PostToolUse` matcher to `.*` to see Grep, Read, and the rest +in that line too, at the cost of one hook process per tool call. + ## What it is, and what it isn't code-context's lane is ranked **content** retrieval and content-relevance diff --git a/bench/questions/infino-by-meaning.json b/bench/questions/infino-by-meaning.json new file mode 100644 index 0000000..0d6484a --- /dev/null +++ b/bench/questions/infino-by-meaning.json @@ -0,0 +1,8 @@ +[ + { "cat": "by-meaning", "q": "Where is the decision made that a set of small files in a table should be merged into a larger one?" }, + { "cat": "by-meaning", "q": "Where does an append become durable, and what is the last step before other readers can see the new rows?" }, + { "cat": "by-meaning", "q": "What stops two writers from committing to the same table at the same time?" }, + { "cat": "by-meaning", "q": "Where are rows that have been deleted filtered out of search results?" }, + { "cat": "by-meaning", "q": "How does a vector search decide how many partitions of the index to visit for a query?" }, + { "cat": "by-meaning", "q": "Where does the code decide whether to read a file from the local disk cache or fetch it from object storage?" } +] diff --git a/bench/questions/infino-known-file.json b/bench/questions/infino-known-file.json new file mode 100644 index 0000000..d8951cf --- /dev/null +++ b/bench/questions/infino-known-file.json @@ -0,0 +1,8 @@ +[ + { "cat": "known-file", "q": "Which Rust toolchain channel does rust-toolchain.toml pin?" }, + { "cat": "known-file", "q": "What are the crate's name, version, and edition in Cargo.toml?" }, + { "cat": "known-file", "q": "Which [[test]] binaries does Cargo.toml declare, and what path does each point at?" }, + { "cat": "known-file", "q": "What does the Makefile's ci target run, step by step?" }, + { "cat": "known-file", "q": "According to docs/architecture/supertable.md, what is the manifest pointer and how does a commit become visible to readers?" }, + { "cat": "known-file", "q": "What commands does CONTRIBUTING.md give for running a single integration test crate?" } +] diff --git a/docs/benchmark.md b/docs/benchmark.md index 265dbb5..ab01045 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -248,7 +248,10 @@ node bench/run-questions.mjs /path/to/repo # default question set Question sets are `bench/questions/*.json` (`{cat, q}` arrays) - point it at your own repo and questions with no code changes. `infino-pinpoint.json` is -the eight-lookup set from the `find` comparison above. Wall-clock is only clean +the eight-lookup set from the `find` comparison above; `infino-known-file.json` +(questions whose right first call is Read) and `infino-by-meaning.json` +(questions that name no identifier) measure tool selection for the surface +comparison that follows it. Wall-clock is only clean run sequentially (`CX_BENCH_CONCURRENCY=1`); tool-call count is the concurrency-independent latency proxy. Expect the aggregation multiple to grow with repo size, and the whole gap to grow on weaker models. diff --git a/src/commands/query-cmds.ts b/src/commands/query-cmds.ts index 02aeda8..d1bb82d 100644 --- a/src/commands/query-cmds.ts +++ b/src/commands/query-cmds.ts @@ -197,6 +197,15 @@ export interface UsageCmdOptions { const truncate = (s: string, n: number): string => (s.length > n ? s.slice(0, n - 3) + "..." : s); +/** `find 4 · Grep 2 · Read 1`, most first; empty string when there is nothing. */ +function countsLine(counts: Record | undefined): string { + if (!counts) return ""; + return Object.entries(counts) + .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) + .map(([name, n]) => `${name} ${n}`) + .join(" · "); +} + /** `cx usage` - the local ledger of what queries went through the index and a * compact summary of what each returned. Read straight off `.infino/usage.jsonl`, * so it's deterministic and needs no running server or model. */ @@ -247,6 +256,12 @@ export async function usageCmd(opts: UsageCmdOptions): Promise { const used = Math.min(session.promptsWithCx, session.prompts); const calls = `${session.cxCalls} call${session.cxCalls === 1 ? "" : "s"}`; console.log(dim(` this session: code-context used in ${used} of ${session.prompts} prompts (${calls})`)); + // Which door, and what the agent reached for first: the two numbers that + // say whether the tool surface steers as intended. + const byTool = countsLine(session.cxCallsByTool); + if (byTool) console.log(dim(` by tool: ${byTool}`)); + const first = countsLine(session.firstToolByPrompt); + if (first) console.log(dim(` first tool of a prompt: ${first}`)); } console.log(""); diff --git a/src/core/usage.ts b/src/core/usage.ts index 30aeba0..33f8899 100644 --- a/src/core/usage.ts +++ b/src/core/usage.ts @@ -220,6 +220,20 @@ export interface PromptStats { promptsWithCx: number; /** transient: has the current prompt already used code-context. */ curPromptUsedCx: boolean; + /** code-context invocations by tool (`find`, `search`, `sql`, `reindex`): + * which door the agent actually walks through. Absent on stats files + * written before it was recorded. */ + cxCallsByTool?: Record; + /** The first tool the agent called in each prompt, counted by name - a + * code-context tool by its short name, anything else (Grep, Read, Bash) + * by the name the hook delivered. This is the selection signal: whether a + * grep-shaped prompt opens with `find` or with Grep. Only the tools the + * PostToolUse hook is configured to forward are visible, so with the + * default `mcp__code-context.*` matcher it records code-context tools + * only; widen the matcher to see the rest. Absent on older stats files. */ + firstToolByPrompt?: Record; + /** transient: has the current prompt's first tool call been recorded. */ + curPromptFirstToolSeen?: boolean; } /** The shape Claude Code delivers to a hook command on stdin (subset we use). */ @@ -234,6 +248,21 @@ export interface HookPayload { * default server and any renamed variant (e.g. code-context-local). */ const isCodeContextTool = (name?: string): boolean => !!name && /^mcp__code[-_]?context/i.test(name); +/** The short tool name inside a code-context MCP tool id: + * `mcp__code-context__find` -> `find`, `mcp__code-context-dev__sql` -> `sql`. + * The server segment may carry a suffix, so the split is on the last `__`. */ +export function codeContextToolName(name: string): string { + const at = name.lastIndexOf("__"); + return at >= 0 ? name.slice(at + 2) : name; +} + +/** Add one to `counts[key]`, creating the map or the key as needed. */ +function bump(counts: Record | undefined, key: string): Record { + const out = counts ?? {}; + out[key] = (out[key] ?? 0) + 1; + return out; +} + const MAX_SESSIONS = 25; function loadPromptStats(indexDir: string): Record { @@ -258,10 +287,14 @@ function savePromptStats(indexDir: string, all: Record): vo } } -/** Fold one Claude Code hook event into the local counters. Best-effort. */ +/** Fold one Claude Code hook event into the local counters. Best-effort. + * Every PostToolUse event counts toward the first-tool-per-prompt tally + * (whatever tools the hook matcher forwards); only code-context's own tools + * count as invocations. */ export function recordHookEvent(indexDir: string, payload: HookPayload): void { const event = payload.hook_event_name ?? ""; - const tracked = event === "UserPromptSubmit" || (event === "PostToolUse" && isCodeContextTool(payload.tool_name)); + const isCx = event === "PostToolUse" && isCodeContextTool(payload.tool_name); + const tracked = event === "UserPromptSubmit" || (event === "PostToolUse" && !!payload.tool_name); if (!tracked) return; const sid = payload.session_id ?? "unknown"; @@ -273,11 +306,23 @@ export function recordHookEvent(indexDir: string, payload: HookPayload): void { if (event === "UserPromptSubmit") { s.prompts++; s.curPromptUsedCx = false; + s.curPromptFirstToolSeen = false; } else { - s.cxCalls++; - if (!s.curPromptUsedCx && s.prompts > 0) { - s.promptsWithCx++; - s.curPromptUsedCx = true; + const rawName = payload.tool_name ?? ""; + const label = isCx ? codeContextToolName(rawName) : rawName; + // The first tool of a prompt is the selection signal; a call that lands + // before any prompt was seen has no prompt to belong to and is not counted. + if (!s.curPromptFirstToolSeen && s.prompts > 0) { + s.firstToolByPrompt = bump(s.firstToolByPrompt, label); + s.curPromptFirstToolSeen = true; + } + if (isCx) { + s.cxCalls++; + s.cxCallsByTool = bump(s.cxCallsByTool, label); + if (!s.curPromptUsedCx && s.prompts > 0) { + s.promptsWithCx++; + s.curPromptUsedCx = true; + } } } s.lastAt = now; diff --git a/test/usage.test.ts b/test/usage.test.ts index cbecb17..c375414 100644 --- a/test/usage.test.ts +++ b/test/usage.test.ts @@ -15,6 +15,8 @@ import { usageLogPath, recordHookEvent, currentSessionStats, + codeContextToolName, + promptStatsPath, } from "../src/core/usage.js"; import type { SearchResult, SearchHit } from "../src/core/searcher.js"; @@ -198,4 +200,57 @@ describe("prompt telemetry (hooks)", () => { it("returns null when nothing is recorded", () => { expect(currentSessionStats(dir)).toBeNull(); }); + + it("counts code-context calls by tool and the first tool of each prompt", () => { + // Prompt 1 opens with find and also uses sql; prompt 2 opens with Grep + // (a non-code-context tool, forwarded by a widened matcher) and then + // reaches search; prompt 3 opens with find. Selection is the first call. + submit("s1"); + recordHookEvent(dir, { hook_event_name: "PostToolUse", session_id: "s1", tool_name: "mcp__code-context__find" }); + recordHookEvent(dir, { hook_event_name: "PostToolUse", session_id: "s1", tool_name: "mcp__code-context__sql" }); + submit("s1"); + otherCall("s1"); // Grep + cxCall("s1"); // search + submit("s1"); + recordHookEvent(dir, { hook_event_name: "PostToolUse", session_id: "s1", tool_name: "mcp__code-context__find" }); + const s = currentSessionStats(dir); + expect(s?.cxCallsByTool).toEqual({ find: 2, sql: 1, search: 1 }); + expect(s?.firstToolByPrompt).toEqual({ find: 2, Grep: 1 }); + // The non-code-context call is not an invocation, and the prompt it + // opened still counts as one that used code-context (search came later). + expect(s?.cxCalls).toBe(4); + expect(s?.promptsWithCx).toBe(3); + }); + + it("names a tool from a renamed server variant by its short name", () => { + submit("s1"); + recordHookEvent(dir, { hook_event_name: "PostToolUse", session_id: "s1", tool_name: "mcp__code-context-dev__reindex" }); + expect(currentSessionStats(dir)?.cxCallsByTool).toEqual({ reindex: 1 }); + expect(currentSessionStats(dir)?.firstToolByPrompt).toEqual({ reindex: 1 }); + }); + + it("loads a stats file written before the per-tool fields existed", () => { + writeFileSync( + promptStatsPath(dir), + JSON.stringify({ + old: { sessionId: "old", startedAt: "2026-01-01T00:00:00Z", lastAt: "2026-01-01T00:00:00Z", prompts: 2, cxCalls: 1, promptsWithCx: 1, curPromptUsedCx: false }, + }), + ); + // A new event on the old session adds the fields rather than tripping on their absence. + recordHookEvent(dir, { hook_event_name: "UserPromptSubmit", session_id: "old" }); + cxCall("old"); + const s = currentSessionStats(dir); + expect(s?.prompts).toBe(3); + expect(s?.cxCallsByTool).toEqual({ search: 1 }); + expect(s?.firstToolByPrompt).toEqual({ search: 1 }); + }); +}); + +describe("codeContextToolName", () => { + it("strips the server prefix however the server was named", () => { + expect(codeContextToolName("mcp__code-context__find")).toBe("find"); + expect(codeContextToolName("mcp__code-context-dev__sql")).toBe("sql"); + expect(codeContextToolName("mcp__code_context_local__search")).toBe("search"); + expect(codeContextToolName("Grep")).toBe("Grep"); + }); }); From 2cc087cc4fb4783a956eb98f5bab9c2e5b233a71 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 12:27:18 +0000 Subject: [PATCH 06/13] bench: point the MCP lanes at another build with CX_BENCH_CLI The lanes ran this checkout's dist/cli.js only, so two builds of the server could not be compared from one harness. CX_BENCH_CLI names another build and CX_BENCH_BUILD labels it; both land on every result row. --- bench/README.md | 5 +++++ bench/lanes.mjs | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bench/README.md b/bench/README.md index 3942ffa..bd78cfb 100644 --- a/bench/README.md +++ b/bench/README.md @@ -49,3 +49,8 @@ Lane design notes (they matter for fairness): - Token totals count input + cache writes + cache reads + output; cost uses the API's per-run accounting. - Model is set in `lanes.mjs` (`BENCH_MODEL`, default `claude-sonnet-4-6`). +- The MCP lanes run this checkout's `dist/cli.js`. To compare two builds of + the server (a tool-surface variant against main), point a run at another + build with `CX_BENCH_CLI=/path/to/other/dist/cli.js` and label it with + `CX_BENCH_BUILD=`; both land on every result row, so one + `questions.jsonl` can hold every variant. diff --git a/bench/lanes.mjs b/bench/lanes.mjs index 8243e82..f8515cf 100644 --- a/bench/lanes.mjs +++ b/bench/lanes.mjs @@ -8,7 +8,13 @@ import { query } from "@anthropic-ai/claude-agent-sdk"; export const BENCH = dirname(fileURLToPath(import.meta.url)); export const WORK = join(BENCH, ".work"); export const RESULTS = join(WORK, "results"); -export const CX = resolve(BENCH, "..", "dist", "cli.js"); +/** The server build the MCP lanes run: this checkout's `dist/cli.js`, or + * `CX_BENCH_CLI` to point the same harness at another build (a variant of the + * tool surface in a sibling worktree), so lanes differ in the server alone. */ +export const CX = process.env.CX_BENCH_CLI ? resolve(process.env.CX_BENCH_CLI) : resolve(BENCH, "..", "dist", "cli.js"); +/** A label for the build under test, recorded on every result so runs from + * different variants can be told apart in one results file. */ +export const BUILD = process.env.CX_BENCH_BUILD ?? null; export const MODEL = process.env.BENCH_MODEL ?? "claude-sonnet-4-6"; /** Lane options: identical hermetic base, only the toolset differs. @@ -82,6 +88,8 @@ export async function runLane({ lane, prompt, system, repoDir, indexDir, maxTurn return { lane, model: MODEL, + build: BUILD, + cli: CX, tokens, usage: u, costUsd, From d4be5a9ccc3f3d76d62ff20ec7efc7160fb41033 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 14:47:35 +0000 Subject: [PATCH 07/13] bench: compare builds, check citations, judge answers Three readers of the multi-build results file and one judge, so a tool- surface comparison is a report rather than a spreadsheet: - compare-builds.mjs: per set and build, the sum over questions of the median tokens, cost and calls, how often the first call was a code-context tool, and every run's first tool; CX_MD=1 for markdown, CX_DETAIL=1 for the per-question view with the tool mix. - cite-check.mjs: every path:line an answer cites must exist and be in bounds, and an identifier written beside it must appear within five lines; bare file names resolve against the repo or the answer's own full paths. - judge.mjs: the blind pairwise judge from the find run, as a script - claude-opus-5 with Read/Grep/Glob on the repo, both answers in random order, a winner, a confidence and unsupported-claim counts per pair, appended to .work/results/judge.jsonl. JUDGE_LIMIT caps a smoke run. - judge-report.mjs: wins, ties, unsupported claims and median confidence per set for each judged pair, latest verdict per pair. Builds are selected by their CX_BENCH_BUILD label or a since..until window for rows recorded before the label existed. --- bench/README.md | 16 ++++ bench/cite-check.mjs | 143 ++++++++++++++++++++++++++++++ bench/compare-builds.mjs | 155 ++++++++++++++++++++++++++++++++ bench/judge-report.mjs | 42 +++++++++ bench/judge.mjs | 186 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 542 insertions(+) create mode 100644 bench/cite-check.mjs create mode 100644 bench/compare-builds.mjs create mode 100644 bench/judge-report.mjs create mode 100644 bench/judge.mjs diff --git a/bench/README.md b/bench/README.md index bd78cfb..a95b52b 100644 --- a/bench/README.md +++ b/bench/README.md @@ -54,3 +54,19 @@ Lane design notes (they matter for fairness): build with `CX_BENCH_CLI=/path/to/other/dist/cli.js` and label it with `CX_BENCH_BUILD=`; both land on every result row, so one `questions.jsonl` can hold every variant. + +Reading a multi-build results file (all default to `.work/results/questions.jsonl`; +a build is its `CX_BENCH_BUILD` label, or `since..until` ISO timestamps for rows +recorded before the label existed): + +```bash +node compare-builds.mjs "" V0,V3 # per set: tokens, cost, calls, first tool; CX_MD=1 for markdown, CX_DETAIL=1 per question +node cite-check.mjs /path/to/repo "" V0,V3 # every cited path:line exists, is in bounds, and names an identifier found nearby +node judge.mjs /path/to/repo V0 V3 # blind pairwise judge (claude-opus-5, Read/Grep/Glob on the repo) -> .work/results/judge.jsonl +node judge-report.mjs # wins, ties, unsupported claims and confidence per set for each judged pair +``` + +The judge sees both answers in random order and returns a winner, a +confidence, and how many claims each answer makes that the code does not +support; `JUDGE_LIMIT=n` caps the pairs for a smoke run. Judging costs about +a quarter of a dollar per pair. diff --git a/bench/cite-check.mjs b/bench/cite-check.mjs new file mode 100644 index 0000000..61395fa --- /dev/null +++ b/bench/cite-check.mjs @@ -0,0 +1,143 @@ +// Mechanical citation check over the answers in a results file: every +// `path:line` or `path:start-end` an answer cites must name a file in the repo +// with the range inside it, and an identifier written beside the citation +// (in backticks, within the same sentence) must appear within a few lines of +// the cited range. Reports per build so variants can be compared. +// +// Usage: node cite-check.mjs [results=questions.jsonl] [builds] +// builds comma-separated build labels, or `since..until` windows for rows +// without a label; default: every build in the file (plus `V0` for +// unlabelled rows when CX_V0_WINDOW=since..until is set) +import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { RESULTS } from "./lanes.mjs"; + +const [repoArg, resultsArg, buildsArg] = process.argv.slice(2); +if (!repoArg) { + console.error("usage: node cite-check.mjs [results.jsonl] [builds]"); + process.exit(1); +} +const repoDir = resolve(repoArg); +const resultsFile = resultsArg ? resolve(resultsArg) : join(RESULTS, "questions.jsonl"); + +/** How far from the cited range an identifier may sit and still count as + * anchored: a citation to a signature line often names the body's symbol. */ +const ANCHOR_SLACK_LINES = 5; +/** Characters of answer text before a citation searched for its identifier. */ +const CITATION_CONTEXT_CHARS = 160; + +const CITATION = /(? JSON.parse(l)) + .filter((r) => r.lane === "combo" && !r.error && r.answer); + +function selector(spec) { + const m = /^(\S+)\.\.(\S+)$/.exec(spec); + if (m) return (r) => !r.build && r.ts >= m[1] && r.ts < m[2]; + return (r) => r.build === spec; +} +let builds; +if (buildsArg) builds = buildsArg.split(","); +else { + builds = [...new Set(rows.map((r) => r.build).filter(Boolean))]; + if (process.env.CX_V0_WINDOW) builds.unshift(process.env.CX_V0_WINDOW); +} + +const fileLines = new Map(); +function linesOf(path) { + if (!fileLines.has(path)) { + const full = join(repoDir, path); + fileLines.set(path, existsSync(full) ? readFileSync(full, "utf8").split("\n") : null); + } + return fileLines.get(path); +} + +/** Every file in the repo by basename, for citations written as a bare file + * name (`mod.rs:842` in a table whose full path stands nearby). Built once. */ +const SKIP_DIRS = new Set([".git", ".infino", "target", "node_modules"]); +const byBasename = new Map(); +(function walk(rel) { + for (const entry of readdirSync(join(repoDir, rel), { withFileTypes: true })) { + if (entry.isDirectory()) { + if (!SKIP_DIRS.has(entry.name)) walk(rel ? `${rel}/${entry.name}` : entry.name); + } else { + const list = byBasename.get(entry.name) ?? []; + list.push(rel ? `${rel}/${entry.name}` : entry.name); + byBasename.set(entry.name, list); + } + } +})(""); + +/** Resolve a cited path: as written when it has a directory; a bare basename + * to the one repo file of that name, or to the full path the same answer + * writes for it elsewhere; null when neither settles it. */ +function resolveCited(path, answer) { + const clean = path.replace(/^\.\//, ""); + if (existsSync(join(repoDir, clean))) return clean; + // A short form: a bare basename, or a trailing part of the path + // (`manifest/mod.rs`). Resolve against the repo's files of that basename. + const base = clean.slice(clean.lastIndexOf("/") + 1); + const candidates = (byBasename.get(base) ?? []).filter((c) => c === clean || c.endsWith(`/${clean}`)); + if (candidates.length === 1) return candidates[0]; + const inAnswer = candidates.filter((c) => answer.includes(c)); + return inAnswer.length === 1 ? inAnswer[0] : null; +} + +function check(answer) { + const out = { citations: 0, missingFile: 0, outOfBounds: 0, anchored: 0, anchoredOk: 0, examples: [] }; + for (const m of answer.matchAll(CITATION)) { + const [, path, s, e] = m; + const start = Number(s); + const end = e ? Number(e) : start; + out.citations++; + const resolved = resolveCited(path, answer); + const lines = resolved ? linesOf(resolved) : null; + if (!lines) { + out.missingFile++; + // A name the repo has more than once with no full path nearby is + // unresolved rather than absent; both count against the answer. + const known = byBasename.has(path.slice(path.lastIndexOf("/") + 1)); + if (out.examples.length < 3) out.examples.push(`${known ? "unresolved" : "missing"} ${path}`); + continue; + } + if (start < 1 || end > lines.length || end < start) { + out.outOfBounds++; + if (out.examples.length < 3) out.examples.push(`out of bounds ${path}:${s}${e ? "-" + e : ""} (${lines.length} lines)`); + continue; + } + // The identifier named beside the citation: the nearest backticked name + // in the preceding sentence fragment. + const before = answer.slice(Math.max(0, m.index - CITATION_CONTEXT_CHARS), m.index); + const frag = before.slice(Math.max(before.lastIndexOf(". "), before.lastIndexOf("\n")) + 1); + const ids = [...frag.matchAll(IDENTIFIER)].map((x) => x[1]).filter((id) => !id.includes("/") && !/\.[a-z]{1,6}$/.test(id)); + if (ids.length === 0) continue; + const id = ids[ids.length - 1].split("::").pop().split(".").pop().replace(/[()#]/g, ""); + out.anchored++; + const lo = Math.max(0, start - 1 - ANCHOR_SLACK_LINES); + const hi = Math.min(lines.length, end + ANCHOR_SLACK_LINES); + if (lines.slice(lo, hi).some((l) => l.includes(id))) out.anchoredOk++; + else if (out.examples.length < 3) out.examples.push(`unanchored ${id} at ${path}:${s}`); + } + return out; +} + +console.log("build runs citations per answer missing file out of bounds anchored ok / anchored"); +for (const spec of builds) { + const rs = rows.filter(selector(spec)); + const tot = { citations: 0, missingFile: 0, outOfBounds: 0, anchored: 0, anchoredOk: 0 }; + const examples = []; + for (const r of rs) { + const c = check(r.answer); + for (const k of Object.keys(tot)) tot[k] += c[k]; + for (const ex of c.examples) if (examples.length < 6) examples.push(`${r.cat} Q${r.q}: ${ex}`); + } + const label = spec.includes("..") ? "V0" : spec; + console.log( + `${label.padEnd(15)} ${String(rs.length).padStart(4)} ${String(tot.citations).padStart(9)} ${(rs.length ? tot.citations / rs.length : 0).toFixed(1).padStart(10)} ${String(tot.missingFile).padStart(12)} ${String(tot.outOfBounds).padStart(13)} ${`${tot.anchoredOk} / ${tot.anchored}`.padStart(22)}`, + ); + for (const ex of examples) console.log(` ${ex}`); +} diff --git a/bench/compare-builds.mjs b/bench/compare-builds.mjs new file mode 100644 index 0000000..7e1eab0 --- /dev/null +++ b/bench/compare-builds.mjs @@ -0,0 +1,155 @@ +// Compare server builds recorded in one results file, the tool-surface +// report: per category and build, the sum over questions of the median (over +// repeats) tokens, cost and tool calls; how often a code-context tool was the +// first call and was used at all; and the first tool of every run by name. +// +// Usage: node compare-builds.mjs [results=questions.jsonl] [builds] [lane=combo] +// builds comma-separated build labels as recorded on the rows (`build`), or +// `since..until` ISO windows for rows written before the label +// existed (they print as the window). Default: every label in the +// file, with CX_V0_WINDOW=since..until prepended when set. +import { readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { RESULTS } from "./lanes.mjs"; + +const [resultsArg, buildsArg, laneWanted = "combo"] = process.argv.slice(2); +const resultsFile = resultsArg ? resolve(resultsArg) : join(RESULTS, "questions.jsonl"); +const all = readFileSync(resultsFile, "utf8") + .split("\n") + .filter(Boolean) + .map((l) => JSON.parse(l)) + .filter((r) => r.lane === laneWanted); + +function selector(spec) { + const m = /^(\S+)\.\.(\S+)$/.exec(spec); + if (m) return (r) => !r.build && r.ts >= m[1] && r.ts < m[2]; + return (r) => r.build === spec; +} +let builds; +if (buildsArg) builds = buildsArg.split(","); +else { + builds = [...new Set(all.map((r) => r.build).filter(Boolean))]; + if (process.env.CX_V0_WINDOW) builds.unshift(process.env.CX_V0_WINDOW); +} +const label = (spec) => (spec.includes("..") ? "V0" : spec); + +const median = (xs) => { + const s = [...xs].sort((a, b) => a - b); + const m = Math.floor(s.length / 2); + return s.length === 0 ? 0 : s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; +}; +const k = (n) => (n >= 1000 ? `${(n / 1000).toFixed(0)}k` : String(Math.round(n))); + +/** Per-category sums of per-question medians, plus selection counts. */ +function summarize(rows) { + const byQ = new Map(); + for (const r of rows) { + const key = `${r.cat} ${r.q}`; + if (!byQ.has(key)) byQ.set(key, { cat: r.cat, runs: [] }); + byQ.get(key).runs.push(r); + } + const cats = {}; + for (const g of byQ.values()) { + const c = (cats[g.cat] ??= { tok: 0, cost: 0, calls: 0, runs: 0, cxFirst: 0, cxAny: 0, first: {}, errors: 0 }); + c.tok += median(g.runs.map((r) => r.tokens)); + c.cost += median(g.runs.map((r) => r.costUsd ?? 0)); + c.calls += median(g.runs.map((r) => r.calls)); + for (const r of g.runs) { + c.runs++; + if (r.error) c.errors++; + const f = r.toolCalls?.[0] ?? "(none)"; + c.first[f] = (c.first[f] ?? 0) + 1; + if (f.startsWith("cx:")) c.cxFirst++; + if ((r.toolCalls ?? []).some((t) => t.startsWith("cx:"))) c.cxAny++; + // Every call by tool name over the category's runs: where a build's + // round-trips went (a tiered result that costs Reads shows up here). + c.mix ??= {}; + for (const t of r.toolCalls ?? []) c.mix[t] = (c.mix[t] ?? 0) + 1; + } + } + return cats; +} + +const summaries = builds.map((spec) => ({ spec, cats: summarize(all.filter(selector(spec))) })); +const catOrder = [...new Set(summaries.flatMap((s) => Object.keys(s.cats)))]; +const fmtFirst = (o) => Object.entries(o).sort((a, b) => b[1] - a[1]).map(([t, n]) => `${t} ${n}`).join(", "); + +console.log(`lane=${laneWanted} builds: ${summaries.map((s) => `${label(s.spec)}=${all.filter(selector(s.spec)).length} runs`).join(", ")}`); +console.log(""); +console.log("category build tokens cost calls cx-first cx-any errors first tool of each run"); +for (const cat of catOrder) { + for (const s of summaries) { + const c = s.cats[cat]; + if (!c) continue; + console.log( + `${cat.padEnd(15)} ${label(s.spec).padEnd(5)} ${k(c.tok).padStart(7)} ${("$" + c.cost.toFixed(2)).padStart(5)} ${String(c.calls).padStart(5)} ${`${c.cxFirst}/${c.runs}`.padStart(8)} ${`${c.cxAny}/${c.runs}`.padStart(6)} ${String(c.errors).padStart(6)} ${fmtFirst(c.first)}`, + ); + } + console.log(""); +} +console.log("build tokens cost calls cx-first runs errors"); +for (const s of summaries) { + const cs = Object.values(s.cats); + const sum = (f) => cs.reduce((a, c) => a + f(c), 0); + console.log( + `${label(s.spec).padEnd(5)} ${k(sum((c) => c.tok)).padStart(7)} ${("$" + sum((c) => c.cost).toFixed(2)).padStart(5)} ${String(sum((c) => c.calls)).padStart(5)} ${`${sum((c) => c.cxFirst)}/${sum((c) => c.runs)}`.padStart(8)} ${String(sum((c) => c.runs)).padStart(4)} ${String(sum((c) => c.errors)).padStart(6)}`, + ); +} + +// CX_MD=1 prints the same summary as markdown tables for docs/benchmark.md: +// one per category with a row per build, plus the blended table. "Right +// first" counts runs whose first call was the tool the question shape is for. +const INTENDED = { + aggregation: (t) => t === "cx:sql", + comprehension: (t) => t === "cx:search" || t === "cx:context", + "by-meaning": (t) => t === "cx:search" || t === "cx:context", + pinpoint: (t) => t === "cx:find", + "known-file": (t) => t === "Read", +}; +if (process.env.CX_MD) { + const rightFirst = (cat, first) => + Object.entries(first).reduce((n, [t, c]) => n + ((INTENDED[cat] ?? (() => false))(t) ? c : 0), 0); + for (const cat of catOrder) { + console.log(`\n**${cat}**\n`); + console.log("| build | tokens | cost | calls | right first | first tool of each run |"); + console.log("|---|---|---|---|---|---|"); + for (const s of summaries) { + const c = s.cats[cat]; + if (!c) continue; + console.log(`| ${label(s.spec)} | ${k(c.tok)} | $${c.cost.toFixed(2)} | ${c.calls} | ${rightFirst(cat, c.first)}/${c.runs} | ${fmtFirst(c.first)} |`); + } + } + console.log(`\n**blended**\n`); + console.log("| build | tokens | cost | calls | right first |"); + console.log("|---|---|---|---|---|"); + for (const s of summaries) { + const cs = Object.entries(s.cats); + const sum = (f) => cs.reduce((a, [, c]) => a + f(c), 0); + const right = cs.reduce((a, [cat, c]) => a + rightFirst(cat, c.first), 0); + console.log(`| ${label(s.spec)} | ${k(sum((c) => c.tok))} | $${sum((c) => c.cost).toFixed(2)} | ${sum((c) => c.calls)} | ${right}/${sum((c) => c.runs)} |`); + } +} + +// CX_DETAIL=1 adds the per-question view: median tokens and calls per build, +// and the tool sequence of every run, so a category-level move can be traced +// to the questions that carry it. +if (process.env.CX_DETAIL) { + const cx = (calls) => calls.filter((t) => t.startsWith("cx:")).map((t) => t.slice(3)).join("+") || "-"; + for (const cat of catOrder) { + console.log(`\n=== ${cat} ===`); + for (const s of summaries) { + const c = s.cats[cat]; + if (c) console.log(`${label(s.spec).padEnd(8)} all calls: ${fmtFirst(c.mix ?? {})}`); + } + const qs = [...new Set(all.filter((r) => r.cat === cat).map((r) => r.q))].sort((a, b) => a - b); + for (const q of qs) { + for (const s of summaries) { + const runs = all.filter(selector(s.spec)).filter((r) => r.cat === cat && r.q === q); + if (runs.length === 0) continue; + console.log( + `Q${String(q).padEnd(2)} ${label(s.spec).padEnd(8)} ${k(median(runs.map((r) => r.tokens))).padStart(6)} tok ${String(median(runs.map((r) => r.calls))).padStart(4)} calls first: ${runs.map((r) => r.toolCalls?.[0] ?? "(none)").join(", ").padEnd(36)} cx: ${runs.map((r) => cx(r.toolCalls ?? [])).join(" | ")}`, + ); + } + } + } +} diff --git a/bench/judge-report.mjs b/bench/judge-report.mjs new file mode 100644 index 0000000..0f5e135 --- /dev/null +++ b/bench/judge-report.mjs @@ -0,0 +1,42 @@ +// Summarize the judge's verdicts (bench/.work/results/judge.jsonl) per +// baseline/candidate pair and category: pairs, wins each way, ties, the +// unsupported-claim totals, and the median confidence. A pair judged more than +// once (a smoke run before the full one) counts its latest verdict only. +// Usage: node judge-report.mjs [judge.jsonl] +import { readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { RESULTS } from "./lanes.mjs"; + +const file = process.argv[2] ? resolve(process.argv[2]) : join(RESULTS, "judge.jsonl"); +const latest = new Map(); +for (const line of readFileSync(file, "utf8").split("\n").filter(Boolean)) { + const v = JSON.parse(line); + latest.set(`${v.baseline} ${v.candidate} ${v.cat} ${v.q} ${v.rep}`, v); +} +const verdicts = [...latest.values()]; + +const median = (xs) => { + const s = [...xs].sort((a, b) => a - b); + const m = Math.floor(s.length / 2); + return s.length === 0 ? 0 : s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; +}; +const label = (spec) => (spec.includes("..") ? "V0" : spec); + +const comparisons = [...new Set(verdicts.map((v) => `${v.baseline}\t${v.candidate}`))]; +for (const cmp of comparisons) { + const [baseline, candidate] = cmp.split("\t"); + const vs = verdicts.filter((v) => v.baseline === baseline && v.candidate === candidate); + const failed = vs.filter((v) => !v.winner).length; + console.log(`${label(baseline)} vs ${label(candidate)} judge=${vs[0]?.judge} pairs=${vs.length}${failed ? ` no verdict: ${failed}` : ""} cost $${vs.reduce((a, v) => a + (v.costUsd ?? 0), 0).toFixed(2)}`); + console.log(`category pairs ${label(baseline).padStart(8)} ${label(candidate).padStart(9)} ties unsupported ${label(baseline)} / ${label(candidate)} conf(med)`); + const cats = [...new Set(vs.map((v) => v.cat))]; + for (const cat of [...cats, "all"]) { + const rows = vs.filter((v) => (cat === "all" || v.cat === cat) && v.winner); + const n = (w) => rows.filter((v) => v.winner === w).length; + const sum = (f) => rows.reduce((a, v) => a + (f(v) ?? 0), 0); + console.log( + `${cat.padEnd(15)} ${String(rows.length).padStart(5)} ${String(n("baseline")).padStart(8)} ${String(n("candidate")).padStart(9)} ${String(n("tie")).padStart(4)} ${`${sum((v) => v.unsupportedBaseline)} / ${sum((v) => v.unsupportedCandidate)}`.padStart(22)} ${median(rows.map((v) => v.confidence ?? 0)).toFixed(2).padStart(9)}`, + ); + } + console.log(""); +} diff --git a/bench/judge.mjs b/bench/judge.mjs new file mode 100644 index 0000000..b10b64b --- /dev/null +++ b/bench/judge.mjs @@ -0,0 +1,186 @@ +// Blind pairwise judge over two builds' answers in a results file: for every +// question and repeat, a stronger model with Read/Grep/Glob on the repo sees +// both answers in random order, checks their claims against the code, and +// returns a winner, a confidence, and how many claims each answer makes that +// the code does not support. Verdicts append to bench/.work/results/judge.jsonl. +// +// Usage: node judge.mjs [results=questions.jsonl] [cats] +// baseline / candidate build labels as recorded on the rows (`build`), or a +// `since..until` ISO window for rows written before the +// label existed (e.g. 2026-09-04T12:05:36Z..2026-09-04T12:30:00Z) +// cats comma-separated categories to judge (default all) +// Model: JUDGE_MODEL (default claude-opus-5). Concurrency: CX_BENCH_CONCURRENCY. +import { readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { query } from "@anthropic-ai/claude-agent-sdk"; +import { RESULTS, record } from "./lanes.mjs"; + +const [repoArg, baselineArg, candidateArg, resultsArg, catsArg] = process.argv.slice(2); +if (!repoArg || !baselineArg || !candidateArg) { + console.error("usage: node judge.mjs [results.jsonl] [cats]"); + process.exit(1); +} +const repoDir = resolve(repoArg); +const resultsFile = resultsArg ? resolve(resultsArg) : join(RESULTS, "questions.jsonl"); +const cats = catsArg ? new Set(catsArg.split(",")) : null; +const JUDGE_MODEL = process.env.JUDGE_MODEL ?? "claude-opus-5"; +const CONC = Number(process.env.CX_BENCH_CONCURRENCY ?? 4); + +/** Rows of one build: by label, or by a `since..until` timestamp window. */ +function selector(spec) { + const m = /^(\S+)\.\.(\S+)$/.exec(spec); + if (m) return (r) => !r.build && r.ts >= m[1] && r.ts < m[2]; + return (r) => r.build === spec; +} + +const rows = readFileSync(resultsFile, "utf8") + .split("\n") + .filter(Boolean) + .map((l) => JSON.parse(l)) + .filter((r) => r.lane === "combo" && !r.error && r.answer && (!cats || cats.has(r.cat))); + +/** Runs of one build grouped by question, in the order they were recorded. */ +function byQuestion(pick) { + const out = new Map(); + for (const r of rows.filter(pick)) { + const key = `${r.cat} ${r.q}`; + if (!out.has(key)) out.set(key, { cat: r.cat, q: r.q, text: r.question, runs: [] }); + out.get(key).runs.push(r); + } + return out; +} + +const base = byQuestion(selector(baselineArg)); +const cand = byQuestion(selector(candidateArg)); + +// Question text comes from the question files; the result rows carry only the +// index. Load every shipped set once and look the text up by (cat, q). +const questionText = new Map(); +for (const file of ["infino.json", "infino-pinpoint.json", "infino-known-file.json", "infino-by-meaning.json"]) { + const items = JSON.parse(readFileSync(join(RESULTS, "..", "..", "questions", file), "utf8")); + items.forEach((item, i) => questionText.set(`${item.cat} ${i + 1}`, item.q)); +} + +const pairs = []; +for (const [key, b] of base) { + const c = cand.get(key); + if (!c) continue; + const n = Math.min(b.runs.length, c.runs.length); + for (let i = 0; i < n; i++) { + pairs.push({ cat: b.cat, q: b.q, rep: i + 1, question: questionText.get(key) ?? "", base: b.runs[i], cand: c.runs[i] }); + } +} +// JUDGE_LIMIT caps the pairs, for a smoke run before spending on the whole set. +if (process.env.JUDGE_LIMIT) pairs.length = Math.min(pairs.length, Number(process.env.JUDGE_LIMIT)); +console.log(`judge=${JUDGE_MODEL} baseline=${baselineArg} candidate=${candidateArg} pairs=${pairs.length}`); + +const system = + `You are judging two answers to a question about the repository checked out at ${repoDir}. ` + + `Use Read, Grep and Glob on that checkout to verify what each answer claims (file paths, line numbers, ` + + `identifiers, counts, behaviour). Judge correctness and how well each claim is supported by the code; ` + + `do not reward length or formatting. Finish with a single JSON object and nothing after it: ` + + `{"winner":"A"|"B"|"tie","confidence":<0..1>,"unsupported_a":,"unsupported_b":,"reason":""} ` + + `where unsupported_* counts the claims in that answer the code does not support.`; + +function parseVerdict(text) { + const start = text.lastIndexOf("{"); + const end = text.lastIndexOf("}"); + if (start < 0 || end < start) return null; + try { + return JSON.parse(text.slice(start, end + 1)); + } catch { + return null; + } +} + +async function judge(pair) { + const swap = Math.random() < 0.5; + const A = swap ? pair.cand : pair.base; + const B = swap ? pair.base : pair.cand; + const prompt = + `Question:\n${pair.question}\n\n=== Answer A ===\n${A.answer}\n\n=== Answer B ===\n${B.answer}\n\n` + + `Verify the claims against the repository, then give the JSON verdict.`; + const t0 = performance.now(); + let text = ""; + let costUsd = null; + let usage = null; + let error = null; + try { + for await (const m of query({ + prompt, + options: { + model: JUDGE_MODEL, + maxTurns: 30, + systemPrompt: system, + permissionMode: "bypassPermissions", + env: { ...process.env, IS_SANDBOX: "1" }, + cwd: repoDir, + settingSources: [], + strictMcpConfig: true, + tools: ["Read", "Grep", "Glob"], + }, + })) { + if (m.type === "result") { + usage = m.usage ?? null; + costUsd = m.total_cost_usd ?? null; + if (m.result) text = m.result; + } + } + } catch (err) { + error = String(err?.message ?? err).slice(0, 300); + } + const v = parseVerdict(text); + const toSide = (w) => (w === "tie" ? "tie" : (w === "A") === !swap ? "baseline" : "candidate"); + const u = usage ?? {}; + return { + cat: pair.cat, + q: pair.q, + rep: pair.rep, + baseline: baselineArg, + candidate: candidateArg, + judge: JUDGE_MODEL, + winner: v ? toSide(v.winner) : null, + confidence: v?.confidence ?? null, + unsupportedBaseline: v ? (swap ? v.unsupported_b : v.unsupported_a) : null, + unsupportedCandidate: v ? (swap ? v.unsupported_a : v.unsupported_b) : null, + reason: v?.reason ?? null, + swapped: swap, + costUsd, + tokens: (u.input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0) + (u.output_tokens ?? 0), + wallMs: Math.round(performance.now() - t0), + error: error ?? (v ? null : `no verdict in: ${text.slice(-200)}`), + ts: new Date().toISOString(), + }; +} + +const verdicts = []; +let cursor = 0; +async function worker() { + while (cursor < pairs.length) { + const p = pairs[cursor++]; + const v = await judge(p); + record("judge.jsonl", v); + verdicts.push(v); + console.log( + `(${verdicts.length}/${pairs.length}) ${p.cat} Q${p.q} r${p.rep}: ${v.winner ?? "ERR"} ${v.confidence ?? ""} ` + + `unsupported ${v.unsupportedBaseline ?? "?"}/${v.unsupportedCandidate ?? "?"} $${(v.costUsd ?? 0).toFixed(2)}${v.error ? " ERR " + v.error : ""}`, + ); + } +} +await Promise.all(Array.from({ length: CONC }, () => worker())); + +const median = (xs) => { + const s = [...xs].sort((a, b) => a - b); + const m = Math.floor(s.length / 2); + return s.length === 0 ? 0 : s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; +}; +console.log("\ncategory pairs baseline candidate ties unsupported b/c conf(med) cost"); +const catsSeen = [...new Set(verdicts.map((v) => v.cat))]; +for (const cat of [...catsSeen, "all"]) { + const vs = verdicts.filter((v) => (cat === "all" || v.cat === cat) && v.winner); + const n = (w) => vs.filter((v) => v.winner === w).length; + const sum = (f) => vs.reduce((a, v) => a + (f(v) ?? 0), 0); + console.log( + `${cat.padEnd(15)} ${String(vs.length).padStart(5)} ${String(n("baseline")).padStart(8)} ${String(n("candidate")).padStart(9)} ${String(n("tie")).padStart(4)} ${`${sum((v) => v.unsupportedBaseline)} / ${sum((v) => v.unsupportedCandidate)}`.padStart(15)} ${median(vs.map((v) => v.confidence ?? 0)).toFixed(2).padStart(9)} $${sum((v) => v.costUsd).toFixed(2)}`, + ); +} From 63fe5383e18b3a972d6264f9c32c4fbc41dd14fa Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 16:12:48 +0000 Subject: [PATCH 08/13] docs: the tool-surface run - names, shapes and prose, measured Plan 101's matrix over the code-context MCP surface: V0 (the surface after find) through V8, one lever at a time, on Sonnet 4.6 with 108 runs per build, second passes of V0 and V3 for the spread, Haiku 4.5 two passes per build, and the Opus 5 blind judge against V0. What it found: the routing prose was not doing the steering (sql 30/30, search 18/18 on every build); the receipt sentence was - dropping it flipped two lookups from find to Grep (12/12 vs 15/15) because it said what the result reports, and naming the field without the request brings them back; the rename to context moved nothing; the tiered search result buys dollars with round trips and answers no better; Haiku needs the answer-from-a-hit sentence in the search description; reindex was called only by Haiku and only where it hurt. V8 is the shipping candidate: 17-20% fewer tokens than V0 on Sonnet at the same round trips, code-context first on 97 of 108 runs. The V8 judge row lands when its run finishes. --- docs/benchmark.md | 247 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/docs/benchmark.md b/docs/benchmark.md index ab01045..054bc81 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -216,6 +216,253 @@ auto-sync exists for exactly that; the bench disables it to keep the index identical across runs, so it must pin the tree instead. Spend for the two lanes: about $14 of agent runs and $14 of judging. +## The tool surface: names, shapes, and prose + +The `find` run above showed an agent choosing tools on nothing but a name, a +description, and a result shape. This run measures how much each of those +steers, one lever at a time, and what the steering costs per turn. Measured +2026-09-04; the design and the decisions are in plan 101. + +**Setup.** Same pinned clone (infino @ `ed4e020`, 401 files, 5,527 chunks, +indexed once, auto-sync off), same hermetic lane (stock file tools including +Grep, plus the code-context build under test), `claude-sonnet-4-6`, fresh +conversation per question. Four question sets, 36 questions: the shipped 16 +(10 aggregation, 6 comprehension), the 8 pinpoint lookups, 6 *known-file* +questions whose right first call is Read ("what does `Cargo.toml` pin for +arrow"), and 6 *by-meaning* questions that name no identifier ("which code +decides when a superfile is compacted"). Three repeats, so 108 runs per +build; V0 and V3 were run twice over to measure the spread directly. Haiku +4.5 ran V0, V3, V6, V7 and V8, two passes each. Quality: the same blind pairwise +judge as the `find` run (`claude-opus-5` with Read/Grep/Glob on the clone; +winner, confidence, unsupported-claim counts), each variant against V0, and +the mechanical citation check. Selection is the primary metric: the first +tool called, against the tool the question shape is for. + +The variants, each one change on the last, with the prose the model pays for +on every turn (chars / 4; JSON schema framing comes on top): + +| build | change | tool text | instructions | +|---|---|---|---| +| V0 | the surface after `find` (`byFile`, limit 500) | ~1,280 tok | ~545 tok | +| V1 | descriptions cut to question shape, not-for, result shape; instructions cut to a routing table | ~790 | ~175 | +| V2 | V1 without the "show the usage line" sentence in three descriptions | ~650 | ~160 | +| V3 | V2 with `reindex` off the tool list | ~570 | ~160 | +| V4a | V3 with `search` renamed `context` | ~575 | ~160 | +| V5 | V3 with a tiered `search` result: content on the top 3 hits, a one-line excerpt below | ~590 | ~170 | +| V6 | V3 plus two sentences: answer from a hit without re-reading (in the instructions), and what `lang` holds | ~580 | ~185 | +| V7 | V6 with the answer-from-a-hit sentence in the `search` description as well | ~610 | ~185 | +| V8 | V7 with one sentence per description saying what the `usage` receipt reports, no request to show it | ~680 | ~185 | + +### Selection: the prose was not doing the steering + +Every build, every pass: `sql` first on 30 of 30 aggregation runs, `search` +first on 18 of 18 comprehension and 18 of 18 by-meaning runs. Cutting the +descriptions to a third and the instructions to a quarter moved none of it. +The known-file tripwire did not move either: Read was the first call on 3 +to 5 of 18 runs on every build, `find` on 6 to 13, Glob on the rest. + +Pinpoint moved, and how it moved is the finding. `find` was the first call +on 19 and 18 of 24 runs in the two V0 passes and on 14 and 15 of 24 in the +two V3 passes. Two questions account for it: the env-var inventory ("every +`std::env::var` read, with name and file:line") and the per-file test +count. The inventory opened with `find` in 6 of 6 V0 runs and 3 of 3 V1 +runs, then with Grep in 9 of 9 runs under V2, V3 and V5 - and back to +`find` in 3 of 3 under V4a, whose only difference from V3 is the *other* +tool's name. A six-pass A/B on the pinpoint set alone pinned it: V1 opened +the inventory with `find` 6 of 6 and the test count with `find` or `sql` 6 +of 6; V2, which differs from V1 only by the dropped receipt sentence ("the +result includes a 'usage' field - a one-line receipt (tokens returned, +matches/files, session total); after you answer, show it verbatim"), opened +them with Grep 6 of 6 and 5 of 6. Across everything run, that question is +`find` 12 of 12 with the sentence and Grep 15 of 15 without it. Two +lessons. A borderline question gets a stable choice for a given prompt text +and flips on wording that has nothing to do with it, so a 3-of-3 flip on +one question is not a signal; set totals are. And a sentence written to +make the model show a receipt was steering its tool choice. + +The cost of the flip is real where it lands: the Grep runs on that question +took 15 to 24 calls and 135k to 192k tokens against 2 to 5 calls and 32k to +96k for the `find` runs, for the same inventory. + +Which half of the sentence steered? V8 keeps a description of the field +("the result includes a 'usage' field, a one-line receipt of tokens +returned, matches and files") and drops the request to show it. Six +pinpoint passes: the inventory back to `find` 5 of 6, the test count to +`sql` 4 of 6, 39 of 48 first calls on a code-context tool against V1's 42 +and V2's 31, at the lowest tokens of the three (191k against 232k and +302k). Telling the model what the result reports is what steers; asking it +to relay the receipt was never the active part. + +### Cost: a tenth off, reproducibly + +Sums over questions of the per-question median; two passes each of V0 and V3: + +| set | V0 | V0 again | V3 | V3 again | +|---|---|---|---|---| +| aggregation (10) | 138k | 146k | 157k | 146k | +| comprehension (6) | 746k | 774k | 669k | 738k | +| pinpoint (8) | 227k | 205k | 258k | 243k | +| known-file (6) | 131k | 131k | 113k | 107k | +| by-meaning (6) | 524k | 586k | 380k | 383k | +| **blended (36)** | **1,766k** | **1,842k** | **1,576k** | **1,617k** | +| cost per pass | $3.03 | $3.23 | $2.94 | $2.95 | +| tool calls | 103 | 105 | 119 | 124 | + +Pass-to-pass spread on one build is 4 to 12% per set, so: by-meaning down +27 to 35% is real, blended down 10 to 12% is real, comprehension down 3 to +10% is at the edge, and aggregation up 6 to 14% is real and has a cause. +On the two aggregation questions that ask for "reasons", every trimmed +build followed the `sql` call with a `search` (9 of 9 runs against 1 of 6 +under V0), and the "largest Rust files" question paid a three-call detour +(`lang = 'rust'`, no rows, list the languages, retry with `rs`) that nothing +in the description prevented. The judge rewarded the extra `search` (below); +V6 fixes the detour with eight words. + +Single-call questions show the per-turn saving directly: an aggregation +question answered in one `sql` call costs 13k tokens under V0 and 11k under +V3, the 1.5k-token prompt difference, every turn of every conversation. + +### Quality: better, not just level + +Blind judge, each variant against V0, same question and repeat paired: + +| variant | pairs | V0 wins | variant wins | ties | unsupported claims V0 / variant | median confidence | +|---|---|---|---|---|---|---| +| V1 | 107 | 39 | 46 | 22 | 185 / 151 | 0.70 | +| V3 | 108 | 31 | 55 | 22 | 283 / 192 | 0.72 | +| V5 | 107 | 35 | 47 | 25 | 243 / 201 | 0.72 | + +V3 wins on every set (aggregation 21 to 8, by-meaning 10 to 7, comprehension +9 to 6, pinpoint 8 to 5 with 11 ties, known-file 7 to 5) and makes a third +fewer claims the code does not support. The aggregation gap is the second +`search` call: a "ranked list with a short reason each" answered from one +`sql` result invents its reasons; answered after a `search` it cites them. +No build cited a line outside its file except one V4a answer; V3 answers +carry more citations than V0's (0.7 against 0.5 per answer). + +### The rename and the shape + +`context` for `search` (V4a) changed nothing the rename was for: 18 of 18 +first calls on both comprehension and by-meaning under either name, and +by-meaning tokens higher than V3 (452k against 380k). The name stays. + +The tiered result (V5) trades dollars for round trips. Blended cost $2.61 +against V3's $2.94, tokens level (1,640k), but 161 tool calls against 119: +Reads went from 49 to 86 on comprehension and from 10 to 54 on by-meaning, +because the agent reads the hits whose content it no longer has. Cheaper on +the bill because the Reads hit the prompt cache; slower because each is a +round trip. The judge calls it level with V0 on the sets the shape is for +(comprehension 8 to 8, by-meaning 9 to 8) where V3 wins them, and the +excerpt tier lost a little on known-file (3 to 6). A shape that costs a +third more round trips to arrive at the same answers does not ship as the +default; the branch stays for a client that pays per token and not per +round trip. + +### The confirmation builds + +V6 adds two sentences to V3: the instructions say again that a hit is +answered from without re-reading the file, and the `sql` description says +`lang` is the file extension. V7 puts the first sentence in the `search` +description as well. V8 adds the receipt-field sentence from the A/B above +to each description. Sonnet, tokens per set, alongside both passes of V0 +and V3: + +| set | V0 | V0 again | V3 | V3 again | V6 | V7 | V8 | +|---|---|---|---|---|---|---|---| +| aggregation | 138k | 146k | 157k | 146k | 142k | 146k | 136k | +| comprehension | 746k | 774k | 669k | 738k | 519k | 572k | 578k | +| pinpoint | 227k | 205k | 258k | 243k | 253k | 203k | 204k | +| known-file | 131k | 131k | 113k | 107k | 121k | 112k | 114k | +| by-meaning | 524k | 586k | 380k | 383k | 539k | 433k | 435k | +| **blended** | **1,766k** | **1,842k** | **1,576k** | **1,617k** | **1,574k** | **1,465k** | **1,468k** | +| cost per pass | $3.03 | $3.23 | $2.94 | $2.95 | $2.93 | $2.87 | $2.77 | +| tool calls | 103 | 105 | 119 | 124 | 118 | 105 | 107 | +| first call on a code-context tool | 94/108 | 94/108 | 88/108 | 87/108 | 90/108 | 94/108 | 97/108 | + +The `lang` sentence removed the detour (the largest-Rust-files question is +one `sql` call again). The restored steer brought comprehension to 519k to +578k, under every V0 and V3 pass, with Reads at 33 against 49 to 53. V7 and +V8 are the first trimmed builds to match V0's round trips and first-call +count while spending 17 to 19% fewer tokens; V8 adds the pinpoint steer +(`find` or `sql` first on 18 of 24 against V7's 17 and V3's 14 to 15) and +the highest code-context first-call count of the run. Its one soft spot is +the tripwire set: `find` was the first call on 13 of 18 known-file runs +against V0's 9 and 10, though at 114k tokens against 131k, because a `find` +that lands on a file the agent should have Read costs one cheap call, not a +detour. The blind judge on V8 against V0 was still running when this was +written; its row joins the quality table when it lands. + +### Haiku 4.5 + +Two passes of each build, tokens per set: + +| set | V0 | V0 again | V3 | V3 again | V6 | V6 again | V7 | V7 again | V8 | V8 again | +|---|---|---|---|---|---|---|---|---|---|---| +| aggregation | 314k | 182k | 190k | 154k | 194k | 262k | 163k | 147k | 183k | 170k | +| comprehension | 889k | 904k | 1,402k | 1,290k | 1,412k | 847k | 864k | 797k | 1,186k | 794k | +| pinpoint | 366k | 323k | 387k | 328k | 287k | 251k | 392k | 411k | 365k | 524k | +| known-file | 126k | 126k | 103k | 111k | 98k | 109k | 109k | 109k | 110k | 110k | +| by-meaning | 866k | 896k | 853k | 896k | 780k | 605k | 622k | 644k | 627k | 726k | +| **blended** | **2,560k** | **2,432k** | **2,935k** | **2,779k** | **2,772k** | **2,074k** | **2,150k** | **2,108k** | **2,471k** | **2,326k** | + +Selection on Haiku is the same story as Sonnet: `sql` first on 27 to 29 of +30 aggregation runs and `search` first on 16 to 18 of 18 on every build, +`find` first on 13 to 16 of 24 pinpoint runs, and never a code-context +tool on the known-file set (Bash or Read, then Read). Haiku is where the +fourth tool cost something: on "break down the crate by top-level module" +it called `reindex` in 2 of 3 V0 runs, once as its first call, and the two +V0 aggregation passes swing 314k to 182k largely on that question. With +`reindex` hidden the set sits between 147k and 262k. + +Haiku is also where the trimmed text lost something Sonnet did not need. +Comprehension went from about 900k tokens under V0 to 1.3M and 1.4M under +V3, with Reads doubled (82 and 97 against 40 and 45) and Grep up (13 and 17 +against 2 and 6): V0's `search` description said to answer from a hit +without re-confirming it by opening the file, the trimmed one said "answer +and cite from the hits", and Haiku went back to reading. Putting the +sentence back in the server instructions (V6) split the passes, 1.4M and +847k; putting it in the `search` description as well (V7), where the model +reads it as the hits arrive, brought both passes to 864k and 797k, under +V0. V8, the same text plus the receipt-field sentence, split again (1,186k +and 794k). Haiku's spread on this six-question set is wide - the same +build lands 65% apart - so the honest reading is: the trimmed text without +the sentence was over V0 in all four passes, and with it in the `search` +description it was at or under V0 in three of four. Haiku also invented a +tool name (`mcp__code_context__search`, underscores for the hyphen) in 9 +of 648 runs on the trimmed builds and never in 216 under V0; each such +call fails and the run falls back to Grep and Read. + +### What this run decides + +- **`search` keeps its name and its shape.** The rename moved nothing it + was meant to move; the tiered result buys dollars with round trips and + answers no better. +- **The descriptions and instructions ship trimmed**, at about 680 tokens + of tool text and 185 of instructions per turn against 1,280 and 545, + with three sentences the trim had cut and the runs showed were + load-bearing: answer from a hit without re-opening the file (in the + `search` description, for Haiku), what the `usage` receipt reports (in + each description, for the two lookups on Sonnet), and what `lang` holds. +- **The "show the usage line" request goes.** It was paid three times per + turn, and the part of the sentence that steered was the description of + the field, which stays. +- **`reindex` leaves the tool list.** Nothing on Sonnet called it in 216 + runs; Haiku called it where it hurt. Auto-sync and auto-index cover the + job in a session and `cx index --full` on the command line. +- **Prose is measured from now on.** Every sentence in a description costs + every turn; this run found two that steered selection and one that + steered how many files got opened, none of them written for that. The + question sets and the comparison scripts in `bench/` are the harness for + the next change. + +**Caveats.** Sonnet's pass-to-pass spread is 4 to 12% per set; Haiku's is +wider (aggregation 314k against 182k on V0). Per-question first-call counts +are stable per prompt and flip on unrelated wording, so only set totals are +read. The pinned clone and the disabled auto-sync are the same discipline as +the `find` run. Spend: about $50 of agent runs (11 Sonnet passes over the +four sets, three pinpoint-only passes, 10 Haiku passes) and about $107 of +judging, four variants at a quarter of a dollar a pair. + ## Indexing at scale Keyword-index timings on an Apple Silicon laptop (the stage that gates From 53d5ac0d80968e01a84bc43ce5065fe1fceca6c6 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 16:29:36 +0000 Subject: [PATCH 09/13] docs: the V8 judge row - 51 to 33 over V0, 23 ties The blind judge finished after the section landed: V8 wins 51 pairs to V0's 33 with 23 ties and 236 unsupported claims against 302, ahead on every set but comprehension (7 to 7), and 6 to 3 on the known-file set where its extra find-first runs were the one soft spot. --- docs/benchmark.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/benchmark.md b/docs/benchmark.md index 054bc81..dc6d2f8 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -331,14 +331,19 @@ Blind judge, each variant against V0, same question and repeat paired: | V1 | 107 | 39 | 46 | 22 | 185 / 151 | 0.70 | | V3 | 108 | 31 | 55 | 22 | 283 / 192 | 0.72 | | V5 | 107 | 35 | 47 | 25 | 243 / 201 | 0.72 | +| V8 | 107 | 33 | 51 | 23 | 302 / 236 | 0.68 | V3 wins on every set (aggregation 21 to 8, by-meaning 10 to 7, comprehension 9 to 6, pinpoint 8 to 5 with 11 ties, known-file 7 to 5) and makes a third fewer claims the code does not support. The aggregation gap is the second `search` call: a "ranked list with a short reason each" answered from one `sql` result invents its reasons; answered after a `search` it cites them. -No build cited a line outside its file except one V4a answer; V3 answers -carry more citations than V0's (0.7 against 0.5 per answer). +V8, the shipping candidate, holds that: aggregation 18 to 10, by-meaning 11 +to 6, pinpoint 9 to 7 with 8 ties, comprehension 7 to 7, and on the +known-file set 6 to 3 with 2 unsupported claims against 7 - the extra +`find`-first runs there did not cost the answers anything. No build cited a +line outside its file except one V4a answer; the trimmed builds' answers +carry more citations than V0's (0.7 to 0.8 against 0.5 per answer). ### The rename and the shape @@ -389,8 +394,7 @@ the highest code-context first-call count of the run. Its one soft spot is the tripwire set: `find` was the first call on 13 of 18 known-file runs against V0's 9 and 10, though at 114k tokens against 131k, because a `find` that lands on a file the agent should have Read costs one cheap call, not a -detour. The blind judge on V8 against V0 was still running when this was -written; its row joins the quality table when it lands. +detour, and the judge preferred V8's known-file answers 6 to 3. ### Haiku 4.5 From dacab7e3470d0b498febbddaff253b21f8b22d5c Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 16:46:27 +0000 Subject: [PATCH 10/13] mcp: the measured tool surface - three tools, trimmed prose The surface plan 101 measured (docs/benchmark.md, "The tool surface: names, shapes, and prose"), as one change: - descriptions cut to question shape, not-for, and result shape, and the server instructions to a routing table: ~680 tokens of tool text plus ~165 of instructions per turn against ~1,280 plus ~545, with selection unchanged (sql first on 30/30 aggregation runs, search first on 18/18 comprehension and by-meaning runs, on every build measured); - three sentences the trim had cut and the runs showed load-bearing, kept: answer from a hit without re-opening the file (in the search description - Haiku doubled its Reads without it), what the usage receipt reports (dropping it flipped two lookups from find to Grep 12/12 vs 15/15; naming the field brings them back), and that lang is the file extension (every trimmed build paid a three-call detour without it); - the request to show the usage line to the user is gone; the field stays and cx usage keeps the ledger; - the reindex tool is gone. No Sonnet run called it in 216; Haiku called it in 2 of 3 runs of one aggregation question, once as its first call. The first query builds the index, every query re-syncs it, and cx index --full rebuilds from a shell. The noIndex error, the status hook line, the CLI help, the skill, README, FAQ, llms.txt, context7 and wiki notes say three tools. On Sonnet 4.6 against the previous surface: 17-20% fewer tokens over the 36-question suite at the same round trips, a code-context tool first on 97 of 108 runs against 94, judged 51 to 33 with 23 ties and a fifth fewer unsupported claims. Two Haiku passes each: aggregation 40% cheaper, comprehension at or under the previous surface in three of four passes. --- .devin/wiki.json | 4 +- AGENTS.md | 21 ++-- README.md | 32 +++--- context7.json | 2 +- docs/architecture.svg | 2 +- docs/faq.md | 19 ++-- llms.txt | 6 +- skills/code-context/SKILL.md | 22 ++-- src/cli.ts | 6 +- src/commands/query-cmds.ts | 2 +- src/core/usage.ts | 6 +- src/mcp/server.ts | 193 +++++++++-------------------------- test/usage.test.ts | 6 +- 13 files changed, 120 insertions(+), 201 deletions(-) diff --git a/.devin/wiki.json b/.devin/wiki.json index 0dfead7..0c552ad 100644 --- a/.devin/wiki.json +++ b/.devin/wiki.json @@ -1,13 +1,13 @@ { "repo_notes": [ { - "content": "code-context is local code search for AI coding agents: a CLI (bins `code-context` and `cx`) and an MCP server over a ranked index that lives in plain files inside the repo, under `.infino/`. It fuses exact keyword matching (BM25) with semantic similarity (vectors) into one ranked pass, and exposes read-only SQL over the index so search composes with aggregation (`GROUP BY`). Drop it into Claude Code with `claude mcp add code-context -- npx -y @infino-ai/code-context mcp`, or install the CLI with `npm install -g @infino-ai/code-context` then `cx index` in a repo. Document it as a tool developers and coding agents use to answer questions about a codebase without crawling files into the context window: lead with the MCP setup, the four MCP tools (find, search, sql, reindex), and how the index stays fresh." + "content": "code-context is local code search for AI coding agents: a CLI (bins `code-context` and `cx`) and an MCP server over a ranked index that lives in plain files inside the repo, under `.infino/`. It fuses exact keyword matching (BM25) with semantic similarity (vectors) into one ranked pass, and exposes read-only SQL over the index so search composes with aggregation (`GROUP BY`). Drop it into Claude Code with `claude mcp add code-context -- npx -y @infino-ai/code-context mcp`, or install the CLI with `npm install -g @infino-ai/code-context` then `cx index` in a repo. Document it as a tool developers and coding agents use to answer questions about a codebase without crawling files into the context window: lead with the MCP setup, the three MCP tools (find, search, sql), and how the index stays fresh on its own (first query builds it, every query re-syncs it; there is no reindex tool)." }, { "content": "Common misconceptions to avoid when documenting code-context, with the corrected statements: (1) It is NOT a cloud service and needs NO account, API key, or database server. It runs locally; embedding uses a small local model downloaded once, and code never leaves the machine. (2) It is NOT semantic/vector search only. Search is hybrid: BM25 and vector rankings fuse in one pass, and until vectors finish backfilling it degrades to keyword-only rather than failing. (3) The index is NOT a proprietary database tier. It is plain files in `.infino/` that you can copy, cache in CI, or gitignore. (4) It is NOT a structural code-intelligence tool. It does ranked content retrieval and content-relevance aggregation, not call-graph tracing, dead-code detection, or type resolution; graph/LSP tools are complementary and stack alongside it. (5) It is NOT only for questions that span the repo. Those (understanding a subsystem, ranking or aggregating across files) are where it saves the most, but the grep case - every occurrence of a known symbol or literal - is served by the find tool from the same index, complete and unranked, with no file scanned." }, { - "content": "How it works: files are chunked at definition boundaries with tree-sitter (WASM, no native compile) for common languages, with a fixed-window fallback for the rest; every chunk carries path, start_line, end_line, lang, and content. Chunks are indexed into one table named `chunks` in `.infino/`, with a BM25 full-text index and an IVF vector index, queried in-process through the infino engine's Node binding (no server). Readiness is staged: the keyword index commits in seconds so search is live immediately, while vectors backfill in the background and hybrid ranking unlocks automatically when they land. Sync is incremental: a per-file state map (size/mtime prefilter, then content hash) re-chunks and re-embeds only changed files, and the MCP server auto-syncs in the background as queries arrive. The MCP surface is exactly four tools by design: find (every line containing an exact string, cited path:line; the index's token match picks candidate chunks and each line is checked for the literal, so it is complete and unranked like grep -n), search (hybrid ranked retrieval), sql (read-only SELECT/WITH, with the ranked search functions bm25_search/hybrid_search usable as table-valued relations so search composes with GROUP BY aggregation), and reindex (incremental sync). code-context is built on the infino engine (https://github.com/infino-ai/infino), whose same index format also serves logs, docs, and agent memory." + "content": "How it works: files are chunked at definition boundaries with tree-sitter (WASM, no native compile) for common languages, with a fixed-window fallback for the rest; every chunk carries path, start_line, end_line, lang, and content. Chunks are indexed into one table named `chunks` in `.infino/`, with a BM25 full-text index and an IVF vector index, queried in-process through the infino engine's Node binding (no server). Readiness is staged: the keyword index commits in seconds so search is live immediately, while vectors backfill in the background and hybrid ranking unlocks automatically when they land. Sync is incremental: a per-file state map (size/mtime prefilter, then content hash) re-chunks and re-embeds only changed files, and the MCP server auto-syncs in the background as queries arrive. The MCP surface is exactly three tools by design: find (every line containing an exact string, cited path:line; the index's token match picks candidate chunks and each line is checked for the literal, so it is complete and unranked like grep -n), search (hybrid ranked retrieval), and sql (read-only SELECT/WITH, with the ranked search functions bm25_search/hybrid_search usable as table-valued relations so search composes with GROUP BY aggregation). Freshness is not a tool: the first query builds the index and every query re-syncs it; cx index --full rebuilds from a shell. code-context is built on the infino engine (https://github.com/infino-ai/infino), whose same index format also serves logs, docs, and agent memory." } ] } diff --git a/AGENTS.md b/AGENTS.md index f24248f..e8c18a5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,9 +24,10 @@ the honest limits in [docs/tradeoffs.md](docs/tradeoffs.md). ## Repo map - `src/cli.ts`: the `cx` / `code-context` command entry (commander). -- `src/mcp/server.ts`: the MCP server, four tools (`find`, `search`, `sql`, - `reindex`). Each takes an optional `path` (repo root) so one server serves - multiple repos in a session, defaulting to the startup root. +- `src/mcp/server.ts`: the MCP server, three tools (`find`, `search`, `sql`). + Each takes an optional `path` (repo root) so one server serves multiple + repos in a session, defaulting to the startup root. Freshness is not a + tool: the first query builds the index and every query re-syncs it. - `src/mcp/repos.ts`: the per-repo registry - resolves and validates a requested root, one engine connection per repo, LRU-capped. - `src/mcp/ensure.ts`: auto-index on first query - a `search`/`sql` on a @@ -53,14 +54,20 @@ before opening a PR. ## Conventions - TypeScript, ES modules. Every source file carries an SPDX header. -- The MCP surface is deliberately four tools, one per question: where does +- The MCP surface is deliberately three tools, one per question: where does this exact text occur (`find`, unranked and complete - the grep replacement), what is most relevant (`search`, ranked top-k), how much of - what is where (`sql`), stay fresh (`reindex`). Adding near-duplicate - retrieval tools worsens an agent's tool selection; resist it. A new tool - must answer a question none of these four does. + what is where (`sql`). Adding near-duplicate retrieval tools worsens an + agent's tool selection; resist it. A new tool must answer a question none + of these three does. A `reindex` tool was the fourth until it was measured + (docs/benchmark.md, "The tool surface"): no Sonnet run called it, Haiku + called it where it hurt, and auto-sync already does the job. - Search results carry chunk content plus `path:line` ranges so answers cite code; keep that contract when touching `searcher` or the tool descriptions. +- Tool descriptions and server instructions are prompt text on every turn + and were measured to steer tool selection sentence by sentence. Change + them with the bench (`bench/`, the four question sets and + `compare-builds.mjs`), not by taste. ## Boundaries diff --git a/README.md b/README.md index 87f64b9..5d3e7fb 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Install the Claude Code plugin - nothing to paste into a config: /plugin install code-context@infino-ai ``` -It registers code-context's four tools with `alwaysLoad` already set, so the +It registers code-context's three tools with `alwaysLoad` already set, so the agent keeps them in view and reaches for the index directly instead of falling back to plain file search. @@ -78,7 +78,7 @@ the plugin or this command, not both.) Then just ask a question about the code. The first `find`, `search`, or `sql` on an unindexed repo builds the index inline and answers on the same call: keyword search is live in seconds, and vectors backfill in the background. (Prefer to -kick it off yourself? The `reindex` tool does the same build on demand.) +kick it off yourself? `cx index` does the same build from a shell.) CI-tested on Linux x64 (glibc) and macOS arm64; linux-arm64, musl, and Windows-via-WSL are expected to work through the engine's prebuilt bindings @@ -115,6 +115,13 @@ same repo, questions, model, and a blind judge: answer quality level (judge calls**, the shipped question set flat (-3% tokens, +1% dollars), and about a thousand tokens per turn of added prompt for the fourth tool. +The tool surface itself was then measured lever by lever - names, result +shapes, and every sentence of description - on two models with a blind +judge: the shipped text is the one that kept selection where it was, cut the +per-turn prompt cost of the tool definitions by more than half, and judged +51 to 33 over the previous surface. That run is also why there are three +tools and not four. + Full methodology and per-question tables are in [docs/benchmark.md](docs/benchmark.md), with the harness in [`bench/`](bench/) so you can run the same lanes on your own repo. @@ -128,11 +135,12 @@ One index and a deliberately small tool surface for agents: | `find` | Every line containing an exact string, cited `path:line` like `grep -n`, plus matching lines per file like `grep -c`. Complete and unranked: the index's token match picks the candidate chunks, then each line is checked for the literal, so no file is scanned and every hit is a real occurrence. | Where an agent would grep: every use or definition of an identifier, an error message, a config key. | | `search` | One ranked pass fusing exact keyword matching (BM25) with semantic similarity (reciprocal-rank fusion). Hits carry the chunk content, so answers come straight from results. | A strong default for finding and understanding code: how a subsystem works, code by meaning or exact term, context before a change, similar implementations - exact identifiers and paraphrases in the same call. | | `sql` | Read-only SQL over the index, with the ranked search functions (`bm25_search`/`hybrid_search`) usable as table-valued relations. | Counts, rankings, aggregates over the whole repo in one query. | -| `reindex` | Incremental sync (the server also auto-syncs in the background). | After significant edits. | -Four tools, each a different question: where does this exact text occur, -what is most relevant to this, how much of what is where, and stay fresh. -There are still no near-duplicate retrieval tools, because those worsen an +Three tools, each a different question: where does this exact text occur, +what is most relevant to this, how much of what is where. Freshness is not +a tool: the first query on an unindexed repo builds the index, every query +re-syncs it against the working tree, and `cx index --full` rebuilds from a +shell. There are no near-duplicate retrieval tools, because those worsen an agent's tool selection: `find` is unranked and complete where `search` is ranked and top-k, and hybrid search's keyword half already ranks exact identifier terms highly, so no separate lexical *ranking* tool exists. @@ -174,8 +182,8 @@ export and pass around. ## Setup for agents code-context is an MCP server over stdio, so any MCP client works. Register -it once and the tools (`find`, `search`, `sql`, `reindex`) become available to -the agent. +it once and the tools (`find`, `search`, `sql`) become available to the +agent.
Claude Code @@ -198,7 +206,7 @@ claude mcp add-json code-context -s user '{"command":"npx","args":["-y","@infino for the index directly. In sessions with many MCP servers Claude Code defers tool definitions behind a tool-search step; without `alwaysLoad` the agent can miss code-context and fall back to grep/read. It's a small, always-loaded set -(four tools). Omit it (or use the shorter `claude mcp add code-context -- npx +(three tools). Omit it (or use the shorter `claude mcp add code-context -- npx -y @infino-ai/code-context mcp`) if you'd rather leave the tools deferred. Use *either* the plugin or the `add-json` command, not both. They register the @@ -262,9 +270,9 @@ when the client's working directory is not the repo.
-Tools: `find`, `search`, `sql`, `reindex` (incremental sync: an unchanged -repo is a fast no-op, and the server also auto-syncs in the background as -queries arrive, so results track your edits without anyone asking). +Tools: `find`, `search`, `sql`. The server auto-syncs in the background as +queries arrive (an unchanged repo is a fast no-op), so results track your +edits without anyone asking; `cx index --full` from a shell forces a rebuild. **Multiple repos in one session.** Each tool takes an optional `path` (an absolute repo root). Omit it and the server uses its startup root; set it to diff --git a/context7.json b/context7.json index 8b5b6e7..758925b 100644 --- a/context7.json +++ b/context7.json @@ -15,7 +15,7 @@ "code-context runs locally: install with 'npm install -g @infino-ai/code-context' (bins: code-context and cx), or zero-install into Claude Code with 'claude mcp add code-context -- npx -y @infino-ai/code-context mcp'.", "Index a repo with 'cx index' from the repo root. Keyword (BM25) search is live seconds after indexing starts; vectors backfill in the background and hybrid ranking unlocks automatically when they land.", "'cx index' is incremental by default: only files that changed since the last index re-chunk and re-embed. Use --full to rebuild from scratch and --watch to sync on file events.", - "The MCP server ('cx mcp', stdio) exposes exactly four tools: find (every line containing an exact string, cited path:line like grep -n; complete and unranked - the grep replacement), search (exact terms AND meaning in one ranked pass, hits carry chunk content with path:line ranges), sql (read-only SELECT/WITH over the index), and reindex (incremental sync).", + "The MCP server ('cx mcp', stdio) exposes exactly three tools: find (every line containing an exact string, cited path:line like grep -n; complete and unranked - the grep replacement), search (exact terms AND meaning in one ranked pass, hits carry chunk content with path:line ranges), and sql (read-only SELECT/WITH over the index). There is no reindex tool: the first query builds the index, every query re-syncs it, and cx index --full rebuilds from a shell.", "The index is one table named chunks(path, start_line, end_line, lang, content[, embedding]) living in .infino/ inside the repo - plain files you can copy, cache in CI, or gitignore (add .infino/ to your .gitignore).", "The engine's search functions are SQL table functions, so one query can rank AND aggregate: SELECT path, SUM(end_line - start_line + 1) AS lines FROM bm25_search('chunks','content','', 300) GROUP BY path ORDER BY lines DESC.", "vector_search and hybrid_search table functions take a query vector via a {{name}} placeholder: pass an embed map like {\"q\": \"query text\"} and the server embeds it locally and substitutes the vector.", diff --git a/docs/architecture.svg b/docs/architecture.svg index de78b40..eca13e2 100644 --- a/docs/architecture.svg +++ b/docs/architecture.svg @@ -28,7 +28,7 @@ CLI (cx) + MCP server - tools: search · sql · reindex + tools: find · search · sql diff --git a/docs/faq.md b/docs/faq.md index bd6537f..d4f79f8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -35,7 +35,7 @@ reports that honestly rather than failing. No. The first `find`, `search`, or `sql` on a repo that has never been indexed builds the index inline and answers on that same call - keyword search is live in -seconds, vectors backfill behind it. Call `reindex` first if you'd rather +seconds, vectors backfill behind it. Run `cx index` first if you'd rather kick the build off explicitly, or set `CX_AUTO_INDEX=0` to make an unindexed query return a "index it first" error instead of building. @@ -72,16 +72,19 @@ and re-index for full coverage. ### What tools does the MCP server expose? -Four, by design, one per question: `find` (every line containing an exact +Three, by design, one per question: `find` (every line containing an exact string, cited `path:line` like `grep -n`; complete and unranked, the grep replacement), `search` (hybrid keyword + semantic retrieval, one ranked pass, -hits carry chunk content with `path:line` ranges), `sql` (read-only +hits carry chunk content with `path:line` ranges), and `sql` (read-only `SELECT`/`WITH` over the index, with the ranked search functions usable as -table-valued relations so search composes with `GROUP BY`), and `reindex` -(incremental sync). Every additional near-duplicate retrieval tool worsens an -agent's tool selection, so the surface is kept deliberately small: `find` and -`search` are not duplicates, one is complete and unranked, the other ranked -and top-k. +table-valued relations so search composes with `GROUP BY`). Every additional +near-duplicate retrieval tool worsens an agent's tool selection, so the +surface is kept deliberately small: `find` and `search` are not duplicates, +one is complete and unranked, the other ranked and top-k. There used to be a +fourth, `reindex`; measured, no Sonnet run ever called it, Haiku called it +where it hurt, and every tool in the list is prompt text on every turn. The +first query builds the index, every query re-syncs it, and `cx index --full` +rebuilds from a shell. ### How is SQL over code useful? diff --git a/llms.txt b/llms.txt index ea2ce3a..43eae5b 100644 --- a/llms.txt +++ b/llms.txt @@ -32,8 +32,8 @@ logs, docs, and agent memory. content with path:line ranges), `sql` (read-only SELECT/WITH over `chunks(path, start_line, end_line, lang, content)`, ranked search functions (bm25_search/hybrid_search) usable as table-valued relations so - search composes with GROUP BY), `reindex` (incremental sync; the server - also auto-syncs in the background). A `find`/`search`/`sql` on a + search composes with GROUP BY). No reindex tool: the server auto-syncs in + the background as queries arrive, and a `find`/`search`/`sql` on a never-indexed repo builds the index inline and answers on the same call (keyword live in seconds, vectors backfilling); `CX_AUTO_INDEX=0` restores a strict "index it first" error. Each tool takes an optional `path` @@ -62,7 +62,7 @@ logs, docs, and agent memory. the crawl-vs-retrieve model, when an index saves tokens, and where a plain grep is still the right tool. - [FAQ](docs/faq.md): what it is, when an agent should use it, local-only - guarantees, freshness, the four-tool MCP surface. + guarantees, freshness, the three-tool MCP surface. - [Tradeoffs](docs/tradeoffs.md): honest limits (no structural code intelligence; no token win on pinpoint lookups; one-time vector cost). diff --git a/skills/code-context/SKILL.md b/skills/code-context/SKILL.md index 212be5a..18ece30 100644 --- a/skills/code-context/SKILL.md +++ b/skills/code-context/SKILL.md @@ -2,7 +2,7 @@ name: code-context description: > How to answer codebase questions with the code-context MCP tools (find, - search, sql, reindex): exact-text lookup that replaces grep, ranked hybrid + search, sql): exact-text lookup that replaces grep, ranked hybrid keyword+semantic search, relevance-ranked SQL aggregation over the index, and index lifecycle. Use when you would grep for an identifier or literal, when a question spans many files ("how does X work", "where is Y @@ -14,16 +14,16 @@ description: > # code-context: search over the repository code-context maintains a local index of the repository (in `.infino/` at the -repo root) and exposes four MCP tools. Every lookup an agent would otherwise +repo root) and exposes three MCP tools. Every lookup an agent would otherwise do with grep or by crawling files runs against the index instead: `find` for the exact-text case, one ranked pass for everything that spans the repo. ## If the tools are deferred When the tool names appear in a deferred-tools listing but their schemas are -not loaded, load all four in ONE ToolSearch call before the first use, e.g. -query `+code-context find search sql reindex` (or `select:` with the exact -listed names, comma-separated). Never load them one call at a time. +not loaded, load all three in ONE ToolSearch call before the first use, e.g. +query `+code-context find search sql` (or `select:` with the exact listed +names, comma-separated). Never load them one call at a time. ## Choosing the right tool @@ -33,7 +33,7 @@ listed names, comma-separated). Never load them one call at a time. | A file you already know the path of | Read | | "How does X work", "where is Y handled", concept without exact name | `search` | | Counts, rankings, GROUP BY across the repo ("which files have the most code about X") | `sql` | -| Working tree changed a lot mid-session | `reindex` (usually unnecessary - see lifecycle) | +| Working tree changed a lot mid-session | nothing - the next query re-syncs (see lifecycle) | ## find @@ -103,8 +103,9 @@ GROUP BY path ORDER BY lines DESC LIMIT 15 while vectors backfill in the background. Do not pre-emptively reindex. - **Later queries auto-sync**: the server re-chunks only files that changed since the last index. An unchanged tree is a fast no-op. -- Call `reindex` explicitly only after sweeping working-tree changes you - want reflected immediately, or `full: true` to force a rebuild. +- There is no reindex tool. If the index is actually wrong (not merely + behind an edit the next query will pick up), `cx index --full` from a + shell rebuilds it. - Each repo's index is keyed to its own root directory: a fresh git worktree is a new root and builds its own index on first query (the main checkout's index does not carry over). @@ -116,7 +117,8 @@ GROUP BY path ORDER BY lines DESC LIMIT 15 possibly-unindexed, not as proof the code doesn't exist. - Find, search, and sql results carry a one-line `usage` receipt (tokens returned, matches or chunks / files, session running total), computed - locally. End your reply by showing that line to the user verbatim. + locally. It is there for the user who asks what a lookup cost; `cx usage` + keeps the ledger. ## Multi-repo sessions @@ -128,6 +130,6 @@ different repository than the one the server started in. - `find`/`search`/`sql` calls are cheap (local, milliseconds). - The first index of a repo and the vector backfill are the expensive part (CPU for the local embedding model, proportional to repo size). Avoid - forcing `full: true` rebuilds unless the index is actually wrong, and + forcing `cx index --full` rebuilds unless the index is actually wrong, and avoid triggering first-time indexing of large repos that the task does not need. diff --git a/src/cli.ts b/src/cli.ts index 9423336..edc8236 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -18,7 +18,7 @@ program "Keyword search seconds after `cx index`; semantic and hybrid search when vectors\n" + "finish backfilling; SQL with relevance-ranked aggregation over the whole repo.", ) - .version("0.1.4") + .version("0.5.0") .addHelpText( "after", ` @@ -30,7 +30,7 @@ Examples: cx sql "SELECT path, SUM(end_line - start_line + 1) AS lines \\ FROM bm25_search('chunks','content','vector index', 300) \\ GROUP BY path ORDER BY lines DESC LIMIT 10" - cx mcp serve the MCP tools (find/search/sql/reindex) over stdio`, + cx mcp serve the MCP tools (find/search/sql) over stdio`, ); program @@ -99,7 +99,7 @@ program program .command("mcp") - .description("serve the MCP tools (find / search / sql / reindex) over stdio") + .description("serve the MCP tools (find / search / sql) over stdio") .option("-C, --path ", "repo root (default: current directory)") .action(async (opts: { path?: string }) => { const { serveMcp } = await import("./mcp/server.js"); diff --git a/src/commands/query-cmds.ts b/src/commands/query-cmds.ts index d1bb82d..fffb0cb 100644 --- a/src/commands/query-cmds.ts +++ b/src/commands/query-cmds.ts @@ -156,7 +156,7 @@ export function statusCmd(opts: StatusCmdOptions): void { console.log( `code-context index: ${fmtCount(m.chunks)} chunks from ${fmtCount(m.files)} files, ` + `vectors ${m.vectors}, indexed ${fmtAge(m.indexedAt)}. ` + - `MCP tools: find (exact text, every occurrence), search (terms + meaning), sql (aggregation), reindex (after big edits).`, + `MCP tools: find (exact text, every occurrence), search (terms + meaning), sql (aggregation); the index re-syncs on every query.`, ); return; } diff --git a/src/core/usage.ts b/src/core/usage.ts index 33f8899..7302c15 100644 --- a/src/core/usage.ts +++ b/src/core/usage.ts @@ -220,9 +220,9 @@ export interface PromptStats { promptsWithCx: number; /** transient: has the current prompt already used code-context. */ curPromptUsedCx: boolean; - /** code-context invocations by tool (`find`, `search`, `sql`, `reindex`): - * which door the agent actually walks through. Absent on stats files - * written before it was recorded. */ + /** code-context invocations by tool (`find`, `search`, `sql`): which door + * the agent actually walks through. Absent on stats files written before + * it was recorded. */ cxCallsByTool?: Record; /** The first tool the agent called in each prompt, counted by name - a * code-context tool by its short name, anything else (Grep, Read, Bash) diff --git a/src/mcp/server.ts b/src/mcp/server.ts index bafb714..6f36af2 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -1,21 +1,27 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Infino Authors // -// The dedicated MCP server: four tools over one code index. +// The dedicated MCP server: three tools over one code index. // // find - the grep door: every line containing an exact string, cited // path:line - complete and unranked // search - find code: exact terms AND meaning in one ranked pass // sql - the power door: relevance-ranked aggregation over the search // table functions (bm25_search / hybrid_search + GROUP BY) -// reindex - sync from the working tree; replies the moment keyword -// search is live and backfills vectors in-process // -// Four tools, each a different question: where does this exact text occur, -// what is most relevant to this, how much of what is where, and stay fresh. +// Three tools, each a different question: where does this exact text occur, +// what is most relevant to this, how much of what is where. Freshness is not +// a tool: the first query on an unindexed repo builds the index, and every +// query re-syncs it against the working tree (auto-sync, below). A reindex +// tool used to be the fourth; measured, no Sonnet run ever called it and +// Haiku called it where it hurt, and every tool in the list is prompt text +// on every turn. `cx index --full` is the forced rebuild. // No near-duplicate retrieval tools - those worsen the agent's tool // selection - so find is unranked and complete where search is ranked and // top-k, and hybrid search's keyword half already ranks exact identifiers. +// Every sentence in the descriptions below is paid for on every turn and +// was measured to steer selection (docs/benchmark.md, "The tool surface"): +// change them with the bench, not by taste. // Results carry took_ms - server-side time for the call (query embedding // included where one happens; no transport). @@ -178,7 +184,7 @@ export async function serveMcp(rootPath?: string): Promise { isError: true, }); const noIndex = (root: string) => - fail(`no index for ${root} yet - call the reindex tool once (keyword search is live in seconds).`); + fail(`no index for ${root} yet - run \`cx index\` there once (keyword search is live in seconds).`); /** Marker attached to a query result when this call built the index. */ const autoIndexNote = (stats: IndexStats) => ({ @@ -199,33 +205,16 @@ export async function serveMcp(rootPath?: string): Promise { { name: "code-context", version: "0.1.2" }, { instructions: - "code-context is a local ranked index over this repository - semantic + keyword search and " + - "SQL over the whole codebase. Reach for it whenever you need to understand or find code: " + - "understanding how a subsystem works, finding code by meaning or by exact term, gathering " + - "context before an edit, locating a bug or the code behind a behaviour, reviewing existing " + - "patterns, planning a refactor, understanding the architecture for feature work, or spotting " + - "similar/duplicate implementations. It is the primary tool for finding and understanding " + - "code here, for almost any question about this codebase. Four tools:\n" + - "- find - every line containing an exact string, cited path:line - the grep replacement: " + - "complete and unranked, for every use or definition of an identifier or literal.\n" + - "- search - find code by meaning or terms across files and understand how something works, " + - "in one ranked pass.\n" + - "- sql - counts, rankings, and aggregates over the whole repo in one query, including " + - "relevance-ranked aggregation ('which files have the most code about X') that file tools " + - "cannot express at any budget.\n" + - "- reindex - sync the index after the working tree changes.\n" + - "Treat a hit's content as authoritative: when it answers the question, answer from it and " + - "cite path plus line range - you don't need to re-confirm with grep or by opening the file. " + - "Read a file only for a hit marked truncated (its cited range), or when the results genuinely " + - "don't cover the question. When one search isn't enough, refine the query and search again - " + - "the ranked hits are already the relevant regions.\n" + - "Every tool takes an optional 'path' (an absolute repo root): omit it for the default repo, " + - "or set it to target a specific one when you're working across more than one repo in a session.\n" + - "If a result carries a 'partial' marker, the repo exceeded the index's file cap and some files " + - "were left out: treat a missing match as possibly-unindexed, not proof it's absent.\n" + - "Each result carries a 'usage' receipt (tokens returned, chunks/files, and a session running " + - "total) - computed locally, nothing leaves the machine. Surface it to the user so they can see " + - "how much context the ranked results put into the conversation.", + "code-context is a local index of this repository. Which tool for which question:\n" + + "- find - every line containing an exact string, where you would grep.\n" + + "- search - how does X work, where is Y handled, code by meaning.\n" + + "- sql - counts, rankings, and aggregates across the repo.\n" + + "Hits carry the code: when a hit answers the question, answer from it and cite path:line " + + "without re-reading the file or re-checking with grep; Read a file only for a hit marked " + + "truncated. " + + "Every tool takes an optional 'path' (an absolute repo root) to target another repository. " + + "A 'partial' marker means files over the index cap were left out, so a missing match is not " + + "proof of absence.", }, ); @@ -234,23 +223,14 @@ export async function serveMcp(rootPath?: string): Promise { { title: "Code search (exact terms + meaning)", description: - "Semantic + keyword code search over the indexed repository - a strong default for finding " + - "and understanding code. Use it to: understand how a subsystem or feature works, find code " + - "by meaning when you don't know the exact name, locate the code behind a behaviour or bug, " + - "gather context before making a change, review existing implementations and patterns, find " + - "everything a refactor would touch, understand the architecture for feature work, or spot " + - "similar/duplicate code. One pass fuses exact keyword matching (BM25: identifiers, error " + - "strings, function names, stemmed and scored) with semantic similarity (renamed symbols, " + - "paraphrases, 'where is X handled'), so it works whether or not you know the words. Each hit " + - "carries path, line range, and the chunk content with a relevance score - treat it as " + - "authoritative and answer directly from it, citing path plus line range; you don't need to " + - "re-confirm a hit with grep or by opening the file. When one search isn't enough, refine the " + - "query and search again - the index has already ranked the relevant regions. Read a file only " + - "for a hit marked truncated (its cited start-end range via offset/limit), or when results " + - "genuinely don't cover the question. (Until the index's vector stage finishes, results are " + - "keyword-ranked and say so.) The result includes a 'usage' field - a one-line receipt " + - "(tokens returned, chunks/files, session total). After you " + - "answer, end your reply by showing that 'usage' line to the user verbatim.", + "Ranked code search fusing exact keyword matching with semantic similarity, so it works " + + "whether or not you know the words. Use it for 'how does X work', 'where is Y handled', code " + + "by meaning, context before a change, similar implementations. Each hit carries path, line " + + "range, and the chunk content: answer and cite from the hits without re-confirming them with " + + "grep or by opening the file; Read a file only for one marked truncated. When one search is " + + "not enough, refine the query and search again. For every occurrence of an exact string use " + + "find; for counts and rankings use sql. The result includes a 'usage' field, a one-line " + + "receipt of tokens returned, chunks and files.", inputSchema: { query: z.string().describe("What you're looking for - terms, a phrase, or a description."), k: z.number().int().positive().max(50).default(DEFAULT_SEARCH_K).describe("Maximum hits."), @@ -305,20 +285,13 @@ export async function serveMcp(rootPath?: string): Promise { { title: "Find exact text (every occurrence, like grep -n)", description: - "Every line in the repository that contains an exact string - the grep replacement. Use it " + - "where you would reach for grep or rg: every use or definition of an identifier, an error " + - "message, a config key, a literal. Complete, not ranked: it returns every matching line (up " + - "to `limit`, and always the repo-wide `total`) as path, line number, and the line's text, " + - "plus the enclosing definition name when known, and `byFile` - matching lines per file over " + - "every match, the `grep -c` answer in the same call. Literal text, not a regex; within a " + - "single line; case-sensitive unless ignoreCase. It reads only the chunks the index says " + - "contain the query's tokens and then checks each line, so every hit is a real occurrence " + - "and no file is scanned. Not for a file you already know: Read that file directly - find is " + - "for locating occurrences across the repository, and Read path:line when you need the code " + - "around a hit. For 'how does X work' or when you don't know the exact words, use search; " + - "for rankings and aggregates beyond per-file counts, sql. The result includes a 'usage' " + - "field - a one-line receipt (tokens returned, matches/files, session total). After you " + - "answer, end your reply by showing that 'usage' line to the user verbatim.", + "Every line in the repository containing an exact string, like grep -n: complete and " + + "unranked, with the repo-wide total and per-file counts (byFile, the grep -c answer). " + + "Literal text within one line, case-sensitive unless ignoreCase. Use it where you would " + + "grep: every use or definition of an identifier, an error message, a config key. Not for a " + + "file you already know - Read that file. For meaning or 'how does X work' use search; for " + + "rankings use sql. The result includes a 'usage' field, a one-line receipt of tokens " + + "returned, matches and files.", inputSchema: { query: z .string() @@ -385,20 +358,16 @@ export async function serveMcp(rootPath?: string): Promise { { title: "SQL over the code index", description: - "Whole-repo analytical questions that file tools cannot express at any budget: counts, " + - "rankings, GROUP BY across the codebase in one query, " + - `on table ${TABLE}(path, start_line, end_line, lang, content[, embedding]). ` + - "Search functions are callable as table-valued relations, so one query can rank AND " + - "aggregate: bm25_search('" + TABLE + "','content','terms', k) needs no embedding; " + - "hybrid_search('" + TABLE + "','content','terms','embedding', {{q}}, k) and " + - "vector_search('" + TABLE + "','embedding', {{q}}, k) take a {{name}} placeholder with an " + - 'embed map: {"q":"query text"}. The canonical move - "which files have the most code about ' + - 'X": SELECT path, SUM(end_line - start_line + 1) AS lines FROM ' + + "Read-only SQL, one SELECT or WITH, over " + + `${TABLE}(path, start_line, end_line, lang, symbol, content[, embedding]) - lang is the ` + + "file extension, e.g. 'rs' - for counts, rankings, and GROUP BY across the whole repo. " + + "Search functions are table-valued: " + + `bm25_search('${TABLE}','content','terms', k) needs no embedding; ` + + `hybrid_search('${TABLE}','content','terms','embedding', {{q}}, k) and ` + + `vector_search('${TABLE}','embedding', {{q}}, k) take a {{name}} placeholder filled from ` + + "the embed map. Canonical: SELECT path, SUM(end_line - start_line + 1) AS lines FROM " + `bm25_search('${TABLE}','content','', 300) GROUP BY path ORDER BY lines DESC LIMIT 15. ` + - "Build queries on bm25_search/hybrid_search so results are ranked by relevance to the topic, " + - "not on a raw scan of the whole table. Read-only, single statement. The result includes a " + - "'usage' field - a one-line receipt (tokens returned, rows, session total). After " + - "you answer, end your reply by showing that 'usage' line to the user verbatim.", + "The result includes a 'usage' field, a one-line receipt of tokens returned and rows.", inputSchema: { query: z .string() @@ -455,76 +424,6 @@ export async function serveMcp(rootPath?: string): Promise { }, ); - server.registerTool( - "reindex", - { - title: "Sync the code index", - description: - "Bring the index up to date with the working tree. Incremental by default: only files that " + - "changed since the last index are re-chunked and re-embedded, and an unchanged tree is a " + - "fast no-op, so call this freely after edits. The server also auto-syncs in the background as " + - "queries arrive. On a repo that has never been indexed this builds the index from scratch, " + - "replying as soon as keyword search is live (seconds) while vectors backfill behind it. " + - "Pass full=true to force a rebuild from scratch. Returns what changed plus index status.", - inputSchema: { - full: z.boolean().optional().describe("Force a full rebuild instead of an incremental sync."), - path: z - .string() - .optional() - .describe( - "Absolute path to the repository root to index. Defaults to the server's configured root; " + - "set it to target a specific repo when a session spans more than one.", - ), - }, - }, - async ({ full, path }) => { - let ctx: RepoCtx; - try { - ctx = repoFor(path); - } catch (err) { - return fail((err as Error).message); - } - try { - const runFull = async () => { - const emb = buildEmbedder(); - const run = await indexRepoStaged({ - root: ctx.root, - db: ctx.db, - indexDirPath: ctx.dir, - embedder: emb, - caps: DEFAULT_CAPS, - }); - backfill(run, emb); - return ok({ status: "rebuilt - keyword search live; vectors backfilling", ...run.text }); - }; - const result = exclusive(ctx, async () => { - if (full) return runFull(); - const outcome = await syncRepo({ - root: ctx.root, - db: ctx.db, - indexDirPath: ctx.dir, - embedder: process.env.CX_NO_EMBED ? undefined : getEmbedder(), - caps: DEFAULT_CAPS, - }); - if (outcome.action === "rebuild-required") { - if (outcome.reason === "vector backfill in progress") { - return ok({ status: "index build already in progress - search is available meanwhile" }); - } - return runFull(); - } - return ok({ - status: outcome.action === "noop" ? "index already up to date" : "synced", - ...outcome, - }); - }); - if (!result) return ok({ status: "a sync is already running - search is available meanwhile" }); - return await result; - } catch (err) { - return fail(`reindex failed: ${(err as Error).message}`); - } - }, - ); - const transport = new StdioServerTransport(); await server.connect(transport); const manifest: Manifest | undefined = readManifest(indexDir(defaultRoot)); diff --git a/test/usage.test.ts b/test/usage.test.ts index c375414..8698950 100644 --- a/test/usage.test.ts +++ b/test/usage.test.ts @@ -224,9 +224,9 @@ describe("prompt telemetry (hooks)", () => { it("names a tool from a renamed server variant by its short name", () => { submit("s1"); - recordHookEvent(dir, { hook_event_name: "PostToolUse", session_id: "s1", tool_name: "mcp__code-context-dev__reindex" }); - expect(currentSessionStats(dir)?.cxCallsByTool).toEqual({ reindex: 1 }); - expect(currentSessionStats(dir)?.firstToolByPrompt).toEqual({ reindex: 1 }); + recordHookEvent(dir, { hook_event_name: "PostToolUse", session_id: "s1", tool_name: "mcp__code-context-dev__search" }); + expect(currentSessionStats(dir)?.cxCallsByTool).toEqual({ search: 1 }); + expect(currentSessionStats(dir)?.firstToolByPrompt).toEqual({ search: 1 }); }); it("loads a stats file written before the per-tool fields existed", () => { From 4aaa32ef5c2dd82c0f0079eb79c66fc72c3f2cd3 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 16:46:27 +0000 Subject: [PATCH 11/13] release: 0.5.0 - the reindex tool leaves the surface A tool leaving the MCP tool list is a breaking change for clients that called it; package.json, the plugin manifest, the .mcp.json pin, the registry manifest and the CLI version move together. --- .claude-plugin/plugin.json | 2 +- .mcp.json | 2 +- package.json | 2 +- server.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 6fac6d5..454447b 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "code-context", "description": "Local hybrid keyword + semantic search and SQL over your codebase, for coding agents.", - "version": "0.4.0", + "version": "0.5.0", "author": { "name": "Infino AI" }, "homepage": "https://github.com/infino-ai/code-context" } diff --git a/.mcp.json b/.mcp.json index ccce499..a29c02f 100644 --- a/.mcp.json +++ b/.mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "code-context": { "command": "npx", - "args": ["-y", "@infino-ai/code-context@0.4.0", "mcp"], + "args": ["-y", "@infino-ai/code-context@0.5.0", "mcp"], "alwaysLoad": true } } diff --git a/package.json b/package.json index f0db677..2b3c484 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@infino-ai/code-context", - "version": "0.4.0", + "version": "0.5.0", "mcpName": "io.github.infino-ai/code-context", "description": "Local code search for AI coding agents: a CLI and MCP server with hybrid keyword + semantic search and SQL relevance-ranked aggregation over an index that lives in plain files. No accounts, no keys, no server.", "license": "Apache-2.0", diff --git a/server.json b/server.json index c715a81..4548815 100644 --- a/server.json +++ b/server.json @@ -6,12 +6,12 @@ "url": "https://github.com/infino-ai/code-context", "source": "github" }, - "version": "0.1.4", + "version": "0.5.0", "packages": [ { "registryType": "npm", "identifier": "@infino-ai/code-context", - "version": "0.1.4", + "version": "0.5.0", "transport": { "type": "stdio" }, From 26017968b8fddcd3ba872ed9a45527f8ee50e471 Mon Sep 17 00:00:00 2001 From: Ekechi Nwokah Date: Fri, 4 Sep 2026 16:50:45 +0000 Subject: [PATCH 12/13] docs: re-render the architecture diagram - tools: find, search, sql The PNG is rendered from docs/architecture.svg (sharp, at the shipped 1984x1344), so the tool list in the picture matches the surface. --- docs/architecture.png | Bin 196397 -> 174754 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/architecture.png b/docs/architecture.png index 01598a18997cbc6b5462773a075b8bec0e359a4a..f6a3ddad39601d8804f9bdfdc5e4aca8fdafda13 100644 GIT binary patch literal 174754 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 literal 196397 zcmeEt^_xvfy#~KW^Q+FM@do zd{T!!Rt$bTFp`rJzlHwi-}mOc=v%j*-;xo3tLB=rJLmFVRg)a!PqeSdB>7LslY8PH zZ+#64@;Ic+tkKvh)T^zj8K@0wz<45l8(taNoVP0|HhC{9;w-%SaNX?NxP}1= z<#pxbv0u-iWM^lGa@+55A~C8+0Qddx@tJvj=E;A50Y4Y@eDCr7@9&@QNPoZkzrVix z{Qtka_5WW22#EiOUxJlA|E=e~xcX29eJ|=72?Hg#?!*A&6H5z=zV6={wHB6+?kBr9 z;T;&Zamkv>da6uwi1Cr#sdgl!>+V5xWo3}8U98AU#KV5U|A
    *VyMEYNFr;=f@&;u9wB{8~vu)+}bvs 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ℜ0;nb=vt1fja#LT_Ajh$ND54$6@X#%>bUrdzG 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 z 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%0AS{qVUnjAL64owoP@L(vp9r(tO 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 Date: Fri, 4 Sep 2026 17:01:42 +0000 Subject: [PATCH 13/13] docs: say ten percent, not a tenth The heading read as a 10x; the number is 10 to 12% fewer tokens. --- docs/benchmark.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/benchmark.md b/docs/benchmark.md index dc6d2f8..f4360e1 100644 --- a/docs/benchmark.md +++ b/docs/benchmark.md @@ -293,7 +293,7 @@ and V2's 31, at the lowest tokens of the three (191k against 232k and 302k). Telling the model what the result reports is what steers; asking it to relay the receipt was never the active part. -### Cost: a tenth off, reproducibly +### Cost: ten percent fewer tokens, reproducibly Sums over questions of the per-question median; two passes each of V0 and V3: