Make SessionStart hook comments explicit that stdout is harness-injected session context, not MCP protocol output. This removes vague same-methodology wording without changing install or hook behavior.
Validation: CBM_ONLY_SUITE=cli ./build/c/test-runner; bash scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
search_graph documented limit as configurable through search_limit, but the BM25 query path used its internal fallback when callers omitted limit. That made default pagination depend on search mode.
Read the config-backed default once in handle_search_graph, apply it to both BM25 and regex/vector paths, and share the search_limit config-key definition with CLI/tests.
Add an MCP regression that sets search_limit=1, omits the request limit, and verifies BM25 returns one result with has_more=true.
Validation: make -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=mcp ./build/c/test-runner; CBM_ONLY_SUITE=tool_consolidation ./build/c/test-runner; CBM_ONLY_SUITE=cli ./build/c/test-runner; bash scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
Use the Claude SessionStart matcher array length instead of the unrelated NUM_DIRS constant, and expose the helper with a Claude-specific name that matches the Gemini session hook API.
Add a focused CLI regression test that verifies startup, resume, clear, and compact SessionStart hooks are all installed and removed.
Validation: make -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=cli ./build/c/test-runner; bash scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
Port upstream OpenClaw MCP config behavior to the fork branch: OpenClaw now writes nested mcp.servers entries with enabled, command, and args instead of the generic mcpServers shape, while preserving existing config.
Also skip .claude-worktrees during discovery to avoid indexing stale duplicate worktree content alongside the existing .claude and .worktrees exclusions.
Verified: build/c/test-runner; build/c/codebase-memory-mcp; CBM_ONLY_SUITE=cli (107); CBM_ONLY_SUITE=discover (84).
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
Use the discover walker for MCP autoindex file-limit checks instead of shelling out to git ls-files, so non-git roots and ignored files follow the same filters as indexing.
Add typed effective config helpers so env-backed registry settings such as CBM_AUTO_INDEX_LIMIT and CBM_REINDEX_ON_STARTUP are honored consistently by MCP startup logic.
Repair the CBM_ONLY_SUITE discover allowlist and add regressions for bounded non-git discovery counts and env-over-db config priority.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
Install, update, and uninstall cleanup paths are scoped to project indexes, but the old .db predicate also selected _config.db. Add a shared project-index predicate, exclude the persistent config store, and keep list/count/remove behavior consistent.
Verification: make -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=cli build/c/test-runner; make -B -f Makefile.cbm build/c/codebase-memory-mcp.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
Deep --no-ff merge of upstream DeusData/main (597 commits, merge-base 988d975)
into fork api-consolidation (HEAD 3018b22), producing a superset that combines
upstream breadth with the fork's surviving depth.
Upstream additions integrated: semantic/embedding search (vendored/nomic),
minhash/SimHash, cross-repo + cross-file LSP + route + pkgmap passes, artifact
import/export, path_alias, git_context, hook_augment, Windows support
(compat_fs/compat_thread), the UI layer, and ~140 expanded tree-sitter grammars.
Fork depth retained: PageRank/LinkRank ranking (src/pagerank/; sort_by=
relevance/linkrank/degree/calls), dependency indexing (src/depindex/;
CBM_INDEX_MODE=DEP), token reduction, and HTTP-endpoint discovery
(src/pipeline/httplink.c, pass_httplinks.c).
Conflict resolution (27 regions across the 39-file intersection):
- src/mcp/mcp.c: kept the fork superset handler pattern (resolve_project_store +
expand_project_param + inject_context_once) as the single path; UNIONed the
tool dispatch table; emit status:"indexed", excluded[], cross_service trace.
- src/store/store.c: upstream base + fork pagerank/linkrank/node_degree tables
and sort_by plumbing; cbm_store_node_degree now excludes INHERITS
(WHERE type != 'INHERITS'); removed the duplicate cbm_store_get_db.
- src/pipeline/pipeline.c: took upstream run_extraction_phase; grafted the fork
passes (httplinks, normalize, flush_store dump branch); call
cbm_pipeline_set_committed_counts before cbm_gbuf_dump_to_sqlite.
- internal/cbm/lang_specs.c: 158 entries combining the fork section_node_types
with upstream ts_factory/embedded_imports; wired 13 orphaned type-arrays.
- src/depindex/depindex.h: renamed CBM_PATH_MAX -> CBM_DEP_PATH_MAX (it collided
with constants.h enum CBM_PATH_MAX=1024) + 11 call sites in depindex.c.
- Makefile.cbm: upstream grammar/nomic units + fork CGO targets; added
httplink.c + pass_httplinks.c to PIPELINE_SRCS; removed mongoose NOSAN
machinery (mongoose was deleted upstream).
- httplinks restored (the merge silently deleted them because the fork had left
them unchanged since the base): httplink.c/.h, pass_httplinks.c,
tests/test_httplink.c; registered RUN_SUITE(httplink) in tests/test_main.c.
- CBM_INDEX_MODE enum unified to FULL=0, MODERATE=1, FAST=2, DEP=3.
Completeness (dual-parent check): 0 upstream files dropped from the merge tree;
6 fork files dropped, all intentional (upstream-superseded): CHANGELOG.md,
vendored/mimalloc/src/{arena-abandon,segment-map,segment}.c,
vendored/mongoose/{mongoose.c,mongoose.h}. Fork features (pagerank, depindex,
token_reduction, httplink) verified present and registered in tests/test_main.c.
src/pipeline/pass_parallel.c has an identical function set to upstream (49=49,
none dropped); the large line-deltas vs upstream are within-function divergence
of the fork extractor, not dropped functions.
Gate (run manually; hooks skip on merge commits): build green
(make -f Makefile.cbm cbm, exit 0); full C test suite 5980 passed / 2 failed /
0 crashes. The fork's pre-existing ASan crash in build_project_list_error_srv is
fixed by upstream's mcp.c rewrite, so the merge is strictly better than the fork
baseline on the crash axis.
Known issue, tracked separately (NOT a merge defect): 2 nondeterministic
failures in tests/test_incremental.c — incr_db_deleted_recovery:772 (diff_pct)
and incr_accuracy_vs_full:803 (full_nodes vs incr_nodes). Cause: the fork's
extractor (internal/cbm/extract_*.c) and parallel-resolve Route-node creation
are not thread-safe under the concurrent use of upstream's parallel pipeline
(run_parallel_pipeline), producing nondeterministic route/infra HANDLES edges
(~234k edge swing between runs). Sequential extraction (CBM_WORKERS=1) is
deterministic and correct. Root cause, bisect, and repro are documented in
notes/2026-06-24-fork-vs-upstream-divergence-assessment.md (section 8f); the fix
is deferred to a dedicated thread-safety audit (the make test-tsan target is
currently a stub and needs wiring).
* fix(cli): copy and sign the built binary on install instead of re-signing the stale target
`install` (especially with --force) only ad-hoc-signed whatever already sat at
~/.local/bin/codebase-memory-mcp and never copied the binary the operator ran
install from. An operator who built from source and ran `install -y --force`
was silently left with the OLD binary at the target — running stale code while
believing they had upgraded (#472).
Install now mirrors `update`: detect the running binary, copy it to the
canonical target (skipping the copy when we already ARE the target, which would
otherwise truncate the running file), make it executable, ad-hoc sign the
TARGET, and point all agent configs at the target. A different pre-existing
binary is only replaced with --force or interactive confirmation; --dry-run
reports the intended swap without touching anything.
The copy+sign also resolves the secondary report: a hand-copied clang build is
linker-signed (flags=0x20002) and gets Killed:9 when spawned by an MCP host —
re-signing ad-hoc (flags=0x2) makes it launchable, which install now does for
the placed binary.
Adds cbm_copy_binary_to_target() (with a same-file guard) plus regression tests
covering the binary swap and the self-copy guard.
Closes#472
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
* fix(cli): address QA round 1
Extend cli_install_same_file_guard_issue472 with a symlink case: two distinct
path strings resolving to the same inode (which a non-canonical
cbm_detect_self_path vs the hardcoded target can produce) must also be detected
as same-file and skipped, not truncated. POSIX-only (#ifndef _WIN32).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
* test(cli): guard install copy exec-bit assertion to POSIX
cli_install_copies_binary_to_target_issue472 asserted the executable bit on the
copied target, but cbm_copy_binary_to_target only chmods on POSIX (the exec bit
is not meaningful on Windows, and MinGW stat() derives it from the file
extension). Wrap the stat/S_IXUSR assertion in #ifndef _WIN32 so test-windows
passes; the content and creation assertions still run on all platforms.
Refs #472
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
---------
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Co-authored-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Codex, Gemini CLI, and Antigravity all gained lifecycle-hook support, so give
them the same non-blocking SessionStart reminder Claude Code has — stdout is
injected as session context, nudging the agent to use codebase-memory-mcp
graph tools before grep.
- Codex: append a sentinel-delimited [[hooks.SessionStart]] block to
~/.codex/config.toml (idempotent upsert, preserves other content, removable).
- Gemini CLI: SessionStart hook in ~/.gemini/settings.json, reusing the shared
JSON hook upsert (alongside the existing BeforeTool reminder).
- Antigravity: same JSON hook in ~/.gemini/antigravity/settings.json (it shares
Gemini hook semantics).
The reminder command is written to be valid both as a TOML single-quoted
literal and a JSON string (no single quotes, no newlines). Wired into install,
uninstall, and the install --plan receipt. remove_hooks_json now prunes an
emptied event key so removal leaves no stale "<Event>": [] cruft. README
Multi-Agent table updated. Tests cover Codex upsert/idempotency/removal and
Gemini parity.
An installer that configures MCP entries, instruction files, Skills, and hooks
across many agents should let callers see what it will mutate before it does.
`--dry-run` already lists planned writes in human-readable form; this adds the
machine-readable `agent.install.plan.v1` JSON receipt the issue asked for.
`install --plan` runs the real install dispatch in record-only mode — a global
g_install_plan recorder that each write-site function appends to (at the same
point it would write) while all mutations are disabled — then emits JSON with
agents_detected, config_files_planned, instruction_files_planned,
hooks_planned, writes_started:false, network_after_install:false, and
next_safe_command. Because the plan is recorded on the actual install code
path, it cannot drift from real behavior. No config is written, no index
deleted, no network used.
cbm_build_install_plan_json is exposed for testing; the test asserts the
receipt content AND that building it creates no config files.
The symlink-attack vector (predictable /tmp/cbm-code-discovery-gate-$PPID)
was removed in c29e6d5 when the blocking bash gate became a stateless
compiled augmenter writing to stdout only. Add a regression guard so a
predictable temp/state file can never be reintroduced: assert the emitted
gate shim contains no /tmp path and no $PPID, and delegates to hook-augment.
Export cbm_install_hook_gate_script (was static) so the security property
is directly testable.
Cursor was never in the editor-detection table, so install/update only
ever wrote the VS Code config and silently skipped ~/.cursor/mcp.json,
even when Cursor was present. Uninstall removed the Cursor entry but a
subsequent install never re-created it.
Detect ~/.cursor/ as the Cursor agent, register the MCP server in
~/.cursor/mcp.json (mcpServers format, same as Windsurf/Gemini) on
install, remove it on uninstall, and list Cursor in detected agents.
The previous PreToolUse hook gated Grep/Glob/Read/Search with 'exit 2'
on the first call per session, which broke Claude Code's
read-before-edit invariant (issue #362) and could deny tool calls under
upgrade/missing-binary failure modes.
Replace it with a structurally non-blocking augmenter:
- New 'codebase-memory-mcp hook-augment' subcommand reads the hook JSON
from stdin and, for Grep/Glob, queries search_graph (in-process, no
shell) and emits hookSpecificOutput.additionalContext. Every failure
path (no project, short token, missing binary, slow query, timeout)
exits 0 with no stdout — the hook physically cannot block a tool call.
- 300 ms SIGALRM/_exit(0) in-process deadline; 5 s settings.json timeout
backstop. Output is written exactly once at the very end, so a
mid-work timeout yields a clean no-op (never partial JSON).
- Matcher narrowed to 'Grep|Glob' (Read explicitly excluded) for Claude;
Gemini matcher narrowed to 'google_search|grep_search' (excludes
read_file) for the same reason.
- The installed shim is a thin wrapper that delegates to the binary;
legacy filename 'cbm-code-discovery-gate' is kept so existing
settings.json entries upgrade with zero migration. Installer refuses
to embed binary paths containing a double quote (shim injection
defense).
- Per-agent 'old matchers' lists let upsert/remove clean up historical
matcher strings during upgrade.
- Smoke tests (8d/8e/8l) updated to assert the new behavior and
regress-test against re-introducing Read in the matcher or 'exit 2'
in the shim.
- Session reminder text updated: 'always Read a file before editing it'
replaces the prior 'fall back to Read only for text content'.
(cherry picked from commit f72c8e68c4d91e52911a569a967ad782ce5472b2)
Detect ~/.kiro/ directory, auto-configure MCP at ~/.kiro/settings/mcp.json
using standard mcpServers format. Adds install, uninstall, detection, and test.
Based on #174 by dLo999, rebased onto refactored install infrastructure.
Co-Authored-By: Dustin Obrecht <dustin@kurtnoble.com>
Merge codebase-memory-exploring, codebase-memory-tracing,
codebase-memory-quality, codebase-memory-reference into single
codebase-memory skill with decision matrix, workflows, and gotchas.
Old skill directories are cleaned up automatically during install.
Factual corrections vs original PR: trace_call_path -> trace_path,
removed non-existent tools (read_file, list_directory) and edge types
(OVERRIDE, USAGE, FILE_CHANGES_WITH), removed risk_labels parameter.
Co-Authored-By: gdilla <gdilla@users.noreply.github.com>
Fixes codebase://architecture returning only 10 results all from graph-ui
by wiring hardcoded limits through the config system and raising defaults to 25.
Key changes:
- mcp.c: add key_functions_count config (default 25); wire into
build_key_functions_sql (was hardcoded LIMIT 10 at line 4317) and
build_resource_architecture call site
- mcp.c: add arch_hotspot_limit config (default 25); wire into classic
get_architecture tool handler
- store.c/store.h: raise CBM_ARCH_HOTSPOT_DEFAULT_LIMIT 10->25; add
hotspot_limit param to cbm_store_get_architecture
- store.c/store.h: add sort_by=calls (ORDER BY calls_in+calls_out DESC)
and sort_by=linkrank (ORDER BY linkrank_in DESC) dispatch cases;
add degree_mode config (weighted|unweighted|calls_only) for
min_degree/max_degree filter column selection
- watcher.c/watcher.h: add poll_base_ms/poll_max_ms to struct cbm_watcher;
change cbm_watcher_run and cbm_watcher_poll_interval_ms signatures to
accept base_ms/max_ms params (0=defaults); wire watcher_poll_base_ms and
watcher_poll_max_ms config keys through main.c
- cli.h: extend cbm_config_entry_t with range and guidance fields (5->7)
- cli.c: replace entire CBM_CONFIG_REGISTRY with 7-field entries for all 32
config keys with broadest feasible ranges and actionable guidance strings;
update config list/get/help display to print [range] + guidance per entry
- scripts/autotune.py: new standalone Python 3.9+ script that sends JSON-RPC
directly to the binary via stdin/stdout, tries 7 experiments, scores
against expected top-10 ground truth for 3 repos, resets config on exit
- tests: update all callers of cbm_store_get_architecture (pass 0 for
hotspot_limit) and cbm_watcher_poll_interval_ms (pass 0,0 for defaults)
All 2238 tests pass.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
Install command:
- Add cbm_kill_other_instances() to kill stale MCP servers
- Add cbm_macos_adhoc_sign() to sign binary if placed unsigned
Update command:
- Replace skills-only reinstall with full cbm_install_agent_configs()
- Replace external unzip with cbm_extract_binary_from_zip() via zlib
Refactor: extract 10-agent config loop into cbm_install_agent_configs()
called by both install and update.
New: cbm_extract_binary_from_zip() — in-memory zip extraction with
stored + deflate support, path traversal rejection, bounds checks.
4 unit tests. Smoke: add install.ps1 E2E for Windows.
TDD: two tests (overwrite read-only file, create new file) written
first, then cbm_replace_binary() implemented and wired into update.
The update command failed when the existing binary had no write
permission (e.g., 0500). unlink() only requires write permission on
the parent directory, not the file itself, so removing first then
creating with O_CREAT | 0755 handles all permission combinations.
Fixes#114.
Config registry (CBM_CONFIG_REGISTRY in cli.c):
- 25 config keys across 5 categories: Indexing, Search, Tools, PageRank,
Dependencies. Each entry has key, default, env var name, category,
description.
- All defaults verified against code-level #define values.
cbm_config_get_effective(): priority chain env > DB > default.
- Checks registry for env var name, reads env first, falls back to DB.
- Used by config get CLI and auto_index in maybe_auto_index.
Env var overrides for key settings:
- CBM_AUTO_INDEX (bool), CBM_AUTO_INDEX_LIMIT (int)
- CBM_REINDEX_ON_STARTUP (bool)
- CBM_KEY_FUNCTIONS_EXCLUDE (comma-separated globs)
- CBM_TOOL_MODE (streamlined/classic)
config list output:
- Grouped by category with [Category] headers
- Shows (env) when env var is active, (set) when DB value differs from default
- All 25 keys visible (was: only 2)
config help:
- Shows storage location (~/.cache/codebase-memory-mcp/_config.db)
- Priority explanation (env > config set > default)
- Examples for config set and env var usage
- Keys grouped by category with [env: VAR_NAME] annotation
Fixed: auto_dep_limit default 5→20, dep_max_files default 5000→1000
to match code-level CBM_DEFAULT_AUTO_DEP_LIMIT and CBM_DEFAULT_DEP_MAX_FILES.
Fixed: hint message provides complete commands, not fragments.
Improved: dependency config descriptions explain what packages/files mean.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
- OpenClaw: detect ~/.openclaw/, write MCP config to openclaw.json
- VS Code was already wired in PR #79 but not counted in README
- Agent struct initializer uses memset instead of counting booleans
- README updated: 8 → 10 agents
The install command detected many editors (Cursor, Zed, Gemini, etc.)
but not VS Code, even though cbm_install_vscode_mcp() already existed.
Add VS Code User config dir detection and wire it into both install
and uninstall flows, writing to ~/.config/Code/User/mcp.json (Linux)
or ~/Library/Application Support/Code/User/mcp.json (macOS).
Complete C implementation of the indexing pipeline (src/), parallel worker
pool, graph buffer with merge support, SQLite writer, and 1893-test suite.
Linter setup: zero warnings from clang-tidy (all checks enabled, no NOLINT
suppressions), cppcheck, and clang-format. All issues fixed at source —
proper headers for external linkage, GROW_ARRAY macro restructured to
eliminate type parameter, null-deref paths guarded, named intermediates
for suspicious-argument checks.