← Back to blog

More features we removed

A while ago we wrote about the features we deleted: six commands, a backend we'd already built, and a hard line that the command-line tool would never call a model. In the months since, we took out a good deal more, and every cut pointed the same way the first ones did: towards a smaller, more deterministic tool that fits in your head.

The model kept shrinking

The first post drew a line at the command-line tool: no LLM in the CLI. What it didn't spell out is that a model still ran in a couple of places further back, away from that line. Two of them are gone now. One is still there, on purpose, and I'll come to it.

The first was indexing. To build the search index, the tool used to ask a model to write a short summary of each chunk of code. It worked, but it made indexing slow, non-deterministic between team members, and dependent on a key being configured before you could get a usable index. We replaced it with a structural summary composed offline: no model, no key, nothing leaving the machine, and byte-identical on every run. Indexing no longer calls a model at all.

The second was an exploration command. inkentry explore ran a model in a loop: search, walk the code graph, read a few files, answer. It demoed well. But it was, honestly, a small agent living inside the app. For the coding agent that calls inkentry this was a downgrade. So we deleted the command and the server route behind it. The multi-hop workflow it used to do is now a few lines of instruction the caller's own agent follows over the same primitives: search, the graph, reading chunks. A better model runs the loop, and your harness gets to pick how to interpret the data.

The one still there, on purpose, is harvest, which reads your git history and writes memory. It's the one that earns a language model, because it produces a durable record rather than an answer you read once and throw away. For now it is the only feature that reaches for one. Nothing in the path of a search reasons or phones a model to think, so the same question comes back with the same answer.

One search, no modes

After the first round of cuts, search still asked you to make a choice. A --mode flag let you pick a retrieval strategy: text, semantic, a blend of the two, or a structural match. Picking the strategy is exactly the kind of decision the tool should take off your hands, not hand back to you, so --mode is gone. In its place is one search that blends full-text and semantic ranking and returns the best result it can. Semantic search didn't go away with the flag; it stopped being a mode you choose and became the default. The only knobs left are a couple of plain filters (--only-text, --only-code, --only-memory) for the rare time you want to narrow the corpus yourself.

One of those modes had a whole engine behind it: a structural search that walked your working tree directly, matching code by shape. Removing the mode let us delete the engine and a dependency along with it. It had earned its place once. It was there to give you something useful in the first minute, before any index existed, back when the alternative was waiting on one. That reason has quietly expired. We spent a good while re-ordering how indexing works, so a plain inkentry init makes the tree searchable in a minute or two: full-text is there straight away, and the richer semantic layer fills in behind it, most important code first. With a usable index arriving that fast, a separate engine that scans files with no index stopped being worth keeping. When search can't yet use the semantic index it falls back to full-text, which has covered every chunk since the moment the tree was parsed. One fewer moving part, and the read paths that remain all read from the index.

Three search commands became one

Search used to be spread across three commands. search looked through code. graph traced how a symbol connected to the rest of the codebase. memory search looked through the decision log. Three commands, three output shapes, three things to hold in your head for what is really one question: where is the thing I'm looking for, and what is attached to it.

We folded them together. A single search now returns code and memory interleaved in one ranked list, so a question can come back with the function and the decision that shaped it sitting next to each other. The graph became a flag on that result rather than a command of its own, and the exact-edges view moved to a plumbing command for scripts and agents. memory search became a filter on the one search.

The uncomfortable part is that this breaks anything that parsed the old commands or the old output shape, and we didn't keep them alive as aliases. We made the cuts while the command surface was still ours to change, rather than carry the old commands forward and be bound to them.

The smaller cuts

A scatter of lesser removals went the same way.

inkentry check is gone. It had quietly become four unrelated jobs behind one verb: is the index fresh, is the server healthy, which files are stale, what work overlaps. Each of those is now the command that actually owns it, and the one verb that implied they belonged together isn't there to mislead you.

Four commands for moving memory between machines (push, pull, watch, and a point-in-time query) collapsed into a single sync for the ordinary two-way case, with a lower-level command underneath for scripts. The streaming and point-in-time variants weren't earning their place yet, so they came out rather than sit in the help text half-used.

What the removals bought

Every one of these pointed in the same direction as the first round of removal. Shrinking the model down to harvest made indexing and search deterministic, with nothing to configure and nothing in a query's path that thinks. Collapsing the modes and the three search commands into one made the surface small enough to keep in your head. Deleting the exploration loop settled, without exception this time, what the tool is. inkentry retrieves context, and you, or the agent working alongside you, reason over it.

None of that reads like progress in the usual sense. It's a list of things that stopped existing. But I still think you learn more about a tool from what it refused to keep than from what it kept adding, and the parts we took out this time are the reason the parts that stayed are easier to understand and use.


inkentry is open source and code-aware, callable from whatever agent you already use. It runs on your machine, with nothing in the path of a query that phones a model to think. Repo and docs: inkentry.com.