Log message:
sem: update to 0.21.0.
## [0.21.0] - 2026-07-10
### Added
- **Cloud-enabled `sem diff` now creates an immutable, owner-private hosted \
review URL.** Snapshot uploads include changed-entity caller/callee relations \
and truthful Git provenance: branch, scope, base/head refs, and available SHAs. \
Working-tree reviews explicitly report `HEAD → WORKTREE` rather than implying \
that uncommitted changes exist on GitHub.
- **GitHub login can now be exchanged for a revocable CLI session.** Raw GitHub \
credentials are not persisted as sem credentials, and the CLI session resolves \
to the same stable principal used by web reviews.
### Fixed
- **Hosted-review upload failures are visible without breaking the local diff.** \
`sem diff` still exits successfully with its complete local result, while stderr \
explains that the private review could not be uploaded.
- **Transitional cloud repository states refresh immediately.** `sem whoami` no \
longer leaves a repository stuck at a cached `pending` state after cloud \
indexing has completed.
- **`sem diff` now collapses contiguous line chunks on unsupported files into \
one summary line.** When a file has no grammar, sem falls back to fixed 20-line \
chunks, so deleting or adding one previously printed a wall of `⊖ chunk lines \
1-20 [deleted]` / `21-40` / `41-60` … lines that ate context for no \
information. Contiguous chunks of the same change type now consolidate to a \
single line, e.g. `⊖ 13 chunks lines 1-246 [deleted]`. Verbose mode (`-v`) \
is unchanged, since it still prints per-chunk content. Thanks @graipher for the \
report (#466).
### Performance
- **`sem context` now answers from an indexed point query instead of loading the \
whole graph, so it scales to millions of entities.** It previously hydrated \
every entity (plus decompressed bodies) just to answer about one, so on a \
2.3M-entity repo each call took ~15s. The SQLite cache is already normalized and \
indexed, so when the git oracle proves the cache fresh (no filesystem walk) `sem \
context` now builds only the k-hop neighbourhood around the target straight from \
the store: batched `IN (...)` edge queries, bodies fetched per hop, stopping \
once there is enough content to cover the token budget so a hub entity's fan-out \
does not explode the fetch. It reuses the existing packer on that subgraph, so \
output is byte-for-byte identical to the full-graph path, and falls back to the \
full load whenever the oracle declines. On the Linux kernel (2.31M entities) \
`sem context` drops from ~15s to 0.44s per call; on Kubernetes (520k) from ~5s \
to ~1.2s.
- **Default (`All`-mode) `sem impact` now answers from the indexed cache too, \
instead of hydrating the whole graph.** A full cache stored entities and edges \
but not test flags, so All/Tests-mode impact (which includes the "covered \
by N tests" answer) fell through to a full-graph load — ~3.5s on \
Kubernetes, ~10.7s for an unbounded `--depth 0`, even for a tiny blast radius. \
The full save now records test flags (shared with the topology save) behind a \
metadata marker, so the existing point-query path can serve All-mode impact \
straight from indexed edge queries. Output is identical to the full-load path \
— verified byte-for-byte against it, including the tests field. On Kubernetes \
(520k entities) default `sem impact` drops from **~3.5s to 0.04s**, and `--depth \
0` from **~10.7s to 0.04s**. Caches built before the marker still take the full \
path, so nothing regresses.
- **The resident MCP server no longer holds the whole graph in RAM by default, \
cutting idle memory dramatically.** It used to proactively build and keep the \
entire deserialized graph in memory on startup so the first query would be warm \
— ~671MB on Kubernetes, ~5GB on the Linux kernel. But `context` and `impact` \
now answer from the indexed cache directly, and the CLI's fast paths bypass the \
resident entirely, so that proactive hold is mostly wasted memory. Prewarm is \
now opt-in (`SEM_PREWARM`); by default the resident stays light and builds the \
full graph lazily, only when a query that genuinely needs it (graph/diff/text) \
runs. On Kubernetes an idle resident drops from **671MB to 10MB**.
## [0.20.0] - 2026-07-05
### Changed
- **Indexing now shows a staged loader with a real, whole-build progress bar, \
not a single "Building entity graph" spinner.** A cold graph build \
renders each phase sem-core reports as a persistent `◆` line — `Scanning \
files — N found`, `Parsing code — done` — and a **single filling bar with \
a live percentage spans the entire build**: the build is two passes over the \
file set (parse, then resolve), so the bar's length is 2×files and its position \
is (files parsed + files resolved). It tops out at ~50% when parsing finishes \
and only reaches 100% when resolution actually completes — so 100% means \
genuinely done, not "parsing done." Fed by two lock-free per-file \
counters (`graph_parse_done`, `graph_resolve_done`) via phase hooks. Ends with \
the existing `✓ N entities · M files in …ms` summary. Warm cache fires \
nothing and stays instant; TTY-only, so agents, pipes, the MCP server, and CI \
see nothing (the bar's poll thread never even spawns off a terminal).
- **`sem setup` now shows a staged progress loader instead of a flat list of \
check lines.** Setup runs as a small tree of steps — `git diff → sem diff`, \
`Claude Code hooks`, `pre-commit hook` — each with a live braille spinner that \
resolves to a green `◆` (did something), a dim `·` (nothing to do / not \
applicable, e.g. not in a git repo), or a yellow `⚠` (left a file untouched on \
purpose, e.g. an unparseable `settings.json`). It ends with a one-line summary \
and the `sem unsetup` revert hint. Same idempotent behaviour, just legible at a \
glance.
### Added
- **sem-core: `set_build_phase_hook` / `clear_build_phase_hook` / `BuildPhase` + \
`graph_parse_done` + `graph_resolve_done`** — an optional per-thread callback \
at graph-build phase boundaries (parsing, resolving) plus a lock-free counter of \
files parsed, so a front-end can render staged progress and a live parse bar. \
No-op for every caller that doesn't read them.
## [0.19.0] - 2026-07-05
### Removed
- **Removed `sem orient` and all fuzzy/ranked retrieval.** The `orient` command \
and its `--pack` briefing, the sem-core ranking (lexical scoring, IDF, recall \
net, structural priming), the `sem_entities query=` intent-search mode, and the \
resident server's `orient` socket op are all gone. Ranking a natural-language \
task to the right entity proved unreliable — a 45-task validation showed the \
ranker's hit-rate (~47%) could not be lifted by heuristics without causing \
regressions — and it was the only non-deterministic thing in sem. sem is now \
purely deterministic: `context` (read an entity plus its callers/callees), \
`impact` (blast radius), `diff`, `entities` (list by path, or `text=` for \
exact-substring search), `blame`, `log`. To find code whose name you don't know, \
use a plain text search to get a candidate name, then hand it to `sem context` \
for the structure grep can't give. The prompt-submit hook keeps its \
deterministic exact-name prefetch and no longer shells out to th
e ranker.
### Fixed
- **Dot-chain extraction is now linear, not quadratic, in file size.** \
`extract_dot_chains_with_positions` computed each match's line number by \
counting newlines from the start of the file every time, so on a large file \
dense with `a.b` chains the cost was O(matches times filelen). Since the regex \
yields matches in increasing byte order, it now tracks the line number \
incrementally and counts only the newlines since the previous match, which is \
linear overall and produces identical one-based line numbers. Verified \
byte-for-byte identical graph output on React (34,251 entities, 73,702 edges). \
No change for typical files; it removes a cliff on very large generated or \
minified sources.
- **Structural hashing no longer allocates a Vec per AST node.** The two \
structural-hash walkers (`hash_structural_tokens` and its name-excluding \
variant) collected every internal node's children into a fresh heap `Vec` (plus \
a fresh tree-sitter cursor) just to push them in reverse, despite a comment \
claiming zero allocations. They now reuse a single cursor and push children in \
place, reversing the appended slice, which is byte-for-byte identical output. On \
a cold graph build this removes roughly 300k allocations (structural hashing \
alone dropped from about 319k allocations to 13.5k, measured with dhat on deno). \
Peak RSS is unchanged and wall time is within noise under mimalloc, but the \
churn reduction helps memory-constrained and non-mimalloc builds. Hashes are \
verified identical across 3,337 entities, so rename detection and existing \
caches are unaffected.
### Added
- **`sem setup` now makes sem a Claude Code session default (macOS/Linux).** \
Beyond the `git diff` alias, it installs two session hooks into \
`~/.claude/settings.json`: a warm resident graph (SessionStart runs `sem mcp \
--resident` detached, so structural queries answer in single-digit ms instead of \
rebuilding) and prompt-time context injection (`sem hook prompt-submit`). The \
JSON edit is idempotent, backs up `settings.json` first, refuses to touch a file \
it can't parse, and preserves every existing user hook and key; `sem unsetup` \
removes exactly the sem hooks and cleans up empty arrays. Local warmth is free \
and login-free — cloud (`sem login`) is repositioned in the README as the \
scale/team/CI upgrade, not the way to get warmth.
### Documentation
- **Benchmarks page rebuilt on the July 2026 paired-run data.** The docs site's \
benchmarks page now reports the real agent A/B numbers (grep+read agent vs sem \
agent on SWE-bench Verified bugs, hidden-test graded): 50-65% faster code \
understanding, verify loop 2.90s to 0.59s per iteration when call-graph edges \
resolve (bimodal, 1.2x floor disclosed), token parity stated plainly, and an \
explicit "what sem does not do" section including the unchanged solve \
rate. Retired the stale "75% fewer tokens" and "2.3x agent \
accuracy" hero claims. Changelog page gains entries for v0.17-v0.18 work \
with the lessons that produced them.
## [0.18.0] - 2026-07-03
### Fixed
- **sem now works on repos using git's reftable ref storage** (`git init \
--ref-format=reftable`, git 2.45+). Previously every command died with libgit2's \
cryptic `unsupported extension name extensions.refstorage`. libgit2 can't read \
reftable refs, but the object database and index are unchanged, so GitBridge now \
tolerates the extension and routes just the ref resolutions (`HEAD`, refspecs, \
revwalk starts) through the git CLI while libgit2 keeps doing everything else by \
OID. Verified end to end on a real reftable repo: working/staged/commit/range \
diffs, blame, and per-file history all produce identical results to a \
files-backend repo. One residual gap: the cache freshness oracle's direct \
`git2::Repository::open` is `.ok()`-guarded, so on reftable repos it just skips \
the acceleration (correctness unaffected). Requires `git` on PATH for the ref \
lookups. Thanks @bengry for the report and clean repro (#451).
### Added
- **Unique-method-name call edges (dynamic languages).** Attribute calls on \
receivers of unknown type (`index.keep_levels(...)`) previously produced no \
graph edge, hiding real dependents and blinding `--tests`. In Python/Ruby — \
where receiver types are statically unknowable — a method name with exactly \
one definition repo-wide now resolves to it: one candidate, one edge; any \
ambiguity, no edge. Static languages keep precision-first resolution (an \
unresolved receiver there is deliberate: shadowed import, instance property).
- **`Parent::child` entity qualifiers.** `sem impact \
"Dataset::set_index"` and friends now work everywhere `Parent.child` \
does (graph and cached lookups).
- **`sem impact --tests` lexical fallback + fast-path fallthrough.** Graph edges \
miss tests that call a target through a module namespace (`xr.where(...)` \
resolves to no entity), so `--tests` could answer "No tests found" for \
a function with dozens of tests. Now: an empty tests answer from the sidecar or \
disk cache is treated as non-authoritative and falls through to the full path, \
which backstops zero graph edges with lexical reachability — test entities \
naming the target as a whole word — clearly labeled as weaker evidence. Found \
live: an agent's graph-selected verify loop (run only the tests that reach your \
change) went from 0 selected tests to a 56-test net on `xr.where`, versus the \
1,900+ tests of whole-file runs.
- **Sharper `--pack` briefings.** Term ranking is now IDF-weighted (a term \
appearing in half the repo is worth almost nothing), `<details>` \
environment dumps in issue text are stripped before extraction, attribute \
accesses glued to receivers ("d2.loc") also emit their `.attr` \
suffixes as terms, and one of the three briefing slots goes to the top name-echo \
orient hit — for bugs where the issue names a surface API the culprit's body \
never mentions.
- **`sem orient --pack <tokens>`: turn-zero briefings from task text.** \
Feed orient a whole issue or task description and it returns a packed briefing \
— the top matching functions' bodies plus their immediate callers/callees — \
sized to the token budget. Ranking is body-term convergence: code-ish terms are \
extracted from the task text (flags, dotted names, identifiers) and entities are \
ranked by how many distinct terms their bodies contain, since issue vocabulary \
lives in bodies, not names. Built for prompt-time injection (the agent-side \
analog of the prompt-submit prefetch hook): the code an agent would spend its \
first turns foraging for arrives at turn zero. Honest calibration: on three \
ground-truth issues it put the exact target function first on two; issues that \
quote the tool's own output can still poison term extraction.
- **sem is published to the official MCP registry** as \
`io.github.Ataraxy-Labs/sem`, so MCP clients that browse the registry (VS Code, \
Cursor, Claude Code, and others) can discover and install the server directly. \
The release workflow now publishes each release to the registry via \
`mcp-publisher` (authenticated with GitHub OIDC, no extra secrets), backed by a \
`server.json` manifest and an `mcpName` field in the npm wrapper.
### Performance
- **Delta-fills: changed entities answer with a diff against the version your \
session saw.** The attention ledger now stores fill contents, so when a session \
re-asks about an entity that changed since its last look, the answer is an \
entity-level delta (`∆ alpha · changed since you read it … - x = 1 / + x = \
42`) instead of the whole packed body. Measured end to end: a post-edit re-ask \
that previously re-sent the body now costs 2 diff lines. Completes the ledger's \
answer set — new entity: full fill; unchanged: one line; changed: delta; \
`SEM_FRESH=1` / `fresh: true` always forces the full re-pack. Deltas larger than \
120 lines fall back to a full fill.
- **Attention ledger covers the MCP path.** `sem_context` (the tool agent \
sessions actually call) now runs through the same per-session fill ledger: an \
MCP server process serves exactly one session, so re-asks for unchanged entities \
collapse to one `≡ unchanged since you read it` line automatically — no \
environment variable needed. New optional `fresh: true` param forces a full \
re-send (for when context compaction dropped the earlier fill).
- **Attention ledger v1: repeated context fills collapse to one line.** The \
resident server now keeps a per-session ledger of every `context` fill it has \
emitted (entity id + content fingerprint). When the same session re-asks for an \
unchanged entity, the answer is a single `≡ unchanged since you read it` line \
instead of the full packed body — the body is already sitting in the asking \
model's context window, so re-sending it is pure token waste. Measured through \
the CLI socket path: 8,586 bytes first fill, 139 bytes on repeat (98.4% \
suppressed). Opt-in via `SEM_SESSION=<id>` in the environment; \
`SEM_FRESH=1` bypasses; anonymous calls are never suppressed. Any change to the \
target entity misses the fingerprint and re-sends in full. This is the first \
piece of the attention architecture (docs/attention-architecture.md): space \
(graph), time (commit index), attention (ledger).
- **`sem entities --text`**: entity-addressed text search from the CLI (the MCP \
tool already had it) — one line per hit (file, innermost entity, line, matched \
text) instead of whole bodies, served from the resident server's warm graph in \
milliseconds with a local-graph fallback. This is the token-cheap way for an \
agent to verify a call site or find a string: a body-level `sem context` costs \
hundreds of tokens where a text hit costs ~15.
- **Auto-resident server: every sem CLI query after the first answers in \
milliseconds, on any repo.** A socket miss now spawns `sem mcp --resident` \
(hidden plumbing) detached in the background: a server that holds the repo's \
graph warm and serves ONLY the per-repo unix socket, exiting on its own when \
idle for 30 minutes or when it loses the bind race to a live session. `sem \
context` and `sem orient` gain sidecar fast paths (impact already had one), so \
the full structural read loop runs against the warm graph: measured on a 30K-LOC \
repo, first `sem context` 0.68s cold (spawning the resident), then \
context/orient/impact all under 10ms. In a controlled agent benchmark (6 \
verified code-understanding questions, classic grep/read agent vs sem agent, \
identical prompts and batching guidance), the sem agent answered in 13s vs 37s \
with equal 6/6 correctness — 65% faster. `SEM_NO_AUTOWARM=1` disables the \
auto-spawn, `SEM_NO_SIDECAR=1` the fast path.
- **Token-efficient tool output**: the same answers at a fraction of the tokens \
the consuming model has to read (and pay for). (1) The context packer stops \
enumerating noise: related test entities are folded into per-role counts instead \
of packed as one-line "#[test]" stubs (unless the target itself is a \
test, when its test neighborhood is the question), bare attribute/comment \
signatures are skipped, and transitive tiers are capped at 25 entries per role \
with the remainder counted. What was dropped is stated explicitly in one line \
("not packed: +64 direct dependents (64 tests) · sem_impact lists \
them"), so the signal survives at a fraction of the cost. Measured: `sem \
context` on a hot sem-core entity 7,272 to 4,113 tokens (-43%, 119 to 48 \
entries); on a hot weave entity 5,216 to 3,498 (-33%, 146 to 27 entries, the 119 \
test stubs now one line). (2) The `sem_entities` MCP tool renders compact \
per-line trees (name · type · lines, children indented, files as group \
headers) ins
tead of pretty-printed JSON: measured 3.7x fewer tokens on a 115-entity file \
(5,028 to 1,361). Applies to the MCP path and query modes; CLI `--json` output \
is unchanged for scripts.
- **Semantic commit index (storage engine layer 2)**: history is now stored as \
entity deltas. Each commit is semantic-diffed against its first parent exactly \
once and persisted as entity-change rows in the cache (`commits` + \
`entity_changes` tables, sha-keyed and branch-agnostic); every later history \
query is a SQLite lookup plus a diff of only the commits git gained since. \
Measured on the sem repo: `sem log` over 500 commits drops from 4.46s to \
**0.03s** on the second query (~150x), with the first query as the one-time \
indexing pass and each new commit costing one incremental diff. Applies to `sem \
log` repo analytics (hotspots + co-change pairs) and the MCP `sem_log` tool; \
per-entity traces are unchanged. Aggregation is shared code between the live git \
walk and the store (`aggregate_history_analytics`), tested to produce identical \
hotspot/co-change output, so the two paths cannot drift. Merge-heavy history \
gets better semantics: each commit is attributed its own first-parent
diff instead of a diff against its arbitrary revwalk neighbor, and merge \
commits contribute no changes of their own (their first-parent diff restates the \
merged-in commits, which index individually). File-filtered queries index \
full-repo diffs once and filter at aggregation, so the first filtered query \
costs more than the old pathspec-scoped walk but every later query on any filter \
is instant. Cache schema v9; existing caches rebuild automatically on first use.
- **CLI sidecar fast path**: `sem impact` now answers from the resident `sem \
mcp` server's warm graph via its unix socket before doing any local work — \
measured **4.5ms** end-to-end on a 158K-LOC repo, versus 22.4ms for the local \
cold path and 7.7ms for a ripgrep scan of the same repo: the full blast radius \
(callers, dependencies, depth-bounded transitive impact, affected tests) is now \
cheaper than a raw text match. Output is byte-identical to the local path (the \
sidecar ships serialized `EntityInfo`s that the CLI feeds to its existing \
printers; verified across all modes and `--json`). The fast path is an \
accelerator, never a requirement: bounded socket timeouts and silent fallback \
mean no resident server (or `SEM_NO_SIDECAR=1`, `--no-cache`, custom scopes, \
`.semignore`, `--entity-id`) just runs the normal local path. Server-side, the \
new sidecar `impact` op classifies affected tests only among the entities the \
impact BFS actually reached, instead of walking the whole corpu
s per call (6.8ms → 0.1ms on a 4.7K-entity graph).
### Fixed
- Internal: rustfmt line-wrap missed in the #445 sidecar change; no behavior change.
- The context packer's token estimator was undercounting real tokens 2-3x on \
dense code (words x 1.3 vs the ~4 chars/token reality: a context reported as 661 \
tokens measured ~2,400 real tokens), silently overshooting every budget. It now \
takes the max of the word- and character-based estimates, so nominal budgets \
match what the consuming model actually pays.
- The context packer's "not packed" summary line pluralizes roles \
correctly ("transitive dependencies", not "dependencys").
- Workspace version bumped to 0.16.0: `ContextResult` gained the public \
`omitted` field (a breaking change for struct-literal constructors, flagged by \
cargo-semver-checks), and 0.x semantics put breaking changes in the minor \
version.
- The docs site deploys through workflow-based GitHub Pages \
(`.github/workflows/docs-pages.yml`: upload `/docs` verbatim, deploy) instead of \
the legacy branch-based Jekyll builder, which began failing repo-wide with \
zero-duration "Page build failed" errors on commits that didn't touch \
docs — including on direct build requests via the Pages API. The site is pure \
static HTML, so the legacy builder added nothing but a failure mode; deploys now \
also skip entirely on commits that don't change `docs/`.
### Changed
- The GitHub Action's PR-comment footer now tells the reader what to do next — \
"add it to your repo in 2 minutes", linking to the action's install \
snippet — instead of only naming the tool. Every entity-diff comment is seen \
by all of a repo's collaborators; the footer is the loop that turns viewers into \
installs.
### Added
- **`sem repos`** — where your code is stored, in one command. Two inventories \
side by side: the **cloud account** (authoritative `GET /v1/repos`: every \
indexed repo with status, entity/file counts, last-indexed time, indexed commit, \
and any indexing error rendered inline) and **local storage** (every entity \
cache under the sem cache root with size on disk, entity count, cache kind, and \
the repo it was built from). `--json` for scripts. Listing the account also \
reconciles this machine's `~/.sem/repos.json` mirror with server truth — stale \
entries (a repo registered mid-index stays "pending, 0 entities" \
forever otherwise) were silently mis-routing the local-vs-cloud decision for \
impact/context queries. Caches are now stamped with their repo root at save time \
(`repo_root` in `cache_metadata`); caches built before this show as unlabeled \
and self-label on their next rebuild.
- Fish shell support, via the `tree-sitter-fish` grammar (gated behind the \
`lang-fish` feature in `grammar-all`). Extracts functions — including the \
config.fish pattern of definitions inside a top-level `if status is-interactive` \
block — and resolves fish call edges (a `command`'s name against repo \
functions, builtins excluded), so `sem impact` sees which fish functions call \
which. A `function` defined inside another function stays part of the outer \
entity's content, matching fish's runtime semantics (inner definitions become \
global, not lexical children). Previously `.fish` files fell back to generic \
line-based chunking with an unsupported-language warning. Thanks @thalys for the \
request (#433).
### Documentation
- **First-principles page on the docs site** (`docs/first-principles.html`, \
linked from every page's nav): four charts explaining why the recent latency \
work changes what an agent can afford to do, not just how fast it runs — the \
scan-vs-index crossover (a text scan pays per byte; residency removes the \
index's ~800ms hydrate floor, so the constant-time line wins at every repo \
size), the ~100ms human-perception threshold every new path now sits under, \
model turns as the real cost unit (3 → 2 → 1 inference turns per structural \
answer via one-call lookup, then prompt-time prefetch), and tokens per answer \
(the measured ~15% entity-tree-vs-JSON ratio). Measured numbers come from this \
changelog; model curves and turn timings are labeled illustrative on the page. \
Charts are dependency-free inline SVG with hover tooltips and a table view each.
### Performance
- **Content-store cache (storage engine layer 1)**: the entity cache no longer \
duplicates source text per entity. Each file's text is stored once (zstd) in a \
`file_contents` table, and any entity whose body is provably a byte slice of it \
(`content == file[start_byte..end_byte]`, verified at save time) stores NULL \
content and is re-sliced on load; unprovable entities (no spans, normalized \
endings) keep content inline. On a 139K-entity corpus (fresh cache both sides) \
this cut the cache 20% (269MB to 216MB; the content layer itself −58%, 80MB to \
24MB inline + 10MB zstd), engaged for 77% of entities. Honest costs: warm \
full-content loads pay ~0.13s extra for decompress+slice on that corpus (0.38s \
to 0.51s); topology loads and the MCP server's in-memory hot path are \
unaffected, and cold build time is unchanged within noise (peak RSS ~−5%). \
Correctness gates: byte-identical graph vs the previous binary on the full \
corpus, byte-identical entity content round-trip (including mult
i-byte unicode and nested entities), and incremental saves keep the file store \
in sync with entity deletes. Cache schema v8 — existing caches rebuild \
automatically on first use.
### Added
- **Entity-addressed text search**: `sem_entities` takes a `text` parameter — \
an exact substring searched across entity bodies in the warm in-memory graph (no \
file reads). Hits come back addressed by the innermost enclosing entity (`file: \
entity (Lline): matched text`), ready to chain into `sem_context`/`sem_impact`, \
in ~20-30ms warm on an 85K-LOC repo. This retires the main remaining reason \
agents fell back to grep (strings, error messages, config keys); misses say \
honestly that comments between entities and non-code files are not covered.
### Performance
- Graph build: the scope resolver no longer allocates its debug resolution log \
(several owned strings per reference, discarded by every production path — \
only a bench consumed it), and edge dedup is index-based instead of cloning both \
entity IDs per edge into a hash set. Output is byte-identical (proven \
edge-for-edge on a 139K-entity build); ~1-3% fewer instructions retired. \
Groundwork toward #320/#322 — the remaining peak-memory work (entity content \
sharing, ID interning) is tracked there.
### Added
- **`sem hook prompt-submit`** (hidden plumbing): the prompt-time prefetch, \
compiled. Reads a Claude Code UserPromptSubmit event, extracts identifier-shaped \
tokens from the prompt (backticked, snake_case, CamelCase, qualified — never \
plain words), resolves them against the resident server's socket sidecar, and \
prints packed entity context for injection. **10ms end-to-end** (was ~40ms as a \
Python hook — interpreter startup and a git subprocess, both eliminated: repo \
root is found by walking to `.git` in-process). Silent on conversational \
prompts, slash commands, unknown names, or when no server is resident.
### Added
- The socket sidecar is unix-only (`cfg(unix)`): Windows builds skip it with a \
no-op and the prefetch hook falls back silently — the sidecar is an \
accelerator, never a requirement. (Fixes the Windows build break the sidecar \
introduced.)
- **Socket sidecar**: `sem mcp` now exposes the warm in-memory graph on a \
per-repo unix socket (`~/.sem/sock/<repo-hash>.sock`, one JSON line in, \
one out). Short-lived local callers — the prompt-prefetch hook, future CLI \
fast paths — get one-call entity context in single-digit milliseconds instead \
of paying a fresh process plus SQLite hydrate (~800ms). Stale sockets from dead \
servers are detected and taken over; the sidecar is a silent accelerator, never \
a requirement.
### Added
- **One-call lookup**: `sem_context`'s `file_path` is now optional. With only an \
`entity_name`, the entity is resolved across the whole repo (unique match \
proceeds; ambiguity returns a compact candidate list with the files; no match \
returns near-name suggestions) and the body plus callers/callees comes back in a \
single round-trip — one agent call where grep needs two (search, then read). \
Measured 26ms wall on a prewarmed server, name-only, unfamiliar repo.
### Performance
- The sem MCP server is now **local-first and prewarmed**. Cloud-first routing \
on `sem_impact`/`sem_context` cost a network round-trip on every call before the \
local answer (and carried the same wrong-entity risk gated in the CLI); it is \
now behind `SEM_MCP_CLOUD=1` until the server resolves name+file strictly. The \
server also builds the CWD repo's graph in the background at startup, so the \
agent's first structural query answers from memory. Measured on an 85K-LOC repo: \
warm `sem_context` runs in under 1ms wall (faster than a ripgrep scan of the \
same repo), and the first call dropped from 129ms cold to ~0 with prewarm.
### Removed
- Team presence was pulled from the `--badge` package before it shipped as a \
feature (product call: not a feature for now). The statusline no longer shows \
teammates and the hook sends nothing anywhere; the dormant server endpoints \
remain unadvertised.
### Added
- The `--badge` statusline is now **live at trigger time**: a PreToolUse hook \
flips the badge to an animated spinner with the entity name the moment the agent \
calls sem (`⊕ sem ⠹ impact validateToken…`), and the completed state \
(count, latency, savings) lands when the call finishes. The render hot path \
never touches the network (renders measured at ~20ms).
### Fixed
- The `sem context` / `sem_context` budget packer no longer starves the target \
while neighbors feast. Previously a target too big for the budget collapsed to \
its first line (2 tokens) while a single large dependency could consume the \
entire budget with its full body. The target now degrades gracefully — full \
body → head-truncated body (docstring, fields, leading code, with an explicit \
`… truncated: N more lines` marker) using up to ~70% of the budget → bare \
signature — and no neighbor may cost more tokens than the target itself did \
(budget/10 floor), oversized neighbors degrading to signatures. On the same \
query (a large class, budget 2000) the target went from 2 tokens to 1,398 and \
the answer-relevant attributes are now in the payload.
### Added
- **Entity-level history analytics**: `sem log` with no entity now analyzes \
recent repo history in one pass and reports **hotspots** (the most-changed code \
entities, with commit counts, distinct authors, and the last commit that touched \
each) and **co-change pairs** (entities that repeatedly change in the same \
commits, with a confidence score — "these two never change apart"). \
Counts are per commit, code entities only (doc headings, config properties, and \
lockfile chunks are excluded so the signal is about code), and bulk commits \
touching >50 entities are excluded from pair-counting to keep quadratic noise \
out. Same via MCP: `sem_log` without `entity_name`. `--file` scopes to one file; \
`--json` returns everything. This is the time axis a snapshot dependency graph \
cannot see: which code churns, and which code moves together.
### Changed
- `sem_impact` MCP results now render as a **blast-radius tree** (`◉` header, \
one `├─▶` branch per file, real callers first, all-test files sunk to the \
bottom, nothing elided) — expanding the tool widget is the live graph, no \
separate viewer process needed. The bundled skill also instructs agents to draw \
the blast radius as a small ASCII tree directly in their reply when an impact \
result drives the answer.
- `sem_impact` and `sem_context` MCP results now render as a compact entity tree \
instead of pretty-printed JSON: dependents/dependencies/transitive impact \
grouped one line per file, every entity name preserved, with the elapsed time \
and source in a footer. The same information lands in about 15% of the tokens, \
and the expanded tool widget in agent UIs reads at a glance (`⊕ entity · \
file`, `← 29 dependents · 10 files`, `⚡ 70 transitively affected`). Context \
entries keep their verbatim content under a per-entry header.
## [0.15.1] - 2026-07-01
### Added
- `npx @ataraxy-labs/sem-skill --badge` (opt-in) installs a live sem badge in \
the Claude Code statusline: it shows how many structural queries ran this \
session, the last command **and the entity it analyzed**, its latency, a \
sparkline of recent latencies, and a rotating stat (distinct entities analyzed, \
top command) (`⊕ sem ×12 impact validateToken 9ms ▁▂▃▅▂ · 7 \
entities analyzed`). It is fed by a PostToolUse hook that catches sem via \
**both** the MCP tools and the `sem` CLI (Bash), and falls back to recent \
activity so the badge never stalls on "idle". Non-destructive: it \
backs up settings and never overwrites an existing statusline (it prints how to \
add the badge yourself instead).
- **GitHub Action** (`Ataraxy-Labs/sem/action`): entity-level semantic diff \
comments on pull requests. One sticky comment per PR showing which \
functions/classes/methods were added, modified, or deleted, updated in place on \
every push; cosmetic-only PRs (formatting/comments) are called out explicitly. \
Installs the prebuilt binary (~2s), needs no config or API keys, and never fails \
the build. sem's own PRs now dogfood it via \
`.github/workflows/pr-entity-diff.yml`.
- The savings meter now lives in the **statusline itself** — no extra process. \
The `--badge` badge always shows the live estimated time + tokens this session's \
sem calls saved vs grep+read (`⊕ sem ×5 diff · ≈ 4m · ≈ 25k tokens \
saved`), and when idle it shows the lifetime total (`⊕ sem idle · ≈ 3h · \
≈ 190k tokens saved`). The PostToolUse hook is the single writer of the \
persisted lifetime tally (`~/.claude/sem-savings.json`), so the counter grows \
from real usage whether or not the live viewer is open. Estimates stay anchored \
to the measured benchmark and labelled `≈`.
- Live viewer for the `--badge` install: `~/.claude/sem-live.py` (run it in a \
spare terminal pane). It redraws an ASCII blast-radius graph each time sem runs \
— the analyzed entity, its direct callers (real ones surfaced, test fan-out \
collapsed), and the transitive count — plus a **savings meter**: a running, \
honestly-estimated tally of the grep+read round-trips, time, and tokens sem \
saved this session, and a lifetime counter persisted across sessions \
(`~/.claude/sem-savings.json`). Estimates are anchored to a measured benchmark \
and labelled `≈`. The badge hook now also records `--file` and cwd so the \
graph can be reconstructed.
### Fixed
- Repository discovery now tolerates Git worktrees that use the \
`extensions.relativeworktrees` config key, avoiding libgit2's \
unsupported-extension error when plain `git` can open the checkout.
- Cloud-backed `sem impact` / `sem context` no longer answer queries they can't \
answer correctly. Two gates added: `--no-cache` now always computes fresh \
locally (previously the cloud snapshot was served anyway), and **file-hinted \
queries (`--file`) stay local** — the cloud resolves entities by name with a \
silent name-only fallback, so for same-named entities (e.g. ten `fn run` command \
handlers) it could return the *wrong entity's* graph, and a stale cloud index \
could drop dependents that exist locally. Local resolution disambiguates \
exactly; the cloud path returns once the server resolves name+file strictly and \
exposes its indexed commit for a freshness check.
- Impact/dependency resolution now follows type-qualified associated calls \
(`Type::method()`) when the receiver is a known repo type, so a caller reached \
only through a static/associated path is no longer dropped from `sem impact`. \
Previously, e.g., a test helper calling `SemPlugin::detect_changes()` was \
invisible to the reverse-dependency graph, and its transitive callers were \
missing from the blast radius. Resolution stays precise: a bare module path \
(`foo::bar::baz()`) still does not bind to a same-name local function, and \
common associated names (`Type::new`, `::default`) are not guessed.
### Performance
- Faster graph hydrate on large repos. The public `EntityGraph` maps now use \
`rustc-hash` (FxHashMap) instead of std SipHash, matching the build's internal \
maps, and the SQLite cache sets read pragmas (`mmap_size`, `cache_size`, \
`temp_store=MEMORY`) on every connection. On a 200K-entity / 800K-edge graph \
this is about 9% faster to hydrate (0.42s to 0.39s, no overlap across repeats); \
negligible on small repos. Output is byte-identical.
## [0.15.0] - 2026-06-30
### Changed
- Whole-repo commands (`sem graph`) now skip the file-discovery walk when git \
proves the cache is fresh (HEAD unchanged and the working tree clean), serving \
the cached topology directly. On a 200K-file repo this is about 9x faster with \
git fsmonitor and about 4.5x faster without it; small repos and non-git repos \
are unchanged. The oracle only ever declines to accelerate, never serves stale \
results, and the `git status` check is time-bounded (`SEM_FRESHNESS_TIMEOUT_MS`) \
with `SEM_FRESHNESS=scan|git|auto` to override.
### Added
- `sem xref` lists cross-repo dependencies across your indexed repos: entities \
in one repo that depend on entities in another. A single-repo local graph can't \
see this, so it's a cloud feature (requires `sem login`) and is gated to the \
team/enterprise tier. Adds `cross_deps()` to the shared cloud client.
- `sem diff` now prints a one-line hint, when run interactively and logged out, \
that `sem login` reveals what your changes break across repos (a cross-repo \
question a local single-repo diff can't answer). It is heavily throttled (at \
most once a week), shown only on a terminal with real entity changes, and stays \
completely silent in CI, pipes, `--json`/non-terminal output, and for logged-in \
users.
### Performance
- Cache freshness checks now run the per-file `stat` + content-hash scan in \
parallel (rayon) instead of sequentially (#351). On touched-file cache hits over \
large repos, the freshness scan was the dominant remaining cost (~42ms of \
sequential filesystem/hash work on a 5K-file touched scenario); it now scales \
across cores. SQLite reads stay serial (the connection isn't shared across \
threads) and fingerprint-refresh writes remain serial and best-effort — only \
the pure filesystem+hash work is parallelized, so cache-hit validity is \
unchanged.
### Documentation
- The bundled `/sem` agent skill no longer hardcodes a language count. It said \
"31 languages", which went stale as grammars were added and disagreed \
with the README ("32") and the crate description ("28"); it \
now says "30+ languages" so it can't drift, and an en-dash was \
replaced with a hyphen.
- README: documented the optional cloud acceleration flow (`sem login` serves \
`impact`/`context`/`entities` from a warm pre-built graph for large repos; local \
is unchanged and `SEM_LOCAL=1` forces local), and added Lua to the \
supported-languages table.
### Added
- Lua support, via the `tree-sitter-lua` grammar (gated behind the `lang-lua` \
feature in `grammar-all`). Extracts global, `local`, table (`t.f`), and method \
(`t:f`) functions. Thanks @mmgeorge for the request (#393).
- `SemanticEntity` now carries optional `start_byte`/`end_byte` offsets, \
populated from the underlying tree-sitter node during code extraction. A \
consumer can slice the exact original bytes of an entity out of a file given \
only its `file_path` and span, without re-parsing. Persisted through the entity \
cache and surfaced in `sem entities --json`. Thanks to Thomas J. for the \
request.
### Added
- `npx @ataraxy-labs/sem-skill`: one-command setup of sem for coding agents. \
Installs the sem skill into `~/.claude/skills/` and registers the `sem mcp` \
server, so an agent uses sem (impact / context / orient / diff) over grep for \
structural questions without manual setup. Builds on the skill contributed in \
#376.
### Added
- An agent skill (`skills/sem/SKILL.md`) documenting sem's semantic diff, \
impact, blame, history, context, and graph workflows for coding agents. Thanks \
@linhlban150612 for the contribution (#376).
- `self-update` Cargo feature (on by default) gates the built-in `sem update` \
and the background update-available check. Distro and package-manager builds \
that own the binary's lifecycle can opt out with `cargo build \
--no-default-features`; `sem update` then prints a "update through your \
package manager" message instead of replacing the binary. Thanks @0323pin \
(pkgsrc/NetBSD) for the request (#390).
### Added
- `sem context --hops N` bounds the related entities to N graph hops from the \
target (instead of filling to the token budget), so you can ask for "the \
entity and just its immediate neighborhood." The `sem_context` MCP tool \
gains the same `hops` parameter. 0 (the default) keeps the existing unbounded, \
budget-driven behavior.
### Changed
- The `sem mcp` instructions now tell agents to read code with `sem_context` \
(which returns an entity's full source plus its callers/callees, addressed by \
name) rather than opening the file, reserving direct file reads for editing and \
non-code. Reading by entity is robust to line drift and arrives with the \
dependency context.
|