Skip to content

find: an exact-text tool that replaces grep over the index - #43

Open
ekechinwokah wants to merge 13 commits into
mainfrom
feat/find-tool
Open

find: an exact-text tool that replaces grep over the index#43
ekechinwokah wants to merge 13 commits into
mainfrom
feat/find-tool

Conversation

@ekechinwokah

@ekechinwokah ekechinwokah commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

find, the measured tool surface, and 0.5.0

Against main (eec2fe7: search, sql, reindex), this branch adds find, measures the whole tool surface lever by lever, and ships the surface that won: three tools, trimmed prose, reindex removed. Everything below is measured on the same pinned repo (infino @ ed4e020), model (claude-sonnet-4-6), harness and question sets.

main vs shipped, Sonnet

Sums over questions of the per-question median. main from the find comparison run (pinpoint 2 repeats, shipped set 3); shipped build (V8) from the surface run (3 repeats). Known-file and by-meaning sets were added for the surface run and have no main pass.

set main tokens shipped tokens Δ main cost shipped cost calls main → shipped
aggregation (10) 186k 136k −27% $0.28 $0.31 11 → 15
comprehension (6) 719k 578k −20% $1.27 $1.07 30 → 29
pinpoint (8) 578k 204k −65% $0.68 $0.34 35.5 → 23
the 24 shared questions 1,483k 918k −38% $2.23 $1.72 (−23%) 76.5 → 67
prompt cost of the tool definitions, per turn ~880 tok tools + ~500 instructions ~680 + ~165 −39%

Where it comes from, in order of size:

  • find on lookups. Definitions, call sites and repo-wide inventories go from grep-then-read to one to three find calls; the env-var inventory's worst main run was 23 calls and 380k tokens. find returns every matching line as path:line, complete and unranked, with per-file counts, and every hit is a real occurrence (token match picks candidate chunks, each line is checked for the literal, no file is scanned).
  • The surface. Descriptions cut to question shape, not-for, and result shape; instructions cut to a routing table; reindex gone. Selection did not depend on the prose: sql first on 30/30 aggregation runs and search first on 18/18 comprehension runs on every build measured, long or short.
  • Three sentences kept because the runs showed them load-bearing: 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 across all runs), and that lang is the file extension (every trimmed build paid a three-call detour without it).

Quality

Blind pairwise judge (claude-opus-5, Read/Grep/Glob on the clone, both answers in random order, unsupported-claim counts):

  • find build vs main: 22 to 29 with 13 ties over 64 pairs, unsupported claims 143 vs 159. Level. find answers carried 111 line-ranged citations on the lookup set against main's 22.
  • shipped surface vs the find build: 51 to 33 with 23 ties over 108 pairs, unsupported claims 236 vs 302, ahead on every set but comprehension (7 to 7). The aggregation gain is a second search after sql on the "with reasons" questions: the answers cite their reasons instead of inventing them.
  • No out-of-bounds line citation on either build.

Not shipped, measured and rejected

  • Renaming search to context: 18/18 first calls either way, by-meaning tokens higher. Branch exp/surface-v4a.
  • A tiered search result (content on the top 3 hits, one-line excerpts below): −11% dollars, +35% round trips, judge level. Branch exp/surface-v5.

Breaking

  • The reindex MCP tool is removed. 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, cx index --full rebuilds from a shell. 0.5.0; package.json, plugin manifest, .mcp.json pin, server.json and the CLI version move together (the last two had drifted at 0.1.4).
  • Tool descriptions and server instructions changed; skill, README, FAQ, llms.txt, context7.json, wiki notes and the architecture diagram say three tools.

Verification

  • npm run build, npm test: 133 tests green at the tip.
  • Pinned clone, index built once, auto-sync off, so every run saw the same index. Pass-to-pass spread on one build is 4–12% per set on Sonnet; the deltas above are outside it.
  • Per-question first-call counts flip on unrelated wording elsewhere in the surface (the env-var question: find 3/3 under one build, Grep 3/3 under a neighbour differing by another tool's name). Set totals are the unit; one question is not.

Full tables, the Haiku passes, and the per-lever ablation: docs/benchmark.md, sections "find, the grep replacement" and "The tool surface: names, shapes, and prose". Harness: bench/.

Follow-ups

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.

@ashishmishra26 ashishmishra26 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the implementation, the tool surface, the CLI path, tests and the doc rewrite, and ran it head to head against main on the same repo, questions, model and judge (details in the report I shared separately).

Design. This argues its way past the three-tool rule correctly. find is unranked and complete where search is ranked and top-k, so it is not a near-duplicate, and the rewritten rule ("a new tool must answer a question none of these four does") is a better rule than the one it replaces. The two-step shape, token intersection to pick candidate chunks and then a literal check per line, is right: no file is scanned and every hit is a real occurrence. The overlap dedupe by path:line is the subtle part and it is handled.

Measured. Quality is level: blind judge 29/22/13 (main/find/tie) over 64 pairs, unsupported claims 159 vs 143, no out-of-bounds citation in 128 answers. Cost falls where the tool applies: -35% tokens, -17% dollars, -38% tool calls on eight exact-lookup questions, flat on the shipped question set. The agent opened with find in 12 of 16 lookup runs against Grep-first in 8 of 16 on main, so the description steers as intended. Two costs of a fourth tool showed up: about a thousand tokens per turn of prompt (a uniform 17k to 18k on single-call questions) and mis-selection on a read-one-known-file question, where the agent ran find three times and then read the file anyway.

Should fix before merge (each anchored inline):

  1. Expose per-file counts. Every match is already in memory before the limit is applied, so a {path, count} breakdown is free. Without it the one grep-shaped question find cannot answer is grep -c, and in testing the agent recognised that and went to Grep for it.
  2. Window the cited text around the match. Head-truncation at 240 characters can return a hit whose text does not contain the query.
  3. Validate --limit on the CLI path. Number("abc") is NaN and the clamp passes it through, so the command prints nothing and reports nothing.
  4. Say what find is not for in the tool description itself. The skill has the "known file: Read" row; the MCP description does not, and that is where the mis-selection came from.

Worth a look: candidate volume on very common tokens (fn, self) walks most of the table synchronously in the server process; a test that analyzerTokens agrees with the engine on a fixed corpus, since the mirror will break silently if the analyzer changes; a dedupe test for a line that lives in two overlapping chunks; partial propagation under the file cap; and the README/tradeoffs rewrite could cite the measurement rather than assert, and "matches grep's cost rather than beating it" undersells it.

Sequencing. #40's denial message still routes agents to bm25_search via sql. If this merges first, #40 needs a rebase and that message rewritten to name find.

Overall: this is a real improvement to the surface and the measurement supports the claim it makes. Item 1 is the one I would insist on, because it is the difference between "grep is optional" and "grep is still needed for one thing".

Comment thread src/core/searcher.ts
query,
ignoreCase,
matches: all.slice(0, limit),
total: all.length,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every match is already in all before the limit is applied, so a per-file breakdown is free here: something like byFile: [{path, count}] alongside total and files. Without it the one grep-shaped question this tool cannot answer is grep -c; in testing the agent recognised that and went to Grep for it every time. This is the one item I would insist on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taken. byFile on the result — {path, count}, most matches first with the tie on path — computed over every match before the slice, so it stays whole when matches is cut, and files is now its length rather than a second set. the cli gets -c / --count and prints path: count like grep -c. the description names it as the grep -c answer, so that last reason to leave for Grep is gone. 126 tests green on the head.

Comment thread src/core/searcher.ts Outdated
all.push({
path,
line: m.line,
text: m.text.length > FIND_LINE_CAP ? m.text.slice(0, FIND_LINE_CAP) + "..." : m.text,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Head-truncation at 240 characters means a match past column 240 returns a hit whose text does not contain the query. An agent reading that will conclude the tool is wrong. Slice a window around the first occurrence instead (and mark both ends when cut).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taken. matchLines now returns the column of the first occurrence and a new excerpt cuts a window of the same cap around it, with ... on whichever ends were cut — sixty characters of lead so the excerpt shows what the match sits in rather than opening on it. pinned in unit tests and on a 900-character fixture line with the marker at column 600.

const handle = openIndex(opts.path);
const result = find(handle, text, {
ignoreCase: opts.ignoreCase,
limit: opts.limit === undefined ? undefined : Number(opts.limit),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Number("abc") is NaN; Math.max(1, NaN) is NaN; matches.slice(0, NaN) is [] and all.length > NaN is false, so cx find x --limit abc prints nothing and sets no truncated. The MCP schema catches this, the CLI does not. Parse and reject non-integers here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taken, in find itself rather than the cli: a non-integer or sub-1 limit throws limit must be a positive integer, got NaN, so cx find x --limit abc dies with that instead of printing nothing, and the mcp path keeps the same rule behind its schema. over the cap still clamps — a big number is a valid wish, not a mistake.

Comment thread src/mcp/server.ts Outdated
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 " +

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth one more sentence on what find is not for. The skill has the "a file you already know the path of: Read" row, but this description is what the agent actually sees. In testing it ran find three times to pull version lines out of Cargo.toml and then read the file anyway, in every run of that question.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taken. the description now says it outright: not for a file you already know — Read that file — find locates occurrences across the repo, and Read path:line for the code around a hit. the skill's find section carries the same line so both surfaces steer the same way.

also flagging so it doesn't get conflated later: #40's deny message still routes to bm25_search via sql. when this lands, #40 rebases and that message names find.

Comment thread src/core/searcher.ts
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 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: with mode: "and" on a very common token (fn, self) this returns most of the table and the loop below splits every chunk synchronously in the server process, blocking auto-sync and other requests while it runs. Fine at ~5k chunks; worth measuring at the 20k-file cap, or capping candidates and degrading total to a lower bound.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not taken here, and agreed it's the open cost. the work is bounded by the chunks that contain every query token, so it only degenerates when the whole query is one ubiquitous token — then the candidate set is the table, read synchronously in the server. a candidate cap would turn total into a lower bound and quietly break the completeness that is the tool's whole point, so i'd rather measure it at the file cap first and decide with a number. recording it as the follow-up.

Comment thread src/core/searcher.ts
* 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[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: this mirrors the engine analyzer client-side and will break silently if the content column analyzer ever changes. A test asserting analyzerTokens agrees with the engine on a fixed corpus would catch that; the current tests only check the JS against itself. A dedupe test for a line that lives in two overlapping chunks would also be worth having, since that is the subtle correctness property.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taken, both. the integration suite now runs five edge-case strings — underscore and punctuation splits, digits, a dropped non-ascii run — through tokenMatch twice, raw text to the engine and the mirror's tokens, and asserts the same candidate chunks come back, so a change in the content analyzer fails the build instead of shrinking recall silently. the dedupe case is a 130-line text fixture whose line 55 sits in two 60-line windows; the test asserts the overlap is real in the table before asserting find reports the line once. partial propagation is pinned in the same block.

Comment thread docs/tradeoffs.md Outdated
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undersells it. On eight exact-lookup questions it beat the grep path by about a third in tokens and a sixth in dollars, because grep answers need follow-up reads to turn a match into a cited line and find does not. The README/AGENTS rewrite could cite the measurement rather than assert the four-tool design the way the old text asserted three.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taken in part. the section now says what the saving is — a grep hit still needs a read to become a cited line and a find hit already is one — and drops the 'matches grep's cost' line. the numbers stay out of the prose until the run lands in docs/benchmark.md with the rest of the measurements; a figure quoted from a review comment ages badly. the readme rule text is the design statement, the benchmark is where the evidence goes.

…iption

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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
ekechinwokah added a commit that referenced this pull request Sep 4, 2026
The message told the agent to reach for bm25_search through the sql tool
for exact identifiers, which was the workaround before find existed. It
now names the three tools by question: find for every line containing an
exact string (what grep did), search for meaning, sql for counts and
rankings. Comments in both the source and the shipped CJS copy say the
same. Merge after #43, which adds find.
The PNG is rendered from docs/architecture.svg (sharp, at the shipped
1984x1344), so the tool list in the picture matches the surface.
The heading read as a 10x; the number is 10 to 12% fewer tokens.

@ashishmishra26 ashishmishra26 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fast turnaround on the threads. I checked each fix in the head: byFile plus cx find -c, the excerpt window with both cut ends marked, the limit check shared by CLI and MCP, the known-file steer in the description and the skill, the analyzer-mirror test against the engine, the overlap-dedupe and partial tests, and the tradeoffs rewording. All good, and I agree with deferring the candidate-volume question: a cap would break the completeness the tool exists for. Measure at the file cap first.

The branch has grown since the review, though, and some of what it now carries should not merge in this PR.

1. Split the PR. Everything up to and including the bench scripts and the measurement docs is the find change and its review follow-ups, and I would merge that today. The trimmed descriptions, dropping the "show the usage line" request, removing reindex and the version bump are a second change with a different risk profile: one of them is breaking for MCP clients. They deserve their own diff, their own review and their own release note.

2. Drop the release commit. Release on merge only tags when a release-* PR opened by the train merges. Merging this PR with package.json at 0.5.0 tags nothing and publishes nothing. The next daily Release prep run classifies the src/ changes since v0.4.0 as minor and runs npm version minor on top, which yields 0.6.0. In between, main's plugin manifest and .mcp.json point at @infino-ai/code-context@0.5.0, which will not exist on npm, so a plugin install in that window fails. Let the train cut the version and stamp the pins; that is what it does.

3. reindex is being removed on the wrong evidence. The bench freezes the index (auto-sync off, tree pinned), so no agent in it ever has a reason to call reindex. "Nothing called it in 216 runs" measures the bench, not the need. The tool is for a session where the tree changes under the agent, or where auto-sync is off, and at least one downstream client (Lantern) allowlists it. Removing it may still be right, but it should be decided on sessions where files change, land in its own PR, and carry a breaking-change note.

4. Retiring the "show the usage line" request is a product decision, not a cleanup. It changes what every user sees after every answer. The study makes a good case that the steering came from the field description, and the field stays, but the visible receipt was a deliberate choice and the call should be made explicitly, not inside a description trim.

5. Before anything here merges, please reword the messages of 63fe538 and dacab7e, and line 224 of docs/benchmark.md. They cite a document that is not in this repository. History and docs here are public; the design rationale belongs in the docs page itself or nowhere.

Suggested order: rebase this branch down to the find work plus the bench and measurement docs with item 5 fixed, merge it, let the train release it, then open the surface PR on top.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants