| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| semlith-v0.16.0-aarch64-apple-darwin.tar.gz | 2026-09-14 | 16.2 MB | |
| semlith-v0.16.0-aarch64-unknown-linux-gnu.tar.gz | 2026-09-14 | 16.2 MB | |
| semlith-v0.16.0-x86_64-pc-windows-msvc.zip | 2026-09-14 | 14.6 MB | |
| semlith-v0.16.0-x86_64-unknown-linux-gnu.tar.gz | 2026-09-14 | 17.2 MB | |
| SHA256SUMS | 2026-09-14 | 448 Bytes | |
| README.md | 2026-09-14 | 8.5 kB | |
| Semlith 0.16.0 source code.tar.gz | 2026-09-14 | 907.1 kB | |
| Semlith 0.16.0 source code.zip | 2026-09-14 | 955.4 kB | |
| Totals: 8 Items | 66.1 MB | 0 | |
0.15.0 made the free product honest and cheap. It did not make it better at finding things. This release spends the harness that release built on the ranking itself, and reports what the harness said — including where it said no.
The graph list stops being a set and becomes a ranking
- Graph neighbours are ranked by personalised PageRank. 0.15.0 expanded one
flat hop from the top eight hits and weighted the result by the confidence of
the edge that reached it. That treats every neighbour of every seed as equally
related, so a symbol reached once from a weak hit ranked alongside one reached
from three strong ones — a set, not a ranking. The walk is now seeded with each
hit's own fusion contribution, splits a chunk's mass between the symbols it
holds, and pushes it outward three rounds at 0.85 damping. Neighbours are still
read one name at a time and are cached, so a name is queried once however many
rounds run and only the frontier is ever held;
graph::MAX_NODESstill bounds it, and ambiguous edges are still refused. - A symbol question is answered with one evidence block.
semlith symbolandsemlith_symbolreturn the definition, the resolved callers and callees, and the ring two hops out, together. That used to be three tool calls, two of which had to be made before the caller knew whether the first had found the right symbol. The second ring is walked only through first-ring names that areextractedorresolved: an ambiguous name is several unrelated definitions wearing one label, and expanding through one would put another symbol's callers into this one's context. - An edge records the line the call was written on.
edgesgains a nullablelinecolumn, so "where is X invoked" returns the call rather than the enclosing definition, which can be hundreds of lines away. NULL on every row an older binary wrote, and a renderer prints nothing rather than falling back to the definition line — a wrong call site is worse than no call site. - A walk crosses a re-export.
pub use x as y,export { x as y },from x import y as zand Go's aliased import becomealiasesedges, andaliasesjoins the kinds a path may follow. A walk that refused to cross a rename answered "not connected" about code that is connected. A re-export that does not rename adds nothing: there is no name change to cross.
Search reads the query before it ranks the answer
- An identifier and a question are weighted differently. FTS5 is exact about a name and vague about a sentence; the embedding is the other way round, and both halves were being scored as though equally likely to know. An identifier-shaped query now weights the keyword list twice; a question-shaped one leaves the two level. The rule is one token of identifier characters, deliberately crude and entirely inspectable, and every surface reports which shape it read — a caller can only correct a misread shape if it can see that one happened. The harness says this is the single largest win in the release: without it, hit@3 falls from 17 to 13 and hit@8 from 25 to 20.
prefer: code | docs | anyis the correction, on the CLI, over MCP and on/api/search. It multiplies rather than filters, soprefer: codeover a corpus of prose still answers with the prose.- The fused set is reranked by graph distance and freshness. Constants, no model, no learned weights; every input is something the store already holds, and both are tiebreaks — the whole span is under 1.5x, asserted — so a chunk the query matched badly cannot climb over one it matched well.
Two new tools, and a tool list that did not grow
semlith readreturns one span or one symbol and nothing around it: the second stage after a locate answer. A name with several definitions returns the list rather than choosing. Reads come from the store's chunks and never from disk, so the boundary that governs indexing governs reading.semlith patternruns a tree-sitter structural query over the indexed files of one language — the questions a regex cannot ask. The grammar comes from the extractor's own table, so a languagepatternaccepts is one the graph accepts. An invalid pattern returns the parser's own message rather than an empty list, because a caller handed "no matches" concludes the code lacks the shape.- Twelve tools now fit the budget ten used to.
tools/listis 3 995 bytes, about 999 tokens, against 3 940 for ten tools in 0.15.0. What paid for the two new tools was theitemsschema on the array properties:path,ext,langandstorehave only ever held strings and their names say so. ThereadOnlyHintannotations stayed — a client acts on those, and buying bytes by making every tool need an approval prompt is the opposite of what the budget is for.
A store has one name
- The portal drew store chips the search route then refused.
/api/storesreports the name the daemon registered — what a person typed, and what the chips are made of — while the fleet every route answers from derived its own label from the store directory's basename. The two agree for a store in~/.semlith/stores/<name>, where the directory is the name, and disagree for every store opened by path: clicking the chip returned "no store called work is open; these are: store". All three fleets the daemon builds — the one it starts with, the one the portal opens lazily, and the reader forwarded MCP calls answer from — now take their names from the registry, sostore:means the same thing on every surface.
The portal
- Neither new tool gets a page, and that is the decision rather than an
omission.
readalready has a view: it is the Search page's second stage, where the button sits on the hit that raised the question with the span already in hand. A page of its own could only be started by retyping a coordinate you got from Search.patterntakes a tree-sitter query in S-expression syntax, which nobody writes from memory — a page for it is a box you can only fill by pasting from documentation, which is a worse manual rather than a view. Both tools work on the CLI and over MCP, where the caller is an agent that can write the query, and the Agents page lists both with what they are for.tests/portal.rsrecords the reasoning besidestartandmcp, which are exempt for their own reasons. - The Search page rebuilt to that design: the query-shape hint, file cards with
span summaries, word-pill fusion badges, the provenance sentence, a signature
strip, a line-number gutter, a read-whole-symbol control, and an ego-graph
panel. The
Prefercontrol is live rather than a disabled promise, and the shape hint is drawn from what the server actually ranked with rather than from a second classifier in the browser. - Three bugs found by rendering the pages rather than reading the diff: image
previews were handed a
blob:URL the page's own CSP had always refused and so had never worked; the Read and Pattern views could let a slow earlier answer overwrite a newer one; and the graph canvas leaked an animation loop per visit. - The Languages page is gone. Its list lives on About, where the design puts
it.
semlith languages,semlith_languagesand/api/languagesare unchanged.
What the harness said no to
A third rerank factor — a lift for a chunk sitting inside a named definition —
was specified, built, measured and removed. In a code repository nearly every
code chunk sits inside a definition and nearly no prose chunk does, so it is a
second and blunter prefer: code applied to every query, including the ones that
asked for prefer: docs. It costs two hits at k=3, and prefer-code-over-prose
ranks first without it and third with it. It is recorded in the release's
iteration log rather than quietly dropped.
Install
macOS and Linux
:::sh
curl -fsSL https://raw.githubusercontent.com/semlith/semlith/main/install.sh | sh
Windows
:::powershell
irm https://raw.githubusercontent.com/semlith/semlith/main/install.ps1 | iex
Or download the archive for your platform below, unpack it, and put
semlith on your PATH. Intel macOS has no prebuilt binary: ONNX
Runtime no longer publishes x86_64 macOS builds.
Verify a download against SHA256SUMS before running it.