Releases: DeusData/codebase-memory-mcp
Release list
v0.10.8
v0.10.8
Note on v0.10.7: this release supersedes v0.10.7, which was lost to a release-pipeline
publishing error (published under the tag0.10.7instead ofv0.10.7; package installers
resolve the v-prefixed tag, and release immutability makes the name unrecoverable). The npm
and PyPI 0.10.7 packages are deprecated/yanked for that reason — their binaries were fine,
their download URLs were not. v0.10.8 carries the identical code content plus the pipeline
hardening that prevents a recurrence.
A community-heavy patch release: 19 pull requests merged since v0.10.6, most from
contributors. Thank you all!
Graph correctness
- Cypher aggregates no longer undercount (#1196). Both truncation mechanisms are fixed:
unlabeled scans stopped collecting candidates at max_rows before aggregation ran
(#1323, @Enferlain), and relationship-expansion buffers were capped so aggregation never
saw rows beyond an internal growth limit (#1698).count()/collect()now see the
complete match set;max_rowslimits only the rows returned, as documented. - Python aliased from-imports resolve to the real definition (#1371, @Joseph-MingEn).
from .gate import execute as bridge_executenow produces a CALLS edge toexecute
itself rather than losing the call, and aliased base-class imports land on the real class
node. On our django benchmark this recovered 26 INHERITS and 42 OVERRIDE edges with zero
losses elsewhere, and left TypeScript graphs byte-identical. - Cross-language suffix matching for qualified-name lookups (#1647, @rudi193-cmd).
- search_graph stops returning semantic-only ghosts — results backed by nothing but a
similarity edge no longer surface as if they were definite matches (#1319, @JJordan0C). - List-valued fields extract correctly instead of collapsing to their first element
(#1325, @Enferlain). - Index responses report persisted coverage truthfully (#1326, @Enferlain): an
incremental run that does not revisit a file with recorded parse gaps no longer reports
those gaps as vanished — the response now composes its skipped/parse-partial summaries
from the stored coverage state, falling back to per-run data only if that read fails.
Windows
- Non-ASCII search content no longer degrades to
?(#1704). The search_code raw scan
pipes PowerShell output, and PowerShell 5.1 encodes that pipe in the console OEM
codepage — so Cyrillic, CJK, or accented content turned into question marks depending on
which console the server inherited. The pipe is now pinned to UTF-8 unconditionally. - search_code understands UTF-8 output paths and gains a context column for matches
(#1253, @anfedoro). - Code search is bounded and cancellable — abandoning a query no longer leaves an
orphaned scan running (#1608, @ertankucukoglu).
Platforms & tools
- FreeBSD support (#1467, @PR9000).
- list_projects pagination: a lean default response plus offset/limit and
include_details for the full view (#1181, @tmonestudio). - Extensionless shebang scripts detect their language (#1203, @ibaldr89).
- Index-mode capability rebuild (#1263, @astandrik).
- is_test detection for tests/ directories (#1308) and a diff-aware clang-tidy
hook (#1310, both @Yyunozor).
Release pipeline (the v0.10.7 lesson, hardened)
- Release gates fail closed on cancelled jobs: a timed-out gate can no longer cascade into
a silently skipped test matrix (#1719). - Malformed version inputs are refused preflight — a bare, non-v-prefixed version can never
again publish an unresolvable tag (#1719).
Internal
- Test harnesses use isolated daemon runtimes so suite runs cannot touch the account
daemon (#1695, @astandrik); LF checkout contracts for shell entrypoints and git hooks
(#1314 @mlandolfi90, #1699 with @xumian520); the stdin-gate regression tests no longer
depend on the registry shipping a zero-argument tool (#1704).
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 61–68).
Every shipped binary was clean.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
cb6dc545a8cb714799461ba7d8c223dbca7d02ca46ea5a4215c1beb450fd8e09 |
clean |
linux-arm64 |
e861b4ae831a24ab69e82bb9c4ca8c70029af55b9a074ef0ed2565a53c0de24c |
clean |
linux-amd64-portable |
1175645cb30560e7e47d78611cd1bcb509478eaf6d4e51f72fe18327ee9c1351 |
clean |
linux-arm64-portable |
0bf1b3cf3659f6dcdb01bb6313a93dcbcb53e2cd1a52820f024df7a0cf48f44e |
clean |
darwin-amd64 |
1d5b79257d91973809ee70482781db3d1a78f62fc1cd5d18a5ab0608451d16d4 |
clean |
darwin-arm64 |
2412e017268bef8f847f38d1b0f79f63185b38c27fe6fba637067bfc87c0eedf |
clean |
windows-amd64 |
b4b403b1d7c4def3785f148b93f345ce8427858f4f5489ce28580c4387a336a6 |
clean |
windows-arm64 |
67b0341ee62f07f850d3954e4f387855f90ea8c6c4b7ed41b8a62d61344373a4 |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
0.10.7
Warning
Broken for npm/PyPI installs — superseded by v0.10.8. A publishing error released this under the tag 0.10.7 instead of v0.10.7. The npm/PyPI package installers resolve their downloads via the v-prefixed tag, which cannot be created retroactively (release immutability tombstones the name). Direct downloads from this page work normally — the binaries themselves are fine and fully verified. Use v0.10.8 or newer for package-manager installs, or scripts/setup.sh.
What's Changed
- fix(extract): converge Function/Method is_test with the tests/ path filter by @Yyunozor in #1308
- fix(hooks): scope pre-commit clang-tidy to staged changes (#1264) by @Yyunozor in #1310
- fix(mcp): isolate semantic-only JSON search by @JJordan0C in #1319
- fix(search): preserve compound requested fields by @Enferlain in #1325
- fix(cypher): scan all unlabeled query candidates by @Enferlain in #1323
- fix(registry): drop suffix_match CALLS across language boundaries by @rudi193-cmd in #1647
- fix: rebuild index when mode adds capabilities by @astandrik in #1263
- fix(daemon): add native FreeBSD process-image identity and /home log-path alias by @PR9000 in #1467
- fix(mcp): restore scalable project discovery by @tmonestudio in #1181
- fix(discover): detect extensionless scripts by shebang by @ibaldr89 in #1203
- fix: preserve UTF-8 in search_code source by @anfedoro in #1253
- fix(build): force LF line endings for shell scripts by @mlandolfi90 in #1314
- fix: unbreak main — stdin-gate #1359 guards + Windows search_code UTF-8 pipe by @DeusData in #1704
- fix(test): isolate harness daemon runtimes by @astandrik in #1695
- fix(python): resolve aliased from-import CALLS to real def by @Joseph-MingEn in #1371
- fix(cypher): expansion materializes every matched row — the cap falsified aggregates (#1196) by @DeusData in #1698
- fix(mcp): bound and cancel Windows code search by @ertankucukoglu in #1608
- fix(build): LF contract for extensionless git hooks + a line-ending guard by @DeusData in #1699
- fix(index): preserve persisted coverage summaries by @Enferlain in #1326
New Contributors
- @Yyunozor made their first contribution in #1308
- @JJordan0C made their first contribution in #1319
- @Enferlain made their first contribution in #1325
- @rudi193-cmd made their first contribution in #1647
- @ibaldr89 made their first contribution in #1203
- @anfedoro made their first contribution in #1253
- @Joseph-MingEn made their first contribution in #1371
Full Changelog: v0.10.6...0.10.7
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 60–68).
Every shipped binary was clean.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
9f24d35e42d511c9d5de8f2f3a2c503bfce7e920901f94d0cff837d1371312cd |
clean |
linux-arm64 |
ee517a1b83cda7232bf9f0e2d02807a25207937796bde26dd1dd518be8fb7dd1 |
clean |
linux-amd64-portable |
e9772b28473c11016e59d4dc272b8290deb1efe633c17eb942676f1595b5011e |
clean |
linux-arm64-portable |
a516c044a59c2e8be40366c84aab2a04c2d112298f496f8204f498cbea7883ba |
clean |
darwin-amd64 |
2d5b042b59189645e6975bd3e9772ec760a8368164223fdb85c55a9c67f1d2d2 |
clean |
darwin-arm64 |
24c8b6ee8711e465db359c2ed3ba87c9c350a105f2e77f7d3e4c72b098cd43af |
clean |
windows-amd64 |
2d55ab63c05011a6c00939fc79e17ae0432c4b1fe7675b6dcc86e242a1be437b |
clean |
windows-arm64 |
c5d26ab2e4f59d7e7bc9fbfaa3e08c92127b5ea75c2b7942087edb8a49828fca |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
v0.10.6
codebase-memory-mcp v0.10.6
Two stories in one release: the 0.10.x indexing slowdown is gone, and the install
failures that survived v0.10.5 are fixed — including every construct from the configs you
sent us. If cbm got slower for you since 0.9.0, or install still refused your Hermes,
goose, OpenCode, or Codex config, this is the release to take.
The indexing regression program (#1669)
Since 0.10.0, indexing large repos had drifted from slower to unusable — java 6× worse,
C# 2.6×, TypeScript 2×. The cause was not one bug but a family: per-file work that scaled
with the whole corpus. All of it is fixed, measured on the same host against the same
corpora as the reports:
- Java 477 s → 86 s (elasticsearch): cross-file resolution rebuilt a registry of every
definition in the corpus per file. It is now built once and shared, with a per-file
overlay for local symbols. - C# 1211 s → 450 s (dotnet/runtime), now with 48% more edges than 0.9.0: three
corpus-proportional scans eliminated, plus an import-context bug that had been silently
suppressing C# usage edges — one 147 KB generated file went from 490 s to 6.8 s. - TypeScript 37 s → ~24 s (microsoft/TypeScript): the expression-type evaluator
re-evaluated shared subexpressions once per enclosing alternative — 2^n on tsc-compiled
spread chains. A per-node memo makes it one evaluation per node; the pathological file
from the TS conformance suite dropped from 20 s to effectively zero. - Linux memory blowup fixed (#1654): v0.10.4 could OOM a 36 GB machine indexing a tree
v0.9.0 handled in 13 minutes. mimalloc's Linux arena-commit default is restored. - It stays fixed: a new complexity guard suite runs in CI on every platform and trips
on counter ratios (never wall time) if any pipeline pass goes superlinear again, and
CBM_PROFILE=1now prints per-pass timings from any production binary.
Install: the constructs from your real configs
Hermes (#1631, reported by @iandol and Galaxy-VN, root-caused with an isolated repro by
@rg6304): four pieces of ordinary YAML made install fail permanently — empty flow
collections (plugins: [], tool_choice: {}), block sequences at column 0, double-quoted
strings continued with a trailing \, and mid-word apostrophes (LET'S GO in a persona
string opened a "quote" that never closed). All four are accepted now; quotes and
anchors are indicators only where a value begins, the same rule the * fix used in
v0.10.4. Both reporters' full configs — 15 KB each — install end-to-end with every
original byte preserved.
Entries we wrote ourselves are recognised again. Older cbm versions wrote command:
unquoted; the goose block gained a required field; your entry was then declared "foreign"
forever. An entry under our key that matches a shape any previous release wrote — or
whose command is our binary at a moved path — is repaired in place. Truly foreign entries
are still refused byte-untouched.
goose (#1673 + #1675): the extension block now carries the name field goose's parser
requires — before this, goose silently dropped the entry and the "successful" install was
invisible. envs: {} in a sibling no longer aborts the write (community fix by
@pcristin — a model contribution, merged as-is). Old blocks repair on upgrade.
OpenCode / annotated MCP entries (#1630, #1582): when a client adds keys to our entry
("enabled": true and friends), repairs now replace only the command value — comments and
client keys survive verbatim. And ownership comparison is separator-insensitive: an entry
storing C:\...\codebase-memory-mcp.exe is the same binary as the installer's
C:/.../codebase-memory-mcp.exe. @gotspatel's posted config was the file that cracked
this one after persistence across four releases.
Windows-authored configs with a UTF-8 BOM (#1656): PowerShell's Set-Content writes a
byte-order mark, and every YAML edit failed on such files regardless of content — the
reporter proved content-independence with a 26-byte file, and those 26 bytes were their
23-byte control plus exactly the BOM. It is treated as a prologue now: skipped for
structure, preserved on write.
Codex (#1633, closing @spam0115's residual from #1580): the hook reconciler accepts
the managed hook pair v0.10.2 wrote (including the Windows CRLF+BOM variant) and rewrites
it in place; refusals now name their reason instead of failing generically.
Windows: the DACL stops fighting you
The runtime directory's security descriptor was re-stamped on every process start,
whether or not anything was wrong. #1601 counted eleven no-op "Security change" USN
records against a single file in a day; #1620 lost atomic publishes to the rewrite window.
The re-stamp now fires only when the directory is not already in the exact owner-only
state — and directories damaged by the pre-v0.10.3 DACL regime (the empty-DACL logs
folder @patxisan found, which explained a month of unreadable 0-byte worker logs across
six reports) are detected and repaired in place. (#1416, #1601, #1620)
Also shipped: CBM_RUNTIME_DIR is forwarded in generated Codex configs (#1664 — a
relocated daemon was invisible to Codex-spawned servers), a missing final newline is no
longer reported as a partial parse, and update no longer names an installer that is not
there (#1653).
Credits
@iandol · Galaxy-VN · @rg6304 · @gotspatel · @spam0115 · @pcristin · @Carnival-z ·
@tmonestudio · @patxisan · @roosteer — the configs, ACL dumps, and minimal reproductions
in this release's issues did most of the diagnostic work. Thank you.
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 60–68).
Every shipped binary was clean.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
749bd2c37c2ad891392e0872b9e1fb0df7432f04c7ca0f49d11d00b24c5f2ec8 |
clean |
linux-arm64 |
e41cba76e58005edd4d21e19cbe1faf2c8f684c56fe78141c9956a9c632c9d26 |
clean |
linux-amd64-portable |
8eea4ec828cd0cfa1c406cda5640a8dfa5381878b4d96b106cdc53aff3733508 |
clean |
linux-arm64-portable |
c783294c009186c1c91c79d3611ef991af4db7816dbad13a922538513af97cac |
clean |
darwin-amd64 |
34851da132369455b35530879c13e95360429251ab5e9604bc301beb25c9bcc5 |
clean |
darwin-arm64 |
8fdbdb2ec81f9526b34e71dffd46589b0e06fd5d60773732fd256c684d0251f4 |
clean |
windows-amd64 |
3099e91c29d8507c94bfb7ed431a1f89459ad9afdcea413cbab23c9ec3cfd972 |
clean |
windows-arm64 |
f5f0585308d7f6130386857e1747a338abe194cb9b082c37d81acf43b8f18e9d |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
v0.10.5
codebase-memory-mcp v0.10.5
An install-and-startup release. If v0.10.4 refused to install for you, refused to write your
config, or told you something that turned out not to be true, this is the one to take.
Several of these are cases where cbm refused its own work — an installer directory our own
binary then rejected, a config entry we wrote ourselves and later called foreign, an error
message that named the wrong directory and sent people to inspect a file that was fine.
Windows: installs that refused themselves
The installer was creating a directory our own binary then rejected. install.ps1 made its
staging directory with New-Item, which inherits whatever %TEMP% carries. The downloaded binary
validates its own directory and refuses inherited cross-account mutation grants — so we handed our
own check a directory we had just made wrong. Measured on a Windows VM: a fresh staging directory
came up unprotected with five inherited ACEs; it now carries a single owner-only entry.
That is the acl-grants-cross-account-mutation to S-1-5-21-… failure. Reported by @RoccoZero,
@aecesr, @Kiborgik and @nasodaengineer, whose corrections stopped us fixing the wrong
thing twice — @Kiborgik warned that keying on the sandbox group's name would miss a synthesized-SID
variant, and @nasodaengineer disproved an off-by-one-RID theory by showing the ACE really was present
on the inherited DACL. @spam0115 independently wrote a working installer whose decisive line was
the same icacls /inheritance:r, and handed it back to us rather than keeping it.
Sandboxed applications no longer block the daemon. The ancestor walk refused any
mutation-granting entry on %LOCALAPPDATA%, including AppContainer identities left by ordinary
installed software. On reported machines the blocking entry resolved to a shipping desktop
application — nothing a user could reasonably remove. Ancestor components now tolerate AppContainer
package and capability identities; the private runtime directory keeps demanding the exact current
user, unchanged. Approach and the ancestor-only boundary follow @mlandolfi90's work.
Startup: when the daemon could not be reached
-
The rendezvous directory can be relocated in a shipped build via
CBM_RUNTIME_DIR. Previously
the only relocation hook was compiled out unless test seams were enabled — so a test build
started where the product build did not, and when the default ancestry was unacceptable
every invocation failed,config listincluded, leaving no way to reach the settings surface
and fix it. (#1537, #1574, #1621)@tmonestudio and @Carnival-z each proposed exactly this, independently, before we built it
— @tmonestudio in #1574 with the ancestry walked and the offending component named, @Carnival-z in
#1621 after finding thatCBM_CACHE_DIRmoves the cache but never the rendezvous. Both had opened
pull requests reaching the same conclusion this change implements; the merged commit credits
@tmonestudio by name for the approach and the variable name. -
Refusal messages now name the directory that actually refused. The ancestor walk validates the
directory it is already in, but printed the name of the child about to be entered — so #1537 was
told.cachewas at fault when the home directory was refusing, and #1621 was told
cbm-daemon-501when/private/tmpwas. Both reporters inspected exactly what we named, found it
clean, and said so. They were right, and we kept pointing at it for weeks. The message now
names the containing directory and says explicitly not to check the one it used to blame.
Errors that name what actually refused
Three fixes, one complaint: our messages did not say enough to act on.
secure CLI coordination could not be created (endpoint)now prints the validation detail —
the directory and the rule that refused. Four separate reporters hit that message; one built an
instrumented binary to discover a single ACE was the cause.- A refused agent-config write says what the target is — whether it exists, and what kind of file
it is. Nine distinct fail-closed conditions previously collapsed into one indistinguishable failure. - Neither prints
errno. It is called from 119 sites and can be stale, which is exactly how an
earlier release handed a reporter a fabricated "file not found" for a permission decision.
Config files we wrongly refused to touch
- An MCP entry your client annotated is no longer treated as foreign (#1630, #1582). OpenCode
writes"enabled": truebeside thecommandandtypewe write; our ownership check demanded an
exact key-set match, so a three-key entry with two recognised keys was classified as somebody
else's and left alone — we refused to touch an entry we had written ourselves. In one reporter's
file every MCP server carried the key, so this is OpenCode's normal shape, not an unusual hand-edit:
anyone who has ever toggled a server on or off in the UI was hit. - YAML values containing
*or&are no longer mistaken for aliases and anchors (part of #1631).
Prose asterisks, a glob inside a description, a kaomoji — all were refused as if they began a node.
The test is now positional. Root-caused by @rg6304, who reproduced it in isolation, read the
source, and corrected our hypothesis: the constructs we had guessed appear nowhere in the failing
file. This is one of four constructs in that report; the rest are still open.
Data loss and diagnosis
uninstall --helpno longer performs a real uninstall (#1038). It removed the binary and every
agent configuration for someone asking what the command does. The guard is checked before argument
parsing, so a-yelsewhere on the line cannot auto-confirm the destruction being prevented.- A crashed worker's log now survives and names its run (#1145, #1130, #1132, #1450, #1133, #1070).
Six reports ended at the same dead end: the supervisor prints the log path and the file is 0 bytes,
so the one diagnostic that would explain the failure was silenced by the failure itself. cli <tool>no longer blocks forever reading standard input (#1359) for a tool that declares no
arguments.--jsonand--progressare stripped before the stdin fallback is reached, so
cli list_projects --jsonhung too — the reporter's correction that "no flags" was not the trigger
was right.
Reliability
- Spawn retries no longer grow without bound (@moffermann). The clamp we shipped could never
fire, while the doubling it was meant to bound ran free — a future budget increase would have meant
a 40-second wait where the retry exists to avoid exactly that. Both current budgets are unchanged. - Two concurrent sessions can no longer be handed the same temporary directory
(@ertankucukoglu) — a process-global buffer in the Windows temp-directory helper. - Instrumented test builds now know they are instrumented (@moffermann). Four places asked
that question four different ways, so some sanitizer lanes measured instrumented code against
native timing budgets.
Correctness
check_index_coverageno longer reports every path as unavailable when a repository has more
than 2,000 ignored files (@SunneeYang). A truncated catalog was treated as proof that no
per-path answer existed, including for paths with a current hash record.- Go routes built by string concatenation now resolve (@AmirF194). A handler registered with
base + "/orders"produced no usable route path; the literal half is now recovered, and a path
assembled from a variable is still left unresolved rather than fabricated. - Cypher queries repeating a variable-length node no longer double it (@jstar0), matching the
unification the fixed-length path already did. - A Windows publish failure says what actually failed. The atomic publish discarded the Win32
error and reported a bareerrno, and one path returnedPERSIST_FAILEDwith no log line at all —
which is how a reporter with an ACL problem was told to check that their repository path existed. - Cross-repo intelligence works again on any project that has ever recorded a parse miss
(@vitaliy-shatskiy, #1609). Indexing writes an internal<name>::missedrow into the same
database, and the validation required exactly one row over all of them — so such a project was
rejected as "not indexed", both as source and as target, with no workaround available to the
operator. The same defect was fixed forlist_projectsin #1044; this site never learned it.
Performance
- Windows code search prefilters simple suffix globs (@ertankucukoglu), so a
*.extfilter no
longer scans and then discards. The pattern reaching the shell stays behind an allowlist, and the
original post-scan filter is retained as a second guard. - A failed search launch no longer leaks its compiled path filter (@ertankucukoglu).
search_codecan report per-phase timings on request (@ertankucukoglu) —scope_ms,
scan_msandenrich_ms, behinddebug: true. Default output is unchanged, and the measurement
itself is skipped unless asked for, so nothing is paid for by callers who do not want it.
Updating from 0.9.x is possible again
If you are on 0.9.x and update has been refusing to install, this is the release that fixes it
(#1134). v0.10.0 consolidated to one archive per platform, so the codebase-memory-mcp-ui-* names
that 0.9.x updaters ask for stopped existing and those updaters 404'd. We fixed that by publishing
byte-identical copies under the legacy names — but that step runs after checksums.txt is
generated and signed, so the aliases never appeared in it, and an updater that verifies the name it
asked for refused what it could not check:
warning: codebase-memory-mcp-ui-darwin-arm64.tar.gz not found in c...
v0.10.4
codebase-memory-mcp v0.10.4
An install-and-diagnostics release. If v0.10.3 refused to install or start for you — particularly on Windows, or on a machine where you installed cbm through a package manager — this is the one to take.
Most of what follows came from people who filed precise reports and, in several cases, root-caused the bug before we did.
Windows: elevated accounts could not install or start at all
Running as an Administrators-group account meant install failed with activation transaction I/O failed: owner-not-current-user, or the MCP server exited during startup with nothing in the log but a closed transport.
The cause was ours and specific: we compared a file's owner against the token's user SID, but Windows stamps newly-created objects with the token's owner SID — which for any member of the Administrators group defaults to BUILTIN\Administrators. So the installer created its own staging file and then refused it, seconds later, as belonging to someone else. No amount of rebooting or closing processes could help, because nothing was running.
We now also accept the SID Windows actually stamps on what we create. This is narrower than "trust any administrator": a file owned by a genuinely different account is still refused. Reported by @spam0115 and @gotspatel, and it is very likely the cause of the long-running #1416 as well.
Installing no longer touches things it doesn't own
Two changes for people whose setup we were trampling:
A binary installed by mise, Homebrew, nix, asdf or cargo is left alone. Previously install dropped a second copy into ~/.local/bin — shadowing the managed one depending on PATH order — and appended to your shell rc file. Now it configures the agents and leaves the binary and your PATH untouched, update refuses honestly and names the command that will work (mise upgrade, brew upgrade), and PATH is only modified when we actually placed the binary. Reported by @SC-CTS.
You can choose which clients get configured. install wrote MCP config, agents, skills and hooks for every detected client, and reverting the ones you didn't want was pointless because the next install recreated them. --clients=claude,codex now restricts it, and --clients on its own lists every supported token. Reported by @PsyTech-Rob.
Errors that tell you what actually happened
Three fixes in a row here, all from the same complaint: our messages were confidently wrong.
- An activation refusal no longer points at evidence it doesn't show. v0.10.3 correctly stopped blaming "active CBM sessions" for failures no session caused, but then told you to check errors that were never printed. It now names the directory and the rule that refused.
- A server that cannot start says why. Client-path startup failures wrote to stderr only, which no MCP client surfaces — so a specific, nameable refusal reached you as "transport closed unexpectedly". Those failures now arrive as a JSON-RPC error carrying the reason.
- The UI listener can be turned off without editing JSON.
ui_enabledgoverns a loopback HTTP port, and the only way to disable it was hand-editingconfig.json; it was invisible toconfig listand rejected byconfig set. Both it andui_portare now first-class config keys. Reported by @PsyTech-Rob, who spent two debugging sessions finding the switch.
Codex, OpenCode and config-file repair
- A duplicated install no longer breaks Codex permanently (@astandrik). An owned inline hook assignment is reconciled into the canonical block instead of being ignored and re-added alongside; foreign hook entries are preserved untouched, and genuinely ambiguous TOML is refused without modifying the file at all.
OpenCode installs land in the file OpenCode actually reads (@iandol).Correction (2026-08-14): this is only half true and we should not have claimed it outright. The targeting is fixed — we now write whichever ofopencode.json/opencode.jsoncalready exists, instead of always creating a.jsonfile OpenCode ignores. But the write itself still fails on v0.10.4 witherror: agent_config agent=OpenCode op=mcp_install, reported on both Linux and Windows. Tracked in #1630. This is the second release running in which we have described this fix as complete; sorry, and thank you to the reporters who kept saying it was not.- An orphan marker we wrote heals itself. An earlier install could leave a closing
# <<<marker with no opener, after which every install failed that client outright. Removal now strips the stray line. A write still refuses on an imbalance — with one marker there is no defensible region to replace.
Query correctness and platform support
OPTIONAL MATCHstopped silently dropping rows (@SEPURI-SAI-KRISHNA). Once the hop buffer saturated, the no-match rows — exactly whatWHERE x IS NULLexists to surface — were discarded. Not a crash, not a memory error: quietly fewer results, with nothing to indicate the answer was incomplete.- FreeBSD resolves its own executable path (@PR9000), which the install and ownership checks depend on, plus a build fix for platforms outside the main three.
Security
The release pipeline now pins and verifies the external tooling it downloads before executing any of it, rather than trusting whatever the upstream project publishes at build time. Verification covers every platform the job may run on, from a single pinned reference.
Reported by Andrew Hundt (#1245).
Reliability
Subprocess spawns survive a busy machine. v0.10.3 started retrying a spawn the kernel refused with "try again", but only for about 30ms — enough for a momentary dip, not for a machine that stays process-starved for hundreds of milliseconds at a stretch, which is exactly when it happens. The retry now backs off exponentially to roughly half a second before giving up. A genuinely exhausted machine still fails fast rather than hanging, and real errors — a missing binary, a permission denial — fail immediately as before.
And the release pipeline no longer publishes its registry entry before the release is public — the bug that made v0.10.3's own registry step fail with a 404 against its own assets.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1. - Package managers: update through them as usual — cbm will now leave that binary alone.
- npm / PyPI: as usual.
- No index, cache, or config migration.
Contributors
Code in this release came from:
- @astandrik — Codex hook reconciliation, with fail-closed handling that leaves an ambiguous config byte-identical rather than guessing
- @PR9000 (Pedro Ramos) — native FreeBSD executable-path resolution, plus a build fix for platforms outside the main three
- @SEPURI-SAI-KRISHNA — the
OPTIONAL MATCHrow-loss fix, including the analysis that separated "in bounds" from "correct" - Andrew Hundt — the release-pipeline hardening above, co-authored
And these reports found bugs we could not have found ourselves, several of them root-caused by the reporter before we looked:
@spam0115 and @gotspatel (Windows elevated accounts) · @SC-CTS (package-manager installs) · @PsyTech-Rob (four separate findings in one install cycle) · @listepo (the diagnostics that identified a refusal we had been guessing at) · @iandol (OpenCode config targeting)
Thank you — particularly to those of you who kept reporting through several releases of install trouble.
Full changelog: v0.10.3...v0.10.4
Security Verification
Before smoke and soak testing, VirusTotal completed 24 executable scans: stripped and unstripped candidates for each of the 8 release products. Every scan had at least 50 decisive engines (observed range: 59–68).
3 candidate(s) had only the documented single Microsoft machine-learning !ml result; no other decisive engine reported malicious or suspicious.
| Product | Stripped candidate | Unstripped candidate | Shipped |
|---|---|---|---|
linux-amd64 |
clean | clean | stripped (09db4f424c1fca97b5d06feb75ca6540629a5b67309e39a9a551e745b081023b) |
linux-arm64 |
Microsoft !ml |
clean | debug-stripped (e0cd04812093d420a889397a603959c9a35ea3c78dea67c44db80e25a04d29b8) |
linux-amd64-portable |
clean | clean | stripped (df1b97b0f5266924072a0c020027a14fa1890fa7457183c2106ff7eab4b62122) |
linux-arm64-portable |
Microsoft !ml |
clean | debug-stripped (8427e22d1445b1e4a9a883034c12c18eb78c30e6ba183235ca79e153641a39e9) |
darwin-amd64 |
clean | clean | stripped (9ae79b267fe60438ffbba046bb5ed9e36b2b0f0c04e32970ee6995d616a8ccc6) |
darwin-arm64 ... |
v0.10.3
codebase-memory-mcp v0.10.3
A fixes-only release, and most of it arrived as finished work from the community: six contributed pull requests are in here, alongside repairs for two regressions we shipped ourselves in v0.10.2.
Two things worth knowing before you upgrade. If you run more than one MCP client against the same project, this release closes a bug that could silently destroy a healthy index — start with the first section. And if installing or updating has been failing for you, three separate causes are fixed here: a permission rule that refused ordinary home directories, an error message that reported the wrong reason entirely, and Codex losing your cache directory. Several of those failed silently or blamed the wrong thing, which is why they survived multiple releases.
Concurrent instances no longer quarantine each other's databases
The worst bug in this release, fixed by @LynxBay. When two cbm instances raced on the same project — two editors, an editor plus a CLI call, a daemon and a stray session — the loser of the lock race could conclude the database was corrupt and quarantine it: renamed away, rebuilt from scratch, hours of indexing gone. The database was healthy; the integrity check just couldn't tell "damaged" from "busy" (#1206).
The same check had the opposite blind spot too: a database with genuinely torn pages passed as long as its top-level bookkeeping looked right, so real damage went unnoticed until queries misbehaved (#1037).
Integrity now returns a three-way verdict. Only confirmed corruption quarantines; a lock race says "project is busy; retry after indexing" and leaves the data alone; and a deep quick_check catches page-level damage the shallow probe missed. Both directions carry regression tests.
Graph determinism, twice
Two contributions from @Studnicky close two long-standing sources of run-to-run drift — the "same repo, slightly different edge counts" behavior tracked in our known issues since the 0.9.1 RC:
- When the same logical edge is produced twice with different attributes (an LSP-resolved call and a textual match, say), the survivor was whichever arrived last — a function of thread scheduling. The merge is now a total order: higher confidence wins, ties break deterministically, and the outcome is independent of arrival order by construction.
- CONFIGURES candidate collection sorted nothing before applying its cap, so which candidates survived depended on worker merge order. Candidates are now ordered by a pure content key first — notably not by node id, which looks stable but is itself handed out in merge order.
Windows: daemon files created with an empty ACL
@Kiborgik diagnosed and fixed a subtle one (#1351): the daemon's private directory combined a protected DACL with a non-inheritable ACE, which on Windows means children inherit nothing — every file created inside was born with an empty ACL and unreadable even by its owner. Directories now carry a properly inheritable owner-only ACE, so the strict security posture is kept and the files actually work. A companion fix from the same contributor closed a trap where a machine named like its user granted permissions to an empty principal; testing that on a non-domain-joined machine surfaced the mirror-image trap, so our build tooling now identifies the account by SID and stops resolving names altogether.
Repairs for two v0.10.2 regressions
Both reported within hours — thank you for the fast signal.
update --uistopped working (#1544, @ehendrix23). v0.10.2 removed the obsolete ui/standard chooser and the flags that drove it, so a command living in scripts and muscle memory became a hard error. Both flags are accepted again — they do nothing, say so once, and genuinely unknown flags still fail.- The generated Pi extension broke Pi entirely (#1550, @musichen). Pi loads an extension by calling its default export; ours exported a named function instead, and a Pi extension that fails to load takes every
picommand down with it,pi doctorincluded. The extension now default-exports its factory. Fixed independently by @musichen and @henri-edh as well, whose sharper description of Pi's loader is the comment now in our source.
Structured output stops dropping evidence
trace_path(include_evidence: true) promised per-hop strategy and confidence columns, and delivered them — in tree format only. With format: "json" both fields silently vanished, which hit exactly the callers most likely to want them: the ones consuming structured output programmatically (#1542, @asemraza). The JSON path now carries both fields with the same semantics, nulls standing where the tree format prints -.
Subprocess spawns survive a busy machine
Chasing a CI flake uncovered a real robustness gap: when the kernel refuses a process spawn with "try again" — a momentarily full process table on a loaded machine — cbm treated it as a permanent failure. A git probe or LSP server would refuse to start for a reason invisible to the user. Transient spawn failures now retry briefly; real errors (missing binaries, permissions) still fail immediately with unchanged semantics.
Install and activation: the refusal nobody could act on
Follow-up to #1537 (@listepo), and it turned out to be two defects stacked on each other.
The daemon refused to run when any ancestor of its cache directory was group-writable — the same rule we removed on the installer side in v0.10.2, in the sibling check that decision covers but that never got changed. A group-writable ~ or ~/.cache is ordinary (WSL2 ships 0775, so do several distro skeletons and any machine with a shared primary group), so for those users the daemon was permanently unusable, with nothing to close and nothing a reboot would fix. World-writable ancestors are still refused — any local user could swap a path component — and the private directory itself is still forced to owner-only, which is what makes admitting the ancestor safe.
Underneath that, the error message was inventing its own evidence: the refusal path set no error code, so the reporting fell back to printing whatever errno happened to hold, and handed the reporter ENOENT for a permission decision. They went looking for a missing file that was there the whole time. The message now names the directory and the rule that refused it.
Also in this area: the activation guard reported every refusal as "active CBM sessions could not be stopped", including failures where nothing is running at all. Those are now separate messages, the second says plainly that nothing needs closing, and neither remedy assumes a codebase-memory-mcp binary is still on PATH — advice that was unusable at exactly the moment it printed, right after uninstall.
Also fixed
- Codex saw no cbm tools when
CBM_CACHE_DIRwas set (#1562, @anton-matosov). Codex passes only explicitly-listed variables into an MCP subprocess, so the server it spawned read the default cache while the daemon used the configured one; they disagreed and the connection closed during initialization. The generated config now forwards the variable. - The installed skill file failed to load in strict YAML readers (#1554, @xezzon). Its
descriptioncontainedTriggers on:unquoted, and a colon-space in an unquoted YAML scalar means "nested mapping" — so parsers rejected the whole document and the skill silently never loaded. search_codeechoed a negativelimitback as the result count (#1511, fix by @lukiod), which reads to an agent as an answer rather than a rejected argument.OpenCode installs went into the wrong file (@iandol).Correction: this was listed here in error. The fix merged a few hours after the v0.10.3 tag was cut, so it is not in this release — it ships in v0.10.4. If your OpenCode config is a.jsoncfile, upgrading to v0.10.3 will not have helped, and we are sorry for the wasted time.- Release archives are validated before extraction (#1510, @cyphercodes): an exact member set, with symlinked members refused outright.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1— re-running the installer is the update. - npm / PyPI: update through your package manager as usual.
- No index, cache, or config migration.
Full changelog: v0.10.2...v0.10.3
Security Verification
VirusTotal completed 17 distinct extracted byte objects covering 56 exact extracted archive members.
The extraction manifest binds those associations to 14 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
16 of 17 scanned objects returned 0 malicious and 0 suspicious verdicts with 57–68 decisive engine results (required minimum: 50).
1 object(s) carry a single Microsoft machine-learning detection (!ml), which this project treats as a known false positive and publishes rather than hides. Every other engine returned clean. See Antivirus False Positives for the evidence and for how to verify these artifacts yourself: objects/scan-c05a4598b3bb23d82efbde5e6bc01989297156ed153dadbd281ba8886c96f2d6--codebase-memory-mcp
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archiv...
v0.10.2
codebase-memory-mcp v0.10.2
An install-and-upgrade release. If v0.10.0 or v0.10.1 refused to install, refused to update, or your MCP client showed a server that connected to nothing — this is the fix, and every one of those failures came from a user report filed in the last 24 hours.
The pattern behind all of them: gates that were right in principle refused ordinary, legitimate setups, and then didn't say why. Each one below keeps the protection that matters, drops the part that was refusing real users, and now names exactly what it refused and how to proceed.
npx (and every ephemeral install path) works again
Running cbm through npx codebase-memory-mcp produced a server that answered nothing: the client waited 30 seconds and exited with zero bytes on stdout, which agents surfaced as -32000: Connection closed. Every npx invocation hit this, deterministically.
The daemon verifies a connecting client's binary image, and it was treating two very different outcomes as one failure: "this image hashes differently than mine" (the tamper case the check exists for) and "this image cannot be examined at all" — which is what an ephemeral npx cache path always produces. The second is now admitted: the handshake immediately before it has already proven semantic version, build fingerprint, and protocol/store/feature ABI, so refusing on top of that traded a real compatibility proof for an unavailable one. A genuine fingerprint mismatch still rejects, hard, and the admission is logged out loud so the weaker check is never invisible.
Separately and unconditionally: a client that cannot reach the daemon now says so in JSON-RPC, naming the reason, instead of exiting silently. Thanks to @wassolles, who arrived with the reproduction, the admission path already read, and three candidate fixes — the one we shipped was theirs.
Installing into an ordinary Linux home works again
install.sh failed with error: failed to stage install candidate: activation transaction I/O failed — a message that sent people hunting disk errors and filesystem types for what was, in fact, a permission policy: the installer required no group-write bit on the install directory and every parent above it. WSL2 ships ~ and ~/.local at 0775, as do several distro skeletons and any site using a shared primary group.
World-writable parents are still refused — any local user could swap a path component mid-install. Group-writable parents are now warned about and accepted, while the install directory itself stays strictly owner-private: that is where the binary lands, and group write there would let another account replace the executable between verification and launch. Refusals now name the directory, its mode, and which rule refused.
Root-caused by @AmirF194 in a clean container, down to the exact predicate and line; independently confirmed by @shochdoerfer and @iandol.
Updating from 0.9.x works again
v0.10.0 consolidated to one archive per platform with the graph UI always embedded, but update still asked which variant you wanted — where "ui" could only 404, and "standard" quietly was the UI build. The chooser and its flags are gone.
Binaries already installed in the field can't be fixed retroactively, so releases now also publish ui-*-named copies of each archive. Every 0.9.x updater that asks for the old name gets the same bytes and completes normally, with nothing required from you. Reported by @iandol.
macOS install diagnostics
install.sh no longer prints No such xattr: com.apple.quarantine — a harmless line (a curl-downloaded archive simply carries no quarantine attribute) that was mistaken for the cause of an unrelated failure, to the point of becoming a bug report's title. And when installation is genuinely blocked by a running session, the error now points at codebase-memory-mcp daemon status to list the processes actually holding it, instead of asserting that sessions exist and leaving you to find them. Reported by @listepo.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1— re-running the installer is the update. - npm / PyPI: update through your package manager as usual.
- From 0.9.x: the old updater works again as of this release; if it already failed for you, re-running the installer is the fastest path.
- No index, cache, or config migration.
Full changelog: v0.10.1...v0.10.2
Security Verification
VirusTotal completed 14 distinct extracted byte objects covering 32 exact extracted archive members.
The extraction manifest binds those associations to 8 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
13 of 14 scanned objects returned 0 malicious and 0 suspicious verdicts with 55–66 decisive engine results (required minimum: 50).
1 object(s) carry a single Microsoft machine-learning detection (!ml), which this project treats as a known false positive and publishes rather than hides. Every other engine returned clean. See Antivirus False Positives for the evidence and for how to verify these artifacts yourself: objects/scan-cac31c5358f3a1dcccc4d4ccbe2591f6fbae58949d23bb341429a6197e93a64b--codebase-memory-mcp
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archive SHA-256 provenance (from the extraction manifest):
| Downloadable archive | SHA-256 provenance |
|---|---|
codebase-memory-mcp-darwin-amd64.tar.gz |
bb6cb47aea9e50e2193cdd917d5dbafa63b7d9c1cbe74bab5ec9bf4faa67e295 |
codebase-memory-mcp-darwin-arm64.tar.gz |
fa3ee085485fd9c16d1c1bd8a102df518862dabd2d6a09e4c6d8dfb3cd2a7eb4 |
codebase-memory-mcp-linux-amd64-portable.tar.gz |
4ead161715fe959ef8a0e38946d63b1728d31e5fb1762a85dbb4b8c4abfbe481 |
codebase-memory-mcp-linux-amd64.tar.gz |
6e3bb7353be21407a78e67b5465e53e3afb1a4a213e7a561606900ac08dcfdd6 |
codebase-memory-mcp-linux-arm64-portable.tar.gz |
dcd6f31f4004c25e7945b355ae23a5a381acb2a620301bca93e1ddb159b672fd |
codebase-memory-mcp-linux-arm64.tar.gz |
b70148686cec55c31673fc0cebc0caf7f664f4ae29f7ba7f07b9617c2e5eaf85 |
codebase-memory-mcp-windows-amd64.zip |
8f08e5c5b480e625adf9d4560765a860d493a690df6ded5b94127283ec5b660a |
codebase-memory-mcp-windows-arm64.zip |
b77b81975021b879ca53cdd94c60b48cc5de102194b52117938a2187a42f89c6 |
v0.10.1
codebase-memory-mcp v0.10.1
A fast-follow fix release. If you use cbm through an MCP client — Claude Code, Codex, or any other spec-compliant client — upgrade now: in v0.10.0, the default output format of the core discovery tools rendered as an empty {} in exactly those clients. The graph was fine, the daemon was fine, the data was in the reply — but the part of the reply your client actually reads was empty. CLI usage was unaffected, which is precisely why this escaped the release gates.
Enormous thanks to @Navier8, whose #1522 reported this within hours of the v0.10.0 release — with a reproduction quality we wish every bug report had: same binary, same index, format-by-format call tables that localized the fault to the MCP response path before we had even read the code. The report named two bugs; the verification sweep it triggered found a third. All three are fixed here, and all three shipped in v0.10.0 sharing one failure shape: an empty result with a success status — the worst possible failure mode for an LLM client, which cannot distinguish "empty" from "nothing found" and silently draws wrong conclusions instead of seeing an error.
What was broken, and what changed
1. Tree-format tool replies rendered as {} in MCP clients (the default format — the primary path). A v0.10.0 wire-size optimization stopped duplicating tool payloads into structuredContent, leaving an empty object in its place — while every tool still declared a permissive outputSchema. The MCP spec tells clients: when a tool declares an output schema, structuredContent is the result. So search_graph, trace_path, query_graph, get_architecture, search_code, and detect_changes all returned {} to schema-honoring clients on every platform. The contract is now truthful end to end: no tool declares an outputSchema (tool output legitimately varies by the format parameter, so no static schema was ever honest), JSON-object payloads carry their parsed structuredContent, error envelopes keep their machine-readable structuredContent.error, and text-shaped payloads simply carry no structuredContent key — which also preserves the v0.10.0 win of never shipping the payload twice.
2. Seven or more pipelined requests killed the MCP server outright. Found while verifying the tool surface for this release: writing 7+ requests to the server in one burst — which is exactly what an agent issuing parallel tool calls does — overflowed an 8-frame input queue, and the overflow handler tore down the whole session: exit code 1, zero bytes of output, every already-computed response lost. A full queue is now backpressure: the reader simply stops accepting input until the worker catches up, and every request gets its answer. Verified with bursts of 7, 24, and 64.
3. config get answered every question with an empty string and exit 0. Unset keys printed nothing (while config list happily showed their defaults), and so did misspelled keys — a typo was indistinguishable from a correctly-read setting. config get now prints the stored value or the key's real runtime default, and unknown keys are an error (exit 1, with the known keys listed) on get, set, and reset alike.
Why the gates missed it — and what guards it now
The honest part: our own test for the structuredContent change pinned the broken behavior as correct, at both the unit level and in the shipped-binary smoke suite — a reminder that a test asserting the wrong contract is worse than no test. The release pipeline was green because the pipeline agreed with the bug. That class is now closed from three directions: the unit suite enumerates the entire tool table and binds all three envelope branches (absent / parsed object / error) for every registered tool, the frontend suite asserts that over-capacity pipelined input is answered without loss, and the smoke suite drives the real shipped binary the way a schema-honoring MCP client does — asserting a default-format reply can never render as {}, that no tool advertises an outputSchema, that a 24-deep pipelined burst gets 24 answers, and that config get returns real values. Every one of these was verified red against v0.10.0 before the fix and red again with the fix reverted.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1— re-running the installer is the update. - npm / PyPI: update through your package manager as usual.
- No index, cache, or config migration; v0.10.0 graphs are fully compatible.
Full changelog: v0.10.0...v0.10.1
Security Verification
VirusTotal completed 14 distinct extracted byte objects covering 32 exact extracted archive members.
The extraction manifest binds those associations to 8 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
13 of 14 scanned objects returned 0 malicious and 0 suspicious verdicts with 58–67 decisive engine results (required minimum: 50).
1 object(s) carry a single Microsoft machine-learning detection (!ml), which this project treats as a known false positive and publishes rather than hides. Every other engine returned clean. See Antivirus False Positives for the evidence and for how to verify these artifacts yourself: objects/scan-3380cf3b868d749c63f564e7c6b81381a140942ec42253f785e158ab5144064f--codebase-memory-mcp
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archive SHA-256 provenance (from the extraction manifest):
| Downloadable archive | SHA-256 provenance |
|---|---|
codebase-memory-mcp-darwin-amd64.tar.gz |
4948d92c5b5a906faee0fd11ffcbb8606a72004eece92f4b5a28e1294b306ab8 |
codebase-memory-mcp-darwin-arm64.tar.gz |
9b6350b4e51cecd49126dc1b145ce81ac0df7aea35d87d7e8fe987683ad6b641 |
codebase-memory-mcp-linux-amd64-portable.tar.gz |
97c6580a13d772d040e936584f3c5234586ab03f31a77354af8a763851a39a7f |
codebase-memory-mcp-linux-amd64.tar.gz |
57cb5e5945a5371e43db3f2184d5b08ddd679d65e285cb2c976e665eac2fb22f |
codebase-memory-mcp-linux-arm64-portable.tar.gz |
ad457b306fa5bf5a1b97d160eeb867260a7946018097222a622fb14e896c6c72 |
codebase-memory-mcp-linux-arm64.tar.gz |
5cb8a073c0e31956698a60ed4bd41c5398096ce3270b1438e7919d0cb06f558b |
codebase-memory-mcp-windows-amd64.zip |
b9169d35f7d10485c449a2a8d6900a999ef5184fb93ab89b449dbe6e8f8c72a1 |
codebase-memory-mcp-windows-arm64.zip |
9cedb3c4f01bf0846a712ff246ed1cd29d74ea7f7cda195d67dfaf7424f5a81b |
v0.10.0
codebase-memory-mcp v0.10.0
⚠️ Please use v0.10.1 instead. v0.10.0 shipped a critical MCP-client regression (#1522): the default output format of the core discovery tools rendered as an empty{}in spec-compliant MCP clients such as Claude Code. Everything below describes this release accurately — but the fix release supersedes it and is the one to install.
A milestone release since v0.9.0 — cbm's backend has been rearchitected around a shared coordination daemon, tool output was rebuilt into a compact tree format (~99% fewer output tokens on large results), the long-standing Windows memory ratchet is fixed and verified, and a new coverage system makes the graph's blind spots visible — introducing cbm's 15th tool, check_index_coverage, and a missed-code skeleton rendered right beside the code galaxy in the graph UI. Since the release candidate, the graph gained a new evidence-backed CALL_REFERENCE edge class, the Linux binaries' allocator override was found silently inactive and switched genuinely on, and every daemon/installer/Windows issue reported against the RC is fixed. Add three new community-contributed language surfaces, Swift cross-package resolution, a Nix extraction overhaul, and a broad community-driven correctness batch across the query engine, store, and extractors.
781 commits · 183 merged PRs · 42 contributors · 40+ community-reported issues fixed (v0.9.0…v0.10.0)
From release candidate to v0.10.0
This is the final release of what shipped as v0.9.1-rc.1 on 2026-07-30. It graduates as v0.10.0, not v0.9.1: a new backend execution model, a new tool, a new output format, and a new edge class are not a patch release, and the version number should say so. There will be no v0.9.1 final — this is it.
To everyone who ran the RC in their daily workflows: thank you — it worked exactly as hoped. The 1.5 weeks since the RC brought 335 commits across 76 PRs, and the most valuable of them started as your field reports. Daemon conflict handshakes that left a client hanging on "pending" (#1383), an installer that removed existing Claude Code hooks instead of adding its own (#1387), hook-augment silently emitting nothing (#1388), Windows install/uninstall refusing to stop active sessions (#1416), focus-stealing console windows from the detached daemon (#1427), a query response size that could kill the MCP server with no diagnostics (#1375), multi-key ORDER BY dropping LIMIT (#1334) — every one of these was reported against rc.1 by an RC user and is fixed in this release.
The coverage ask stands. The coverage system makes the gap between your code and the graph measurable — help us shrink it. Ask your agent to run check_index_coverage across your repositories (or click the missed skeleton's report-an-edge-case callout in the UI) and paste the resulting summary into a GitHub issue or discussion: miss classes, affected languages and file kinds, counts. That's aggregate numbers, not source code — everything stays on your machine unless you choose to paste it. Every real-world coverage summary points us at a concrete parser edge case, resolver gap, or language quirk we can fix — your repos are the test corpus we can't clone.
⚠️ Behavior changes
-
New edge class:
CALL_REFERENCE— andCALLSnow means exactly one thing. The graph now distinguishes invoking a callable from passing it as a value (app.register(pluginFn, opts)).CALLSis reserved exclusively for invocations;CALL_REFERENCEis emitted only when deterministic parser/LSP/registry evidence proves the exact callable value (occurrence-exact spans, lexical binding state, resolved symbol identity); ambiguous, shadowed, or dynamically rebound occurrences remainUSAGE. Queries that assumed every callable relationship was aCALLSedge should account for the new label — and gain a precision they didn't have before: aCALL_REFERENCEedge is proof, not a guess. -
One archive per platform — the UI is always included. The
ui-*/ non-UI variant split is gone: every platform now ships a single archive with the graph UI embedded, and the npm/PyPI packages include it by default (theCBM_VARIANT=uiopt-in from the RC is obsolete). Fewer artifacts, no wrong-variant downloads, one composition to verify.
Highlights
- The coordination daemon — a backend milestone. Until now every cbm session was an island: each MCP server, CLI call, and hook opened the store on its own and knew nothing about its neighbors. As of this release, all of them coordinate through a per-user daemon — build/version conflict handshakes, indexing admission control, and a shared graph UI. New
daemon start|stop|statusCLI; a warm daemon removes per-command startup cost — measured 40–58% faster CLI commands on an 8.5M-node graph (~2.6 s saved per call). Hardened by RC field reports: every rejection path now answers the client with a reason instead of leaving it waiting. Beyond the immediate wins, this is the foundation the next releases build on: centralized controls for multi-agent workflows and local-only reporting/observability infrastructure — everything stays on your machine; nothing is sent anywhere. - Evidence-backed call graphs. The new
CALL_REFERENCEedge class (see behavior changes) closes a long-standing precision gap: higher-order patterns — plugins registered by value, callbacks, handler tables — are now first-class graph relationships backed by deterministic evidence, whileCALLSstops absorbing guesses. Optionalinclude_evidenceoutput shows why an edge exists: the resolution provenance behind it. - Tree-format tool output — TOON-compact, prefix-grouped rows with exactly-once pagination, real
detect_changesimpact reporting, and graph cycle detection. Measured ~99% fewer output tokens vs v0.9.0 on large query results — and MCP responses no longer duplicate their payload instructuredContent, roughly doubling the usable response budget on top. - Windows memory ratchet fixed (#581) — and the Linux override switched on. The allocator override silently compiled out under MinGW, so the CRT kept freed pages committed and long-lived daemons ratcheted memory for months. mimalloc now genuinely owns Windows allocations, thread heaps release at thread exit, and an out-loud ownership audit makes any future regression announce itself at startup. Verified on release artifacts: a 10-minute pure-query soak that measured 53× committed-heap growth on v0.9.0-era builds now measures 1.06×. The same audit then caught the mirror image on Linux: shipped Linux binaries had never actually routed ordinary malloc through mimalloc — as of this release they do.
- One install, 43 coding agents — the installer automatically (or conditionally, where a client's documented markers are present) configures 43 agent and client surfaces — Claude Code, Codex CLI, Gemini CLI, Cursor, Zed, VS Code, Windsurf, Cline, Aider, Goose, Amp, Copilot CLI, Sourcegraph Cody, and 30 more — with pi and OpenCode extensions now generated straight from the tool registry. Experimental tool profiles (
--tool-profile=analysis|scout) expose restricted inspection surfaces for lightweight scout agents. And it no longer touches what isn't its own: existing hooks are preserved even when a script rewrite is refused. - Honest coverage — the missed graph — what did not make it into the graph is now first-class, headlined by
check_index_coverage— the 15th cbm tool: parse-coverage signals per file, deliberately-ignored files reported as a by-design class, coverage folded intoindex_statuswith notes onget_code_snippet, and a queryable miss graph persisted in the store. In the UI, the missed code renders as a skeleton beside the code galaxy — click-to-focus, with a report-an-edge-case callout. - New language surfaces — Perl LSP-tier semantic resolution, InterSystems IRIS ObjectScript support, and Mojo wiring — all three community-contributed — plus Swift cross-package IMPORTS resolved through
Package.swiftmanifests and a two-part Nix extraction overhaul (function-rooted files no longer drop their definitions; attrpaths modeled with scoped qualified names). - Query engine robustness —
coalesce()and multi-arg scalar functions in WHERE, correct var-length path semantics with an advertised depth clamp, DISTINCT-before-LIMIT, the full multi-keyORDER BYlist honored withLIMITintact, composite/escaped property returns, a wall-clock execution deadline for runaway queries, andtools/listpagination for non-paginating clients. - Store & artifact integrity — a four-report series from one community member exposed torn WAL-mode exports, swallowed
SQLITE_CORRUPT, stale WAL replay over fresh DBs, and invalid JSON on async edges; all four are fixed. Reindex publication is now atomic end to end — staged, verified, and quarantined on failure rather than replacing a good database with a broken one. - CI, completely wrapped up — local CI, PR CI, dry runs, and releases all execute the same canonical leg scripts, enforced by a venue-parity contract that turns any drift into a red build. Parallel per-suite test execution with a zero-loss gate, release-gating soak legs, per-run isolation, artifact attestation, and VirusTotal scanning of the real archive artifacts as a standard pre-release step.
What's changed
Coordination daemon & concurrency
This is the largest backend change since the project began: cbm's execution model moved from independent per-session processes to a coordinated per-user backend. Concurrent sessions coordinate through the daemon (daemon start|stop|status, permanent or session-managed) with authenticated IPC, build-fingerprint/version/cache-root conflict detection, phy...
v0.9.1-rc.1
codebase-memory-mcp v0.9.1-rc.1
A milestone release since v0.9.0 — cbm's backend has been rearchitected around a shared coordination daemon, tool output was rebuilt into a compact tree format (~99% fewer output tokens on large results), the long-standing Windows memory ratchet is fixed and verified, and a new coverage system makes the graph's blind spots visible — introducing cbm's 15th tool, check_index_coverage, and a missed-code skeleton rendered right beside the code galaxy in the graph UI. Add three new community-contributed language surfaces and a broad community-driven correctness batch across the query engine, store, and extractors.
438 commits · ~90 merged PRs · 25+ contributors · 30+ community-reported issues fixed (v0.9.0…v0.9.1-rc.1)
Why this one is a release candidate
This release changes cbm at a deeper level than a normal point release: the backend now runs through a coordination daemon (a fundamental shift in how every session, CLI call, and hook executes) and the memory/allocator layer was reworked on Windows. Both are extensively tested — the full CI gate, cross-platform soak legs, and benchmark verification all ran green on this exact build — but changes of this nature earn real-world mileage before a final tag.
Please run the RC in your day-to-day workflows and report anything that feels off — especially in multi-agent setups: several agents or editors hitting the same projects concurrently, long-running agent loops, parallel indexing, daemon start/stop cycles. Those concurrency paths are exactly what the new backend exists for, and field reports from them are the most valuable input for the final release. Nothing is too small to file.
A second ask: send us your coverage summaries. The new coverage system makes the gap between your code and the graph measurable — help us shrink it. Ask your agent to run check_index_coverage across your repositories (or click the missed skeleton's report-an-edge-case callout in the UI) and paste the resulting summary into a GitHub issue or discussion: miss classes, affected languages and file kinds, counts. That's aggregate numbers, not source code — everything stays on your machine unless you choose to paste it. Every real-world coverage summary points us at a concrete parser edge case, resolver gap, or language quirk we can fix, and each one fixed makes graph analysis more precise for everyone — your repos are the test corpus we can't clone. This is the single highest-leverage thing an RC user can send us.
And an apology on timing. We previewed this RC in discussion #1144 targeting mid-July, and it took clearly longer than we said. The reason is the nature of what's in it: the daemon was a genuine architecture shift, not a feature, and the memory work had to be verified rather than assumed — both had to be done right rather than quickly, and for a stretch they consumed nearly all capacity, which also slowed PR reviews and issue responses. A third, less visible reason: recurring antivirus false positives on release artifacts blocked several otherwise-green release runs in the final stretch. They never shipped — our gate requires zero detections — and this release scans clean; the full story is in Antivirus false positives on release binaries below. To everyone who contributed, reported, and waited (and nudged us — deservedly): thank you for the patience. The full explanation lives in that discussion thread.
Highlights
- The coordination daemon — a backend milestone. Until now every cbm session was an island: each MCP server, CLI call, and hook opened the store on its own and knew nothing about its neighbors. As of this release, all of them coordinate through a per-user daemon — build/version conflict handshakes, indexing admission control, and a shared graph UI. New
daemon start|stop|statusCLI; a warm daemon removes per-command startup cost — measured 40–58% faster CLI commands on an 8.5M-node graph (~2.6 s saved per call). Beyond the immediate wins, this is the foundation the next releases build on: centralized controls for multi-agent workflows (scheduling, budgets, priorities across concurrent agents) and local-only reporting/observability infrastructure — everything stays on your machine; nothing is sent anywhere. - Tree-format tool output — TOON-compact, prefix-grouped rows with exactly-once pagination, real
detect_changesimpact reporting, and graph cycle detection. Measured ~99% fewer output tokens vs v0.9.0 on large query results — the difference between a result that fits in an agent's context and one that doesn't. - Windows memory ratchet fixed (#581) — the allocator override silently compiled out under MinGW, so the CRT kept freed pages committed and long-lived daemons ratcheted memory for months. mimalloc now genuinely owns Windows allocations (link-time wrap + static-CRT override), thread heaps release at thread exit, and an out-loud ownership audit makes any future regression announce itself at startup. Verified on release artifacts: a 10-minute pure-query soak that measured 53× committed-heap growth on v0.9.0-era builds now measures 1.06×, cross-checked green on macOS/Linux/Windows.
- One install, 43 coding agents — the installer now automatically (or conditionally, where a client's documented markers are present) configures 43 agent and client surfaces — Claude Code, Codex CLI, Gemini CLI, Cursor, Zed, VS Code, Windsurf, Cline, Aider, Goose, Amp, Copilot CLI, Sourcegraph Cody, and 30 more — so cbm drops into whatever agent stack you already run. Alongside it, experimental tool profiles (
--tool-profile=analysis|scout) expose restricted inspection surfaces so lightweight scout agents get a smaller, cheaper tool set (the first slice of the evidence-tier work previewed in #1144). - Honest coverage — the missed graph — what did not make it into the graph is now first-class, headlined by
check_index_coverage— the 15th cbm tool and the first new tool since the core surface stabilized: parse-coverage signals per file, deliberately-ignored files reported as a by-design class, coverage folded intoindex_statuswith notes onget_code_snippet, and a queryable miss graph persisted in the store. In the UI, the missed code renders as a skeleton beside the code galaxy — click-to-focus to inspect exactly which files and regions the index couldn't fully parse, with a report-an-edge-case callout to send them our way. Agents (and you) can now see the boundary of what the graph knows — and every edge case you report makes it smaller. - New language surfaces — Perl LSP-tier semantic resolution, InterSystems IRIS ObjectScript support, and Mojo wiring — all three community-contributed.
- Query engine robustness —
coalesce()and multi-arg scalar functions in WHERE, correct var-length path semantics with an advertised depth clamp (no more fabricated 100-hop paths from a self-loop), DISTINCT-before-LIMIT, composite/escaped property returns, a wall-clock execution deadline for runaway queries, andtools/listpagination for non-paginating clients. - Store & artifact integrity — a four-report series from one community member exposed torn WAL-mode exports, swallowed
SQLITE_CORRUPT, stale WAL replay over fresh DBs, and invalid JSON on async edges; all four are fixed, plus atomic cache publish on the writer. - CI, completely wrapped up — the entire test infrastructure was unified: local CI, PR CI, dry runs, and releases now all execute the same canonical leg scripts, enforced by a venue-parity contract that turns any drift into a red build. Parallel per-suite test execution with a zero-loss gate, release-gating soak legs, and artifact attestation included. For users this means one thing: faster, more confident iteration — fixes and features can move from patch to verified release-shaped build much quicker from here on.
What's changed
Coordination daemon & concurrency
This is the largest backend change since the project began: cbm's execution model moved from independent per-session processes to a coordinated per-user backend. Concurrent sessions coordinate through the daemon (daemon start|stop|status, permanent or session-managed) with authenticated IPC, build-fingerprint/version/cache-root conflict detection, physical-job admission control for parallel indexing, and the graph UI served from the daemon. Because a per-user daemon is a security boundary, its IPC was hardened accordingly — on Windows, pipe clients are validated by process identity rather than impersonation (and the daemon reads a client's first frame before ever impersonating it), private-namespace objects are owner-stamped with the token user's SID, and DACLs are strictly validated; on POSIX, socket identity is checked against inode-pinned records. Everything remains strictly local. It is deliberately conservative in this RC — coordination, admission control, and lifecycle — but it is the substrate for centralized multi-agent controls and local-only observability going forward.
Preparation for worktree-native multi-agent workflows. The daemon is also the groundwork for how multi-agent setups actually run today: one agent per git worktree. Right now each worktree has to be indexed as if it were a separate repository — a full reindex of what is largely the same code. With the centralized daemon layer in place, upcoming releases will rework exactly this: worktrees become children of the main repository's graph, sharing everything unchanged and indexing only what differs — making the agent-per-worktree pattern dramatically cheaper in time and disk. This lands once the daemon has proven itself in the field, which is another reason your RC reports from worktree-heavy workflows matter. Release paths and verif...