Skip to content

Releases

v0.12.1

Choose a tag to compare

@github-actions github-actions released this 16 Oct 23:35
Immutable release. Only release title and notes can be modified.
a7158ac

v0.12.1

Every install path works again, and Helm charts join the graph.

v0.12.1 fixes v0.12.0's package installs and the other install paths we found broken while checking every channel. It also links Helm charts to what they deploy, and stops the graph from inventing a registry for container images.

Upgrading? The first index of each existing project after the upgrade is a full rebuild, once (image identities changed; see below).

Fixed: installs

  • npm, PyPI and Go installs. npm i, pnpm add, pip install and the Go wrapper could not install v0.12.0 on any platform. Each wrapper refused an archive member above 256 MiB, and every v0.12.0 binary is 331-335 MB. The size limits are gone from all three wrappers. The archive is still verified against the release's checksums.txt before it is unpacked, its contents must be exactly the expected files, and the binary must answer --version. Thank you, @hydroplexus, for the precise report (#2657).
  • Homebrew. The formula was still at 0.10.3, and on Linux it installed the dynamically linked build, which needs glibc 2.38 (Ubuntu 22.04, Debian 12 and RHEL 9 do not have it). It now installs the current release, and on Linux the static -portable build that runs on any distribution.
  • go install. A pinned go install .../pkg/go/cmd/[email protected] never worked, and @latest installed a stale version. Releases now carry pkg/go/vX.Y.Z tags, and the wrapper installs the release matching its own version.
  • npm next. The next dist-tag no longer points to an old release candidate; a stable release moves it.
  • Docs. winget and Chocolatey were listed as install channels but are not published; they are removed until they are. Windows arm64 builds ship and are now listed.

Install-path smoke tests. Every pull request and every release now installs the npm, PyPI and Go wrappers, the way a user's package manager does, against the real release archives, and runs the binary each one installed. A release is published only after these installs pass, so an installer that cannot install what we ship can no longer reach you.

Helm charts in the graph

A Helm chart used to be an isolated node. Now it links to what it deploys (#2663):

  • Chart → objects (DEPLOYS, via: chart): every Kubernetes object rendered from the chart's templates/ (a subchart's objects belong to the subchart).
  • values file → Chart (CONFIGURES): values.yaml and values-<x>.yaml beside Chart.yaml.
  • Chart → images (DEPLOYS, ref: image): every image its objects name literally (a templated image names none).

HelmRelease → Chart → Deployment → Image is now one traversable path. On 20 infrastructure repositories, charts with no outgoing edge dropped from 238 of 260 to 109, and 2,511 chart-to-object links appeared; every other edge stayed identical.

Images keep the registry the code names

An image without a registry host used to be stored under an invented docker.io/library/... (#2654). Docker Hub is only Docker's default: Podman and CRI-O resolve short names through a search list, mirrors rewrite them, and a local build tag may exist on no registry at all. An image now keeps its name as written, with registry: unknown when no host is stated; a stated host is kept (Docker Hub's spellings become docker.io). FROM nginx and image: docker.io/library/nginx are now two Image nodes. This changes image identities, which is why each project is rebuilt once after the upgrade.

Upgrade notes

  • The first index of each existing project after the upgrade is a full rebuild, once.
  • Image nodes without a stated registry now carry registry: unknown and the name as written, instead of docker.io/library/....

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–69).
Every shipped binary was clean.

Product Shipped binary VirusTotal verdict
linux-amd64 6761236d038de87bf49da5695dd52d7928180d814624d4ceb8d8e849ba07f4c8 clean
linux-arm64 43a096f3655a9ea0606146da1dbd92aece344e24248e305fc007d239e3b891ae clean
linux-amd64-portable 77745491f0a9c346d6da23eed4d92b5feaa31a27a883c9faa4fa9f47c8183705 clean
linux-arm64-portable 6a64e20e2ca8dc15b4a412e981e5aea60804ed3e952d693ab491c93ccd36ed45 clean
darwin-amd64 f3ba4d7dd462be868eff85f0dccb7375405f58084da800826065df2312b028ab clean
darwin-arm64 a36cfa1f5d10d8c4762501d63feef082711aae4704b9e8f9066db52779a8601e clean
windows-amd64 39f6a789cd858d7428561ac5afaefb9a47b018dcba0ad6fa41ae71a45e0ed656 clean
windows-arm64 b19816bb88ef3145a0f4b81856665c67a684cc038e06ae696e76960277c67b41 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.12.0

Choose a tag to compare

@github-actions github-actions released this 15 Oct 22:08
Immutable release. Only release title and notes can be modified.
ec6427c

v0.12.0

Meet CBM Lens, and a code graph that reads your documentation.

v0.12.0 is the biggest release of codebase-memory-mcp yet: 231 merged pull requests since v0.11.0, 40 of them from the community.

Four headlines define it.

  1. CBM Lens Beta: the code graph, for humans. Until now the graph's power reached people only through their agents. Lens is our new human interface. It brings the code knowledge graph to you directly. It ships inside the binary you already run and listens on loopback only. It puts source, the graph, the architecture, index coverage and a browser-local assistant side by side, and every fact it shows says where it comes from. Lens is built on the work of @BernhardJackiewicz, who now joins the project to own it.

  2. Documentation joins the graph. Until now the graph stopped where the code stopped. Now doc comments, Markdown, reStructuredText, AsciiDoc and PDF documents, architecture decision records, diagrams and OpenAPI specs are part of it. Each is linked to the functions, types and files it talks about. An agent can now traverse your documentation and land on the exact code a page describes, and walk from a function back to every page that explains it. So can you, in Lens. Linking is strict by design: a reference becomes an edge only when it names exactly one definition. Link families that have not yet cleared their accuracy bar are counted and reported, never guessed into the graph.

  3. Smart CI: the graph picks the tests. detect_changes can now say which tests a change can affect. It seeds the graph from the diff, walks everything that depends on the changed code, and names the tests at the end of those paths, each with its reason. When the evidence is missing or doubtful, it answers "run everything" and says why; it never narrows silently. codebase-memory-mcp's own CI is the first user: pull requests run only the test lanes their change touches, and the graph's test selection runs beside every PR.

  4. Infrastructure as code in the graph. Kubernetes manifests, Helm charts, Flux and Argo CD, Compose files, Dockerfiles and the scripts, Makefiles and CI jobs that deploy them now link up. A Service can be followed through its workload, its image and its Dockerfile to the program the container runs.

The code graph itself got sharper too:

  • C typedefs, macros and enumerators get identities of their own.
  • Process launches become SPAWNS edges.
  • Resolution fixes cover C#, C++, TypeScript, Python, PHP, Rust and Go.

Upgrading? The first index of each existing project after the upgrade is a full rebuild, once. Details are in Upgrade notes. A few identities and defaults also changed; see Breaking changes.

Welcome, @BernhardJackiewicz

@BernhardJackiewicz joins codebase-memory-mcp as the subsystem owner for the UI. @BernhardJackiewicz built the reading interface on the CBM read surface that became CBM Lens, and contributed it to the project (#2068; design and tracking in #1964). He also rewrote the README's Lens section with new walkthroughs and screenshots (#2383). From this release on he owns the UI subsystem and takes care of Lens. We are very glad to have him on board.

Highlights

  • CBM Lens Beta, an interface for people. It explores a project by the file you read or the code you mark, by graph neighbourhood, and by architecture, routes and hotspots. It also explains index coverage, edits the project's architecture decision record, and answers questions about your selection with a model that runs in your browser. It is built into the binary: codebase-memory-mcp --ui=true (#2068).
  • Documentation joins the graph.
    • Doc-comment references become MENTIONS edges from the documented definition to the code they name. Nine languages: C#, Go, Java, Kotlin, Rust, Perl, TypeScript/JavaScript, PHP and C (#2550, #2593).
    • Markdown, reStructuredText/Sphinx, AsciiDoc/Antora and PDF documents link their sections to the code they reference (#2551).
    • To list every section that names a definition, run trace_path inbound over MENTIONS.
  • Architecture decisions become nodes. ADR files become ADR nodes carrying their status, date, deciders and the decision itself. A record that says "Supersedes ADR-3" gets a SUPERSEDES edge (#2551).
  • Diagrams and API specs enter the graph.
    • Mermaid, PlantUML, Graphviz, D2, draw.io and Excalidraw diagrams become Diagram and DiagramElement nodes joined by DIAGRAM_ARROW edges, whether embedded in a document or standing on their own.
    • Each OpenAPI/Swagger operation becomes a Route node with the same identity as the route your code registers (#2593).
  • Smart CI, driven by the graph.
    • detect_changes with scope: "tests" answers which tests a change can reach and why. Incomplete evidence becomes a "run everything" answer that names its reason (#2458).
    • The new walk parameters edge_types, fixpoint and via expose the same dependency walk for any repository (#2458).
  • From Service to code. trace_path in run mode walks Service → workload → Image → Dockerfile → program, together with the container's environment. A deploy mode follows what deploys what, and a topology mode what references what (#2597).
  • A sharper code graph.
    • C and C++ get typedef nodes, distinct macro and enumerator identities, and member access resolved through the object's type (#2543, #2458).
    • A definition written once per #if branch, #[cfg] or Go build tag is now one node; C/C++ platform files (src/unix/, src/win/, arch/x86/, ...) keep their own path-named nodes, related by a shared variant_key that Smart CI follows (#2458, #2630).
    • Process launches become SPAWNS edges in about 30 languages (#2458).
    • Doc comments are captured whole: Go full-doc capture rose from 35% to 99.999% and C# from 10% to 99.8% (#2459).
  • Long indexes survive client deadlines.
    • Pass index_repository(async: true) to start an index in the daemon and get control back at once, then poll with status: true (#2357).
    • index_status now tells you whether a project is actually being watched, and why not if it isn't (#2348).
  • Observed vs. possible. ingest_traces now really stores the runtime call paths you send it. Lens marks the hops that actually ran (#2068).

CBM Lens Beta (#2068)

The MCP tools are the agent's interface to the graph. CBM Lens Beta is yours: our attempt to bring the power of the code knowledge graph to humans. It replaces the previous graph UI and is built into the binary:

codebase-memory-mcp --ui=true --port=9749
  • Explore: follows the current file or marked source, with its direct dependencies and a file-level impact summary from graph and local Git evidence.
  • Galaxy: search nodes, files and folders, then expand incoming or outgoing relationships by depth and edge type.
  • Architecture: the repository map, routes, hotspots, system structure and behavior.
  • Coverage and System: what the index missed and why, plus daemon status, indexes, diagnostics and MCP configuration.
  • ADR: edit the project's decision document in the browser, shared with manage_adr.
  • Add project index (Alt+P): pick a local repository, name it and follow the daemon's progress.
  • Local chat: ask about the code you have selected. Inference runs in your browser. Model weights are not bundled; they download from Hugging Face only when you activate a model.
  • Runtime evidence: call paths sent through ingest_traces are stored, and Lens marks the hops that actually ran.

Lens runs on loopback, keeps source files read-only, and names where each fact comes from. Frontend errors are journaled on the server, so a bug report can include them.

CBM Lens is built on the work of @BernhardJackiewicz on the CBM read surface, contributed with the author's agreement (design and tracking in #1964). @BernhardJackiewicz now owns the UI subsystem. Thank you, @BernhardJackiewicz. This release would look very different without your work.

Documentation in the graph

Doc-comment links (#2550, #2593)

  • C# <see cref>, <seealso cref>, <exception cref> and <inheritdoc cref> bind by the compiler's own lookup rules, including MSBuild global usings evaluated from your project files (#2550).
  • Go doc links, Javadoc and KDoc tags ({@link}, @see, @throws, {@value}, @sample), Rust intra-doc links, Perl POD L<>, TypeScript/JavaScript {@link}, PHPDoc @see and Doxygen @ref/@see follow the same rule (#2593).
  • Each of the 17 doc-comment link families that ship in #2593 passed a blind audit on repositories never used to build it (bar: Wilson 95% lower bound ≥ 0.90).
  • Families still below the bar resolve but stay out of the graph, for example TS/JS {@linkcode} and @see, diagram links and OpenAPI operationId links.
  • index_status gains a doc_links block: the MENTIONS count, plus unresolved references counted by reason (missing, ambiguous, external, …). Pass diagnostics='full' for samples.

Documents (#2551)

  • Markdown: sections link to the code they name through links, bare paths, and code spans holding paths or qualified names. A line range such as #L3-L9 binds the innermost definition that holds it. A bare name is never an edge.
  • reStructuredText / Sphinx: a Section per title, with links from roles (:class:, :meth: …), object directives, autodoc, literalinclude and inline literals. Python names resolve the way Sphinx imports them. conf.py is read as text, never executed.
  • AsciiDoc / Antora: sections link through include:: directives and tag regions.
  • PDF: every page becomes a Section whose text is searchable, linked to the paths, file names and qualified names it mentions. The extractor is dependency-free and built for untrusted input: 44,070 seeded mutant PDFs ran under ASan and UBSan with zero findings. Encrypted documents are rep...
Read more

v0.11.0

Choose a tag to compare

@DeusData DeusData released this 15 Sep 23:40
Immutable release. Only release title and notes can be modified.
8972ea6

v0.11.0

A large release, and an unusually community-heavy one: 171 pull requests merged since
v0.10.8 (9 of them dependency bumps), 76 of them from contributors outside the maintainer.
Thank you — this release is mostly your work. It is a minor version rather than another 0.10.x
patch because the index format moves (one rebuild, below) and the tool output contract changes.

The reason v0.11.0 exists is install, uninstall and daemon-startup reliability, and memory: if cbm
ever refused to install, refused to uninstall cleanly, died at startup without telling you why, or
blew through the memory budget on a large repository, this is the release to take.

Two things to know before you upgrade, and one thing you will notice.

Highlights

The first run after upgrading rebuilds your index once

This is expected. It is not an error, and nothing is lost.

PR #1104 (feb72080, by @LA-10) completes the File-node change from #1108: a File node's
qualified name now keeps its file extension, so auth.py and auth.ts in the same directory are
finally distinct nodes instead of colliding on one name. That moves the on-disk index format
boundary, so an index built by v0.10.8 or earlier carries the old, collided identities.

Rather than migrate in place — an old collided database cannot reconstruct the siblings it lost —
cbm stamps the format in PRAGMA user_version and takes a stale-format index through one full
reindex
. Your ADRs and index metadata are preserved across it (#516). After that single run, the
stamp is current and incremental indexing resumes as normal.

What you will see: the first index_repository (or the first watcher/daemon-triggered index) after
upgrading takes as long as a cold index of that repository instead of the usual incremental
seconds. A large monorepo will feel it. Everything after that is incremental again.

You may also see one orphan_removed line on that first run — that is #2111's new sweep clearing
staging files left behind by a worker that was killed under an older version. Also harmless, also
once.

Lean, lossless output — findability preserved, no capability removed

PR #1597 (3909e5a0) makes CLI and MCP responses compact by default. To be explicit about what
this is not: no tool was removed, no field you could query before became unreachable, and no
answer got harder to find. What changed is that operational detail moved behind explicit flags and
parameters, and the default response carries the graph answer itself.

Kept in the default response, always:

  • graph rows and node identities
  • exact totals, not "about N"
  • truncation state, so you always know when you are seeing a subset
  • continuation cursors for everything that was omitted

Moved behind explicit opt-in: diagnostics, evidence columns, progress chatter, and JSON output.

Two companion changes complete the contract. 14c229b1 adds lossless semantic pagination —
max_output_tokens is enforced as a deterministic, model-neutral byte ceiling that cuts at whole
semantic units instead of slicing bytes mid-record, and every omission is independently pageable.
Cursors are bound to the query arguments, the result state and the index generation, so a stale
cursor fails closed rather than silently paging a different snapshot. 4a9fcd97 adds an explicit
quiet CLI mode for scripting.

Measured on the same serialized MCP tools/list fixture: 23,075 → 15,071 bytes (−34.7%), and
−31.4% / −30.7% on cl100k / o200k token counts. For result tables with repeated paths, response-local
directory-prefix compression cut token-shaped output by 38.3% (Unix paths) and 44.5% (Windows paths);
inputs where compression would not pay are left uncompressed, so no response gets larger.

Invalid UTF-8 in source is now preserved reversibly instead of being replaced, and code search is
scoped to canonically indexed files with globs that stay portable to BusyBox/Alpine.

Memory: the budget is honoured, and the index needs far less of it

PR #2202 moves the indexer's memory onto one allocation core and makes CBM_MEM_BUDGET_MB a
promise rather than a hint. When an index approaches the budget, finished per-file results are
parked on disk and loaded back only for the moment a later pass reads them; the phases after
extraction start from the floor instead of inheriting everything that was extracted. The
semantic pass sizes its working set to the headroom left. What no budget can move is the graph
itself, so a budget below the graph's own size still fails whole, cleanly, as #2112 describes.

Measured on the Linux kernel (8.5 M nodes), same machine as v0.10.8: the default budget
(24,576 MB) used to abort the run; it now completes without spilling. At 16,000 MB and at
15,000 MB it completes through the spill path with the charged memory peaking at 14.6–14.7 GB,
and the graph is identical. On the ten LSP corpora we benchmark, peak memory dropped between
40% and 80% (Go 17.2 → 3.9 GB, Java 27.6 → 11.0 GB, C# 27.0 → 11.2 GB, TypeScript 12.0 → 2.5 GB)
with no corpus slower than v0.10.8: C# indexes in 128 s instead of 463 s, the kernel in 233 s
instead of 295 s.

Three things fell out of the proof runs and ship in the same release:

  • A per-file CPU budget covers the whole file, not just the parse. Pathological files (a
    23 MB single-expression JIT test in dotnet/runtime, a few generic-nesting stress tests) used to
    hold a worker for minutes in the usage walk; they now stop at the budget, keep what was
    extracted, and skip the type-aware walks, logged by path.
  • A crash in cross-file resolution on large C/C++ trees is fixed: the C++ class walk wrote a
    per-file scratch signature into the shared type registry, which another worker then read after
    the file's memory was gone. Found with an ASan build of the server on dotnet/runtime; the
    registry is copy-on-write now, and a test guards the contract.
  • Locale-free formatting on macOS: every vsnprintf took the process locale lock, and
    eighteen workers formatting type names serialized on it. Each thread formats with a locale of
    its own now.

The daemon and the CLI are unchanged in behaviour; a 30-minute soak on all three platforms
holds a flat memory line (Linux 11.6 MB, macOS 17.4 MB, Windows 16.8 MB) under continuous
queries.

Upgrade notes

  • One full reindex on first run. See above. Expected, once per index, ADRs preserved.
  • A new <db>.stage.*.lock sidecar appears next to the database while an index runs (#2111).
    It is the ownership marker that lets cbm tell a live writer from a dead one; it is removed when
    the run finishes.
  • An over-budget index spills first, then fails whole instead of soft-overshooting (#2202,
    #2112). Near the budget, finished results are parked on disk under <cache>/spill/ for the rest of
    the run and the run usually completes at the floor; an index whose graph alone exceeds the budget
    stops cleanly with reason:"over_memory_budget" and a named budget_mb / peak_rss_mb, and your
    previously serving index is left byte-identical. Raise CBM_MEM_BUDGET_MB if you hit it — the
    message names the variable.
  • No configuration file changes are required.

Install and reliability fixes

Windows 10 could not install at all

#2188 (042a58bb, reported and diagnosed by the reporter of #1856). install.ps1 set
SecurityProtocol = Tls12 -bor Tls13 unconditionally. Windows 10's schannel has no TLS 1.3 — it
arrives with Windows 11 and Server 2022 (build 20348) — but .NET Framework 4.8 still defines the
enum value, so the assignment succeeded silently and the first HTTPS request then died with
The request was aborted: Could not create SSL/TLS secure channel. An unsupported flag in that
bitmask is a hard failure, not a downgrade. Every Windows 10 machine hit this. The protocol set
is now computed: TLS 1.2 always, TLS 1.3 only on build 20348 and later, with an enum-name probe so
the script still parses on .NET Framework 4.7.

The same PR makes an activation refusal actionable. It used to print "Check the errors above" with
nothing above it; it now names the check that refused, says whether the offending ACE was
inherited, and gives a remedy that actually works for that case
(icacls <dir> /inheritance:r /grant:r "%USERNAME%":(OI)(CI)F — /remove:g cannot remove an
inherited ACE, which is why the old advice appeared to succeed while the refusal persisted).
No safety check was relaxed: an install directory writable by other accounts is still refused,
now in terms you can act on.

Elevated and non-standard Windows installs

#2139 (1b067e3b, closes #1705, #2023, #1686). A runtime directory created by an elevated
process is owned by the machine's built-in Administrator account, even when that account is renamed
or disabled — and cbm refused it, so install and activation failed outright on those machines. cbm
now resolves this machine's built-in Administrator SID (via the local SAM account domain) and
trusts it. Deliberately not a trailing-RID-500 match: a domain admin's -500, or another machine's
Administrator, is still foreign and still rejected. Separately, an owner-trust refusal that used to
report a bare status -3, os 0 now names the offending owner's SID alongside the path.

Daemon startup failures now tell you what happened

#2113 (91bee4fb, closes #1828). A daemon that could not start — /tmp full being the
reported case — reported a misleading 30-second "active or starting" timeout and no cause. Every
listener stage now carries stage=, errno= and path= (for example
stage=pending_publication errno=ENOSPC path=/tmp/cbm-daemon-<uid>/…), and a doomed start now
surfaces in about a second as CBM daemon failed to start: <stage> failed with <errno> at <path>
instead of burning the full deadline and then reporting the wrong thing.

#2138 (055fbb7d, closes #1830). Inside a single-uid user namespace
(unshare -U --map-current-user, `podman --user...

Read more

v0.10.8

Choose a tag to compare

@github-actions github-actions released this 19 Aug 02:42
Immutable release. Only release title and notes can be modified.
46ae198

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 tag 0.10.7 instead of v0.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_rows limits only the rows returned, as documented.
  • Python aliased from-imports resolve to the real definition (#1371, @Joseph-MingEn).
    from .gate import execute as bridge_execute now produces a CALLS edge to execute
    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

0.10.7 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Aug 18:22
Immutable release. Only release title and notes can be modified.
afebf00

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

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

Choose a tag to compare

@github-actions github-actions released this 17 Aug 14:12
Immutable release. Only release title and notes can be modified.
f9e95e3

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=1 now 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

Choose a tag to compare

@github-actions github-actions released this 15 Aug 13:12
Immutable release. Only release title and notes can be modified.
7719563

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 list included, 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 that CBM_CACHE_DIR moves 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 .cache was at fault when the home directory was refusing, and #1621 was told
    cbm-daemon-501 when /private/tmp was. 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": true beside the command and type we 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 --help no 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 -y elsewhere 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. --json and --progress are stripped before the stdin fallback is reached, so
    cli list_projects --json hung 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_coverage no 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 bare errno, and one path returned PERSIST_FAILED with 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>::missed row 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 for list_projects in #1044; this site never learned it.

Performance

  • Windows code search prefilters simple suffix globs (@ertankucukoglu), so a *.ext filter 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_code can report per-phase timings on request (@ertankucukoglu) — scope_ms,
    scan_ms and enrich_ms, behind debug: 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...
Read more

v0.10.4

Choose a tag to compare

@github-actions github-actions released this 14 Aug 07:08
Immutable release. Only release title and notes can be modified.
c0bd4bb

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_enabled governs a loopback HTTP port, and the only way to disable it was hand-editing config.json; it was invisible to config list and rejected by config set. Both it and ui_port are 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 of opencode.json / opencode.jsonc already exists, instead of always creating a .json file OpenCode ignores. But the write itself still fails on v0.10.4 with error: 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 MATCH stopped silently dropping rows (@SEPURI-SAI-KRISHNA). Once the hop buffer saturated, the no-match rows — exactly what WHERE x IS NULL exists 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 MATCH row-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 ...
Read more

v0.10.3

Choose a tag to compare

@github-actions github-actions released this 13 Aug 01:12
Immutable release. Only release title and notes can be modified.
0c64de9

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 --ui stopped 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 pi command down with it, pi doctor included. 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_DIR was 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 description contained Triggers 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_code echoed a negative limit back 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 .jsonc file, 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...

Read more

v0.10.2

Choose a tag to compare

@github-actions github-actions released this 11 Aug 17:08
Immutable release. Only release title and notes can be modified.
b377c62

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