diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 000000000..f44b5f3c2 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,15 @@ +{ + "name": "codebase-memory", + "description": "The codebase-memory-mcp code knowledge graph server, as a Claude Code plugin.", + "owner": { + "name": "DeusData", + "url": "https://github.com/DeusData" + }, + "plugins": [ + { + "name": "codebase-memory", + "source": "./plugin", + "description": "Codebase knowledge graph for AI agents: sub-ms queries, 99% fewer tokens." + } + ] +} diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 000000000..5c2a1ea68 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,27 @@ +# CodeQL scope: the repository's first-party source. +# +# Read by .github/workflows/codeql.yml (every scan, full and PR) and by +# scripts/ci/select-lanes.sh (which changed paths select which language's PR +# scan). Keep the lists plain: one path prefix per line, no globs. +# +# paths / paths-ignore narrow what the INTERPRETED languages extract +# (javascript-typescript, python, actions). For c-cpp they have no effect: +# CodeQL extracts exactly what the traced build compiles. A PR's c-cpp scan +# keeps vendored C out by compiling it before tracing starts (except vendored +# files the PR changes); the full scan on main, the dry run and the release +# traces everything, vendored C included. +name: cbm first-party source +paths: + - src + - internal + - graph-ui + - pkg + - scripts + - tests + - .github +paths-ignore: + - internal/cbm/vendored + - graph-ui/vendor + - graph-ui/fixtures + - graph-ui/verification + - tests/fixtures diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a..c6bbd96e1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,9 @@ updates: directory: "/" schedule: interval: "weekly" + groups: + # init and analyze must run the same CodeQL Action version within a job, and + # upload-sarif is kept in step with them, so one PR bumps all three. + codeql-action: + patterns: + - "github/codeql-action*" diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml index 47b5b17b5..163376ab0 100644 --- a/.github/workflows/_build.yml +++ b/.github/workflows/_build.yml @@ -41,7 +41,9 @@ jobs: goarch: arm64 cc: gcc cxx: g++ - - os: macos-14 + # The macOS minimum (14.0) is pinned in Makefile.cbm, not inherited + # from this runner's SDK, so both darwin binaries still start on macOS 14. + - os: macos-15 goos: darwin goarch: arm64 cc: cc @@ -64,7 +66,8 @@ jobs: - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: @@ -117,7 +120,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit @@ -179,7 +182,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANGARM64 path-type: inherit @@ -248,7 +251,8 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/_lint.yml b/.github/workflows/_lint.yml index 6fcda15e4..bddcee7df 100644 --- a/.github/workflows/_lint.yml +++ b/.github/workflows/_lint.yml @@ -1,5 +1,5 @@ # Reusable: lint only (cppcheck + clang-format). -# Security-static and CodeQL gate are separate — see _security.yml. +# Security-static is separate — see _security.yml; CodeQL — see codeql.yml. name: Lint & Security on: @@ -37,14 +37,16 @@ jobs: run: bash scripts/check-no-test-skips.sh - name: Install build deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev cmake + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev cmake - name: Install LLVM 20 + timeout-minutes: 10 run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main" | sudo tee /etc/apt/sources.list.d/llvm-20.list - sudo apt-get update - sudo apt-get install -y clang-format-20 + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update + sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y clang-format-20 - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cppcheck-cache @@ -81,14 +83,16 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install build deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev - name: Install LLVM 21 (pinned analyzer toolchain) + timeout-minutes: 10 run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list - sudo apt-get update - sudo apt-get install -y clang-tidy-21 + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update + sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y clang-tidy-21 - name: Memory-analyzer gate run: scripts/ci/lint-mem.sh clang-tidy-21 diff --git a/.github/workflows/_memwaste.yml b/.github/workflows/_memwaste.yml index 174490db6..52ae8a8fb 100644 --- a/.github/workflows/_memwaste.yml +++ b/.github/workflows/_memwaste.yml @@ -37,7 +37,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 clang llvm + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev python3 clang llvm - name: Event lane on this repository run: | # `cmd | tee` reports TEE's status: without this a failing lane @@ -66,7 +67,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 clang llvm + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev python3 clang llvm - name: CPU scaling lane (src/ at 2x against 4x) # Report-only: the script exits 1 when it flags a super-linear function. continue-on-error: true @@ -93,7 +95,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 clang llvm + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev python3 clang llvm - name: Access lane on this repository run: | # `cmd | tee` reports TEE's status: without this a failing lane @@ -115,7 +118,7 @@ jobs: event-macos: if: ${{ inputs.mode == 'full' }} - runs-on: macos-14 + runs-on: macos-15 timeout-minutes: 60 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -146,7 +149,7 @@ jobs: timeout-minutes: 90 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit diff --git a/.github/workflows/_security.yml b/.github/workflows/_security.yml index 666573471..40514091a 100644 --- a/.github/workflows/_security.yml +++ b/.github/workflows/_security.yml @@ -1,4 +1,6 @@ -# Reusable: security-static + CodeQL gate. +# Reusable: security-static + license gate. +# CodeQL is not here: PRs do not run it. It runs on push to main, weekly, and +# inside dry-run.yml / release.yml, which call codeql.yml directly. # Runs independently from lint/test/build — does not block the main pipeline. # Affects overall workflow success status. name: Security Gate @@ -52,96 +54,3 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: scripts/audit-license-provenance.py - codeql-gate: - if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'codeql-gate') }} - runs-on: ubuntu-latest - timeout-minutes: 240 - # Without security-events:read this job inherits the workflow's - # contents:read, the code-scanning alert API answers 403, and the gate - # below reported "0 alerts" on every run it has ever made. actions:read is - # what the CodeQL-run poll above needs for the same reason. - permissions: - contents: read - security-events: read - actions: read - steps: - - name: Wait for CodeQL on current commit (max 150 min) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # On pull_request events github.sha is the synthetic merge commit; - # CodeQL runs are recorded against the PR head SHA. - CURRENT_SHA="${{ github.event.pull_request.head.sha || github.sha }}" - echo "Waiting for CodeQL to complete on $CURRENT_SHA..." - # 300 attempts x 30s = 150 min. The previous budget was 90 x 30s = 45 min, - # which is shorter than CodeQL actually takes on this repository: a measured - # run on PR #1426 (head 7b72652a) completed with conclusion=success after - # 124 min, 7 minutes AFTER this gate had already given up. That marked at - # least five contributor PRs red for a scan that passed. The job's own - # timeout-minutes is 240, so 150 still leaves headroom. - for attempt in $(seq 1 300); do - LATEST=$(gh api "repos/${{ github.repository }}/actions/workflows/codeql.yml/runs?head_sha=$CURRENT_SHA&per_page=1" \ - --jq '.workflow_runs[] | "\(.conclusion) \(.status)"' 2>/dev/null | head -1 || echo "") - if [ -z "$LATEST" ]; then - echo " $attempt/300: no run yet..."; sleep 30; continue - fi - CONCLUSION=$(echo "$LATEST" | cut -d' ' -f1) - STATUS=$(echo "$LATEST" | cut -d' ' -f2) - if [ "$STATUS" = "completed" ] && [ "$CONCLUSION" = "success" ]; then - echo "=== CodeQL passed ==="; exit 0 - elif [ "$STATUS" = "completed" ]; then - echo "BLOCKED: CodeQL $CONCLUSION"; exit 1 - fi - echo " $attempt/300: $STATUS..."; sleep 30 - done - echo "BLOCKED: CodeQL timeout"; exit 1 - - - name: Check for open code scanning alerts - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # FAIL CLOSED. The previous form was `gh api ... 2>/dev/null || echo "0"`, - # which looks defensive and is the opposite. gh writes the API error - # BODY to stdout, so a 403 made ALERTS the string - # {"message":"Resource not accessible by integration",...,"status":"403"}0 - # every `[` comparison then failed as non-integer, the `if` took its - # false branch, and the step printed "CodeQL gate passed (0 alerts)" - # and exited 0. That is exactly how a security gate should NEVER fail: - # silently, in the direction of "everything is fine". An unreadable - # alert list is now an error, not a zero. - # NOTE: this runs in a command substitution, so it must `return`, not - # `exit` -- an `exit` here would only end the subshell and let the - # caller continue with an empty count, recreating the very bug this - # replaces. The callers below check the status explicitly. - count_open_alerts() { - local body status - body=$(gh api 'repos/${{ github.repository }}/code-scanning/alerts?state=open' \ - --jq 'length' 2>/dev/null) - status=$? - if [ $status -ne 0 ]; then - echo "BLOCKED: cannot read code scanning alerts (gh exit $status)." >&2 - echo " Refusing to treat an unreadable alert list as zero." >&2 - echo " Most likely cause: the job lacks security-events:read." >&2 - return 1 - fi - case "$body" in - ''|*[!0-9]*) - echo "BLOCKED: alert count was not a number: ${body:-}" >&2 - return 1 - ;; - esac - printf '%s' "$body" - } - echo "Waiting 60s for alert API to settle..." - sleep 60 - ALERTS=$(count_open_alerts) || exit 1 - sleep 15 - ALERTS2=$(count_open_alerts) || exit 1 - [ "$ALERTS" -lt "$ALERTS2" ] && ALERTS=$ALERTS2 - if [ "$ALERTS" -gt 0 ]; then - echo "BLOCKED: $ALERTS open alert(s)" - gh api 'repos/${{ github.repository }}/code-scanning/alerts?state=open' \ - --jq '.[] | " #\(.number) [\(.rule.security_severity_level // .rule.severity)] \(.rule.id) — \(.most_recent_instance.location.path):\(.most_recent_instance.location.start_line)"' 2>/dev/null || true - exit 1 - fi - echo "=== CodeQL gate passed (0 alerts) ===" diff --git a/.github/workflows/_smoke.yml b/.github/workflows/_smoke.yml index 1a4d4eb57..ac4ca595d 100644 --- a/.github/workflows/_smoke.yml +++ b/.github/workflows/_smoke.yml @@ -33,7 +33,7 @@ jobs: CORE_UNIX='[ {"os":"ubuntu-latest","goos":"linux","goarch":"amd64"}, {"os":"ubuntu-24.04-arm","goos":"linux","goarch":"arm64"}, - {"os":"macos-14","goos":"darwin","goarch":"arm64"}, + {"os":"macos-15","goos":"darwin","goarch":"arm64"}, {"os":"macos-15-intel","goos":"darwin","goarch":"amd64"} ]' # Broad legs reuse existing goos/goarch artifacts on additional OS @@ -47,9 +47,10 @@ jobs: # (ubuntu-22.04 / 22.04-arm). So the dynamic broad legs stay on # forward-compatible OSes only (macOS); running the dynamic binary on # ubuntu-22.04 would fail Phase 1 (glibc too old), not a real regression. - BROAD_UNIX='[ - {"os":"macos-15","goos":"darwin","goarch":"arm64"} - ]' + # Empty since the move off macos-14 (retired 2026-11-02): the darwin-arm64 + # artifact is now built AND smoked on macos-15, the core leg, which was + # this broad leg. + BROAD_UNIX='[]' CORE_WIN='[{"os":"windows-latest","arch":"amd64"}]' # windows-11-arm now runs the NATIVE arm64 binary (build-windows-arm64), # not the x86_64 binary under emulation — we smoke the artifact we ship. @@ -143,8 +144,9 @@ jobs: - name: ClamAV scan (Linux) if: startsWith(matrix.os, 'ubuntu') + timeout-minutes: 10 run: | - sudo apt-get update -qq && sudo apt-get install -y -qq clamav > /dev/null 2>&1 + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update -qq && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y -qq clamav > /dev/null 2>&1 # apt auto-starts the clamav-freshclam daemon, which holds a lock on # freshclam's log/db; stop it so the manual freshclam below can run # (else: "Failed to lock the log file ... Resource temporarily unavailable"). @@ -190,7 +192,7 @@ jobs: go-version-file: pkg/go/go.mod cache: false - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: ${{ matrix.arch == 'arm64' && 'CLANGARM64' || 'CLANG64' }} path-type: inherit diff --git a/.github/workflows/_soak.yml b/.github/workflows/_soak.yml index abf365f54..c25a1988d 100644 --- a/.github/workflows/_soak.yml +++ b/.github/workflows/_soak.yml @@ -44,7 +44,7 @@ jobs: goarch: arm64 cc: gcc cxx: g++ - - os: macos-14 + - os: macos-15 goos: darwin goarch: arm64 cc: cc @@ -66,7 +66,8 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps (Linux) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 git + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev python3 git - name: Build if: ${{ !inputs.use_release_artifacts }} run: scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} @@ -118,7 +119,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit @@ -196,7 +197,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANGARM64 path-type: inherit @@ -283,7 +284,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install soak dependencies - run: sudo apt-get update && sudo apt-get install -y python3 git + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y python3 git - name: Download selected portable release container uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -335,7 +337,7 @@ jobs: goarch: arm64 cc: gcc cxx: g++ - - os: macos-14 + - os: macos-15 goos: darwin goarch: arm64 cc: cc @@ -356,7 +358,8 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps (Linux) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 git + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev python3 git - name: Build (ASan) run: | SANITIZE="-fsanitize=address,undefined -fno-omit-frame-pointer" @@ -386,7 +389,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 39baa516c..d6ec6a85b 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -57,18 +57,21 @@ jobs: run: | # Every CORE leg is tagged with the lane (scripts/ci/select-lanes.sh) # it serves; the tag is what lane selection filters on below. + # The lane names unix-macos14 and tsan-mac are kept as lane keys + # (select-lanes.sh, ci-ok and the lane contracts key on them); both + # run on macos-15 because GitHub retires the macos-14 image on 2026-11-02. CORE_UNIX='[ {"os":"ubuntu-latest","cc":"gcc","cxx":"g++","lane":"unix-x86"}, {"os":"ubuntu-24.04-arm","cc":"gcc","cxx":"g++","lane":"unix-arm64"}, - {"os":"macos-14","cc":"cc","cxx":"c++","lane":"unix-macos14"}, + {"os":"macos-15","cc":"cc","cxx":"c++","lane":"unix-macos14"}, {"os":"macos-15-intel","cc":"cc","cxx":"c++","lane":"unix-macos-intel"} ]' # Broad matrix legs are REQUIRED gates (no optional/continue-on-error # escape hatches): the release dry run must be all-green, every platform. + # macos-15 is the CORE macOS arm64 leg now, so it is not repeated here. BROAD_UNIX='[ {"os":"ubuntu-22.04","cc":"gcc","cxx":"g++"}, - {"os":"ubuntu-22.04-arm","cc":"gcc","cxx":"g++"}, - {"os":"macos-15","cc":"cc","cxx":"c++"} + {"os":"ubuntu-22.04-arm","cc":"gcc","cxx":"g++"} ]' # Each Windows leg pins the msys2 environment + package arch to the # RUNNER architecture so the build is native, never emulated: @@ -92,7 +95,7 @@ jobs: TSAN='[ {"os":"ubuntu-latest","cc":"clang","cxx":"clang++","lane":"tsan-x86"}, {"os":"ubuntu-24.04-arm","cc":"clang","cxx":"clang++","lane":"tsan-arm"}, - {"os":"macos-14","cc":"cc","cxx":"c++","lane":"tsan-mac"} + {"os":"macos-15","cc":"cc","cxx":"c++","lane":"tsan-mac"} ]' # Lane selection. "all" (dry run, release) keeps every leg and drops # the tags, so those matrices -- and their job names -- stay exactly @@ -166,7 +169,8 @@ jobs: - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev ccache # Ubuntu 23.10+ ships kernel.apparmor_restrict_unprivileged_userns=1, which # blocks unshare(CLONE_NEWUSER) for unprivileged users. Without this, the @@ -314,7 +318,7 @@ jobs: with: node-version: "22" - - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: "3.12" @@ -367,14 +371,16 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev ccache - name: Install LLVM 21 (pinned diagnostic toolchain) + timeout-minutes: 10 run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list - sudo apt-get update - sudo apt-get install -y clang-21 + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update + sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y clang-21 - name: Compiler cache (content-verified, restore) uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -525,7 +531,8 @@ jobs: - name: Install deps (Ubuntu) if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y clang zlib1g-dev ccache + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y clang zlib1g-dev ccache - name: Install deps (macOS) if: runner.os == 'macOS' @@ -587,7 +594,7 @@ jobs: # preflight script, so AV-interaction coverage lives there; runners run # without RTP by platform constraint. - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: ${{ matrix.msystem }} path-type: inherit @@ -739,7 +746,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit diff --git a/.github/workflows/bug-repro.yml b/.github/workflows/bug-repro.yml index 2910606d0..357aec5b2 100644 --- a/.github/workflows/bug-repro.yml +++ b/.github/workflows/bug-repro.yml @@ -40,7 +40,7 @@ jobs: group: linux cc: gcc cxx: g++ - - os: macos-14 + - os: macos-15 group: macos cc: cc cxx: c++ @@ -55,7 +55,8 @@ jobs: - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev - name: Run bug-reproduction board if: ${{ github.event_name == 'push' || inputs.platforms == 'all' || inputs.platforms == matrix.group }} @@ -68,7 +69,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit diff --git a/.github/workflows/cache-warm.yml b/.github/workflows/cache-warm.yml index ff15fbb1a..661533238 100644 --- a/.github/workflows/cache-warm.yml +++ b/.github/workflows/cache-warm.yml @@ -27,7 +27,7 @@ jobs: include: - { os: ubuntu-latest, cc: gcc, cxx: g++ } - { os: ubuntu-24.04-arm, cc: gcc, cxx: g++ } - - { os: macos-14, cc: cc, cxx: c++ } + - { os: macos-15, cc: cc, cxx: c++ } - { os: macos-15-intel, cc: cc, cxx: c++ } runs-on: ${{ matrix.os }} timeout-minutes: 60 @@ -36,7 +36,8 @@ jobs: - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev ccache - name: Install ccache (macOS) if: startsWith(matrix.os, 'macos') @@ -81,7 +82,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 621d407fc..1a1c8d6fc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,47 +1,195 @@ name: CodeQL SAST +# One analysis per language: c-cpp, javascript-typescript (graph-ui, pkg/npm), +# python, actions. Scope: .github/codeql/codeql-config.yml (first-party source). +# +# FULL scan -- push to main, weekly, dry run, release: every language; c-cpp +# traces the whole production build, vendored C included. +# PR scan -- pr.yml's `codeql` job (light: true), needed by ci-ok. One leg per +# language whose scope the PR changes (codeql-* lanes of +# scripts/ci/select-lanes.sh; an undeterminable diff or a change to the +# workflows, the build or the CodeQL machinery selects every language). +# Each leg analyses its language's WHOLE first-party scope, so cross-file +# data flow is kept; c-cpp compiles the vendored objects before tracing +# starts, except vendored files the PR changes. Diff-informed analysis then +# restricts the REPORTED alerts to the changed lines; what is extracted and +# analysed is never narrowed by the diff. +# There is no pull_request trigger and no job anywhere that polls for a CodeQL +# run: the former PR gate waited up to 150 min per PR for a full analysis and, +# with ~30 PRs pushed at once, starved PR CI for hours. on: push: branches: [main] - pull_request: - branches: [main] + schedule: + - cron: "23 5 * * 1" + workflow_dispatch: + inputs: + light: + description: "PR-style c-cpp build (vendored objects compiled untraced) -- to time it" + type: boolean + default: false + workflow_call: + inputs: + light: + description: "PR scan: vendored C compiled untraced except changed files; diff-informed results" + type: boolean + default: false + matrix: + description: 'The legs to run, {"include":[...]} (pr.yml: the selected codeql-* lanes); empty = every language' + type: string + default: "" permissions: contents: read -# One analysis per pull request and one per pushed branch. A newer push to main -# cancels the analysis of an older main commit; only the newest commit's -# results matter for main, and a merge-heavy day otherwise queues one 40-100 -# minute analysis per merge ahead of every pull request run. -concurrency: - group: codeql-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - jobs: analyze: runs-on: ubuntu-latest - # CodeQL needs security-events: write to upload results; scoped to this job so - # the workflow's top-level token stays read-only (Scorecard TokenPermissions). + strategy: + fail-fast: false + # The same legs (and keys) everywhere, so a PR's analysis and main's + # share one code-scanning configuration per language. + matrix: ${{ fromJSON(inputs.matrix || '{"include":[{"language":"c-cpp","build-mode":"manual","lane":"codeql-cpp"},{"language":"javascript-typescript","build-mode":"none","lane":"codeql-js"},{"language":"python","build-mode":"none","lane":"codeql-py"},{"language":"actions","build-mode":"none","lane":"codeql-actions"}]}') }} + # Superseded runs are always cancelled: a newer push to main cancels the + # analysis of an older main commit (batched merges -> only the newest main + # commit is scanned), a newer push to a PR cancels that PR's scan. Every + # other trigger gets a group of its own and is never cancelled: a dry run + # or a release tests ONE commit, and two dispatched together must not + # cancel each other's gate. Per language, so legs never cancel each other. + # In a called workflow `github` is the CALLER's context: event_name is + # pull_request for pr.yml, github.workflow names the dry run or release. + concurrency: + group: codeql-${{ matrix.language }}-${{ github.event_name == 'push' && github.ref || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('{0}-{1}', github.workflow, github.run_id) }} + cancel-in-progress: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }} + # CodeQL needs security-events: write to upload results (and read for the + # alert check); scoped to this job so the workflow's top-level token stays + # read-only (Scorecard TokenPermissions). permissions: security-events: write actions: read contents: read + env: + # One code-scanning configuration for every trigger. The action derives + # the key from the RUN's workflow file, which for a called workflow is + # the caller: pr.yml / dry-run.yml / release.yml would each open a + # configuration of their own, and on main an alert fixed by a later push + # would stay open in a stale one. The value is the key the push-to-main + # analysis has always used (the action documents this variable as + # settable for non-standard setups). + CODEQL_ACTION_ANALYSIS_KEY: .github/workflows/codeql.yml:analyze + # Diff-informed analysis: codeql-action v4.38.1 (CLI 2.27.0) reports a + # pull request's alerts only on its changed lines when the + # diff_informed_queries feature is on (default on; this variable forces + # it, src/feature-flags.ts). It applies only when the event payload is a + # pull request -- pr.yml's call -- and is inert on main, dry run, release. + CODEQL_ACTION_DIFF_INFORMED_QUERIES: ${{ inputs.light && 'true' || '' }} + # No compiler cache: a cache hit skips the compiler, and CodeQL extracts + # only what a traced compiler invocation actually compiles. + CBM_NO_CCACHE: "1" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # The PR scan reads the merge commit's first parent (the base) to + # find the vendored files the PR changes. + fetch-depth: ${{ inputs.light && 2 || 1 }} - name: Install build dependencies - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev + if: ${{ matrix.language == 'c-cpp' }} + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev + + # PR c-cpp: compile the third-party objects (tree-sitter grammars, + # sqlite, mimalloc, lz4, zstd, the tree-sitter runtime: ~1.3 GB of + # vendored C against ~45 MB first-party) BEFORE tracing starts, so the + # traced build below compiles first-party code only ... + - name: Build third-party objects (PR scan, untraced) + if: ${{ matrix.language == 'c-cpp' && inputs.light }} + run: scripts/build.sh --vendored-only + + # ... except the vendored files this PR changes: those are rebuilt + # under tracing (an undeterminable diff leaves the list empty; the scan + # still covers all first-party C). + - name: Vendored files this PR changes (traced too) + if: ${{ matrix.language == 'c-cpp' && inputs.light }} + run: scripts/ci/codeql-retrace-vendored.sh "$RUNNER_TEMP/codeql-retrace.txt" - name: Initialize CodeQL - uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4 with: - languages: c-cpp - build-mode: manual + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + config-file: ./.github/codeql/codeql-config.yml - - name: Build for CodeQL analysis + - name: Build for CodeQL analysis (full) + if: ${{ matrix.language == 'c-cpp' && !inputs.light }} run: scripts/build.sh + - name: Build for CodeQL analysis (PR scan, first-party + changed vendored) + if: ${{ matrix.language == 'c-cpp' && inputs.light }} + run: scripts/build.sh --reuse-vendored --retrace-from "$RUNNER_TEMP/codeql-retrace.txt" + - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4 with: - category: "/language:c-cpp" + category: "/language:${{ matrix.language }}" + + # Alert policy: the default branch carries ZERO open code-scanning + # alerts. Any open alert on the analysed ref fails this leg: on main the + # commit turns red and GitHub notifies whoever triggered the run; the + # dry run and the release cannot pass; a PR (refs/pull//merge: what + # its changed lines introduce) fails ci-ok. With a zero baseline "any + # open alert" is exactly "a new alert"; a dismissal (with a reason, in + # the Security tab) holds on every ref. Every leg checks after its own + # upload, so each language's alerts are seen by at least its own leg. + # Moved from _security.yml's former PR-CI codeql-gate job. + - name: Check for open code scanning alerts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ALERT_REF: ${{ github.ref }} + run: | + # FAIL CLOSED. The previous form was `gh api ... 2>/dev/null || echo "0"`, + # which looks defensive and is the opposite. gh writes the API error + # BODY to stdout, so a 403 made ALERTS the string + # {"message":"Resource not accessible by integration",...,"status":"403"}0 + # every `[` comparison then failed as non-integer, the `if` took its + # false branch, and the step printed "CodeQL gate passed (0 alerts)" + # and exited 0. That is exactly how a security gate should NEVER fail: + # silently, in the direction of "everything is fine". An unreadable + # alert list is now an error, not a zero. + # NOTE: this runs in a command substitution, so it must `return`, not + # `exit` -- an `exit` here would only end the subshell and let the + # caller continue with an empty count, recreating the very bug this + # replaces. The callers below check the status explicitly. + count_open_alerts() { + local body status + body=$(gh api "repos/${{ github.repository }}/code-scanning/alerts?state=open&ref=$ALERT_REF" \ + --jq 'length' 2>/dev/null) + status=$? + if [ $status -ne 0 ]; then + echo "BLOCKED: cannot read code scanning alerts (gh exit $status)." >&2 + echo " Refusing to treat an unreadable alert list as zero." >&2 + echo " Most likely cause: the job lacks security-events:read." >&2 + return 1 + fi + case "$body" in + ''|*[!0-9]*) + echo "BLOCKED: alert count was not a number: ${body:-}" >&2 + return 1 + ;; + esac + printf '%s' "$body" + } + echo "Waiting 60s for alert API to settle..." + sleep 60 + ALERTS=$(count_open_alerts) || exit 1 + sleep 15 + ALERTS2=$(count_open_alerts) || exit 1 + [ "$ALERTS" -lt "$ALERTS2" ] && ALERTS=$ALERTS2 + if [ "$ALERTS" -gt 0 ]; then + echo "::error title=Open code scanning alerts::$ALERTS open alert(s) on $ALERT_REF -- fix, or dismiss with a reason in the Security tab" + echo "BLOCKED: $ALERTS open alert(s)" + gh api "repos/${{ github.repository }}/code-scanning/alerts?state=open&ref=$ALERT_REF" \ + --jq '.[] | " #\(.number) [\(.rule.security_severity_level // .rule.severity)] \(.rule.id) — \(.most_recent_instance.location.path):\(.most_recent_instance.location.start_line)"' 2>/dev/null || true + exit 1 + fi + echo "=== CodeQL gate passed (0 alerts) ===" diff --git a/.github/workflows/dry-run.yml b/.github/workflows/dry-run.yml index 600dbad04..c582716fe 100644 --- a/.github/workflows/dry-run.yml +++ b/.github/workflows/dry-run.yml @@ -2,9 +2,10 @@ # Each step can be skipped for faster iteration. # # Pipeline: lint → test → build + candidate scan/select → smoke/soak -# Security: security-static + codeql-gate (independent island) +# Security: security-static + license-gate (independent island) +# CodeQL: codeql.yml full scan of this commit + alert check (independent island) # -# Security does NOT block lint/test/build/smoke/soak. All jobs must pass +# Security and CodeQL do NOT block lint/test/build/smoke/soak. All jobs must pass # for the overall workflow to be green. # # Candidate VirusTotal scanning and tuple-local selection are part of the @@ -62,15 +63,24 @@ jobs: # ── Security (independent island — does not block main pipeline) ── security: uses: ./.github/workflows/_security.yml - # The called job requests security-events:read to count code-scanning - # alerts; a reusable workflow cannot request more than its caller grants, - # so withholding this here fails the whole run at startup. + # No job in _security.yml reads code scanning or the Actions API any more + # (the CodeQL gate moved to codeql.yml), so the token stays read-only. permissions: contents: read - security-events: read - actions: read secrets: inherit + # ── CodeQL: full scan of this exact commit + the open-alert check ── + # Every language, vendored C included (PRs run the narrower PR scan). + # codeql.yml uploads results and reads the alerts: a called workflow + # cannot request more than its caller grants, so withholding either + # permission fails the whole run at startup. + codeql: + uses: ./.github/workflows/codeql.yml + permissions: + contents: read + security-events: write + actions: read + # ── Lint (cppcheck + clang-format) ──────────────────────────── lint: if: ${{ inputs.skip_lint != true }} @@ -132,7 +142,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev clang llvm libclang-rt-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev clang llvm libclang-rt-dev - name: Fuzz smoke (extract, cypher, config) run: scripts/fuzz.sh all --runs 20000 --seed 1 - name: Upload reproducers diff --git a/.github/workflows/fast-repro.yml b/.github/workflows/fast-repro.yml index 5f38c1772..be6228e48 100644 --- a/.github/workflows/fast-repro.yml +++ b/.github/workflows/fast-repro.yml @@ -26,7 +26,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev - name: test-repro (single platform, ASan; CBM_REPRO_ONLY filters suites) env: # Optionally narrow to specific suites for a fast targeted check, e.g. diff --git a/.github/workflows/nightly-soak.yml b/.github/workflows/nightly-soak.yml index 2bd5bc7e8..d4357aad6 100644 --- a/.github/workflows/nightly-soak.yml +++ b/.github/workflows/nightly-soak.yml @@ -45,7 +45,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev clang llvm libclang-rt-dev + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev clang llvm libclang-rt-dev - name: Fuzz exploration (30 min per target) run: scripts/fuzz.sh all --max-total-time 1800 --seed 0 - name: Upload reproducers diff --git a/.github/workflows/pr-acknowledgement.yml b/.github/workflows/pr-acknowledgement.yml index ef36a9fb0..760483abb 100644 --- a/.github/workflows/pr-acknowledgement.yml +++ b/.github/workflows/pr-acknowledgement.yml @@ -51,17 +51,25 @@ jobs: run: | set -euo pipefail MESSAGE_FILE=.github/pr-acknowledgement.md + BODY_FILE="$RUNNER_TEMP/pr-acknowledgement-body.md" if [ ! -f "$MESSAGE_FILE" ]; then echo "No $MESSAGE_FILE in the base tree — acknowledgements are off." exit 0 fi - # An empty or whitespace-only file is the documented off switch. - if [ -z "$(tr -d '[:space:]' < "$MESSAGE_FILE")" ]; then - echo "$MESSAGE_FILE is blank — acknowledgements are off." + # HTML comments in the template are notes for maintainers. Posted + # verbatim they become text a reader cannot see but a tool can, and + # our own PR screening holds every comment that carries hidden text. + # Strip them (an unterminated one runs to the end, as GitHub renders + # it) so the posted body is exactly what the contributor sees. + perl -0777 -pe 's/|\z)[ \t]*\n?//gs' "$MESSAGE_FILE" > "$BODY_FILE" + + # A body with no visible text is the documented off switch. + if [ -z "$(tr -d '[:space:]' < "$BODY_FILE")" ]; then + echo "$MESSAGE_FILE has no visible text — acknowledgements are off." exit 0 fi - gh pr comment "$PR_NUMBER" --repo "$REPO" --body-file "$MESSAGE_FILE" + gh pr comment "$PR_NUMBER" --repo "$REPO" --body-file "$BODY_FILE" echo "Acknowledged PR #${PR_NUMBER}." diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 815146253..3b4ea807c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -26,13 +26,10 @@ jobs: security: needs: [changes] uses: ./.github/workflows/_security.yml - # The called job requests security-events:read to count code-scanning - # alerts; a reusable workflow cannot request more than its caller grants, - # so withholding this here fails the whole run at startup. + # No job in _security.yml reads code scanning or the Actions API any more + # (the CodeQL gate moved to codeql.yml), so the token stays read-only. permissions: contents: read - security-events: read - actions: read secrets: inherit with: lanes: ${{ needs.changes.outputs.lanes }} @@ -70,8 +67,9 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Build this checkout's engine + timeout-minutes: 20 run: | - sudo apt-get update && sudo apt-get install -y zlib1g-dev + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev scripts/build.sh CC=gcc CXX=g++ - name: Select the tests id: select @@ -128,6 +126,7 @@ jobs: full: ${{ steps.select.outputs.full }} lanes: ${{ steps.select.outputs.lanes }} smoke: ${{ steps.select.outputs.smoke }} + codeql: ${{ steps.select.outputs.codeql }} steps: - name: List the changed files env: @@ -177,9 +176,13 @@ jobs: cat "$RUNNER_TEMP/selection.txt" >> "$GITHUB_OUTPUT" # pr-smoke's legs, each tagged with its lane (kept in the job name, # where ci-ok reads it back); the job gets the selected ones. - SMOKE='[{"os":"ubuntu-latest","lane":"smoke-ubuntu"},{"os":"macos-14","lane":"smoke-mac"},{"os":"windows-latest","lane":"smoke-win"}]' + SMOKE='[{"os":"ubuntu-latest","lane":"smoke-ubuntu"},{"os":"macos-15","lane":"smoke-mac"},{"os":"windows-latest","lane":"smoke-win"}]' LANES=$(sed -n 's/^lanes=//p' "$RUNNER_TEMP/selection.txt") echo "smoke={\"include\":$(jq -c --argjson sel "$LANES" 'map(select(.lane | IN($sel[])))' <<< "$SMOKE")}" >> "$GITHUB_OUTPUT" + # The CodeQL legs, tagged the same way (codeql.yml's full matrix has + # the same entries, so PR and main share a configuration). + CODEQL='[{"language":"c-cpp","build-mode":"manual","lane":"codeql-cpp"},{"language":"javascript-typescript","build-mode":"none","lane":"codeql-js"},{"language":"python","build-mode":"none","lane":"codeql-py"},{"language":"actions","build-mode":"none","lane":"codeql-actions"}]' + echo "codeql={\"include\":$(jq -c --argjson sel "$LANES" 'map(select(.lane | IN($sel[])))' <<< "$CODEQL")}" >> "$GITHUB_OUTPUT" # ── Light smoke: build the PRODUCTION binary and run the core smoke on the # three RELIABLE NATIVE platforms. Catches built-binary regressions at PR @@ -209,13 +212,14 @@ jobs: - name: Install deps (Ubuntu) if: matrix.os == 'ubuntu-latest' - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev ccache - name: Install ccache (macOS) - if: matrix.os == 'macos-14' + if: matrix.os == 'macos-15' run: command -v ccache >/dev/null 2>&1 || brew install ccache - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 if: matrix.os == 'windows-latest' with: msystem: CLANG64 @@ -252,7 +256,7 @@ jobs: SMOKE_REQUIRE_WRAPPERS: "1" - name: Build prod + smoke (macOS) - if: matrix.os == 'macos-14' + if: matrix.os == 'macos-15' run: | scripts/build.sh CC=cc CXX=c++ codesign --sign - --force build/c/codebase-memory-mcp @@ -316,8 +320,9 @@ jobs: run: scripts/ci/test-impact-shadow.sh --fetch-release "$RUNNER_TEMP/cbm-bin" - name: Build the binary from this checkout (download failed) if: ${{ steps.release.outcome == 'failure' }} + timeout-minutes: 20 run: | - sudo apt-get update && sudo apt-get install -y zlib1g-dev + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev scripts/build.sh CC=gcc CXX=g++ mkdir -p "$RUNNER_TEMP/cbm-bin" cp build/c/codebase-memory-mcp "$RUNNER_TEMP/cbm-bin/" @@ -327,8 +332,9 @@ jobs: # released binary predates it, so the checkout is built either way. - name: Build this checkout's engine if: ${{ steps.release.outcome != 'failure' }} + timeout-minutes: 20 run: | - sudo apt-get update && sudo apt-get install -y zlib1g-dev + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev scripts/build.sh CC=gcc CXX=g++ - name: Select with the team artifact (engine shadow) env: @@ -361,13 +367,34 @@ jobs: - name: Contract steps only run: scripts/test.sh --contracts-only + # ── CodeQL PR scan (lanes codeql-cpp / -js / -py / -actions): one leg per + # language whose first-party scope this PR changes; each analyses that + # language's whole first-party scope, c-cpp with the vendored objects + # compiled before tracing (except vendored files the PR changes), and + # reports alerts on the changed lines (diff-informed). A job of this run, + # needed by ci-ok -- nothing waits for a separate CodeQL run. Push to + # main, dry run and release run the full scan (codeql.yml). ── + codeql: + needs: [changes] + if: ${{ !cancelled() && needs.changes.result == 'success' && (contains(fromJSON(needs.changes.outputs.lanes), 'codeql-cpp') || contains(fromJSON(needs.changes.outputs.lanes), 'codeql-js') || contains(fromJSON(needs.changes.outputs.lanes), 'codeql-py') || contains(fromJSON(needs.changes.outputs.lanes), 'codeql-actions')) }} + uses: ./.github/workflows/codeql.yml + # codeql.yml uploads results and reads alerts: a called workflow cannot + # request more than its caller grants (startup failure otherwise). + permissions: + contents: read + security-events: write + actions: read + with: + light: true + matrix: ${{ needs.changes.outputs.codeql }} + ci-ok: # The one required context (besides dco) — fails unless every PR stage # succeeded, so matrix renames can never silently deadlock merges, and # every lane the selector chose ran green: `skipped` is OK only for a lane # nobody selected (a selected lane that skipped is a silent gate loss). # memwaste and the test-impact shadow are report-only and not needed here. - needs: [changes, security, lint, test, pr-smoke, contracts] + needs: [changes, security, lint, test, pr-smoke, contracts, codeql] if: ${{ always() }} runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 134b425e2..020ea3f0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ # Release pipeline: lint → test → build → smoke/soak → draft → verify → publish # -# Security (security-static + codeql-gate) starts immediately and runs in -# parallel with lint/test/build/smoke/soak. It only blocks the final verify +# Security (security-static + license-gate) and CodeQL (codeql.yml: full scan +# of this commit + the open-alert check) start immediately and run in +# parallel with lint/test/build/smoke/soak. They only block the final verify # step — everything else proceeds independently. name: Release @@ -44,15 +45,24 @@ jobs: # ── Security (starts immediately, blocks only verify) ─────────── security: uses: ./.github/workflows/_security.yml - # The called job requests security-events:read to count code-scanning - # alerts; a reusable workflow cannot request more than its caller grants, - # so withholding this here fails the whole run at startup. + # No job in _security.yml reads code scanning or the Actions API any more + # (the CodeQL gate moved to codeql.yml), so the token stays read-only. permissions: contents: read - security-events: read - actions: read secrets: inherit + # ── CodeQL (starts immediately, blocks only verify) ────────────── + # Full scan of this exact commit (every language, vendored C included) + + # the open-alert check. codeql.yml uploads results and reads the alerts: + # a called workflow cannot request more than its caller grants, so + # withholding either permission fails the whole run at startup. + codeql: + uses: ./.github/workflows/codeql.yml + permissions: + contents: read + security-events: write + actions: read + # ── 0. Preflight: refuse malformed dispatch inputs ────────────── # The tag is inputs.version VERBATIM. A bare "0.10.7" publishes a release the # installers can never resolve (they fetch releases/download/v/...), @@ -247,7 +257,7 @@ jobs: # fail if its SBOM cannot be attested. An unattested SBOM is worse than # an absent one, because the artifact still ships looking complete. - name: Attest SBOM - uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 env: NODE_OPTIONS: '--max-http-header-size=32768' with: @@ -312,12 +322,12 @@ jobs: body: ${{ inputs.release_notes || '' }} generate_release_notes: ${{ inputs.release_notes == '' }} - # ── 7. Verify + Publish (requires security gate) ─────────────── + # ── 7. Verify + Publish (requires the security and CodeQL gates) ─ verify: - needs: [release-draft, security] + needs: [release-draft, security, codeql] # Explicit condition: a deliberately skipped test ancestor must not skip # the publish chain; the draft itself proves smoke and soak both succeeded. - if: ${{ !cancelled() && !failure() && needs.security.result == 'success' && needs.release-draft.result == 'success' }} + if: ${{ !cancelled() && !failure() && needs.security.result == 'success' && needs.codeql.result == 'success' && needs.release-draft.result == 'success' }} runs-on: ubuntu-latest permissions: contents: write @@ -525,7 +535,7 @@ jobs: # ── PyPI ───────────────────────────────────────────────── - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: '3.12' diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index fb0a52bbc..d5a794173 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -26,13 +26,13 @@ jobs: persist-credentials: false - name: Run OpenSSF Scorecard - uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 + uses: ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e # v2.5.0 with: results_file: results.sarif results_format: sarif publish_results: true - name: Upload SARIF results - uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4 + uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4 with: sarif_file: results.sarif diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index e9b4f9357..62193d060 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -39,9 +39,6 @@ jobs: - os: ubuntu-24.04-arm cc: gcc cxx: g++ - - os: macos-14 # arm64 - cc: cc - cxx: c++ - os: macos-15 # arm64 cc: cc cxx: c++ @@ -54,7 +51,8 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install deps (Linux) if: startsWith(matrix.os, 'ubuntu') - run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 git + timeout-minutes: 10 + run: APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev python3 git - name: Build (production path with explicit test seams) run: scripts/build.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} TEST_SEAMS=1 - name: Smoke invariants @@ -72,7 +70,7 @@ jobs: timeout-minutes: 240 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANG64 path-type: inherit @@ -102,7 +100,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2 + - uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2 with: msystem: CLANGARM64 path-type: inherit diff --git a/.github/workflows/test-impact-artifact.yml b/.github/workflows/test-impact-artifact.yml index 4db216ecd..49f70dd67 100644 --- a/.github/workflows/test-impact-artifact.yml +++ b/.github/workflows/test-impact-artifact.yml @@ -44,12 +44,13 @@ jobs: persist-credentials: false - name: Install build deps and LLVM 21 (coverage toolchain) + timeout-minutes: 10 run: | - sudo apt-get update && sudo apt-get install -y zlib1g-dev + APT_RETRIES=3; [ -f /etc/apt/apt-mirrors.txt ] && APT_RETRIES=0; sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update && sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y zlib1g-dev wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list - sudo apt-get update - sudo apt-get install -y clang-21 llvm-21 libclang-rt-21-dev + sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 --error-on=any update + sudo apt-get -o Acquire::Retries=$APT_RETRIES -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 install -y clang-21 llvm-21 libclang-rt-21-dev - name: Build the production binary and the coverage runner run: | diff --git a/.gitignore b/.gitignore index 1533c93e0..c388fa360 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,8 @@ Thumbs.db # MCP config (user-local, generated by install command) .mcp.json +# ...except the Claude Code plugin's server definition, which is published +!plugin/.mcp.json # MCP Registry auth tokens .mcpregistry_* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 93596d06f..8263c54fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,9 @@ Contributions are welcome. This guide covers setup, testing, and PR guidelines. ## Build from Source -**Prerequisites**: C compiler (gcc or clang), make, zlib, Git. Optional: Node.js 22+ (for graph UI). +**Build prerequisites**: C compiler (gcc or clang), make, zlib, Git. Optional: Node.js 22+ (for graph UI). + +**Contributor check prerequisites**: `scripts/test.sh` also requires `zip`, `python3`, and `curl`. `scripts/lint.sh` requires `cppcheck`, `clang-format`, and `clang-tidy`; `scripts/lint.sh --ci`, which is what CI runs, skips `clang-tidy`. ```bash git clone https://github.com/DeusData/codebase-memory-mcp.git diff --git a/MAINTAINERS.md b/MAINTAINERS.md index d749762b6..6c4b07368 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -52,7 +52,7 @@ Operational authority is stricter than code review authority. | Operation | Current authority | Project rule | | --- | --- | --- | -| PR validation (`pr.yml`, DCO, CodeQL) | Automatic | Anyone may trigger it by opening or updating a PR. Required checks must pass. | +| PR validation (`pr.yml`, DCO, PR CodeQL scan) | Automatic | Anyone may trigger it by opening or updating a PR. Required checks must pass. | | Dry run (`dry-run.yml`) | `@DeusData` | Delegated release operators may run dry runs after promotion. Dry-run delegation does not imply release authority. | | Smoke/soak/repro manual runs | `@DeusData` | Area reviewers may operate these runs when delegated for diagnosis. Results are advisory. | | Release workflow (`release.yml`) | `@DeusData` only | Owner-only until a release operator is explicitly promoted. Publishing, replacing releases, and tag movement remain owner-gated. | diff --git a/Makefile.cbm b/Makefile.cbm index 6d19f430a..3265590c4 100644 --- a/Makefile.cbm +++ b/Makefile.cbm @@ -18,8 +18,24 @@ # that use their own *_CFLAGS — and so it survives a command-line `CC=` override. # ARCHFLAGS is empty on Linux/Windows and for native direct `make` invocations, # leaving CC/CXX unchanged there. -override CC := $(CC) $(ARCHFLAGS) -override CXX := $(CXX) $(ARCHFLAGS) +# +# Minimum macOS version (Apple targets only). Without one, clang takes the +# minimum from the SDK it compiles against (LC_BUILD_VERSION minos), so a +# release built on a macos-15 runner would refuse to start on macOS 14. 14.0 +# keeps every macOS that could run a release built on the macos-14 runner +# (retired 2026-11-02). A flag folded into the drivers like ARCHFLAGS, not an +# exported MACOSX_DEPLOYMENT_TARGET: it reaches every compile AND link on every +# build path (build.sh, test.sh, direct make) and is part of each command line. +# Detected from the compiler's target, not the host, so a cross compiler run +# on a Mac is left untouched. +MACOS_MIN_VERSION := 14.0 +IS_APPLE_TARGET := $(shell echo | $(CC) $(ARCHFLAGS) -dM -E - 2>/dev/null | grep -q '__APPLE__' && echo yes || echo no) +MACOS_MIN_FLAGS := +ifeq ($(IS_APPLE_TARGET),yes) +MACOS_MIN_FLAGS := -mmacosx-version-min=$(MACOS_MIN_VERSION) +endif +override CC := $(CC) $(ARCHFLAGS) $(MACOS_MIN_FLAGS) +override CXX := $(CXX) $(ARCHFLAGS) $(MACOS_MIN_FLAGS) # ── Common flags ───────────────────────────────────────────────── @@ -45,8 +61,9 @@ endif # -Wdate-time: with -Werror, any use of __DATE__/__TIME__/__TIMESTAMP__ fails the # build. Build-time entropy is how one identical source tree produced a new hash -# on every compile (see the local patch in vendored/mimalloc/src/options.c) — -# once removed, it must not creep back through a new call site. +# on every compile (mimalloc's version banner, patched locally until upstream +# dropped the clock in v3.4.5; see vendored/mimalloc/PATCHES.md) — once +# removed, it must not creep back through a new call site. CFLAGS_COMMON = -std=c11 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall -Wextra -Werror \ -Wno-unused-parameter -Wno-sign-compare -Wdate-time \ $(GCC_ONLY_FLAGS) \ @@ -182,8 +199,9 @@ endif ifeq ($(IS_MINGW),yes) # --no-insert-timestamp: the PE header otherwise carries the link wall clock, so # two Windows builds of identical source are never byte-identical and every -# release is a brand-new file to a reputation system. Same motivation as the -# mimalloc __DATE__ patch; ELF and Mach-O have no equivalent field to clear. +# release is a brand-new file to a reputation system. Same motivation as +# dropping mimalloc's __DATE__ banner; ELF and Mach-O have no equivalent field +# to clear. WIN32_LIBS := -lws2_32 -lpsapi -lshell32 -ladvapi32 -lbcrypt -Wl,--allow-multiple-definition -Wl,--stack,8388608 -Wl,--no-insert-timestamp -static endif @@ -397,6 +415,7 @@ EXTRACTION_SRCS = \ $(CBM_DIR)/diagram.c \ $(CBM_DIR)/route_path.c \ $(CBM_DIR)/openapi.c \ + $(CBM_DIR)/extract_nextjs.c \ $(CBM_DIR)/sql_values.c \ $(CBM_DIR)/helpers.c \ $(CBM_DIR)/cpp_branch_views.c \ @@ -537,6 +556,7 @@ PIPELINE_SRCS = \ src/pipeline/pass_compile_commands.c \ src/pipeline/pass_infrascan.c \ src/pipeline/pass_k8s.c \ + src/pipeline/helm_template.c \ src/pipeline/pass_operator.c \ src/pipeline/pass_shell.c \ src/pipeline/pass_terraform.c \ @@ -643,6 +663,18 @@ ifneq ($(filter yes,$(IS_MINGW) $(IS_LINUX)),) MIMALLOC_OVERRIDE_DEFINE := -DMI_MALLOC_OVERRIDE=1 GLOBAL_OVERRIDE_DEFINE := -DCBM_MEM_GLOBAL_OVERRIDE=1 endif +# MI_MINGW_UCRT64: since 3.5.3, mimalloc treats a MinGW build as having a +# process-attach hook only when this is defined (src/prim/windows/prim.c); +# upstream's CMake sets it, this Makefile must too. Without it src/prim/prim.c +# also compiles its constructor, so _mi_auto_process_init runs twice (that +# constructor plus the CRT TLS callback) and the second options flush writes a +# stray newline to stderr before main: a blank first line on every run, which +# failed smoke-win's one-line --version check. Every MinGW toolchain we build +# with (MSYS2 CLANG64/CLANGARM64) is UCRT. 3.3.2 had no such condition. +MIMALLOC_WIN_DEFINE := +ifeq ($(IS_MINGW),yes) +MIMALLOC_WIN_DEFINE := -DMI_MINGW_UCRT64=1 +endif # MI_DEBUG=0: the production allocator is mimalloc's RELEASE build. Its header # defaults to MI_DEBUG=2 unless NDEBUG is defined, and nothing here defined it, # so every shipped binary up to v0.11.0 ran mimalloc's debug build: internal @@ -652,15 +684,39 @@ endif # work inside the allocator (2026-09-17). Committed-bytes accounting, which the # memory budget reads, is kept in every build (subproc stats, not MI_STAT). # The TEST build below keeps mimalloc's debug checks. +# MI_PROFILE=0: mimalloc 3.5.2+ compiles its allocation-sampling profiler in by +# default, which puts a sample countdown on the allocation path. The product +# never uses it, so the production allocator leaves it out (the contract in +# tests/test_prod_allocator_contract.sh checks the banner has no profiling line). MIMALLOC_CFLAGS = -std=c11 -O2 -w \ -Ivendored/mimalloc/include \ -Ivendored/mimalloc/src \ - -DMI_OVERRIDE=1 -DMI_DEBUG=0 \ - $(MIMALLOC_OVERRIDE_DEFINE) + -DMI_OVERRIDE=1 -DMI_DEBUG=0 -DMI_PROFILE=0 \ + $(MIMALLOC_OVERRIDE_DEFINE) $(MIMALLOC_WIN_DEFINE) MIMALLOC_CFLAGS_TEST = -std=c11 -g -O1 -w \ -Ivendored/mimalloc/include \ -Ivendored/mimalloc/src \ - -DMI_OVERRIDE=0 + -DMI_OVERRIDE=0 $(MIMALLOC_WIN_DEFINE) +# MI_WIN_INIT_USE_FLS (MinGW TEST build only) is an EXPERIMENT (R32) for the +# Windows test leg's nondeterministic crash: 1-3 suites die in their first +# test with 0xC0000005 and no sanitizer report, seen only on trees with +# mimalloc 3.5.3. 3.5.3 is the first version whose loader hooks are live on +# MinGW: TLS callbacks in .CRT$XLB/.CRT$XLY run _mi_auto_process_init under the +# loader lock and _mi_thread_done on every thread detach. With FLS, +# src/prim/windows/prim.c compiles none of those hooks; process init runs once, +# from the constructor in src/prim/prim.c (no double init), and thread-done +# goes through a fiber-local destructor. MI_DEBUG=1 is what FLS needs to run at +# all: the first thread init (src/init.c) associates the main theap before the +# FLS key exists, which trips an internal assertion in prim.c and aborts every +# MI_DEBUG=2 process at startup. Level 1 keeps the fill patterns, padding and +# double-free checks and drops only the internal assertions. The production +# allocator (MIMALLOC_CFLAGS) is unchanged. Drop or promote this when the +# experiment ends. +MIMALLOC_WIN_TEST_DEFINE := +ifeq ($(IS_MINGW),yes) +MIMALLOC_WIN_TEST_DEFINE := -DMI_WIN_INIT_USE_FLS=1 -DMI_DEBUG=1 +MIMALLOC_CFLAGS_TEST += $(MIMALLOC_WIN_TEST_DEFINE) +endif # sqlite3 (vendored amalgamation — compiled ourselves for ASan instrumentation) # SQLITE_ENABLE_FTS5: enables the FTS5 full-text search extension used by the @@ -1298,6 +1354,16 @@ $(BUILD_DIR)/prod_zstd.o: $(CBM_DIR)/vendored/zstd/zstd.c $(BUILD_DIR)/.build-co OBJS_VENDORED_PROD = $(MIMALLOC_OBJ_PROD) $(SQLITE3_OBJ_PROD) $(TRE_OBJ_PROD) $(GRAMMAR_OBJS_PROD) $(TS_RUNTIME_OBJ_PROD) $(LSP_OBJ_PROD) $(PP_OBJ_PROD) $(LZ4_OBJ_PROD) $(ZSTD_OBJ_PROD) $(UNIXCODER_OBJ) +# The part of OBJS_VENDORED_PROD that holds no first-party code: everything but +# prod_lsp_all.o (first-party LSP unity) and prod_preprocessor.o (first-party +# code around vendored simplecpp). CodeQL's light PR scan builds these BEFORE +# it starts tracing (scripts/build.sh --vendored-only), so the traced build +# (--reuse-vendored) compiles first-party code only: the grammars alone are +# most of the 1.3 GB of vendored C, against ~45 MB of first-party code. +OBJS_THIRD_PARTY_PROD = $(MIMALLOC_OBJ_PROD) $(SQLITE3_OBJ_PROD) $(TRE_OBJ_PROD) $(GRAMMAR_OBJS_PROD) $(TS_RUNTIME_OBJ_PROD) $(LZ4_OBJ_PROD) $(ZSTD_OBJ_PROD) $(UNIXCODER_OBJ) +.PHONY: cbm-third-party +cbm-third-party: $(OBJS_THIRD_PARTY_PROD) + MAIN_SRC = src/main.c # Rebuild when the build CONFIGURATION changes, not only when sources do. The @@ -1375,6 +1441,7 @@ LLVM_BIN := $(shell brew --prefix llvm 2>/dev/null)/bin CLANG_TIDY ?= $(shell [ -x "$(LLVM_BIN)/clang-tidy" ] && echo "$(LLVM_BIN)/clang-tidy" || echo clang-tidy) CLANG_FORMAT ?= $(shell [ -x "$(LLVM_BIN)/clang-format" ] && echo "$(LLVM_BIN)/clang-format" || echo clang-format) CPPCHECK ?= cppcheck +CPPCHECK_JOBS ?= 2 # macOS SDK sysroot (needed for Homebrew LLVM to find system headers) SYSROOT = $(shell xcrun --show-sdk-path 2>/dev/null) @@ -1399,10 +1466,16 @@ lint-tidy: lint-tidy-diff: @scripts/lint-tidy-diff.sh "$(CLANG_TIDY)" $(LINT_SRCS) -- $(CFLAGS_COMMON) $(SYSROOT_FLAG) -# cppcheck: complementary analysis (config in .cppcheck) +# cppcheck: complementary analysis (config in .cppcheck). A build directory +# retains whole-program analysis with parallel workers; each run owns its state. lint-cppcheck: @echo "=== cppcheck ===" - @$(CPPCHECK) --enable=warning,style,performance,portability \ + @set -e; \ + cppcheck_dir=$$(mktemp -d "$${TMPDIR:-/tmp}/cbm-cppcheck.XXXXXX"); \ + trap 'status=$$?; rm -rf "$$cppcheck_dir" || :; exit "$$status"' EXIT; \ + trap 'exit 129' HUP; trap 'exit 130' INT; trap 'exit 143' TERM; \ + $(CPPCHECK) -j$(CPPCHECK_JOBS) --cppcheck-build-dir="$$cppcheck_dir" \ + --enable=warning,style,performance,portability \ --std=c11 --language=c \ --suppressions-list=.cppcheck \ --error-exitcode=1 \ diff --git a/README.md b/README.md index c34d20b98..be8338f2a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# codebase-memory-mcp: local MCP server for code search, lower token usage and visualization +# codebase-memory-mcp: local code intelligence for AI agents via MCP [![GitHub Release](https://img.shields.io/github/v/release/DeusData/codebase-memory-mcp?style=flat&color=blue)](https://github.com/DeusData/codebase-memory-mcp/releases/latest) [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![CI](https://img.shields.io/github/actions/workflow/status/DeusData/codebase-memory-mcp/release.yml?branch=main&label=main%20CI)](https://github.com/DeusData/codebase-memory-mcp/actions/workflows/release.yml) -[![C tests](https://img.shields.io/badge/C_tests-9388_cases-blue)](#tests) +[![Native and frontend tests](https://img.shields.io/badge/tests-native_%2B_frontend-blue)](#tests) [![MCP tools](https://img.shields.io/badge/MCP_tools-17-blue)](#mcp-tools) [![Languages](https://img.shields.io/badge/languages-162-orange)](https://github.com/DeusData/codebase-memory-mcp) [![Hybrid LSP](https://img.shields.io/badge/Hybrid_LSP-type_resolution-blue)](#hybrid-lsp) @@ -14,33 +14,28 @@ [![SLSA 3](https://slsa.dev/images/gh-badge-level3.svg)](https://slsa.dev) [![VirusTotal](https://img.shields.io/badge/VirusTotal-scanned_every_release-brightgreen?logo=virustotal)](https://github.com/DeusData/codebase-memory-mcp/releases/latest) [![arXiv](https://img.shields.io/badge/arXiv-2603.27277-b31b1b?logo=arxiv)](https://arxiv.org/abs/2603.27277) +[![Join the community on Discord](https://img.shields.io/badge/Discord-Join_the_community-5865F2?logo=discord&logoColor=white)](https://discord.gg/KguGXxg2X) -**Search your code, follow call graphs, and explore software architecture in your browser.** codebase-memory-mcp is a local [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that gives Claude Code, Codex, and other coding agents a persistent code knowledge graph. It indexes functions, classes, imports, HTTP routes, and their relationships across [162 languages](#language-support). [Hybrid LSP](#hybrid-lsp) adds semantic type resolution for **Python, TypeScript, JavaScript, JSX/TSX, PHP, C#, Go, C, C++, Java, Kotlin, Rust, and Perl**. +**Understand your repository, from source code to the systems around it.** codebase-memory-mcp is an MCP server that connects code, documentation, configuration, and infrastructure in a persistent local knowledge graph. Use it to find an unfamiliar feature, follow a call across services, trace a deployment back to its source, or assess which parts of a project a change could affect. -**About 120× fewer tokens** in the project's reported five-query comparison: ~3,400 vs ~412,000 with file-by-file exploration. Published backend benchmarks also report **Linux kernel indexing in ~3 minutes** (28M lines of code) and **Cypher relationship queries under 1 ms** on an Apple M3 Pro. [Benchmark details](#performance). +Give your coding agent structural context through [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), or query the graph directly from the CLI. **The MCP server is the core of the project.** Its optional visual companion, **CBM Lens, is in open beta (v0.0.1)**, with a source reader, architecture maps, the Galaxy graph, coverage diagnostics, architecture decisions, and local code chat. -**CBM Lens Beta helps you understand unfamiliar code and stay in control as it grows.** Use it to get up to speed in a new codebase, tackle rescue engineering on an inherited project, or make sense of 50,000 lines of AI-generated code from a single day. Its IDE-style source reader, local code chat, 3D architecture map, and Galaxy graph let you see how the code fits together and follow dependencies and call paths back to the source. Use that context to review changes, investigate fragile areas, and keep code quality high. Architecture decisions stay alongside the project's index. +The native C backend exposes **17 MCP tools**, parses [languages and file formats](#language-support) with vendored grammars, and adds [Hybrid LSP](#hybrid-lsp) type resolution for supported languages. The installer supports **45 automatic or conditional client integrations**, including client configuration, skills, agent profiles, and applicable hooks. Indexing and graph queries need no hosted service or API key. Focused graph queries can reduce repeated file reads and token use; see the [measured results](#performance). -The native C backend exposes 17 MCP tools and supports 45 automatic or conditional client integrations. Indexing and graph queries need no hosted service or API key. - -> **CBM Lens Beta is new on `main`:** it arrived with [PR #2068](https://github.com/DeusData/codebase-memory-mcp/pull/2068) and is not part of the current v0.11.0 release, whose installer still ships the previous graph UI. To run the interface shown below now, [build from source](#build-from-source). - -[Quick start](#quick-start) · [Explore CBM Lens](#cbm-lens-beta-code-visualization-and-source-navigation) · [Chat with your codebase locally](#chat-with-your-codebase-locally) · [MCP tools](#mcp-tools) · [Build from source](#build-from-source) - -> **Research:** The [preprint](https://arxiv.org/abs/2603.27277) compares agents across 31 repositories, reporting **10× lower token use** and **2.1× fewer tool calls**, with answer-quality scores of 83% for CBM versus 92% for file-by-file exploration. These backend results are separate from the five-query comparison above and from CBM Lens. +[Quick start](#quick-start) · [Capabilities](#features) · [Documentation](#documentation-connected-to-code) · [Infrastructure](#infrastructure-and-deployment) · [Smart CI](#smart-ci-and-test-impact) · [Explore CBM Lens](#cbm-lens-beta-code-visualization-and-source-navigation) · [Agent setup](#agent-setup-skills-profiles-and-hooks) · [MCP tools](#mcp-tools) > **Local processing:** Source analysis and graph storage run on your machine. CBM Lens can optionally download a model for browser AI after you enable it; inference stays on the device. The installer writes agent configuration for the clients you select. See [Security](#security) for release verification and [SECURITY.md](SECURITY.md) for reporting issues. -**Architecture overview.** Folders become nested platforms, files and symbols become selectable blocks, and arrows show indexed relationships. The recording zooms into the map of this repository, selects `src/pipeline`, opens the area, selects `artifact.c` and opens its source. Height can show source size; hotspot gravity reflects static fan-in. [Still image](docs/images/lens-architecture.png). +**See the graph with CBM Lens (open beta v0.0.1).** Folders become nested platforms, files and symbols become selectable blocks, and arrows show indexed relationships. The recording opens `src/foundation` in the map of this repository, selects `arena.c` and opens its source. Height can show source size; hotspot gravity reflects static fan-in. [Still image](docs/images/lens-architecture.png).

- CBM Lens Beta architecture walkthrough: zoom into the source folders of this repository, open src/pipeline, select artifact.c and open its source + CBM Lens Beta architecture walkthrough: open src/foundation, select arena.c and read its source beside the map

-**Galaxy.** Explore the code knowledge graph in 3D. The recording searches `cbm_pipeline_run`, follows its outgoing calls one layer further, switches to the hierarchy and shows the path to `cbm_store_check_integrity`. Edge pulses show relationship direction; they do not represent live execution. [Current still: two dependency layers around `cbm_max_file_bytes`](docs/images/lens-galaxy.jpg). +**Galaxy.** Explore the code knowledge graph in 3D. The recording follows the outgoing relationships of `cbm_arena_alloc` one layer further, switches to the hierarchy and shows the two-hop call path to `cbm_alloc`. Edge pulses show relationship direction; they do not represent live execution. [Still: two dependency layers around `cbm_max_file_bytes`](docs/images/lens-galaxy.jpg).

- Code knowledge graph in CBM Lens Beta Galaxy: search a function, expand its outgoing calls, switch to the hierarchy and follow a path + Code knowledge graph in CBM Lens Beta Galaxy: expand cbm_arena_alloc, switch to the hierarchy and follow its call path through arena_grow to cbm_alloc

**Explore.** Browse files in an IDE-style workspace, read syntax-highlighted source in Monaco, and keep the code graph alongside it. File impact shows what depends on the open file, and Code details shows the callers and calls of the symbol under the caret. Select code to [ask the local chat about it](#chat-with-your-codebase-locally). @@ -58,27 +53,27 @@ The native C backend exposes 17 MCP tools and supports 45 automatic or condition -These captures show CBM Lens Beta on 12–13 October 2026, using public source from this repository. See [capture details](docs/images/README.md) for each capture's build, indexed scope and recording setup. +The high-resolution GIFs show CBM Lens Beta on 20 October 2026; the still images were captured on 12–13 October. All show public source from this repository. See [capture details](docs/images/README.md) for each capture's build, indexed scope and recording setup. ## Why codebase-memory-mcp -- **Indexing performance**: Linux kernel (28M LOC, 75K files) in 3 minutes. RAM-first pipeline: LZ4 compression, in-memory SQLite, fused Aho-Corasick pattern matching. Memory released after indexing. -- **Plug and play**: native executable plus authenticated release-owned assets for macOS (arm64/amd64), Linux (arm64/amd64), and Windows (arm64/amd64). The native install needs no Docker, language runtime, or API keys. Download → `install` → restart agent → done. -- **162 languages**: vendored tree-sitter grammars compiled into the binary. No separate parser installation. -- **About 120× fewer tokens in the reported five-query comparison**: ~3,400 tokens vs ~412,000 via file-by-file search. See [Performance](#performance) for the scope and sources. -- **45 supported automatic/conditional client surfaces**: `install` configures detected clients and safely activates conditional clients only when their documented platform, marker, or explicit existing config path is present. See [Multi-Agent Support](#multi-agent-support) for the complete matrix and manual/UI-only boundaries. -- **CBM Lens Beta**: read source, chat about selected code with a local model, explore architecture and call graphs, inspect coverage gaps, and edit architecture decision records in your browser. -- **Infrastructure-as-code indexing**: Dockerfiles, container images, docker compose, Kubernetes manifests, and Kustomize overlays indexed as graph nodes with cross-references. `Resource` nodes for K8s kinds and compose services, `Image` nodes joining the images that are built and run, `Module` nodes for Kustomize overlays with `IMPORTS` edges to referenced resources; a Service walks to the program its container runs. -- **17 MCP tools**: search, trace, architecture, impact analysis, targeted index-coverage checks, Cypher queries, dead code detection, cross-service HTTP linking, ADR management, and more. +- **Find the right code with context**: combine symbol, text, and semantic search with callers, dependencies, source snippets, and file outlines. Targeted queries and compact, paged results can reduce token use and repeated file reads; see [Performance](#performance) for scoped measurements. +- **Connect documentation to implementation**: index document sections, PDF text pages, architecture decisions, and supported references into code. Find the explanation alongside the symbol. +- **Follow the system beyond source files**: trace supported relationships through Docker, Kubernetes, Helm, Kustomize, Terraform/OpenTofu, Terragrunt, Ansible, scripts, Make, and CI configuration. +- **Understand a change before running it**: inspect affected symbols, compare indexed snapshots, and find related tests. [Experimental Smart CI](#smart-ci-and-test-impact) combines static relationships and per-test coverage for supported C/C++ test harnesses, falling back to broader runs when evidence is insufficient. +- **Explore visually with CBM Lens Beta**: read source, follow call paths, inspect architecture and coverage gaps, manage architecture decisions, and ask a local model about selected code. +- **Equip your coding agents**: `install` sets up supported client configuration, durable guidance, skills, Scout/Verify/Auditor profiles, and applicable context hooks. Support varies by client; see the [integration matrix](#multi-agent-support). +- **Keep context local and reusable**: indexes persist between sessions, eligible active MCP sessions can keep them synchronized, and optional graph artifacts let a team share a baseline. +- **Run a native tool on your machine**: standard releases include CBM Lens for macOS, Linux, and Windows. The native runtime needs no Docker or language runtime; parser grammars are included. ## Quick Start -**Install the latest release** (macOS / Linux). For CBM Lens Beta shown above, [build from source](#build-from-source) until the next release. +**Install the latest release** (macOS / Linux): ```bash curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh | bash ``` -The released graph UI is included in the standard installer. +The standard native release includes CBM Lens. Start it after installation with `codebase-memory-mcp daemon start --open --port=9749`. The [Nix packages](#nix-flake) and [source builds](#build-from-source) also offer builds without the UI. **Windows** (PowerShell): ```powershell @@ -136,7 +131,19 @@ Restart your coding agent. Say **"Index this project"**: done. The `install` command automatically strips macOS quarantine attributes and ad-hoc signs the binary; no manual `xattr`/`codesign` needed. -The `install` command auto-detects installed coding agents and configures their documented MCP entries plus durable instructions, skills, and lifecycle hooks where supported. +### Agent setup: skills, profiles, and hooks + +Installation also equips supported coding agents to use the graph in daily work. The native `install` command detects clients and installs the integration pieces their documented formats support: + +| Component | What it adds | +|-----------|--------------| +| **MCP configuration** | Connects the client to the local server and its tools. | +| **Instructions and skills** | Explains graph-first exploration, index freshness, coverage checks, and when to read source. | +| **Scout, Verify, and Auditor profiles** | Offers fast provisional lookup, task-directed verification, or bounded full verification, where custom agents are supported. | +| **Session and compaction hooks** | Restores graph context at supported lifecycle events. | +| **Search and read hooks** | Adds relevant graph context to supported searches and flags recorded coverage gaps after supported file reads. | + +Hook augmentation is context-only and fail-open: it does not replace or block the requested tool call. Features vary by client and platform; some clients use durable instructions or a parent-agent handoff instead of hooks or direct subagent MCP access. Codex users review and trust installed hooks through `/hooks`. See [Multi-Agent Support](#multi-agent-support) and [Sessions, compaction, and subagents](#sessions-compaction-and-subagents) for exact support and activation requirements. ### Session Coordination Daemon @@ -154,18 +161,18 @@ Thin frontends still write immediate startup and session-specific errors to thei All active CBM processes must run the exact same version, executable build, coordination ABI, and canonical cache root. Equivalent `CBM_CACHE_DIR` aliases resolve to the same root; a genuinely different root is rejected while any CBM process is active. MCP servers, hooks, one-shot CLI commands, temporary index workers, and the daemon share a crash-safe OS admission barrier; starting an ordinary conflicting process fails before doing work and records an explicit conflict in `${CBM_CACHE_DIR}/logs/daemon-conflicts.ndjson`. -The native `install`, `update`, and `uninstall` commands are the deliberate exception to that conflict rule. Download, verification, and private same-filesystem staging happen first so a bad candidate never disrupts active work. Activation then publishes account-wide maintenance intent, asks the daemon and every temporary local operation to cancel, and waits to a finite deadline for all coordinated CBM processes to exit. It holds the admission and lifetime barriers exclusively while changing the active binary, configuration, PATH, or indexes. New CBM work cannot enter during this window. Activation progress and results are recorded in `${CBM_CACHE_DIR}/logs/activation-events.ndjson`, and a successful command tells you to restart open coding-agent sessions so they launch the activated build. +The native `install` and `uninstall` commands are the deliberate exception to that conflict rule. For installation, download, verification, and private same-filesystem staging finish before activation so a bad candidate never disrupts active work. The native `update` command prints the installer command that performs the update. Activation then publishes account-wide maintenance intent, asks the daemon and every temporary local operation to cancel, and waits to a finite deadline for all coordinated CBM processes to exit. It holds the admission and lifetime barriers exclusively while changing the active binary, configuration, PATH, or indexes. New CBM work cannot enter during this window. Activation progress and results are recorded in `${CBM_CACHE_DIR}/logs/activation-events.ndjson`, and a successful command tells you to restart open coding-agent sessions so they launch the activated build. Package-manager setup (npm, PyPI, or Go) verifies and caches the native executable for that package. It does not replace the active native installation and therefore does not stop running CBM sessions. When that cached binary is executed, it still enters the same exact-build admission barrier. The shell and PowerShell installers invoke the verified candidate's native `install` command, so they do receive the full account-wide activation guarantee. -The ordinary `cli` mode runs one command locally, but it is not disconnected from the daemon above: it connects to the shared coordination daemon (starting one if none is already running) for that same admission barrier plus per-project locks on graph mutations. Its connection is a `cli_session`: it never registers with the background watcher and never starts a UI, and it holds the admission lease only for the command's lifetime. If the command's own connection is what started the daemon, that daemon exits again once the command closes and no other session is attached; if a daemon was already running, the command simply joins and leaves it exactly as found. While the command is running, a temporary monitor separately lets activation cancel that operation and its supervised worker safely; the monitor exits with the command and never becomes a standing daemon. See [CLI Mode](#cli-mode) for details. +The ordinary `cli` mode runs one command locally, but it is not disconnected from the daemon above: it connects to the shared coordination daemon (starting one if none is already running) for that same admission barrier plus per-project locks on graph mutations. Its connection is a `cli_session`: it does not register a project watcher or open a browser, and it holds the admission lease only for the command's lifetime. The shared daemon may serve Lens according to its UI configuration and available assets. If the command's own connection is what started the daemon, that daemon exits again once the command closes and no other session is attached; if a daemon was already running, the command simply joins and leaves it exactly as found. While the command is running, a temporary monitor separately lets activation cancel that operation and its supervised worker safely; the monitor exits with the command and never becomes a standing daemon. See [CLI Mode](#cli-mode) for details. ### CBM Lens Beta: code visualization and source navigation -CBM Lens Beta is the browser interface built into the binary. It builds on [CodeAtlasWeb](graph-ui/README.md) by [Bernhard Jackiewicz](https://github.com/BernhardJackiewicz) (design and tracking in #1964). It runs on loopback; the only request that leaves your machine is the optional model download from Hugging Face, and only after you start it. Each panel names where its facts come from, and what the index did not record is shown as a gap. Start the local server: +**CBM Lens is an optional visual companion to the MCP server, currently in open beta (v0.0.1).** Standard native releases include its browser interface, so you can launch Lens directly after installation; UI-enabled source builds include it too. It builds on [CodeAtlasWeb](graph-ui/README.md) by [Bernhard Jackiewicz](https://github.com/BernhardJackiewicz) (design and tracking in #1964). Lens is served on loopback. Browser AI downloads its optional model only after you enable it; inference stays on-device. Each panel names where its facts come from, and what the index did not record is shown as a gap. Start the local server: ```bash -./build/c/codebase-memory-mcp daemon start --open --port=9749 +codebase-memory-mcp daemon start --open --port=9749 ``` The browser opens at `http://127.0.0.1:9749`. A permanent daemon keeps the UI available after the command exits. Use `daemon status` to inspect it and `daemon stop` when you are finished. For an MCP session, `--ui=true --port=9749` enables the same shared UI. Active clients must use the same executable build and cache directory. @@ -209,9 +216,9 @@ Open **Chat** with the panel button at the top right to ask about the code you a Questions about callers and callees, such as "Who calls cbm_pipeline_run?", are answered from the loaded graph without the model, and the answer says so. When you select a symbol, the chat lists its indexed facts and the lines of its code, and the model adds one sentence; that sentence is left out when it claims something the source does not show. Text written by the model is labelled as such. -The recording shows a question about callers answered from the indexed graph, next to the automatic explanation of the selected symbol. [Still image](docs/images/lens-chat.png). +The recording asks who calls `arena_grow` and what it calls, with both answers listed directly from the indexed graph beside the selected symbol. [Still image of a model explanation and graph answer](docs/images/lens-chat.png). -

Chat with your codebase locally in CBM Lens Beta: a selected symbol, its listed facts and a caller question answered from the indexed graph

+

Chat with your codebase locally in CBM Lens Beta: callers and callees of arena_grow answered directly from the indexed graph

The chat history is kept for 24 hours in this browser, per project. Each new message uses the source currently open in the reader; earlier source snapshots are not silently accumulated, and messages about another file or selection are not sent along. If a prompt is too long for the model, older messages are left out first; if it still does not fit, CBM Lens asks you to select less code instead of cutting the code. This chat explains the context you give it; it cannot edit files or run tools. Check its answers against the source. The small default model words things loosely, especially in languages other than English. @@ -232,7 +239,7 @@ The Agents workspace and activity overlays are hidden in normal builds. To inclu VITE_EXPERIMENTAL_AGENTS=true scripts/build.sh --with-ui ``` -Activity comes from explicitly configured tool hooks. The bundled adapter supports Claude Code PostToolUse; other clients need their own adapter. Recorded events go to the local daemon, and the reader must enable activity before the browser polls them. This does not automatically observe every agent. See the [frontend setup guide](graph-ui/README.md#die-agentenebene-was-live-arbeitende-agenten-auf-dem-graphen-zeigen). +Activity comes from explicitly configured tool hooks. The bundled adapter supports Claude Code PostToolUse; other clients need their own adapter. Recorded events go to the local daemon, and the reader must enable activity before the browser polls them. This does not automatically observe every agent. See the [frontend setup guide](graph-ui/README.md#experimental-agent-activity). @@ -246,13 +253,13 @@ Enable automatic indexing on MCP session start: codebase-memory-mcp config set auto_index true ``` -When enabled, new projects are indexed automatically on first connection. Previously-indexed projects are registered with the background watcher for ongoing git-based change detection. Configurable file limit: `config set auto_index_limit 50000`. +`auto_index` is off by default. When enabled, new projects are indexed automatically on first connection. The configurable file limit is `config set auto_index_limit 50000`. Previously indexed projects can be watched through `auto_watch` (on by default), subject to its global setting and session ownership. -The watcher follows MCP sessions: it watches the project each open MCP session is rooted in (the client's working directory) and stops when the last session for that project closes. A repository indexed with `cli index_repository`, or indexed from a session rooted somewhere else, is not watched; re-run `index_repository` after changing it. `index_status` reports this in its `watch` object: `watched`, and either the poll cadence and last scan time or the `reason` it is not watched. +Each initialized, long-lived MCP session can register its root project when it connects and any target from a successful `index_repository` call, including custom project names and roots in another worktree. While `auto_watch` is enabled, these watches belong to the registering sessions and remain while at least one owning session is connected; closing the last owner removes the watch. Under a daemon started with `daemon start` a watch outlives its last owning session: the project stays watched and is re-indexed in the background when it changes, until that daemon stops, the project is deleted, its directory disappears, or `auto_watch` is set to `false`. An async index registers its target only after success and while its requesting session remains connected. CLI indexing and failed MCP indexes do not register watches. Global `auto_watch` controls registration and removes all watches on the next refresh or registration request when set to `false`. `index_status` reports this in its `watch` object: `watched`, and either the poll cadence and last scan time or the `reason` it is not watched. -Watcher registration is controlled separately by `auto_watch` (default `true`). Set `config set auto_watch false` to keep a session from registering its project with the background watcher; useful when working across many projects and you want each session contained to explicit indexing. +Watcher registration is controlled globally by `auto_watch` (default `true`). On the next refresh or registration request, `true` allows MCP sessions to register their root on connect and their successful `index_repository` targets; `false` removes all watches. CLI indexing and failed MCP indexes do not add watches. -To turn the watcher off entirely, set `config set watcher_enabled false` (default `true`): the background poll thread never starts and no project is registered, while `auto_index` and manual `index_repository` keep working. Unlike `auto_watch`, which is consulted per session, `watcher_enabled` is read once when the background daemon starts, so run `codebase-memory-mcp daemon stop` after changing it; reconnecting your MCP client alone will not restart the daemon. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md#2-cli-managed-runtime-settings). +To turn the watcher off entirely, set `config set watcher_enabled false` (default `true`): the background poll thread never starts and no project is registered, while `auto_index` and manual `index_repository` keep working. Unlike `auto_watch`, which takes effect on the next refresh or registration request, `watcher_enabled` is read once when the background daemon starts, so run `codebase-memory-mcp daemon stop` after changing it; reconnecting your MCP client alone will not restart the daemon. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md#2-cli-managed-runtime-settings). The watcher follows **git** projects only by default: a project indexed from a directory that is not a git repository is not watched and stays at its last index until you run `index_repository` again. To have such roots polled too, set `config set watch_non_git true` (default `false`, read when the daemon starts): they are then checked on the same cadence with a file-tree scan that uses the indexer's own ignore rules. @@ -267,7 +274,7 @@ bash "/install.sh" ```powershell # Windows -powershell -ExecutionPolicy Bypass -File "\install.ps1" +powershell -File "\install.ps1" ``` The install script is placed next to the binary at install time, so the printed path resolves beside the executable. It is idempotent, so re-running it *is* the update: it stops the daemon, retires the running binary, installs the new one, and cleans up. @@ -299,9 +306,10 @@ The install script placed beside the binary is **reported, not deleted**: uninst ### Graph & analysis - **Architecture overview**: `get_architecture` returns a compact summary by default. Request specific `aspects` for routes, hotspots, dependencies, clusters, or cycles. `system_structure` provides the projection used by CBM Lens and must be requested on its own. -- **Architecture Decision Records**: `manage_adr` persists architectural decisions across sessions +- **Architecture Decision Records**: `manage_adr` persists the project's decision record across sessions; indexing also extracts supported ADR metadata and supersession from documentation files - **Louvain community detection**: Discovers functional modules by clustering call edges -- **Git diff impact mapping**: `detect_changes` maps uncommitted changes to affected symbols with risk classification +- **Change impact**: `detect_changes` maps uncommitted changes to affected symbols with risk classification and can report test impact with `scope: "tests"`; `compare_graphs` compares indexed snapshots +- **Runtime observations**: `ingest_traces` adds observed call relationships from separately collected traces alongside static analysis; static call paths alone do not show what ran - **Call graph**: Resolves function calls across files and packages (import-aware, type-inferred) - **Dead code candidates**: find functions with no indexed callers, excluding entry points. Check dynamic dispatch, coverage gaps, and framework conventions before removing code. - **Cypher-like queries**: `MATCH (f:Function)-[:CALLS]->(g) WHERE f.name = 'main' RETURN g.name` @@ -312,6 +320,27 @@ The install script placed beside the binary is **reported, not deleted**: uninst - **Structural search** (`search_graph`): regex name patterns, label filters, min/max degree, file scoping - **Code search** (`search_code`): graph-augmented grep over indexed files only +### Documentation connected to code + +Find the explanation behind a symbol and follow supported references back to its implementation. Documentation participates in the graph alongside source files: + +- **Markdown, reStructuredText, and AsciiDoc** become navigable document sections; **PDFs with text layers** become searchable text pages. PDF extraction does not perform OCR. +- **Document-to-code references** and supported language doc-comment references create `MENTIONS` relationships when the target can be resolved. +- **Architecture decision records** contribute metadata such as status, deciders, and supersession, so decisions can be explored with the code they describe. +- **Supported diagrams and OpenAPI descriptions** contribute structured entities. Extraction and reference resolution differ by format; not every textual mention becomes a graph edge. + +Use graph and text search to locate the relevant section, follow its recorded relationships, and open the source evidence. Check `check_index_coverage` before treating a missing result as proof that the repository has no such documentation or reference. + +### Infrastructure and deployment + +Ask **"What deploys this service?"**, **"Which image runs it?"**, or **"Where does this configuration reach code?"** CBM connects supported infrastructure forms to the source they build, configure, and run: + +- **Containers and clusters**: Dockerfiles, Compose services, Kubernetes resources, Helm charts and releases, and Kustomize overlays and patches. +- **Provisioning and configuration**: Terraform/OpenTofu resources and modules, tfvars overrides, Terragrunt dependencies, Ansible tasks and handlers, and supported configuration keys and environment-variable access. +- **Build and delivery**: shell commands, Make targets, and supported CI steps that build images, invoke repository scripts, or deploy resources. + +For resolvable inputs, a trace can follow **Service → workload → image → Dockerfile → program**, or follow deployment and configuration relationships. Use `trace_path` modes for topology, deployment, and run paths. These are static relationships from supported source forms; dynamic values and unresolved templates can leave gaps. See [Edge types](#edge-types-selected) for the exact relationship rules and limitations. + ### Cross-service linking - **HTTP** route ↔ call-site matching with confidence scoring - **gRPC, GraphQL, tRPC** service detection with protobuf Route extraction @@ -323,6 +352,21 @@ The install script placed beside the binary is **reported, not deleted**: uninst - **Run status**: `index_status` (and the UI layout next to `linked_projects`) carries a `cross_repo` object, `{"status":"never_run"}` until `index_repository(mode="cross-repo-intelligence")` has run with that project as the source, then `"ran"` with `last_run_at`, `outcome`, `targets`, `projects_scanned`, `total_cross_edges` and `skipped_projects`. That is how you tell "never linked" apart from "linked, nothing matched". The status is stored in the project's own index next to its `CROSS_*` edges, so a reindex that rebuilds the index drops both at once and the status goes back to `never_run`. - **Skipped stores**: `target_projects: ["*"]` skips an index built before the #768 schema change rather than failing the whole run. The response lists each one as `skipped_projects: [{"name", "reason": "pre_768_schema", "hint": "reindex this project"}]`. +### Smart CI and test impact + +Use the graph to explain which tests a change reaches, and use that evidence to focus supported CI runs. **Smart CI is experimental** and has two layers: + +| Layer | What it does | What you supply | +|-------|--------------|-----------------| +| **CI job selection** | Chooses jobs from the changed-file list. | Repository-specific path and job rules; this repository's selector is an example. | +| **Test selection** | Combines static reachability, changed tests, and admitted per-test coverage to select suites or tests. | A supported C/C++ test harness, configuration, a coverage map, and a runner that accepts named selections. | + +`codebase-memory-mcp test-impact select` produces a selection with reasons; `test-impact publish` produces the graph-and-coverage bundle used as its baseline. For agent-assisted investigation, `detect_changes` with `scope: "tests"` exposes test impact through MCP. + +**Narrowing currently supports C/C++ family code with this repository's test conventions or declared equivalents.** It is not general test selection for every language CBM indexes. Missing, stale, incompatible, or incomplete evidence broadens the selection or returns `run_all`; static reachability without admitted coverage never omits a suite. Begin with report-only shadow mode and validate your harness before enabling gating. + +In this repository, test narrowing is wired to the Linux x86_64 test leg when `TEST_IMPACT_MODE=gate`; release and dry-run workflows retain full execution. See the [Smart CI guide](docs/SMART_CI.md) for supported conventions, coverage admission, setup, version requirements, and fallback behavior. + ### Edge types (selected) - `CALLS`: a callable is invoked at the source site - `CALL_REFERENCE`: a callable is used at a supported reference site (for example, a direct value argument) and resolves to one exact target @@ -333,6 +377,7 @@ The install script placed beside the binary is **reported, not deleted**: uninst - `INFRA_MAPS`: a Kubernetes Service to each workload whose pod labels carry every `key: value` of the Service's selector, within one namespace - `CONNECTS_TO`: a Kubernetes workload or compose service to the Service its containers' environment addresses (`PRODUCT_CATALOG_SERVICE_ADDR: productcatalogservice:3550`, a URL's host, `..svc...`, a bare name on an `*_ADDR` / `*_HOST` / `*_URL` / ... key; compose: the project's `.env` interpolated): a Kubernetes Service of the same bundle and namespace, or a compose service of the same file. The edge keeps `via: env`, `key` and `container`, never the value - `USAGE` with `ref: image`: a Kubernetes workload (any pod template: containers, initContainers, CronJobs, custom resources) or a docker compose service to the `Image` it runs +- Helm templates are rendered for their images: with the chart's values (the `Chart` `DEPLOYS` them, `default_of`: the values keys read) and with each release's (a Flux HelmRelease, a kustomize `helmCharts` entry, a Terraform `helm_release`: `override_of`). An image the render cannot know is left out, never guessed: after a change of values it does not run exactly, a `tpl` or include it cannot look into, a release value it cannot read, or a work budget it runs past. Known limit: each template file renders on its own, while Helm renders a chart's files with one shared `.Values` (a subchart's is its parent's `.Values.`), so a chart that changes `.Values` in one file and reads it in another can still show a wrong image - `DEPLOYS`: what puts manifests, kustomizations or charts on a cluster, to what it puts there: a Flux Kustomization or Argo Application to the kustomizations / manifests / chart of its path, a Flux HelmRelease to its chart (`spec.chart` or `chartRef` → OCIRepository), a shell command or Make recipe (`kubectl apply -f/-k`, `kustomize build | kubectl apply`, `helm install/upgrade/template`), a Go installer loading a chart with the Helm SDK, a Terraform `helm_release`, a Terraform `kubernetes_*` resource the object it declares (a `Resource` like a manifest's, `ecosystem: terraform`, when its name is a literal), a kustomization inflating a chart (`helmCharts`; its values file an `IMPORTS`). A chart outside the repository is an external `Chart`, keyed by its publisher when its repository URL is stated (`__chart__/`, the same node in every project, the URL in `repository`), else one per project and name; the edge says `via` (`flux`, `argo`, `helmrelease`, `kubectl`, `kustomize`, `helm`, `helm-sdk`, `terraform`, `helmcharts`, ...) - `DEPENDS_ON`: a Helm chart to the charts it declares (the local chart for a resolvable `file://` repository, otherwise an external `Chart`; the edge keeps `repository`), and a `go.mod` / `requirements.txt` file to an external `Package`; a Dockerfile to the `Image`s it builds on (`via: from`, `copy-from`); an `Image` to the Dockerfile it is built from (`via: build`; `tool`: `skaffold`, `compose`, `docker-build` for `docker` / `podman` / `nerdctl` / `buildah` build commands in scripts, Make and CI); a compose service to the services its `depends_on` / `links` name - `CONFIGURES`: code reading an environment variable to the shared `EnvVar` of that name (`strategy: env_access`); a Kubernetes container, compose service or Dockerfile (`ENV` of the image) setting one to that container's own `EnvVar` (`strategy: env_set`, with `source`: `value`, `configMapKeyRef`, `secretKeyRef`, `fieldRef`, ..., `compose`, `dockerfile`; values are never stored); and that container's `EnvVar` to the code reading the name inside the files its image is built from (`strategy: env_read`, with `dockerfile`): the image's files are the sources its Dockerfile's `COPY` / `ADD` put into it, less the context's `.dockerignore` @@ -357,7 +402,7 @@ The install script placed beside the binary is **reported, not deleted**: uninst ### Distribution & operation - **Native runtime set, zero infrastructure services**: SQLite-backed, persists to `~/.cache/codebase-memory-mcp/` -- **Auto-sync**: Background watcher detects file changes in the project an MCP session is working in and re-indexes it automatically (other indexed repos: re-run `index_repository`) +- **Auto-sync**: Background watcher detects file changes in projects registered by active MCP sessions, including successful explicit `index_repository` targets, and re-indexes them automatically. - **Route nodes**: REST endpoints are first-class graph entities - **CLI mode**: `codebase-memory-mcp cli search_graph --project my-project --name-pattern ".*Handler.*"` - **Available on**: npm, PyPI, Homebrew, Scoop, AUR, `go install` @@ -423,15 +468,15 @@ Published backend benchmarks on Apple M3 Pro. These are separate from the CBM Le | Dead code detection | ~150ms | Full graph scan with degree filtering | | Trace call path (depth=5) | <10ms | BFS traversal | -**RAM-first pipeline**: All indexing runs in memory (LZ4 HC compressed read, in-memory SQLite, single dump at end). Memory is released back to the OS after indexing completes. +**Memory-aware pipeline**: Indexing keeps hot data in memory and can spill extraction results to disk when needed. Memory is released back to the OS after indexing completes. -**Token efficiency**: The project's [original README report](https://github.com/DeusData/codebase-memory-mcp/blob/4ff6feeab639c4668da0f491930326ed5ec63093/README.md#performance) lists ~3,400 tokens for five structural queries via codebase-memory-mcp versus ~412,000 via file-by-file exploration. That is approximately **120× fewer tokens**, or a **99.2% reduction**, for that comparison. The separate [31-repository study](https://arxiv.org/html/2603.27277v1#S4.SS1) reports a 10× token reduction. Its [system-performance measurements](https://arxiv.org/html/2603.27277v1#S4.SS3) include Linux kernel indexing in about 3 minutes and Cypher relationship queries under 1 ms on an Apple M3 Pro. +**More focused context, fewer tokens**: The project's [31-repository preprint](https://arxiv.org/html/2603.27277v1#S4.SS1) reports **10× lower token use** and **2.1× fewer tool calls** compared with file-by-file exploration. These March 2026 backend measurements cover the study's workloads and agent setup; the paper documents the full evaluation. An earlier [five-query comparison](https://github.com/DeusData/codebase-memory-mcp/blob/4ff6feeab639c4668da0f491930326ed5ec63093/README.md#performance) reported ~3,400 versus ~412,000 tokens for structural queries. The study's [system-performance measurements](https://arxiv.org/html/2603.27277v1#S4.SS3) include the Linux indexing and Cypher timings above. -To measure comparable quality, latency, and agent-efficiency metrics on your own workload, see [Measuring quality, latency, and agent savings](docs/MEASURING_SAVINGS.md). Exact reproduction of the figures above requires the original inputs and raw artifacts. +Explore the [measurement guide](docs/MEASURING_SAVINGS.md) to evaluate quality, latency and token use on your own workload. ## Troubleshooting & Diagnostics -codebase-memory-mcp runs **100% locally and collects no telemetry**: your code, queries, environment, and usage never leave your machine. That privacy guarantee also means that when you hit something we can't reproduce on our side (a slow memory climb over hours, a performance regression, a leak that only appears after days of real use), **we have no data at all unless you choose to send it.** Here is how to capture it yourself. +codebase-memory-mcp **indexes and queries locally and collects no telemetry**. When you hit something we can't reproduce on our side (a slow memory climb over hours, a performance regression, a leak that only appears after days of real use), **we have no diagnostic data unless you choose to send it.** Here is how to capture it yourself. ### Capture a diagnostics log @@ -533,6 +578,30 @@ Working in a clone? Use `.` in place of the flake URL, e.g. `nix run .#codebase- You: "Install this MCP server: https://github.com/DeusData/codebase-memory-mcp" ``` +### Install via Claude Code plugin + +This repository is also a Claude Code plugin marketplace, so you can find and +add the server from inside Claude Code: + +```bash +claude plugin marketplace add DeusData/codebase-memory-mcp +claude plugin install codebase-memory@codebase-memory +``` + +The plugin registers the MCP server only. It starts the server with +`npx -y codebase-memory-mcp@`, pinned to the current release; the first +start downloads that release's binary through the npm package, which verifies +it against the release checksums. The skill, the three graph agents and the +context hooks are not part of the plugin: they come from +`codebase-memory-mcp install`. + +Use one or the other. `install` registers the same server for Claude Code, so +with both you run it twice, under two tool namespaces +(`mcp__codebase-memory-mcp__*` and +`mcp__plugin_codebase-memory_codebase-memory-mcp__*`). Choose `install` for the +full Claude Code setup; choose the plugin to add just the server without +running an installer. + ### Build from Source
@@ -548,6 +617,8 @@ You: "Install this MCP server: https://github.com/DeusData/codebase-memory-mcp"
+> **Contributor checks need a few extra tools.** `scripts/test.sh` requires `zip`, `python3`, and `curl`. `scripts/lint.sh` requires `cppcheck`, `clang-format`, and `clang-tidy`; `scripts/lint.sh --ci`, which is what CI runs, skips `clang-tidy`. + ```bash git clone https://github.com/DeusData/codebase-memory-mcp.git cd codebase-memory-mcp @@ -570,7 +641,7 @@ Counts on `main` at `4d9592b5`, where CBM Lens arrived. Native and Go counts use | npm package wrapper | 24 tests | Counted in the source | | Python package wrapper | 43 tests | Counted in the source | -Run the test suite (9,388 tests across 189 suites): +Run the native test suite: ```bash scripts/test.sh # full: clean sanitizer build + all suites + guards @@ -626,7 +697,7 @@ Restart your agent. Verify with `/mcp`; you should see `codebase-memory-mcp` wit ## Multi-Agent Support -`install` configures 45 client surfaces: 39 detected automatically and 6 +`install` configures **45 supported automatic/conditional client surfaces**: 39 detected automatically and 6 conditional or explicit. “Conditional” means the installer writes only when the documented platform or an explicit, already-existing config path proves the target is active. It never flips experimental feature flags, enables plugins, @@ -649,15 +720,15 @@ overwrite user-modified agents. | Agent | Activation | MCP config | Durable context / augmentation | |-------|------------|------------|--------------------------------| -| Claude Code | Detected | `~/.claude.json` | Skill + three exact-tool graph agents; `SessionStart`, `SubagentStart`, non-blocking `PreToolUse` for `Grep`/`Glob`/`Bash`, and post-`Read` coverage | -| Codex CLI | Detected | `$CODEX_HOME/config.toml` | Managed `AGENTS.md` activation pointer, skill, three read-only agents; `SessionStart` + `SubagentStart` | +| Claude Code | Detected | `~/.claude.json` | Skill + three exact-tool graph agents; `SessionStart`, `SubagentStart`, non-blocking `PreToolUse` for `Grep`/`Glob`/`Bash`, post-`Read` coverage, and a drift note after repeated searches without a graph call | +| Codex CLI | Detected | `$CODEX_HOME/config.toml` | Managed `AGENTS.md` activation pointer, `~/.agents/skills/codebase-memory/SKILL.md`, three read-only agents; `SessionStart` + `SubagentStart`, `PreToolUse` graph lookup for `Bash` searches | | Gemini CLI | Detected | `.gemini/settings.json` | `GEMINI.md`, three explicit read/graph-tool subagents; `BeforeTool`, `AfterTool` `read_file` coverage, and `SessionStart` | | Zed | Detected | platform `settings.json` (JSONC) | `AGENTS.md` + shared skill | -| OpenCode | Detected | `$OPENCODE_CONFIG` or resolved global config | `AGENTS.md`, skill, three deny-by-default read-only agents; plugin adds grep/glob graph lookup, post-`read` coverage, first-tool-result session context, and post-compaction reinjection | +| OpenCode | Detected | `$OPENCODE_CONFIG` or resolved global config | `AGENTS.md`, skill, three deny-by-default read-only agents; plugin adds grep/glob graph lookup, post-`read` coverage, first-tool-result session context, and post-compaction reinjection on OpenCode 1.18.x | | Antigravity | Detected | `.gemini/config/mcp_config.json` | `.gemini/GEMINI.md` | | Aider | Detected | none | `CONVENTIONS.md` via `.aider.conf.yml` | | KiloCode | Detected | `.config/kilo/kilo.jsonc` | Rule + three graph-tool subagents with deny-by-default permissions | -| VS Code | Detected | platform `Code/User/mcp.json` | `~/.copilot/skills`, three read-only agents, `sessionStart` + `subagentStart` | +| VS Code | Detected | platform `Code/User/mcp.json` | `~/.copilot/skills`, three read-only agents, `sessionStart` + `subagentStart`, post-search `PostToolUse` graph lookup | | Cursor | Detected | `.cursor/mcp.json` | Skill + three read-only parent-handoff agents; context hooks withheld because session injection races and `readonly` blocks MCP | | Windsurf | Detected | `~/.codeium/windsurf/mcp_config.json` | Always-on `global_rules.md` | | Augment / Auggie | Detected | `~/.augment/settings.json` | Rule, three read-only handoff subagents, `SessionStart` + post-`view` coverage | @@ -669,7 +740,7 @@ overwrite user-modified agents. | Cline | Detected | `~/.cline/mcp.json` + `${CLINE_DATA_DIR:-~/.cline/data}/settings/cline_mcp_settings.json` | Rule + skill; automatic file hooks withheld because they auto-activate and their output is not reliably consumed; child agents cannot use MCP | | Warp | Detected, skill only | UI, Warp Drive, or per invocation (manual) | Shared `~/.agents/skills/codebase-memory/SKILL.md` | | Qwen Code | Detected | `.qwen/settings.json` | `QWEN.md`, skill, three explicit read/graph-tool agents; `SessionStart`, `SubagentStart`, and post-`ReadFile` coverage | -| GitHub Copilot CLI | Detected | `$COPILOT_HOME/mcp-config.json` | Instructions, skill, three read-only agents; `sessionStart` + `subagentStart` | +| GitHub Copilot CLI | Detected | `$COPILOT_HOME/mcp-config.json` | Instructions, skill, three read-only agents; `sessionStart` + `subagentStart`, post-search `PostToolUse` graph lookup | | Factory Droid | Detected | `.factory/mcp.json` | `AGENTS.md`, skill, three droids with exact per-tier graph-tool lists (without additive whole-server exposure); `SessionStart` + post-`Read` coverage on macOS/Linux, withheld on Windows | | Crush | Detected | `.config/crush/crush.json` | Managed context path with explicit parent-to-child handoff | | Goose | Detected | `.config/goose/config.yaml` | `.goosehints` | @@ -699,6 +770,11 @@ For Codex, install keeps only a tiny managed activation pointer in global `$CODEX_HOME/AGENTS.md`; all detailed behavior lives in the installed `codebase-memory` skill. Fresh installs create the pointer, upgrades replace the legacy full managed block while preserving all user-owned bytes, and uninstall removes only the managed pointer. +Skill placement follows the cross-client user skill root and does not follow +`CODEX_HOME`. On upgrade, the installer migrates an installer-owned legacy +`$CODEX_HOME/skills/codebase-memory/SKILL.md` only after verifying the canonical +copy. Modified or foreign files are preserved; `--force` explicitly authorizes +replacement before migration. ### Sessions, compaction, and subagents @@ -706,7 +782,14 @@ Hooks installed by this project are fail-open and context-only. Claude Code's `PreToolUse` observes `Grep`/`Glob`/`Bash` and injects matching graph symbols as `additionalContext`; `PostToolUse` on `Read` adds targeted coverage context when the graph could not fully parse or index that file. It never denies or replaces -the requested tool call. +the requested tool call. Codex CLI's `PreToolUse` on `Bash` gives its shell +searches the same context. GitHub Copilot CLI and VS Code's Copilot runtime get +it after the search: their `preToolUse` can only decide permissions, so a +`PostToolUse` entry for `Bash`, `Grep` and `Glob` appends the graph symbols to +the tool result. In Claude Code, once a session has run three searches with no +codebase-memory-mcp tool call, the next augmentation adds one line saying so; +the next note comes after six searches, then twelve. Claude's `PostToolUse` +entry also matches the graph tools, and a graph tool call resets the count. Claude Code, Codex CLI, Qwen Code, GitHub Copilot CLI, and VS Code's Copilot runtime receive paired session/subagent context where the vendor exposes a @@ -820,7 +903,7 @@ codebase-memory-mcp cli search_graph --project my-project --label Function --for codebase-memory-mcp cli list_projects --format json --detail stats | jq '.projects[].name' ``` -JSON arguments can also be piped on stdin, for tools that take arguments. A tool whose input schema declares none; `list_projects`; never reads stdin, so it stays responsive when it inherits a pipe the caller never closes (the default for `child_process.spawn` and similar wrappers). Inline JSON remains accepted for backward compatibility but is deprecated in favor of flags, `--args-file`, or stdin. +Tools requiring arguments accept JSON piped on stdin. Tools with only optional arguments, such as `list_projects`, accept flags, `--args-file`, or redirected JSON files without waiting on an inherited pipe. Inline JSON remains accepted for backward compatibility but is deprecated in favor of flags, `--args-file`, or stdin. ## MCP Tools @@ -828,9 +911,9 @@ JSON arguments can also be piped on stdin, for tools that take arguments. A tool | Tool | Description | |------|-------------| -| `index_repository` | Index a repository into the graph. Auto-sync keeps it fresh only while it is the project of an open MCP session; repos indexed from the CLI or from another project's session need a re-run (`index_status` shows `watch`). Waits for the whole index by default; pass `async: true` to start it in the daemon and return at once, then poll with `status: true` (see below). | -| `list_projects` | List all indexed projects with node/edge counts. | -| `delete_project` | Remove a project and all its graph data. | +| `index_repository` | Index a repository into the graph. A successful call from an active MCP session registers the target for auto-sync while that session remains connected (under `daemon start`, also after it disconnects), subject to the global `auto_watch` setting; CLI calls do not register watches. Waits for the whole index by default; pass `async: true` to start it in the daemon and return at once, then poll with `status: true` (see below). | +| `list_projects` | List indexed projects with paging; use `detail: "stats"` for node/edge counts and database sizes. | +| `delete_project` | Remove a project and all its graph data, including its ADR sidecar (`.adr.db`). | | `index_status` | Check indexing status of a project. | | `check_index_coverage` | Check whether exact paths or a scope are indexed and fresh. A clean result means no recorded gap, not proof of completeness. | @@ -882,6 +965,16 @@ mode. `index_status` keeps describing the published graph and its freshness. `manage_adr` query modes (`get` and `sections`) use the server's cached query store so they can proceed while a same-project reindex is running. If another process publishes a replacement store during reindexing, they can return the pre-publication ADR until idle eviction refreshes that cache. Updates remain serialized through the project mutation guard. +`trace_path` exposes the start nodes' stored `self_recursive` (direct recursion) +and `recursive` (direct recursion or a call-graph cycle) signals in both tree and +JSON output. Across multiple start nodes, a flag is `true` if any records true, +`false` if all record false, and `null` otherwise (including missing legacy +metadata). These are static-analysis signals, not counts of resolved self-calls. +Static `CALLS` indexing omits direct self-call edges; traces that include `CALLS` state +`self_call_policy: omitted`. Caller/callee totals still count graph-reachable +nodes, so a recursive function can have zero callees. Recursion metadata remains +visible on continuation pages and when an output budget prevents rows from fitting. + ## Graph Data Model ### Node Labels @@ -922,7 +1015,7 @@ See [docs/cbmignore.md](docs/cbmignore.md) for the full `.cbmignore` how-to: syn codebase-memory-mcp config list # show all settings codebase-memory-mcp config set auto_index true # auto-index on session start codebase-memory-mcp config set auto_index_limit 50000 # max files for auto-index -codebase-memory-mcp config set auto_watch false # don't register background git watcher (default: true) +codebase-memory-mcp config set auto_watch false # remove/disable all background watches (default: true) codebase-memory-mcp config set doc_candidates false # skip doc<->code candidate scoring on huge repos (default: auto = on for full, off for moderate/fast) codebase-memory-mcp config set watcher_enabled false # stop the watcher thread entirely (default: true) codebase-memory-mcp config set watch_non_git true # also poll non-git project roots (default: false) @@ -947,7 +1040,7 @@ is preserved. See [Index resource limits](docs/INDEX_RESOURCE_LIMITS.md). | `CBM_DOWNLOAD_URL` | *(GitHub releases)* | Override the download URL for updates. Used for testing or self-hosted deployments. | | `CBM_LOG_LEVEL` | role-aware | Set the minimum log level. Thin MCP/CLI/hook frontends default to `warn`; the detached daemon and its supervised index workers default to `info` so lifecycle and liveness records remain available. Accepted values (case-insensitive): `debug`, `info`, `warn`, `error`, `none`; or their numeric equivalents `0` to `4`. A physical worker retains INFO liveness records even under a stricter override because its private log drives the supervisor's no-progress timeout. Frontend messages go to that session's stderr; detached daemon events go to `${CBM_CACHE_DIR}/logs/cbm-daemon.log`. Stdout is reserved for MCP JSON-RPC. | | `CBM_WORKERS` | *(detected)* | Override the parallel-indexing worker count returned by `cbm_default_worker_count`. Useful inside containers where `sysconf(_SC_NPROCESSORS_ONLN)` reports host CPUs rather than the cgroup's effective quota. Range 1 to 256; invalid values are ignored with a warning. | -| `CBM_MEM_BUDGET_MB` | *(detected)* | Override the in-memory graph budget with an explicit cap in MiB, taking precedence over the `ram_fraction × total_RAM` default. Useful on bare-metal hosts without a cgroup limit, or to pin a budget *below* the cgroup limit so headroom is left for sibling processes. Must be a positive integer; it is clamped to detected total RAM (logged as `mem.budget.clamped`), and non-numeric or non-positive values are ignored with a warning (`mem.budget.env.invalid`). | +| `CBM_MEM_BUDGET_MB` | *(detected)* | Override the in-memory graph budget with an explicit cap in MiB, taking precedence over the derived default (`ram_fraction × total_RAM`, adjusted for available memory and daemon worker caps). Useful on bare-metal hosts without a cgroup limit, or to pin a budget *below* the cgroup limit so headroom is left for sibling processes. Must be a positive integer; it is clamped to detected total RAM (logged as `mem.budget.clamped`), and non-numeric or non-positive values are ignored with a warning (`mem.budget.env.invalid`). | | `CBM_DUMP_VERIFY_MIN_RATIO` | `0.5` | After indexing, compare persisted SQLite node count to the in-memory dump count. When persisted nodes fall below this fraction of committed nodes (and committed > 50), `index_repository` returns `status:"degraded"` instead of silent `indexed`. Range 0 to 1; set `0` to disable. Invalid values are ignored with a warning. | Environment used by daemon-owned components (such as diagnostics, daemon logging, and process-wide indexing resource limits) is captured from the first daemon-backed session that starts the daemon. Later sessions join that process and cannot replace those values. To change them, close all daemon-backed sessions, update the relevant agent configurations consistently, and restart a session. `CBM_ALLOWED_ROOT` remains session-specific, a conflicting `CBM_CACHE_DIR` is rejected, and a one-shot CLI command is not exempt from the rule above: it connects to the coordination daemon like any other session, starting one if none is running. Its own environment becomes the captured daemon-owned environment only when its invocation is the one that starts the daemon; joining an already-running daemon, it inherits that daemon's already-captured values instead. @@ -977,9 +1070,9 @@ Need the full config-file reference? See [docs/CONFIGURATION.md](docs/CONFIGURAT Each entry maps an extension (which **must** start with `.`) to a language name. Language names are matched **case-insensitively**. Accepted values (aliases in parentheses) are: -`bash` (`sh`), `c`, `c++` (`cpp`), `c#` (`csharp`), `clojure`, `cmake`, `cobol`, `common lisp` (`commonlisp`, `lisp`), `css`, `cuda`, `dart`, `dockerfile`, `elixir`, `elm`, `emacs lisp` (`emacslisp`), `erlang`, `f#` (`fsharp`), `form`, `fortran`, `glsl`, `go`, `graphql`, `groovy`, `haskell`, `hcl` (`terraform`), `html`, `ini`, `java`, `javascript`, `json`, `julia`, `kotlin`, `lean`, `lua`, `magma`, `makefile`, `markdown`, `matlab`, `meson`, `nix`, `objective-c` (`objc`), `ocaml`, `perl`, `php`, `protobuf`, `python`, `r`, `ruby`, `rust`, `scala`, `scss`, `sql`, `svelte`, `swift`, `toml`, `tsx`, `typescript`, `verilog`, `vimscript`, `vue`, `wolfram`, `xml`, `yaml`, `zig`. +`arkts`, `asciidoc`, `bash` (`sh`), `c`, `c++` (`cpp`), `c#` (`csharp`), `chialisp`, `clojure`, `cmake`, `cobol`, `common lisp` (`commonlisp`, `lisp`), `css`, `cuda`, `dart`, `diagram`, `dockerfile`, `elixir`, `elm`, `emacs lisp` (`emacslisp`), `erlang`, `f#` (`fsharp`), `form`, `fortran`, `glsl`, `go`, `graphql`, `groovy`, `haskell`, `hcl` (`terraform`), `html`, `ini`, `java`, `javascript`, `jinja2` (`jinja`), `json`, `julia`, `kotlin`, `lean`, `lua`, `magma`, `makefile`, `markdown`, `matlab`, `meson`, `mojo`, `nix`, `objective-c` (`objc`), `ocaml`, `openapi`, `pdf`, `perl`, `php`, `plsql`, `protobuf`, `python`, `r`, `ruby`, `rust`, `scala`, `scss`, `sql`, `svelte`, `swift`, `toml`, `tsx`, `typescript`, `verilog`, `vimscript`, `vue`, `wolfram`, `xml`, `yaml`, `zig`. -Project config overrides global for conflicting extensions. An entry whose language name is unknown, or whose extension does not start with `.`, is skipped and a warning is logged to stderr (shown at the default `info` log level). Missing config files are ignored. +Project config overrides global for conflicting extensions. An entry whose language name is unknown, or whose extension does not start with `.`, is skipped and logged as a warning. Missing config files are ignored. ## Persistence @@ -987,6 +1080,8 @@ SQLite databases stored at `~/.cache/codebase-memory-mcp/`. Persists across rest ## Troubleshooting +Windows users: see [Windows troubleshooting](docs/WINDOWS_TROUBLESHOOTING.md) for versioned known issues and workarounds. + | Problem | Fix | |---------|-----| | `/mcp` doesn't show the server | Check `.mcp.json` path is absolute. Restart agent. Test: `echo '{}' \| /path/to/binary` should output JSON. | @@ -1069,7 +1164,7 @@ Every release is verified through a multi-layer pipeline before publication: - **Sigstore cosign**: keyless signatures on all artifacts; bundles included in every release - **SHA-256 checksums**: `checksums.txt` published with every release; verified by both install scripts before extraction - **CodeQL SAST**: blocks release pipeline if any open alerts remain -- **No language-runtime dependency chain**: the native backend uses libraries vendored at compile time, and the frontend is embedded by the UI build +- **No language-runtime dependency chain**: the native backend needs no language-runtime environment; UI builds embed the frontend ### VirusTotal release scans @@ -1088,6 +1183,10 @@ The [v0.11.0 release notes](https://github.com/DeusData/codebase-memory-mcp/rele The release also publishes [candidate scan results](https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.11.0/virustotal-candidate-results.tsv), [binary provenance](https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.11.0/release-candidates.tsv), and [selection records](https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.11.0/release-selection.tsv). For subsequent versions, use the scan evidence linked from the [latest release](https://github.com/DeusData/codebase-memory-mcp/releases/latest). +## Help shape what comes next + +The next idea could be yours. We invite the OSS community to bring ideas, tricky integration questions, and feedback to [our Discord community](https://discord.gg/KguGXxg2X). When a direct conversation works better, reach out to **DeusData** or the project's co-developers there. Let's build what comes next together. + ## License MIT diff --git a/SECURITY.md b/SECURITY.md index 48da1d1a1..ec9fea712 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -111,7 +111,7 @@ This project implements multiple layers of security verification. Every release - **All dangerous function calls** require a reviewed entry in `scripts/security-allowlist.txt` - **Time-bomb pattern detection** — scans for `time()`/`sleep()` near dangerous calls (could indicate delayed activation) - **MCP tool handler file read audit** — tracks file read count in `mcp.c` against an expected maximum (detects added file reads that could exfiltrate data through tool responses) -- **CodeQL SAST** — static application security testing on every push (taint analysis, CWE detection, data flow tracking). Any open alert blocks the release. +- **CodeQL SAST** — static application security testing of the C/C++, JavaScript/TypeScript, Python and GitHub Actions code (taint analysis, CWE detection, data flow tracking): a full scan on every push to main, weekly, and in every dry run and release; a first-party scan of the languages a pull request changes, with alerts reported on its changed lines. Any open alert fails the run and blocks the release. - **Fuzz testing** — random/mutated inputs to MCP server and Cypher parser (60 seconds per build). Catches crashes, segfaults, and memory errors that structured tests miss. - **Native antivirus scanning** on every platform (any detection fails the build): - **Windows**: Windows Defender with ML heuristics — the same engine end users run diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md index 4b76c28e8..17e88a695 100644 --- a/THIRD_PARTY.md +++ b/THIRD_PARTY.md @@ -121,10 +121,12 @@ License summary: | wyhash | `internal/cbm/vendored/wyhash/` | Unlicense (public domain) | [wangyi-fudan/wyhash](https://github.com/wangyi-fudan/wyhash) | Local modifications to these libraries are documented next to the -vendored sources (currently only SQLite: `vendored/sqlite3/PATCHES.md`, -raising the Unix VFS `MAX_PATHNAME` ceiling from 512 to 4096 to match -CBM's 4 KiB path support). Patches must be reapplied on every upstream -refresh and are covered by `scripts/vendored-checksums.txt`. +vendored sources: `vendored/sqlite3/PATCHES.md` (raising the Unix VFS +`MAX_PATHNAME` ceiling from 512 to 4096 to match CBM's 4 KiB path support) +and `vendored/mimalloc/PATCHES.md` (the upstream source pin, mimalloc v3.5.3 +at commit `d4881d33`, plus the macOS decommit-recommit change and one comment +rewording). Patches must be reapplied on every upstream refresh and are +covered by `scripts/vendored-checksums.txt`. The graph-UI HTTP server is a first-party implementation (`src/ui/httpd.c` + `src/ui/http_server.c`) — no third-party HTTP library diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 2f0d6717a..a2adfd63d 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -87,9 +87,9 @@ Current keys: |---|---|---| | `auto_index` | `false` | Automatically index new projects when an MCP session starts. | | `auto_index_limit` | `50000` | Maximum file count allowed for automatic indexing of a new project. | -| `auto_watch` | `true` | Register the session's project with the background git watcher on connect. Set `false` to keep a session from registering its project (the watcher still runs for other projects). | +| `auto_watch` | `true` | Global switch for MCP session watch registration. On the next refresh or registration request, `true` allows session roots and successful `index_repository` targets to be watched; `false` removes all watches. The background watcher can still run with no registered projects. | | `doc_candidates` | `auto` | Score documentation sections against code to store doc↔code candidates (the possible targets of a section, and the sections that may describe a function or file, each with a judged probability). Unset (`auto`), the index mode decides: on for `full` indexes (the default mode), off for `moderate` and `fast`. Set `true` or `false` to choose for every mode; `config reset doc_candidates` returns to `auto`. The scoring grows with sections × functions: under a second up to ~100k functions, ~15 s at ~400k, ~3 minutes on the Linux kernel. Off stores no candidates and leaves the rest of the graph unchanged; on in `fast` mode stores candidates without adding `fast`'s skipped `SIMILAR_TO` / `SEMANTICALLY_RELATED` edges. `index_repository` also takes `doc_candidates` for a single index, which wins over this setting. Applies from the next index. | -| `watcher_enabled` | `true` | Master switch for the background watcher subsystem. Set `false` to stop the watcher from starting at all — no poll thread and no project registration. Reindex manually with `index_repository` when disabled. Only an open MCP session's own project is ever watched; `index_status` reports the current state in its `watch` object. | +| `watcher_enabled` | `true` | Master switch for the background watcher subsystem. Set `false` to stop the watcher from starting at all — no poll thread and no project registration. Reindex manually with `index_repository` when disabled. Active MCP sessions can register their root project and successful explicit index targets; under `daemon start` those watches outlive the session until the daemon stops, the project is deleted, its directory disappears, or `auto_watch` is set to `false`. `index_status` reports the current state in its `watch` object. | | `watch_non_git` | `false` | Also poll project roots that are **not git repositories**. By default the watcher only follows git projects, so a project indexed from a plain directory is never refreshed after its first index — reindex it manually with `index_repository`. Set `true` to poll such roots on the same adaptive cadence with a file-tree scan: the indexer's own discovery walk (same skip lists, `.gitignore` and `.cbmignore` rules) hashed over each file's path, size and mtime. Any change reindexes once; paths the indexer skips (including cbm's own `.codebase-memory/` output) never trigger. The first poll after the daemon starts reindexes each such project once, since nothing records which tree state the index holds. The scan walks the whole tree every poll, so it costs more than git polling on very large trees. Read once when the daemon starts, like `watcher_enabled`. | | `index_max_files` | `off` | Optional maximum number of accepted source files in one discovery run. | | `index_max_source_mb` | `off` | Optional maximum accepted source size in MiB in one discovery run. | @@ -104,15 +104,23 @@ has not run yet). > **`watcher_enabled` vs `auto_watch`.** `watcher_enabled` controls whether the > watcher *subsystem* starts at all (the background poll thread). `auto_watch` is -> narrower: it only controls whether a connecting session registers *its own* -> project with an already-running watcher. When `watcher_enabled=false`, +> narrower: it globally controls MCP session watch registration. On the next +> refresh or registration request, `false` removes all watches; `true` allows +> session roots and successful explicit `index_repository` targets to register. +> Watcher refreshes retain `mode`, `persistence`, and the resolved +> `doc_candidates` boolean from the latest successful explicit index registered +> for that target. This boolean can come from the request or its global setting. +> When neither supplied a boolean, refresh uses the current setting or mode default. +> A watcher callback also observes `auto_watch=false`: it removes all watches +> before admitting any new refresh work. +> When `watcher_enabled=false`, > `auto_watch` has no effect — there is no watcher to register with. > > They also differ in **when they are read**, which matters because the watcher > lives in the background daemon, not in your MCP client: > -> - `auto_watch` is consulted each time a session would register its project, so -> a change applies to sessions that connect afterwards. +> - `auto_watch` takes effect on the next refresh or registration request and +> applies globally to session roots and successful `index_repository` targets. > - `watcher_enabled` is read **once, when the daemon starts**, because it decides > whether the watcher is built at all. The daemon is long-lived and outlives > individual MCP sessions, so **reconnecting your client is not enough** — retire @@ -241,6 +249,15 @@ root because they are too broad or too sensitive to index as a unit: - a credential directory at any depth — `.ssh`, `.aws`, `.gnupg`, `.kube`, `.docker`, `.netrc`, `.git-credentials`, `.password-store`, macOS `Keychains`. +A single top-level directory can still be indexed as one root on purpose, for +example a container or build machine that keeps its project in `/work`, `/app` or +`/src`, by approving that exact path in a terminal: +`codebase-memory-mcp allow-root --approve-broad /work`. The approval is recorded +like any other allowed root, listed as `(approved broad)` by `allow-root --list`, +and covers no other top-level directory; as with any recorded root, indexing is +then confined to the recorded roots. Filesystem, drive and share roots cannot be +approved. + Two limits are worth stating plainly. This constrains *scope*, not *sensitivity*: inside a root that is allowed, every file the process can read may be indexed and later returned. And the credential list is a denylist, so it diff --git a/docs/WINDOWS_TROUBLESHOOTING.md b/docs/WINDOWS_TROUBLESHOOTING.md new file mode 100644 index 000000000..559204f99 --- /dev/null +++ b/docs/WINDOWS_TROUBLESHOOTING.md @@ -0,0 +1,57 @@ +# Windows Troubleshooting + +This page collects known Windows-specific symptoms, with the version range each affects, confirmed workarounds, and links to the canonical tracking issues. + +## Non-ASCII paths in CBM_CACHE_DIR or CBM_ALLOWED_ROOT + +**Symptom:** Configuration appears not to persist, the config database cannot be opened (`error: cannot open config database`), or repositories inside `CBM_ALLOWED_ROOT` are rejected during indexing. + +**Affected versions:** Affects v0.9.x and earlier. Fixed in v0.10.0 (environment variables are read as wide strings via `GetEnvironmentVariableW`). + +**Workaround:** On an older build, use ASCII-only paths for these two variables or upgrade with `install.ps1`. + +**Tracking:** #1165. + +## Upgrade completes but binary version does not change + +**Symptom:** `codebase-memory-mcp update` reports success and downloads files, but `codebase-memory-mcp --version` still reports the previous version (with `.exe.old` remaining alongside). + +**Affected versions:** Affects v0.9.x and earlier (in-process self-update on Windows). From v0.10.0, `update` prints the `install.ps1` command instead of replacing itself. + +**Workaround:** On an older build, run `install.ps1` once; it replaces the binary out-of-process and verifies the installed version. + +**Tracking:** #1168 (closed; fixed in v0.10.0). + +## Index worker hangs, crashes, or leaves an empty worker log + +**Symptom:** `index_repository` reports worker hangs or crashes. (An *empty* worker log is a separate, fixed bug: v0.10.4 and earlier.) + +**Affected versions:** Empty worker logs affect v0.10.4 and earlier (fixed in v0.10.5). Misleading "crashed on a file" reports for returned errors fixed in v0.10.0. From v0.11.0, the Windows worker is memory-capped. From v0.12.0, a worker that exits without returning a result is no longer counted as a success, and its log is kept. The underlying hang (#1130) and startup exit (#1145) remain open for investigation. + +**Workaround:** For repositories containing large binary, media, or model directories, exclude those paths with `.cbmignore` before indexing (see [`.cbmignore` documentation](cbmignore.md)). From v0.10.5, the worker log names the in-flight file; if indexing still fails on v0.11.0 or newer, attach that log to #1130 or #1145. + +**Tracking:** #1130, #1132, #1145. + +## Antigravity 2.0 configuration not picked up + +**Symptom:** Installation completes but Antigravity 2.0 does not see the MCP server or hooks, or the environment is detected as Gemini-CLI; duplicate large binaries may also be left on disk. + +**Affected versions:** All releases through v0.12.1 when neither detection signal below is present (v0.9.x checks only for `~/.gemini/antigravity-cli`; from v0.10.0, `antigravity` on PATH also counts). The duplicate binary left behind by `install.ps1` affects v0.9.x and earlier (fixed in v0.10.0). + +**Workaround:** Antigravity is configured only when `~/.gemini/antigravity-cli` exists or `antigravity` is on PATH; the target is `~/.gemini/config/mcp_config.json`, and no hooks are installed for it. If neither signal is present, the machine is treated as Gemini-CLI. From v0.10.0, `install.ps1` keeps a single binary; running `codebase-memory-mcp install` manually without `--dir` can still create a second copy in `~/.local/bin`. + +**Tracking:** #1161. + +## OpenCode installed but not detected + +**Symptom:** `codebase-memory-mcp install` detects other clients but skips OpenCode on Windows. + +**Affected versions:** v0.9.x and earlier detect OpenCode only through `opencode` on PATH. v0.10.0 also detects its configuration (the config file, `%USERPROFILE%\.config\opencode\`, or `OPENCODE_CONFIG_DIR`), and v0.12.0 also detects its data folder `%USERPROFILE%\.local\share\opencode\`, which the OpenCode CLI and the Desktop app both create, so detection no longer depends on PATH. In v0.12.1 and earlier, a PATH longer than 4 KB hides every CLI from detection, and npm, mise and scoop shim folders are not searched; both are fixed after v0.12.1 (#2381). + +**Workaround:** On v0.12.0 or newer, start OpenCode once (CLI or Desktop app) so that its data folder exists, then re-run `codebase-memory-mcp install`. On v0.10.x or v0.11.0, make sure `%USERPROFILE%\.config\opencode\` exists (or point `OPENCODE_CONFIG` at an existing config file, or `OPENCODE_CONFIG_DIR` at an existing folder), then re-run `codebase-memory-mcp install`. On v0.9.x, `opencode` must be on PATH; upgrading with `install.ps1` is the simpler fix. + +**Tracking:** #1167. + +--- + +When a release resolves one of these issues, update or remove the corresponding entry rather than letting this page become a second issue tracker. diff --git a/docs/images/README.md b/docs/images/README.md index 1c1ecc96d..89f811c37 100644 --- a/docs/images/README.md +++ b/docs/images/README.md @@ -1,49 +1,53 @@ # CBM Lens Beta README captures -These are screenshots and browser recordings of the application, captured on 12–13 October 2026. The application data and text were not retouched. Bernhard Jackiewicz captured the recordings and PNG stills on 12 October. The Explorer and Galaxy JPEG stills were refreshed on 13 October. One addition is drawn on top of the recordings: a small mouse pointer and click ring, because a headless browser shows no pointer of its own. +These are screenshots and browser recordings of the real application using public source from this repository. The application data and text were not retouched. The GIFs were refreshed on 20 October 2026; the still images preserve the earlier captures from 12–13 October. -## Original captures: 12 October +## High-resolution recordings: 20 October - Repository: [DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp) -- Branch: `main`, after [PR #2068](https://github.com/DeusData/codebase-memory-mcp/pull/2068) brought CBM Lens Beta in -- Commit: `4d9592b5` -- Build: `scripts/build.sh --with-ui` -- Browser: headless Chromium (Playwright) on macOS, English locale, viewport 1440 × 900, device scale factor 1 -- Agents: standard build, experimental agent activity disabled +- Source commit: `4cf44147e639ae070ee44d9230244bfb9d9676e0` +- Build: `make -f Makefile.cbm cbm-with-ui`, with the standard frontend and experimental agent activity disabled +- Capture: built-in browser screenshots on macOS, English locale, native viewport **1600 × 1000**, **12 frames per second** +- Encoding: JPEG screenshot frames converted to GIF with one 256-color palette per recording, no dithering, infinite looping; no upscaling, sharpening or pointer overlays -The `v0.0.1` label in the application is the frontend package version. It is not the backend release version. +The `v0.0.1` label is the frontend package version, not the backend release version. Standard native releases include Lens; using a source build for these recordings is a capture-provenance detail, not an installation requirement. -### Data shown +### Data and interaction -The project `codebase-memory-mcp` is an export of the same public commit (`git archive`), indexed in `full` mode into an isolated local cache. No private repository was used. Because the export has no `.git` folder, Explore reports "Git unavailable" in its file impact line. +The project `codebase-memory-mcp` is a clean `git archive` export of the source commit, indexed in `full` mode into an isolated local cache. No private repository was used. Because the export has no `.git` folder, the interface reports "Git unavailable" where applicable. -The index reported 75,037 nodes and 361,349 edges. These are properties of this source scope, not a benchmark. The interface keeps its coverage and display-limit notices. +The index reported 81,190 nodes and 411,072 edges. These counts describe this source scope, not a benchmark or proof of complete repository coverage. The interface retains its coverage and display-limit notices. The clips join recorded interaction segments with pauses shortened between them; their displayed application states and answers are unchanged. Directional pulses represent indexed relationships, not observed execution. -## Refreshed stills: 13 October +| Recording | Duration | What it shows | +|-----------|----------|---------------| +| [lens-overview.gif](lens-overview.gif) | 12.5 seconds | Architecture overview, opening `src/foundation`, browsing its files, selecting `arena.c` and reading its source beside the map. | +| [lens-galaxy.gif](lens-galaxy.gif) | 12 seconds | Expanding outgoing relationships around `cbm_arena_alloc`, switching to the hierarchy and tracing the two-hop call path `cbm_arena_alloc → arena_grow → cbm_alloc`. | +| [lens-chat.gif](lens-chat.gif) | 11.5 seconds | `arena_grow` selected in Galaxy, with caller and callee questions answered directly from the indexed graph. | + +All three GIFs are 1600 × 1000. Their frames and playback duration were decoded and checked after export. README and website use these same files. + +### Local chat check + +The optional Qwen2.5 Coder 0.5B model was downloaded and loaded through the application's own setup. Automatic explanations were disabled for this recording. Both displayed answers are deterministic graph answers, labelled "Listed from the indexed graph; not generated by the model." The clip shows one incoming `CALLS` relationship from `cbm_arena_alloc` and two outgoing `CALLS` relationships to `cbm_alloc` and `cbm_free`, alongside the other indexed relationship types. It does not demonstrate model-generated prose. + +## Original still images: 12 October + +Bernhard Jackiewicz captured the PNG stills on 12 October 2026, from commit `4d9592b5` on `main` after [PR #2068](https://github.com/DeusData/codebase-memory-mcp/pull/2068). The build used `scripts/build.sh --with-ui`; headless Chromium on macOS used English locale, a 1440 × 900 viewport and device scale factor 1. Experimental agent activity was disabled. + +The indexed project was a public `git archive` export of that commit in `full` mode, with 75,037 nodes and 361,349 edges. The PNG stills are 1440 × 900, except the chat still at 1440 × 1200 so the explanation and answer fit together. These are historical application views, also used as reduced-motion alternatives on the website; they are not extracted from the new GIFs. + +| Still | What it shows | +|-------|---------------| +| [lens-architecture.png](lens-architecture.png) | The whole repository on the architecture map. | +| [lens-chat.png](lens-chat.png) | A local model explanation of `cbm_pipeline_pass_doclinks` and a caller answer listed from the graph. | +| [lens-system-structure.png](lens-system-structure.png) | Component groups and their dependencies in System structure, with `src` selected. | +| [lens-behavior.png](lens-behavior.png) | Possible direct calls of `compileCards`, with its declaration. | + +## Refreshed still images: 13 October `lens-explore.jpg` and `lens-galaxy.jpg` are unedited browser captures at 1280 × 720 from the locally served CBM Lens Beta UI. The running binary reports `dev`; its exact build commit is unavailable, so these captures do not certify an exact source revision. The local project is named `cbm-triage-main-20260919`; the screenshots show public CBM source and indexed relationships. -- **Explore:** `src/foundation/limits.c`, its compact file impact summary and direct dependencies (24 nodes, 26 edges). The UI's freshness, sampling and coverage caveats remain visible. -- **Galaxy:** `cbm_max_file_bytes`, both directions, all edge types, two layers (160 nodes, 218 edges). +- **[Explore](lens-explore.jpg):** `src/foundation/limits.c`, its compact file impact summary and direct dependencies (24 nodes, 26 edges). The UI's freshness, sampling and coverage notices remain visible. +- **[Galaxy](lens-galaxy.jpg):** `cbm_max_file_bytes`, both directions, all edge types, two layers (160 nodes, 218 edges). -These counts describe the displayed index snapshot, not a benchmark or proof of complete repository coverage. The original recordings and other stills are unchanged. - -## Media - -| File | What it shows | -|------|---------------| -| [lens-overview.gif](lens-overview.gif) | Architecture overview of the repository: zoom and a small orbit, selecting `src/pipeline`, opening that area, selecting `artifact.c` and opening its source. | -| [lens-architecture.png](lens-architecture.png) | Still image of the whole repository on the architecture map. | -| [lens-galaxy.gif](lens-galaxy.gif) | Galaxy: searching `cbm_pipeline_run`, following its outgoing calls one layer further, the hierarchy view and the path to `cbm_store_check_integrity`. | -| [lens-galaxy.jpg](lens-galaxy.jpg) | Two dependency layers around `cbm_max_file_bytes`, with direction and edge-type controls. | -| [lens-explore.jpg](lens-explore.jpg) | `src/foundation/limits.c` in the reader with its file impact summary, file tree and direct graph dependencies. | -| [lens-chat.gif](lens-chat.gif) | The chat with the local Qwen2.5 Coder 0.5B model: the explanation of `cbm_pipeline_pass_doclinks` and "Who calls cbm_pipeline_pass_doclinks?" answered from the graph. | -| [lens-chat.png](lens-chat.png) | Still image of the same explanation and listed answer. | -| [lens-system-structure.png](lens-system-structure.png) | Component groups and their dependencies in System structure, `src` selected. | -| [lens-behavior.png](lens-behavior.png) | Possible direct calls of `compileCards`, with its declaration. | - -The recordings were captured as browser video, then encoded as GIF at 960 pixels wide and 10 frames per second with a palette per recording. While a search is typed, the Galaxy briefly shows "No matching indexed nodes." until the index search answers; those frames (under one second) were cut. The original PNG stills are 1440 × 900, the chat still 1440 × 1200 so that the explanation and the answer fit together. The refreshed JPEG stills are 1280 × 720. Directional pulses represent indexed relationships, not observed execution. - -## Local chat check - -The chat recording uses the actual browser model, downloaded through the application's own setup. The explanation lists the indexed facts and the declaration of the selected function; the one sentence after them was written by the model and is labelled as such. The caller answer is listed from the indexed graph without the model. +These counts describe the displayed index snapshot, not a benchmark or proof of complete repository coverage. diff --git a/docs/images/lens-chat.gif b/docs/images/lens-chat.gif index 1eaaecb86..946f777e7 100644 Binary files a/docs/images/lens-chat.gif and b/docs/images/lens-chat.gif differ diff --git a/docs/images/lens-galaxy.gif b/docs/images/lens-galaxy.gif index 1aa8899e3..3c906eea1 100644 Binary files a/docs/images/lens-galaxy.gif and b/docs/images/lens-galaxy.gif differ diff --git a/docs/images/lens-overview.gif b/docs/images/lens-overview.gif index 733833553..058397d1e 100644 Binary files a/docs/images/lens-overview.gif and b/docs/images/lens-overview.gif differ diff --git a/docs/index.html b/docs/index.html index e39ded17e..fac7e3b5b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,838 +3,504 @@ - codebase-memory-mcp — Code Intelligence Knowledge Graph for AI Coding Agents - - + Codebase Memory — Local Code Intelligence & MCP + - - - - - + + + - - - - - - - + + + + + + - - - - + + + + - - - - - - + + - - - - - - - -
-
-

codebase-memory-mcp

- -

- The fastest, most efficient code intelligence engine for AI coding agents. It indexes any - repository into a persistent knowledge graph — full-indexing an average repo in seconds and the - Linux kernel in 3 minutes — so your agent answers structural questions with ~120x fewer tokens. - Tree-sitter parsing across 162 languages, Hybrid LSP type resolution, native executable with verified assets. -

-
-
~120x
fewer tokens
-
158
languages
-
3 min
Linux kernel index
-
44
client surfaces
+ + - -
- 3D knowledge-graph visualization of the codebase-memory-mcp graph showing thousands of nodes and edges -

Built-in 3D graph visualization (UI variant) — explore your knowledge graph at localhost:9749.

-
- - - -
-

What is codebase-memory-mcp?

-

- codebase-memory-mcp is an open-source Model Context - Protocol (MCP) server that indexes a codebase into a persistent knowledge graph of functions, - classes, call chains, HTTP routes, and cross-service links. Instead of reading files one at a time, - an AI coding agent queries the graph — answering structural questions with roughly 120x fewer - tokens. It parses 162 languages and ships as a native executable with authenticated release-owned assets; - the native install needs no language runtime. -

-

- It is a structural-analysis backend, not a chatbot: there is no embedded LLM and no API key. Your - MCP client (Claude Code, or any MCP-compatible agent) is the intelligence layer; codebase-memory-mcp - builds and serves the graph. All processing happens locally — your code never leaves your machine. -

-
- -
-

Why do AI agents waste tokens exploring code?

-

- AI coding agents explore codebases by reading files one at a time. Every structural question - triggers a cascade of grep → read file → grep again → read more files. The cost compounds fast. -

-

- Across five structural questions about a real codebase, file-by-file search consumed - ~412,000 tokens; the same questions answered from the - knowledge graph took ~3,400 tokens — a ~120x reduction. -

-

- The win is not about fitting the context window. It is cost (at $3–15 per million tokens, exploration - adds up), latency (sub-millisecond graph queries versus seconds of file reading), and accuracy - (less noise means better answers and no "lost in the middle" problem). -

-

Source: project benchmark, 5 structural queries — see the - full benchmark report.

-
- -
-

How do I install codebase-memory-mcp?

-

- Install with a single command, then tell your agent to index the project. The native runtime set contains - an executable with verified assets for macOS, Linux, and Windows — no Docker, language runtime, or API key. -

-
- # 1. One-line install (macOS / Linux). Add --ui for the 3D graph UI.
- curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh | bash

- # 2. The installer configures detected clients; explicit flags cover conditional clients.

- # 3. Restart your agent, then say:
- "Index this project" + +
+
+
+
+
+

Local MCP server · Open source

+

Understand the
whole repository.

+

Codebase Memory (CBM) is an open-source server for the Model Context Protocol (MCP). Give your AI coding agent a persistent knowledge graph of code, documentation and infrastructure. Search symbols, trace relationships and retrieve source evidence through 17 MCP tools.

+

Focused graph queries help reduce repeated code reading and token use. See the evidence →

+ +

macOS · Linux · Windows  /  Native C · MIT licensed

+
+
+ For your agent

17 MCP tools, source evidence and coverage checks, with client-specific skills, profiles and hooks.

+ For your workflow

Native CLI access, persistent indexes and optional visual exploration in Lens.

+
-

- One command configures 45 automatic/conditional client surfaces. Detected automatically (39): - Claude Code, Codex CLI, Gemini CLI, Zed, OpenCode, Antigravity, Aider, KiloCode, VS Code, Cursor, - Windsurf, Augment / Auggie, OpenClaw, Kiro, Junie, Hermes, OpenHands, Cline, Warp, Qwen Code, - GitHub Copilot CLI, Factory Droid, Crush, Goose, Mistral Vibe, Grok Build, Qoder CLI, Kimi Code CLI, - GitLab Duo CLI, Rovo Dev CLI, Amp, Devin CLI / Local, Tabnine, Amazon Q Developer IDE, - CodeBuddy Code CLI, IBM Bob Shell, Pochi, Pi, and Oh My Pi (omp). -

-

- Conditional or explicit (6): Continue / cn, Visual Studio, TRAE, Roo Code, IBM Bob IDE, - and Sourcegraph Cody. Manual, UI, cloud, or repository-managed (not counted): Qodo, - Warp MCP, JetBrains AI/ACP, GitHub Copilot coding agent, Jules, CodeRabbit, Replit, BLACKBOX AI, - Plandex, and SWE-agent. Warp is counted above for its detected skill installation; its MCP connection - remains manual. Windows users run install.ps1. Also available via npm, - pip, Homebrew, Scoop, AUR, and go install. -

-

- Lifecycle installation follows documented context contracts: Qoder uses SessionStart, - SubagentStart, and post-Read coverage, including its documented Windows - PowerShell executor. Kimi uses UserPromptSubmit; - on macOS/Linux, GitLab Duo gets a fail-open user SessionStart, while Devin gets - UserPromptSubmit, PostCompaction, and a deduplicated SessionStart - when Claude does not already provide it. GitLab Duo, Devin, and Factory hooks are withheld on - Windows where no deterministic shell/executor contract is documented. Cline's auto-activating file hooks - are withheld because their context output is not reliably consumed; CodeBuddy's beta hooks are not - auto-installed; Junie's EAP - SessionStart output is documented as ignored; and Cursor context hooks are withheld because - the documented events cannot safely provide race-free MCP context to read-only subagents. -

-

- Documented custom-agent formats receive three exact-owned evidence tiers: Scout for narrow provisional - discovery, Verify as the task-directed default, and Auditor for bounded, paginated, current-generation - verification. Every direct tier calls check_index_coverage for cited paths and relevant scopes, - then reads flagged ranges or skipped files directly. Kiro and Junie use positive-allowlist - --tool-profile scout and --tool-profile analysis server surfaces; - Junie selects dedicated named aliases because its subagent schema filters by server. - Qoder combines named-server selection with exact tier-specific MCP tool IDs. Factory uses - exact registered tool IDs without its additive whole-server mcpServers field. - A foreign Junie alias is preserved and causes the installed profiles to fail closed to parent handoff. - Cursor, Rovo, Augment, Pochi, and Cline use explicit - parent handoff where direct child MCP is unavailable or unsafe; Pochi is limited to readFile. - Neither IBM Bob surface receives an invented hook or custom agent. Amazon Q Developer - IDE defaults to ~/.aws/amazonq/default.json while preserving either existing documented - alternative. -

-
- -
-

What is Hybrid LSP?

-

- Hybrid LSP is semantic type resolution beyond tree-sitter. Tree-sitter alone produces a syntactic - AST — it handles naming, structure, and call sites, but it cannot tell that - user.profile.display_name() resolves to Profile.display_name declared - three modules away, because it does not track imports, generics, inheritance, or stdlib types. -

-

- codebase-memory-mcp ships a lightweight C implementation of language type-resolution - algorithms, structurally inspired by and compatible with major language servers — - tsserver/typescript-go, pyright, gopls, Roslyn, Eclipse JDT, and rust-analyzer — - embedded directly into the native executable. There is no language-server process, no - per-project setup, and no API key. This layer runs alongside tree-sitter on every parse and refines - CALLS, USAGE, and RESOLVED_CALLS edges with type information, - so the graph mirrors what an IDE "Go to Definition" would resolve. -

-

Languages with full Hybrid LSP

- - - - - - - - - - - - - -
LanguageWhat it resolves
PythonImports and dotted submodule walks, dataclasses, Self return types, generics, @property, match/case patterns, SQLAlchemy 2.0 Mapped[T], Pydantic models, typing annotations, async/await, isinstance/walrus narrowing, and common stdlib.
TypeScript / JavaScript / JSX / TSXGenerics, JSX component dispatch, JSDoc inference for plain JS, .d.ts declarations, module re-exports, and method chaining via return-type propagation across a shared cross-file registry.
PHPNamespaces, traits, late-static-binding, PHPDoc inference, parameter binding, and return-type inference.
C#Global usings, file-scoped namespaces, records (incl. C# 12 primary constructors), LINQ method syntax, async Task<T>/ValueTask<T> unwrap, generic methods, var inference, and common BCL stdlib.
GoPre-built per-package cross-file registry, generics, embedded structs, interface satisfaction, and package-aware import resolution.
C / C++Shared cross-language registry: macros, typedef chains, and header-vs-source linking on the C side; templates, namespaces, auto inference, and class-hierarchy method resolution on the C++ side.
Java (new in v0.8.0)Imports (single-type, on-demand, static), class hierarchies with this/super dispatch, generics, annotations, overload matching by arity and parameter types, lambdas and method references bound to functional interfaces, and common JDK stdlib.
Kotlin (new in v0.8.0)Imports and same-package resolution, classes / objects / companion objects, extension functions, data classes, nullable-type unwrapping, scope functions (let/apply/run/also/with), infix calls, and common stdlib.
Rust (new in v0.8.0)use declarations and module paths, impl blocks and trait methods, struct fields, generics with trait bounds, operator-trait desugaring, derive-macro method synthesis, UFCS static paths, and common std prelude.
-

- The two-layer pipeline runs a fast syntactic tree-sitter pass for every one of the 162 languages, - then a type-aware Hybrid LSP pass on top for the families above. Languages without a Hybrid LSP pass - yet fall back to textual resolution, so you always get an answer. -

-
- - -
-

How much does the knowledge graph save?

-

- Each common structural question costs hundreds of tokens against the graph versus tens of thousands - via file-by-file search. Totals across five queries: ~3,400 vs ~412,000 tokens. -

- - - - - - - - - - - - -
Question typeGraphFile-by-fileSavings
Find function by pattern~200~45,000225x
Trace call chain (depth 3)~800~120,000150x
Dead code detection~500~85,000170x
List all routes~400~62,000155x
Architecture overview~1,500~100,00067x
Total~3,400~412,000~121x
-

- A separate evaluation across 31 real-world repositories, described in the preprint, reported 83% answer quality, - 10x fewer tokens, and 2.1x fewer tool calls versus file-by-file exploration. -

-

Source: “Codebase-Memory: Tree-Sitter-Based Knowledge - Graphs for LLM Code Exploration via MCP”, arXiv:2603.27277 — and the - project benchmark report.

+
+
+

Beyond file search

Find the relationship.
Then read the evidence.

+

A function rarely tells the whole story. Follow its callers, find the decision behind it, and trace the infrastructure that runs it.

+
+
+
+ 01 / CODE

What calls this?

+

Explore symbols, call paths, types, dependencies and routes. Move across files, services and indexed repositories.

+
  • Structural, full-text and local semantic search
  • Hybrid LSP type resolution for supported languages
  • HTTP, gRPC, GraphQL, tRPC and async links
+ Explore code intelligence → +
+
+ 02 / DOCUMENTATION

Why does it work this way?

+

Bring documentation into the graph. Search sections, follow resolved code references, and keep architecture decisions close to their implementation.

+
  • Markdown, reStructuredText and AsciiDoc sections
  • Searchable pages from PDFs with text layers
  • ADRs, supersession and supported doc comments
+ Documentation support → +
+
+ 03 / INFRASTRUCTURE

What builds and deploys it?

+

Connect supported infrastructure and configuration to source. Trace the path from a service to the program inside its container.

+
  • Docker, Compose, Kubernetes, Helm and Kustomize
  • Terraform / OpenTofu, Terragrunt and Ansible
  • Shell, Make and supported CI steps
+ Infrastructure support → +
+
+
+ A deployment pathServiceWorkloadImageDockerfileProgram +
+

Relationships come from supported source forms. Dynamic values and unresolved references can leave gaps; PDF extraction requires a text layer and does not perform OCR.

-
-

How fast is it?

-

- Indexing is RAM-first (LZ4 compression, in-memory SQLite, single dump at end) and memory is released - to the OS afterward. Queries run in under a millisecond. -

- - - - - - - - - -
OperationTimeNotes
Linux kernel full index3 min28M LOC, 75K files → 4.81M nodes, 7.72M edges
Django full index~6 s49K nodes, 196K edges
Cypher query<1 msRelationship traversal
Name search (regex)<10 msSQL LIKE pre-filtering
Trace call path (depth 5)<10 msBFS traversal
-

Source: project Performance benchmarks, measured on Apple M3 Pro.

+
+
+

Code intelligence

Ask better questions
of your repository.

+

Give your coding agent structural context through 17 MCP tools. Query the same graph from the CLI and open source evidence whenever a result needs verification.

+
+ +

Follow actual relationships

Tree-sitter parsing and Hybrid LSP resolution build call and dependency relationships for supported languages. Trace callers, callees, data flow and supported cross-service links; query more complex patterns with Cypher.

Language and framework coverage varies. Check the support matrix →

+

Understand change

Map a diff to affected symbols, investigate the blast radius, compare graph snapshots and inspect related tests. Near-clone and semantic relationships help surface related implementations worth reviewing.

detect_changescompare_graphsSIMILAR_TO
+

Connect the wider system

Link multiple repositories in the same store. Explore service relationships across HTTP, gRPC, GraphQL, tRPC and supported async patterns, or ingest runtime traces to add observed relationships.

Cross-repo intelligenceingest_tracesget_architecture
+

Know what the index knows

Inspect index status and recorded coverage before making a claim. Coverage diagnostics identify skipped, partial or stale evidence so you can read the relevant source directly. Preserve architecture decisions with ADR management.

check_index_coverageindex_statusmanage_adr
+
Explore all 17 MCP tools
index_repositorysearch_graphquery_graphtrace_pathget_code_snippetget_file_outlineget_graph_schemacompare_graphsget_architecturesearch_codelist_projectsdelete_projectindex_statuscheck_index_coveragedetect_changesmanage_adringest_traces

Tool reference, inputs and examples →

-
-

Features

-
-
-

162 languages

-

Python, Go, JS, TS, TSX, Rust, Java, C++, C#, C, PHP, Ruby, Kotlin, Scala, Zig, Elixir, Haskell, OCaml, Swift, Dart, Lean 4, and many more via vendored tree-sitter grammars compiled into the binary.

-
-
-

Hybrid LSP type resolution

-

Language-server-grade type inference for Python, TS/JS, PHP, C#, Go, C/C++, Java, Kotlin, and Rust — embedded in the binary, no server process or per-project setup.

-
-
-

Pure C, no language runtime

-

A native executable with authenticated release-owned assets for macOS, Linux, and Windows. The native install needs no Docker, language runtime, or API keys; fully static Linux builds are available in the -portable archives.

-
-
-

Call-graph tracing

-

Trace callers and callees across files and packages with import-aware, type-inferred resolution. BFS traversal up to depth 5.

-
-
-

Dead-code detection

-

Find functions with zero callers, with smart filtering that excludes entry points like route handlers, main(), and framework decorators.

-
-
-

Cross-service linking

-

Matches REST routes to HTTP call sites across services with confidence scoring — and detects gRPC, GraphQL, and tRPC services plus pub/sub channels (EMITS/LISTENS_ON for Socket.IO, EventEmitter, and message buses) and async queue dispatch.

-
-
-

Infrastructure-as-code indexing

-

Dockerfiles, Kubernetes manifests, and Kustomize overlays become graph nodes with cross-references to the resources they configure.

-
-
-

Auto-sync

-

A background watcher detects changes in the project your MCP session is working in and re-indexes it incrementally. No manual reindex after editing files there.

-
-
-

Team-shared graph artifact

-

Commit one zstd-compressed snapshot (.codebase-memory/graph.db.zst); teammates bootstrap from it and skip the full reindex.

-
-
-

3D graph visualization

-

The optional UI variant adds one verified, content-addressed frontend pack and serves an interactive 3D graph at localhost:9749 to explore nodes, edges, and clusters visually.

-
-
-

17 MCP tools

-

search_graph, trace_path, detect_changes, query_graph (Cypher), get_architecture, get_code_snippet, check_index_coverage, manage_adr, and 9 more.

-
-
-

Cypher graph queries

-

Run read-only Cypher-style queries against the graph for multi-hop patterns that structured search can't express.

-
-
-

Semantic code search

-

Find code by meaning, not just name, via semantic_query vector search — powered by nomic-embed-code embeddings baked into the binary. No API key, fully local.

-
-
-

Clone & similarity detection

-

SIMILAR_TO edges (MinHash + LSH) surface near-duplicate code; SEMANTICALLY_RELATED edges link conceptually similar functions across the graph.

-
-
-

Cross-repo intelligence

-

Index multiple repositories in one store and link them with CROSS_* edges. A multi-galaxy 3D layout and cross-repo architecture summary span the whole fleet.

-
-
-

Data-flow tracing

-

DATA_FLOWS edges follow values from argument to parameter, with field-access chains — trace how data moves, not just who calls whom.

-
-
-

Change-impact analysis

-

detect_changes maps an uncommitted git diff to affected symbols and their blast radius, with risk classification — see what a change touches before you ship it.

-
-
-

Architecture Decision Records

-

manage_adr persists architectural decisions alongside the graph, so design rationale survives across sessions and teammates.

+
+
+

Smart CI Experimental

Turn change impact
into test selection.

Combine static reachability, changed tests and admitted per-test coverage to explain which tests a change reaches. Use the result to focus supported CI runs.

Job selection uses repository-specific path rules. Test narrowing currently supports C/C++ with supported TEST/SUITE-style harnesses or declared equivalents.

Start in shadow mode and validate your harness before enabling gating. This repository narrows its Linux x86_64 PR unit-test leg; release and dry-run workflows retain full execution.

Read the Smart CI setup guide →
+
+
FROM CHANGE TO EXPLAINED SELECTION
+
01
Establish a baseline

Publish the graph and compatible per-test coverage with test-impact publish.

+
02
Trace the change

Combine changed tests, static relationships and admitted coverage.

+
03
Select with reasons

test-impact select explains its selection for a runner that accepts named tests or suites.

+
↳
Broaden when evidence is insufficient

Missing, stale or incomplete evidence broadens the selection or returns run_all. Static reachability alone never omits a suite.

-
-

What are the recent releases?

-

- The latest release notes are loaded live from GitHub. Each entry links to its full changelog. -

-
-

Loading recent releases from GitHub…

+
+

Agent setup

Context that fits
your coding workflow.

The installer configures 45 automatic/conditional client surfaces. MCP connections, instructions, skills, profiles and hooks are installed according to each client's supported capabilities.

+
+
01 / CONNECTSet up your clients

Configure supported integrations for clients such as Claude Code, Codex CLI, Gemini CLI, Cursor, Zed and OpenCode.

+
02 / GUIDEGive agents a working method

Skills and instructions guide graph queries, coverage checks and source verification. Supported custom-agent formats get Scout, Verify and Auditor profiles.

+
03 / MAINTAIN CONTEXTUse supported hooks

Client-specific lifecycle and read/search hooks help surface relevant graph context. Hook and subagent capabilities differ by client and platform.

-

- View all releases on GitHub → -

+
See the supported clients and integration boundaries +

Detected automatically (39): Claude Code, Codex CLI, Gemini CLI, Zed, OpenCode, Antigravity, Aider, KiloCode, VS Code, Cursor, Windsurf, Augment / Auggie, OpenClaw, Kiro, Junie, Hermes, OpenHands, Cline, Warp, Qwen Code, GitHub Copilot CLI, Factory Droid, Crush, Goose, Mistral Vibe, Grok Build, Qoder CLI, Kimi Code CLI, GitLab Duo CLI, Rovo Dev CLI, Amp, Devin CLI / Local, Tabnine, Amazon Q Developer IDE, CodeBuddy Code CLI, IBM Bob Shell, Pochi, Pi, and Oh My Pi (omp).

+

Conditional or explicit (6): Continue / cn, Visual Studio, TRAE, Roo Code, IBM Bob IDE, and Sourcegraph Cody.

+

These are client surfaces, not a promise of identical MCP, hook or subagent support. For example, Pi receives instructions and a skill; Warp's detected skill installation is counted while its MCP connection remains manual. Other manual, UI, cloud and repository-managed integrations are documented separately.

+

Review the client matrix and setup details →

+
-
-

Frequently asked questions

+
+
+

CBM Lens · Open beta v0.0.1

A visual companion
to the MCP server.

+

An optional way to explore the same graph your agent queries. Included in standard native releases, with source navigation, architecture maps and the Galaxy view.

+
+
+
Lens source reader beside the file impact summary and graph dependencies for limits.c
Source navigation with indexed relationships and impact context.
+
+

Read code in context

Browse files and symbols, inspect callers and dependencies, and keep the relevant source beside the graph.

+

See the shape of the system

Explore architecture, system structure and the Galaxy graph. Inspect coverage diagnostics and architecture decisions from the same workspace.

+
+
+ +

Lens is served on loopback. Browser AI downloads its optional model after you enable it. Its explanations should be checked against the displayed source.

+ Read the Lens guide and watch the walkthroughs → +
-

Does codebase-memory-mcp send my code anywhere?

-

No. All indexing and querying happen 100% locally. There is no embedded LLM and no API key. Release - archives are signed and checksummed. Before testing, three behaviourally identical executable candidates - (unstripped, debug-stripped, stripped) for every platform and link mode are submitted to VirusTotal; the selected candidate is packaged with its - SHA-256 unchanged. Release notes link every measured result. Publication permits only the - documented single-Microsoft !ml tolerance.

+
+

Get started

Install. Index. Explore.

Install the MCP server and configure your coding agent. The native runtime needs no Docker or language runtime; indexing and graph queries need no API key. Standard releases also include the optional Lens open beta.

+
+
TERMINAL · macOS / Linux
# Install the native release and agent integrations
+curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh | bash
 
-        

Does it support semantic or natural-language code search?

-

Yes. Alongside structural and full-text search, search_graph's semantic_query - parameter runs vector search over the whole graph, powered by nomic-embed-code embeddings compiled - into the binary — so it finds publish when you search send. No API key, no - Ollama, no Docker; the embeddings run on-device. The indexer also builds SEMANTICALLY_RELATED - edges between similar functions and SIMILAR_TO edges for near-clone detection.

+# Restart your agent, then ask: +"Index this project" -

Do I need Docker or a runtime?

-

The native install needs no language runtime. It is a native executable with authenticated release-owned - assets for macOS (arm64/amd64), Linux (arm64/amd64), and Windows (arm64/amd64); npm, PyPI, and Go wrapper - installations naturally use their package runtime.

+# Optional: open CBM Lens (open beta) +codebase-memory-mcp daemon start --open
+

Your machine. Your index.

Source analysis and graph storage run locally. Native releases contain the executable and authenticated release-owned assets, including the Lens frontend and agent integration templates.

Use native packages or the npm, PyPI and Go wrappers. Package wrappers use their respective runtimes. Nix and source builds also offer builds without the UI.

Choose the clients you want to configure, restart them after installation, then build your first index.

Download the latest release
+
+
-

How does it stay up to date as I edit code?

-

A background watcher detects file changes and re-indexes incrementally — typically a sub-millisecond - no-op when nothing changed. You only run a manual index for the first build or after a large - git pull.

+
+

Performance and research

More useful context.
Less repeated work.

Reuse the graph to find relevant symbols, follow relationships and retrieve focused source snippets. Compact, paged results help keep repeated repository exploration out of your agent’s context window.

Our March 2026 preprint reports 10× lower token use and 2.1× fewer tool calls across 31 repositories compared with file-by-file exploration.

These measurements cover the study’s backend workloads from March 2026. See the paper for the evaluation setup and full results.

+
-

Why is there no built-in LLM?

-

Other code-graph tools embed an LLM to translate natural language into graph queries, which means - extra API keys and cost. With MCP, the agent you are already talking to is the query translator — - codebase-memory-mcp just builds and serves the graph.

+
+

Common questions

Is Codebase Memory
right for your workflow?

+
When should I use Codebase Memory?

Choose CBM when your coding agent needs a persistent local graph of code, docs and infrastructure. The MCP server brings structural search, source evidence, agent integrations and experimental test selection into one open-source package. The CLI provides direct access, and CBM Lens adds an optional visual workspace in open beta. Native releases run on macOS, Linux and Windows without Docker or a language runtime. Explore the capabilities and Smart CI’s current scope.

+
How does Codebase Memory reduce AI token use?

Your agent can query indexed relationships and retrieve the relevant symbols or source snippets instead of repeatedly exploring whole files. Compact results and pagination control how much context each query returns. The benefit depends on the task; explore the published token-use measurements.

+
Does Codebase Memory send my source code anywhere?

The backend indexes source and stores and queries its graph locally. Your coding agent has its own data-handling settings. Lens can optionally download a browser model after you enable it, with inference on-device. Installers and update checks use the network to obtain releases and assets.

+
How does the index stay current?

Incremental indexing updates changed files. With auto-watch enabled, an eligible active MCP session can watch an already indexed project. Automatic first indexing is a separate opt-in setting. The local UI daemon is not a promise of a permanent background watcher.

+
How much language coverage is available?

Bundled tree-sitter grammars cover a broad range of languages and formats. Supported language families also have Hybrid LSP semantic resolution. Extraction depth, type resolution and framework relationships vary; the language support matrix describes the details.

+
Does Smart CI select tests for every indexed language?

No. Test narrowing currently supports C/C++ family code with supported test-harness conventions or declared equivalents. It requires configuration, admitted coverage and a compatible runner. Missing or incompatible evidence broadens execution or returns run_all. See the Smart CI guide.

+
How do I verify the release?

Use the signed release artifacts and checksums, and review the release's verification information. The security policy explains artifact verification, antivirus reports and vulnerability reporting.

+
Is it free and open source?

Yes. Codebase Memory is MIT licensed. Source, release binaries, detailed documentation and contribution guidelines are available on GitHub.

+
+
-

Is it free and open source?

-

Yes. It is MIT licensed. The full source, signed release binaries, and checksums are on - GitHub.

+
+

Project updates

What's shipping.

Recent releases from GitHub, with the implementation details and fixes behind each version.

+

Loading recent releases from GitHub…

+ + View all releases on GitHub →
- - + - diff --git a/docs/llms.txt b/docs/llms.txt index 0d0c5a49c..83829939f 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -1,26 +1,49 @@ # codebase-memory-mcp -> An open-source Model Context Protocol (MCP) server that indexes a codebase into a persistent knowledge graph of functions, classes, call chains, HTTP routes, and cross-service links. AI coding agents query the graph instead of reading files one by one, answering structural questions with roughly 120x fewer tokens (~3,400 vs ~412,000 across five structural queries). Parses 162 languages via vendored tree-sitter grammars, with Hybrid LSP semantic type resolution for Python, TypeScript/JavaScript, PHP, C#, Go, C/C++, Java, Kotlin, and Rust. It also generates semantic graph edges (SEMANTICALLY_RELATED for vocabulary-mismatch matches, SIMILAR_TO for near-clone detection) and supports semantic vector search via bundled nomic-embed-code embeddings compiled into the native executable — no API key, no Ollama, no Docker. Ships a native executable with an authenticated integration asset; the UI variant adds one content-addressed frontend pack. All processing is local — the embeddings run on-device; there is no embedded LLM and no API key. +> Codebase Memory is an open-source local MCP server for AI coding agents. It connects code, documentation, configuration, and infrastructure in a persistent knowledge graph, with 17 MCP tools and native CLI access. Search for a feature, follow callers and dependencies, trace services and deployments, and inspect change impact. Indexing, graph queries, and semantic embeddings run locally without a hosted service or API key. CBM Lens is the optional visual companion, currently in open beta (v0.0.1); its optional browser model supports on-device code chat after an opt-in download. -## Key facts +## Capabilities + +- Code exploration: symbol and structural search, BM25 text search, semantic search, source snippets and file outlines, call paths, data flow, architecture summaries, hotspots, cycles, and coverage diagnostics. The server registers 17 MCP tools. +- Semantic and similarity relationships: bundled embeddings support natural-language discovery; SEMANTICALLY_RELATED edges connect vocabulary-mismatch matches, and SIMILAR_TO edges expose near-clone candidates. +- Languages and formats: vendored parsers cover source, documentation, and configuration formats. Hybrid LSP adds semantic type resolution for Python, TypeScript/JavaScript/JSX/TSX, PHP, C#, Go, C/C++, Java, Kotlin, Rust, and Perl. Support and resolution depth differ by language; use the README language matrix and check_index_coverage for the indexed project. +- Documentation connected to code: Markdown, reStructuredText, and AsciiDoc sections; PDF text pages (no OCR); supported document and language doc-comment references to code; ADR metadata and supersession. Supported diagrams and OpenAPI descriptions contribute entities, with format-specific reference-resolution limits. +- Infrastructure and deployment: Docker/Compose, Kubernetes, Helm, Kustomize, Terraform/OpenTofu, Terragrunt, Ansible, shell, Make, and supported CI relationships. Trace deployments, images, entrypoints, configuration keys, and environment-variable access back to source when inputs can be resolved statically. +- Cross-service and cross-repository relationships: supported HTTP, asynchronous messaging, gRPC, GraphQL, and tRPC links. Cross-repo analysis explicitly links projects indexed in the same store; missing links do not prove that no runtime relationship exists. +- Change impact: detect_changes maps diffs to affected symbols and investigates related tests with scope: "tests"; compare_graphs compares indexed snapshots. +- Smart CI (experimental): repository-specific changed-file rules select CI jobs; test-impact select combines static reachability, changed tests, and admitted per-test coverage to select tests. Narrowing supports C/C++ family code with supported test-harness conventions and an explicitly configured runner. Missing or doubtful evidence broadens the selection or runs all tests. Other indexed languages are not automatically supported for test narrowing. test-impact publish creates the baseline graph-and-coverage bundle. Start in shadow mode; consult the guide for version requirements. +- Architecture decisions and runtime evidence: manage_adr stores the project's decision record; ingest_traces adds separately collected observed call relationships alongside static analysis. +- CBM Lens open beta (v0.0.1): optional visual companion to the MCP server, with a local browser interface with source exploration, Galaxy graph navigation, architecture and behavior views, ADR editing, index-coverage diagnostics, daemon/system information, and optional local code chat. Static graph animations are not live execution traces. Activity overlays are experimental source-build features, not a standard-release promise. +- Agent integrations: 45 automatic or conditional client surfaces. Installation configures supported MCP entries, durable instructions, skills, Scout/Verify/Auditor profiles, and applicable session, compaction, search, and read hooks. Coverage and activation vary by client and platform. Hooks provide context without blocking or replacing the user's tool call; some clients require trust or use parent-agent handoff instead. +- Persistent operation: a per-account daemon coordinates sessions and indexing. Existing indexes can stay synchronized during eligible active MCP sessions; initial auto-indexing is separately opt-in. Optional team graph artifacts share a baseline while each user keeps a local query database. +- Token-efficient retrieval: targeted graph queries, focused source snippets, and compact, paged results can reduce token use and repeated file reads during code exploration. +- Distribution: native C backend, MIT license, standard native releases for macOS, Linux, and Windows include CBM Lens; Linux portable archives provide static builds. Nix and development source builds can omit the UI. Package-manager options and release verification are documented in the README. + +## Client coverage -- License: MIT, open source. -- Languages: 158 (158 vendored tree-sitter grammars compiled into the binary). -- Hybrid LSP type resolution: 9 language families (Python, TypeScript/JavaScript/JSX/TSX, PHP, C#, Go, C/C++, Java, Kotlin, Rust) — a lightweight C implementation of language type-resolution algorithms, structurally inspired by and compatible with major language servers including tsserver, pyright, gopls, Roslyn, Eclipse JDT, and rust-analyzer. -- MCP tools: 15 (search_graph incl. semantic_query vector search, trace_path (alias: trace_call_path), check_index_coverage, query_graph (Cypher), detect_changes, get_architecture, get_code_snippet, manage_adr, and more). -- Semantic search: natural-language code discovery via bundled nomic-embed-code embeddings (768-dim, compiled into the binary); 11-signal combined scoring; fully local, no API key. -- Semantic & similarity edges: SEMANTICALLY_RELATED (vocabulary-mismatch matches) and SIMILAR_TO (MinHash + LSH near-clone / duplicate detection). -- Cross-repo intelligence: CROSS_* edges link nodes across multiple repos indexed in one store; multi-galaxy 3D layout and cross-repo architecture summary. -- Cross-service linking: HTTP route ↔ call-site matching, plus gRPC/GraphQL/tRPC detection and pub/sub channels (EMITS/LISTENS_ON for Socket.IO, EventEmitter, generic buses). - Supported agents: 45 automatic/conditional client surfaces (39 automatically detected + 6 conditional/explicit): Claude Code, Codex CLI, Gemini CLI, Zed, OpenCode, Antigravity, Aider, KiloCode, VS Code, Cursor, Windsurf, Augment / Auggie, OpenClaw, Kiro, Junie, Hermes, OpenHands, Cline, Warp, Qwen Code, GitHub Copilot CLI, Factory Droid, Crush, Goose, Mistral Vibe, Grok Build, Qoder CLI, Kimi Code CLI, GitLab Duo CLI, Rovo Dev CLI, Amp, Devin CLI / Local, Tabnine, Continue / cn, Visual Studio, TRAE, Roo Code, Amazon Q Developer IDE, CodeBuddy Code CLI, IBM Bob IDE, IBM Bob Shell, Pochi, Pi, Oh My Pi (omp), and Sourcegraph Cody. -- Agent profiles: documented custom-agent formats receive Scout (fast/provisional), Verify (default/task-directed), and Auditor (bounded/full verification) definitions. Every direct tier checks exact path/scope coverage with check_index_coverage and falls back to source for flagged gaps; unsafe child-MCP formats use explicit parent handoff. Kiro and Junie use positive-allowlist Scout/Analysis server profiles (7/11 tools); Qoder combines named-server selection with exact tier-specific MCP tool IDs, while Factory uses exact registered IDs without additive whole-server exposure. Foreign Junie aliases are preserved and force parent handoff. -- Performance: Linux kernel (28M LOC, 75K files) full index in 3 minutes → 4.81M nodes, 7.72M edges; Cypher queries in under 1ms. -- Distribution: verified runtime set (native executable plus authenticated release-owned assets); fully static Linux builds are available in the `-portable` archives. Also available through npm, PyPI, Homebrew, Scoop, AUR, and `go install`. + +## Measurement and limits + +A March 2026 preprint across 31 real-world repositories reports 10× lower token use and 2.1× fewer tool calls compared with file-by-file exploration. These historical backend measurements cover the study’s workloads and agent setup. The paper documents the full evaluation; the README Performance section links to the research and earlier measurements. + +Graph evidence depends on index generation, freshness, and exact path/scope coverage. Partial, skipped, excluded, stale, or unknown coverage requires source verification, especially for absence claims. Infrastructure relationships are static, and test-selection support is narrower than language-indexing support. ## Links - Homepage: https://deusdata.github.io/codebase-memory-mcp/ -- Source code (GitHub): https://github.com/DeusData/codebase-memory-mcp +- Source and feature reference: https://github.com/DeusData/codebase-memory-mcp - Latest release: https://github.com/DeusData/codebase-memory-mcp/releases/latest +- Quick start and installation: https://github.com/DeusData/codebase-memory-mcp#quick-start +- Documentation indexing: https://github.com/DeusData/codebase-memory-mcp#documentation-connected-to-code +- Infrastructure and deployment: https://github.com/DeusData/codebase-memory-mcp#infrastructure-and-deployment +- CBM Lens: https://github.com/DeusData/codebase-memory-mcp#cbm-lens-beta-code-visualization-and-source-navigation +- Agent integrations: https://github.com/DeusData/codebase-memory-mcp#multi-agent-support +- Smart CI guide: https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/SMART_CI.md +- Configuration: https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/CONFIGURATION.md +- MCP tools: https://github.com/DeusData/codebase-memory-mcp#mcp-tools +- Token savings and evidence: https://deusdata.github.io/codebase-memory-mcp/#token-savings +- Performance and research: https://github.com/DeusData/codebase-memory-mcp#performance - Benchmark report: https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/BENCHMARK.md - Research paper: https://arxiv.org/abs/2603.27277 +- Security and release verification: https://github.com/DeusData/codebase-memory-mcp#security diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 086be8523..550a3cabe 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -2,7 +2,6 @@ https://deusdata.github.io/codebase-memory-mcp/ - 2026-06-12 - 1.0 + 2026-10-20 diff --git a/graph-ui/package-lock.json b/graph-ui/package-lock.json index c444f4e41..c20a98c36 100644 --- a/graph-ui/package-lock.json +++ b/graph-ui/package-lock.json @@ -32,8 +32,8 @@ "playwright": "1.62.1", "three-stdlib": "2.36.1", "typescript": "5.9.3", - "vite": "6.4.3", - "vitest": "4.1.11" + "vite": "6.4.5", + "vitest": "4.1.12" } }, "node_modules/@asamuzakjp/css-color": { @@ -1812,16 +1812,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", - "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.12.tgz", + "integrity": "sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.11", - "@vitest/utils": "4.1.11", + "@vitest/spy": "4.1.12", + "@vitest/utils": "4.1.12", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1830,13 +1830,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", - "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.12.tgz", + "integrity": "sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.11", + "@vitest/spy": "4.1.12", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1857,9 +1857,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", - "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.12.tgz", + "integrity": "sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==", "dev": true, "license": "MIT", "dependencies": { @@ -1870,13 +1870,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", - "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.12.tgz", + "integrity": "sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.11", + "@vitest/utils": "4.1.12", "pathe": "^2.0.3" }, "funding": { @@ -1884,14 +1884,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", - "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.12.tgz", + "integrity": "sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.11", - "@vitest/utils": "4.1.11", + "@vitest/pretty-format": "4.1.12", + "@vitest/utils": "4.1.12", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1900,9 +1900,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", - "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.12.tgz", + "integrity": "sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==", "dev": true, "license": "MIT", "funding": { @@ -1910,13 +1910,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", - "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.12.tgz", + "integrity": "sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.11", + "@vitest/pretty-format": "4.1.12", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2135,9 +2135,9 @@ } }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -4938,9 +4938,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", - "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { @@ -5277,9 +5277,9 @@ } }, "node_modules/vite": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", - "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.5.tgz", + "integrity": "sha512-4enwjFfi4IwtEh0w+jPzpLVOFQiPHmmzPxcHXQOyEur47o6u0jE573DmGpjA+7irXoMLYRZD5ZaFUEneS3abPg==", "dev": true, "license": "MIT", "dependencies": { @@ -5367,19 +5367,19 @@ } }, "node_modules/vitest": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", - "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.12.tgz", + "integrity": "sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.11", - "@vitest/mocker": "4.1.11", - "@vitest/pretty-format": "4.1.11", - "@vitest/runner": "4.1.11", - "@vitest/snapshot": "4.1.11", - "@vitest/spy": "4.1.11", - "@vitest/utils": "4.1.11", + "@vitest/expect": "4.1.12", + "@vitest/mocker": "4.1.12", + "@vitest/pretty-format": "4.1.12", + "@vitest/runner": "4.1.12", + "@vitest/snapshot": "4.1.12", + "@vitest/spy": "4.1.12", + "@vitest/utils": "4.1.12", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -5407,12 +5407,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.11", - "@vitest/browser-preview": "4.1.11", - "@vitest/browser-webdriverio": "4.1.11", - "@vitest/coverage-istanbul": "4.1.11", - "@vitest/coverage-v8": "4.1.11", - "@vitest/ui": "4.1.11", + "@vitest/browser-playwright": "4.1.12", + "@vitest/browser-preview": "4.1.12", + "@vitest/browser-webdriverio": "^4.1.11", + "@vitest/coverage-istanbul": "4.1.12", + "@vitest/coverage-v8": "4.1.12", + "@vitest/ui": "4.1.12", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/graph-ui/package.json b/graph-ui/package.json index a38ba6faa..0fcf6ab3e 100644 --- a/graph-ui/package.json +++ b/graph-ui/package.json @@ -62,8 +62,8 @@ "playwright": "1.62.1", "three-stdlib": "2.36.1", "typescript": "5.9.3", - "vite": "6.4.3", - "vitest": "4.1.11" + "vite": "6.4.5", + "vitest": "4.1.12" }, "overrides": { "sharp": "$sharp", diff --git a/graph-ui/src/architecture/container-topology.test.ts b/graph-ui/src/architecture/container-topology.test.ts index 3cfa2db5f..2cef1596e 100644 --- a/graph-ui/src/architecture/container-topology.test.ts +++ b/graph-ui/src/architecture/container-topology.test.ts @@ -343,3 +343,9 @@ describe('Spring service connection evidence', () => { expect(links(result)).toEqual([]); }); }); + +it.each(['$endpoint', 'endpoint$'])('tracks a literal identifier through a transport wrapper: %s', parameter => { + const compose = 'services:\n web:\n build: ./web\n api:\n image: example/api\n'; + const source = `function load(${parameter}) { return fetch(${parameter}); }\nload("http://api:8080/items");`; + expect(links(buildContainerTopology([project(compose, { 'web/client.js': source })]))).toEqual(['web>api:http']); +}); diff --git a/graph-ui/src/architecture/container-topology.ts b/graph-ui/src/architecture/container-topology.ts index 51de635aa..b1735aaf7 100644 --- a/graph-ui/src/architecture/container-topology.ts +++ b/graph-ui/src/architecture/container-topology.ts @@ -300,7 +300,12 @@ function networkCall(name: string): boolean { || /^(?:requests|http|https|axios|urllib\.request|urllib3|grpc|redis|psycopg2?|pymysql|mysql|amqp|amqplib|net|socket|ConnectionMultiplexer|Dns)\./i.test(name) && /(?:get|post|put|patch|delete|request|urlopen|dial|newclient|connect|connectasync|createclient|createconnection|insecure_channel|secure_channel|gethostentry(?:async)?)$/i.test(name); } -const wordIn = (code: string, name: string): boolean => new RegExp(`\\b${name.replace(/[$]/g, '\\$&')}\\b`).test(code); +function wordIn(code: string, name: string): boolean { + for (const [identifier] of code.matchAll(/[A-Za-z_$][\w$]*/g)) { + if (identifier === name) return true; + } + return false; +} interface Wrapper { parameter: number; hint: string } /** Bounded local argument propagation to known transport APIs, not a function-name dictionary. */ function connectionWrappers(code: string, calls: Call[]): Map { diff --git a/graph-ui/src/architecture/repository-guide.test.ts b/graph-ui/src/architecture/repository-guide.test.ts index 0cb221754..a161a1216 100644 --- a/graph-ui/src/architecture/repository-guide.test.ts +++ b/graph-ui/src/architecture/repository-guide.test.ts @@ -64,3 +64,10 @@ it('follows only actual call edges across components and retains their evidence' expect(paths[0].edges[0]).toBe(map.evidence[0]); expect(entryRoutes(graph.nodes[2], map.evidence)).toEqual([]); }); + +it('keeps token boundaries when dropping markup from repository prose', () => { + const quote = sourceQuote('README.md', 'This repository provides useful source browsing and <