Commit Graph
17 Commits
Author SHA1 Message Date
Andrew Hundt 34db7cf044 fix(mcp): reuse canonical project paths
Consolidate tilde-aware path canonicalization for project slugging and path auto-index resolution. Add a graph-backed regression test for search_graph(project='~/repo') using an isolated fake HOME so explicit repo-directory params resolve to the same slug that indexing creates.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-01 20:30:59 -04:00
Andrew Hundt 13511cd7f8 test(portability): reuse filesystem wrappers in suites
Replace direct environment and filesystem cleanup calls in MCP, CLI, input validation, and HTTP link tests with existing cbm compatibility wrappers. Also updates the MCP publish metadata fixture to use the current three-file containment fallback instead of the now-exact new-folder path.

Validation: git diff --check; bash scripts/check-source-safety.sh; make -j8 -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=mcp ./build/c/test-runner; CBM_ONLY_SUITE=cli ./build/c/test-runner; CBM_ONLY_SUITE=input_validation ./build/c/test-runner; CBM_ONLY_SUITE=httplink ./build/c/test-runner.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-01 19:33:52 -04:00
Andrew Hundt 608b51374e fix: apply autoindex limits consistently
Use the bounded discover count for startup, first-use, and explicit path autoindexing so CBM_AUTO_INDEX_LIMIT cannot be bypassed by a later MCP handler path.

Move sync first-use indexing into resolve_project_store via a shared MCP-local helper, leaving REQUIRE_STORE responsible for joining an in-flight startup index and reporting missing stores. This avoids duplicate indexing attempts and duplicate over-limit warnings while preserving structured stderr logging.

Add an input-validation regression for over-limit explicit path projects and clarify the config help text to say the limit counts indexable files.

Verified: build/c/test-runner; build/c/codebase-memory-mcp; CBM_ONLY_SUITE=input_validation (44), mcp (113), discover (83), cli (104).
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 17:09:51 -04:00
Andrew Hundt a1c8bf9a0e fix: honor auto_index on first use
Share the MCP auto-index enablement decision across startup, synchronous first-use indexing, and explicit path indexing. Production servers still attach config and keep the registry default auto_index=true, while embedded/no-config servers remain manual unless CBM_AUTO_INDEX explicitly opts in.

This prevents search/trace/resource paths from bypassing auto_index=false after startup and avoids accidental full worktree indexing in test or embedded contexts. The path auto-index regression test now opts in explicitly and restores CBM_AUTO_INDEX afterward.

Validation:

- make -f Makefile.cbm build/c/test-runner

- CBM_ONLY_SUITE=mcp build/c/test-runner: 113 passed

- CBM_ONLY_SUITE=tool_consolidation build/c/test-runner: 94 passed

- CBM_ONLY_SUITE=input_validation build/c/test-runner: 43 passed

- git diff --check

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 11:43:46 -04:00
Andrew Hundt 0839cb60b1 fix: align streamlined MCP trace surface
Use trace_path as the single canonical call-tracing tool across classic and streamlined tool lists, with the streamlined list reusing the canonical schema to prevent drift. Revealing hidden tools now makes the classic-only tools discoverable through tools/list for clients that only call advertised tools.

Extend trace_path with the existing optional filters and modes exposed by the fork, validate cheap arguments before project resolution so malformed calls cannot trigger auto-indexing, and handle string-array allocation failures explicitly. Add focused coverage for schema parity, hidden-tool reveal, invalid trace modes, test filtering, risk labels, and exclude filters.

Verified with: make -f Makefile.cbm cbm; 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=input_validation build/c/test-runner; CBM_ONLY_SUITE=token_reduction build/c/test-runner; git diff --check.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 09:40:25 -04:00
Andrew Hundt 60cad1ebbb fix: consolidate trace API and rank persistence
Use trace_path as the single public call-tracing tool across classic and streamlined MCP surfaces, while exposing existing semantic_query and search_code mode parameters in the advertised schemas. Keep the streamlined surface compact, but ensure the hidden-tool reveal covers the full classic capability set without trace aliases.

Persist PageRank, LinkRank, and node_degree rows with direct VALUES inserts instead of per-row SELECT probes. The node/edge project is loaded once with the graph rows so full-scope dependency attribution stays identical to the previous behavior.

Add focused coverage for dependency project attribution, streamlined parameter contracts, and targeted suite allowlist entries so these checks cannot silently run zero tests.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 08:36:56 -04:00
Andrew Hundt 74a5c63eff feat(mcp): split search_code_graph into focused tools (§4b)
Replace the consolidated search_code_graph mega-tool — which merged graph
search + source grep + cypher behind two nearly disjoint parameter
families — with the three existing focused tools as the DEFAULT surface:
search_graph (graph/structural), search_code (source grep), query_graph
(cypher). These are drawn from TOOLS[] (single schema source, DRY);
trace_call_path + get_code remain on the default surface. The
search_code_graph schema and its thin dispatch router are deleted; the
underlying handlers (handle_search_graph/search_code/query_graph) stay.

The _hidden_tools progressive-disclosure hint now lists 11 hidden tools
(was 14). Repointed three user-facing hint strings (handle_trace_call_path
missing-name and function-not-found; two unknown-tool path hints) from the
deleted tool to search_graph.

Tests updated to the new 5-tool surface:
- tests/test_mcp.c: mcp_tools_list + server_handle_tools_list assert the
  5 default tools present and search_code_graph absent.
- tests/test_tool_consolidation.c: renamed streamlined_mode_shows_3_tools
  -> ..._5_default_tools; repurposed the two search_code_graph dispatch
  tests into search_graph_dispatch / query_graph_dispatch (query_graph
  handler accepts both cypher= and query=); revised
  schema_compact_documented_as_graph_only (the graph-vs-source compact
  collision it warned about no longer exists post-split).
- tests/test_input_validation.c: remapped 16 dispatch calls by mode --
  cypher -> query_graph, source-grep -> search_code, graph/structural ->
  search_graph.
- tests/test_depindex.c: tool_index_dependencies_listed asserts
  search_graph (was search_code_graph).
- src/cli/cli.c: six config-description strings (search_limit, tool_mode,
  context_injection, compact, default_sort_by, default_include_dependencies).

Back-compat: blast radius is in-repo only (0 references in Go internal/cmd,
docs, or the CGO wrapper). External callers of search_code_graph must
switch to search_graph / search_code / query_graph, which are strictly
more capable. query_graph keeps its query= backward-compat alias.

Context -- of the three section-4 fixes in the merge plan, only section 4b
was a real change: section 4c (function<->class edges) was already
implemented (commit b950bf2 added the missing MEMBER_OF reverse-edge
coverage), and section 4a (passive resources) was already mitigated by
inject_context_once plus the get_graph_schema/get_architecture/index_status
tools -- both verified against code, not assumed.

Gate: 5981 passed / 2 failed (the 2 are the deferred B1 store-layer
incremental failures, unrelated to section 4b).

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-24 22:00:24 -04:00
Andrew Hundt a53c55f6b1 fix(mcp): auto-index path-based projects not under session_root (Bug #4)
Previous behavior: when project= was a full directory path (e.g.
"/path/to/react-grid-layout") and that path was NOT under srv->session_root
(e.g. it was in the parent project's .gitignore), codebase-memory returned
"project not found" instead of indexing the requested directory. The auto-index
guard only checked srv->session_root, so .gitignore-excluded sub-repos with
their own .git (passed as absolute paths) were never indexed on first query.

What changed:
- src/mcp/mcp.c: in resolve_project_store(), save the resolved filesystem path
  before expand_project_param() frees raw_project (expand_tilde + realpath into
  _raw_path local variable)
- src/mcp/mcp.c: add path-based auto-index fallback after the session_root block:
  when store is still NULL and _raw_path is an accessible directory, run
  cbm_pipeline_new(_raw_path, ...) to index it, then resolve the store by slug
- tests/test_input_validation.c: add path_project_auto_indexes_separate_directory
  test -- creates two temp dirs, queries the first to establish session_root,
  then queries the second (separate path) and asserts the sentinel function is found

Result: 2917 passed (was 2916); path-based separate-repo queries now auto-index
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-11 01:46:14 -04:00
Andrew Hundt 12c22e201b feat(config): add context_injection config key to enable/disable _context header
Adds boolean config key context_injection (default true, env CBM_CONTEXT_INJECTION)
that controls whether inject_context_once embeds the _context header in the first
tool response.

WHAT _context contains: node/edge counts, all node labels with counts, edge types
with counts, PageRank stats, and detected ecosystem (Go/Python/Rust/etc.).

WHEN TO DISABLE (context_injection=false):
  - Scripted/programmatic use: extra JSON adds noise to parsed output
  - CI pipelines: token cost is metered, model needs no schema context
  - Model has explicit codebase instructions via system prompt
  - Benchmarking: removes schema-query overhead from latency measurements

WHEN TO KEEP ENABLED (default):
  - Interactive AI sessions benefit from automatic situational awareness
  - Saves 2-3 round-trips otherwise needed for get_architecture/get_graph_schema

Usage:
  codebase-memory-mcp config set context_injection false   # per-install
  CBM_CONTEXT_INJECTION=false codebase-memory-mcp          # per-session

Implementation:
  - Config check placed BEFORE setting context_injected flag so toggling
    mid-session works (re-enable without server restart)
  - session_project is still always included regardless of this setting
  - cbm_config_get_bool reads CBM_CONTEXT_INJECTION env var automatically
    via cbm_config_get_effective (the standard env-override path)

TDD: config_context_injection_disabled, config_context_injection_enabled_by_default
Tests: 2274 passed, 0 failed
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 04:26:20 -04:00
Andrew Hundt 346cd4c900 fix(mcp): tilde expansion + bare "." detection in project path helpers
project_slug_from_path previously detected "~/..." as a path but passed it
directly to realpath(3), which doesn't expand tilde (that's a shell feature).
The slug was then derived from the literal "~" string, giving a wrong result.

Fix: extract expand_tilde() helper that expands "~" and "~/..." to
$HOME + rest using getenv("HOME"). "~user/..." is intentionally left
unexpanded (would require getpwnam — not worth the complexity for this use).

Also fix project_is_path() to detect bare "." (current directory) as a
path, in addition to the already-handled "./" prefix.

The three helpers are now:
  project_is_path(s)      — pure predicate, no heap allocation
  expand_tilde(s)         — tilde expansion, heap result or NULL
  project_slug_from_path(s) — full path→slug pipeline: tilde→realpath→slug

TDD: source_search_tilde_project_expands — writes a file under $HOME (via
  tmpdir), passes project="~/..." and verifies grep returns matches.
  Skips gracefully when tmpdir is not under $HOME.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 03:01:05 -04:00
Andrew Hundt 9da28251b5 refactor(mcp): DRY path→slug conversion + broaden path-arg fix coverage
DRY refactor:
  Extract project_is_path() and project_slug_from_path() helpers (mcp.c:1239)
  that encode the filesystem-path detection and realpath+cbm_project_name_from_path
  conversion in one place. Previously this logic was duplicated between
  expand_project_param Rule 0 and the get_project_root path-arg fix.
  Both sites now call the shared helpers.

  Documents known limitations: "." not detected (use "./"), "~" detected
  but realpath(3) doesn't expand tilde (use absolute path).

Bug coverage: handle_detect_changes and handle_manage_adr also call
  get_project_root with a raw project arg and were silently broken for
  path-style args. They are now fixed as a side-effect of the get_project_root
  fix (no code change needed — they already go through get_project_root).

New TDD tests (5):
  detect_changes_slug_project_finds_root — detect_changes doesn't error on
    valid slug project (regression for get_project_root slug path)
  manage_adr_slug_project_finds_root — same for manage_adr
  source_search_no_project_falls_back_to_session — search_in="source" with
    no project arg uses session_project (set via cbm_mcp_server_set_session_project)
  source_search_via_search_in_param — upgraded to write a real source file
    and assert matches array returned (stronger than previous dispatch-only check)
  source_search_path_project_normalizes_to_slug — slug project returns
    matches, not "project not found"

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 02:55:01 -04:00
Andrew Hundt bfe127ee68 fix(mcp): source search fails when project arg is a filesystem path
get_project_root(srv, project) passed the raw project string directly to
resolve_store() and cbm_store_get_project(). When project="/Users/.../repo"
(a path, not a slug), project_db_path() embedded slashes in the .db filename,
producing an invalid path like ~/.cache/.../Users/athundt/.../repo.db.

Fix: get_project_root now normalizes project args the same way
expand_project_param Rule 0 does — if the arg looks like a filesystem path
(starts with /, ~, ./, or contains /), use realpath() + cbm_project_name_from_path()
to convert it to a slug before looking up in the DB.

Also: get_project_root now falls back to srv->session_project when project
is NULL, so search_in="source" works without an explicit project arg when
the session project is already detected (e.g. after initialize).

Two new tests:
  source_search_via_search_in_param — upgraded: writes a real file in tmpdir
    and verifies grep returns matches (not just "dispatch fired")
  source_search_path_project_normalizes_to_slug — verifies slug project arg
    returns matches array, not "project not found"

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 02:37:44 -04:00
Andrew Hundt a86d3d5659 fix(mcp): 6 bugs found in post-implementation review
store.h:108 — comment typo: "AND" → "OR" for pattern OR-search field

mcp.c:1754 — indentation bug in mode-error free() block: free(file_pattern)
  had 1-space indent instead of 8 and a spurious blank line

mcp.c:2422 — OOM leak in QN lookup: when cbm_store_find_node_by_qn succeeds
  but calloc fails, qn_tmp heap fields (name/project/label/etc.) leaked.
  Fix: free_node_contents(&qn_tmp) in else branch. Added forward declaration
  of free_node_contents before handle_trace_call_path so it resolves.

mcp.c:2464 — "function not found" hint was wrong when only qualified_name
  passed. Old hint said "use name_pattern" but user passed a QN. New hint
  says "use pattern= to find the correct qualified_name".

mcp.c:472 — search_in schema description now documents the response format
  difference: graph mode returns {total,results:[{qualified_name,...}]},
  source mode returns {matches:[{file,line,content}],count}.

tests: 4 new TDD tests covering:
  - pattern glob wildcard auto-convert (*foo* → .*foo.*)
  - pattern invalid regex returns error with hint
  - trace: qualified_name takes priority over function_name when both given
  - trace: QN-not-found returns specific hint mentioning qualified_name
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 02:04:27 -04:00
Andrew Hundt 6da71c7973 fix(tests): correct 2 test regressions from api-consolidation
test_tool_consolidation:539 — resource_client_never_gets_context_across_multiple_calls
  - Cold-start fix (resolve_project_store) now opens the real indexed DB, so
    search results include node names containing "_context" as a substring
    (e.g. "inject_context_once", "gets_context_across_multiple_calls").
  - strstr(r, "_context") was a false positive: matched substrings in node
    names, not the injected "_context" JSON key.
  - Fix: check for "\"_context\":" (quoted JSON key + colon) which only
    matches the actual injected context object, not substrings in results.
  - Same fix applied to legacy_client_gets_context_only_on_first_call test.

test_input_validation:695 — trace_accepts_qualified_name_param
  - QN lookup calls cbm_store_find_node_by_qn(store, NULL, qn, &node).
    SQL "WHERE project = NULL" never matches (SQL null semantics).
  - Test setup uses cbm_mcp_server_set_project("validation-test") which sets
    current_project but NOT session_project, so cold-start project fix does
    not fire, leaving pe.value = NULL.
  - Fix: pass project="validation-test" in test args so pe.value is set.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 01:40:36 -04:00
Andrew Hundt 9b0537519f feat(mcp): add pattern OR-search+search_in+case_sensitive to search_code_graph; qualified_name to trace; compact/sort/include_deps config; cold-start session fix
- search_code_graph: pattern param (OR-search name+qn), search_in='source' dispatch,
  case_sensitive, summary=bool alias, compact/sort_by/include_deps config defaults
- trace_call_path: qualified_name param with QN-first lookup via B-tree,
  compact config default, accepts function_name OR qualified_name
- get_code: compact config default
- store: pattern OR clause in SQL builder, pattern field in search params
- inject_context_once: auto_indexing status + actionable guidance
- REQUIRE_STORE: improved auto-index failure error message
- schemas: added search_in, pattern, case_sensitive, summary, max_rows to search;
  qualified_name to trace; compact to get_code
- config registry: compact, default_sort_by, default_include_dependencies entries
- tests: 9 new TDD tests (pattern_or, case_sensitive, source_search, summary_alias,
  config_compact, config_sort_by, trace_qualified_name, regression)

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-05 00:40:56 -04:00
Andrew Hundt 6026fa6647 fix(mcp): accept sort_by calls/linkrank, auto-convert glob wildcards, fuzzy trace_call_path
Three bugs fixed in MCP tool validation layer:

1. sort_by: validator only checked 3 of 5 valid values (relevance/name/degree).
   Added missing strcmp for 'calls' and 'linkrank' at mcp.c:1625-1627.

2. name_pattern/qn_pattern: glob wildcards (*tool*) rejected as invalid regex.
   On regex compile failure, glob_to_regex() converts bare * -> .* and ? -> .
   before retrying. Valid regex patterns have zero overhead (no conversion).
   Applied to both name_pattern and qn_pattern validation blocks.

3. trace_call_path: exact case-sensitive name match too strict.
   On node_count==0, fallback to cbm_store_search with case_sensitive=false
   and min/max_degree=-1 (no degree filter). Uses found result's project for
   the re-query to handle NULL project correctly. Zero overhead on exact hit.

TDD: 10 new tests in test_input_validation.c covering all three bugs:
- f6_sort_by_calls_accepted, f6_sort_by_linkrank_accepted
- f9_glob_star_autoconverted, f9_glob_question_autoconverted,
  f9_valid_regex_still_works, f9_truly_invalid_pattern_still_errors,
  f9_qn_pattern_glob_autoconverted
- trace_case_mismatch_finds_via_fallback, trace_exact_match_still_works,
  trace_truly_missing_still_errors

All 2252 tests pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-03 21:15:01 -04:00
Andrew Hundt a775040358 fix(mcp,store,pagerank,pipeline): 18 bugs fixed, DF-1 degree precompute, pass_normalize, 11 TDD tests
Phases 1-8 from comprehensive plan (notes/2026-03-26-0013-plan-*.md):

Phase 1 — Input validation (F1,F4,F6,F7,F9,F10,F15):
  mcp.c: empty label→NULL, limit≤0→default, sort_by/mode enum validation,
  regex pre-validation via cbm_regcomp, depth clamp, direction validation

Phase 2 — B7 Cypher param fix + CQ-2 project expansion:
  mcp.c:handle_query_graph reads "cypher" first with "query" fallback,
  uses resolve_project_store for "self"/"dep"/path shortcuts

Phase 3 — DRY resolve_project_store in 5 handlers:
  handle_get_graph_schema, handle_index_status, handle_get_architecture,
  handle_get_code_snippet, handle_index_dependencies

Phase 4 — DF-1 degree precompute (100× faster queries):
  store.c: node_degree table DDL, search SELECT uses LEFT JOIN with
  HC-6 fallback to edge COUNT, cbm_store_node_degree reads precomputed
  table, arch_hotspots uses nd.calls_in, arch_boundaries adds behavioral types
  pagerank.c: is_calls field, degree accumulation during edge iteration,
  node_degree batch INSERT after LinkRank, OOM-safe allocations

Phase 5 — B2/B5 name-based caller fallback:
  pass_calls.c: 3-step resolution (exact QN → shared helper → Module)
  graph_buffer.c: cbm_gbuf_resolve_by_name_in_file DRY helper (HC-1)

Phase 6 — B17/B13 class-method edge repair:
  NEW pass_normalize.c: enforces I2 (Method→Class) and I3 (Field→Class)
  invariants via QN prefix + name+label+file fallback. O(M+F) runtime.
  pipeline.c: normalize pass before dump. Makefile.cbm updated.

Phase 7 — CBMLangSpec section_node_types field:
  lang_specs.h: added section_node_types (17th field)
  lang_specs.c: all 64 language specs updated with NULL initializer

Phase 8 — IX-1..3 indexing pathway fixes:
  mcp.c: autoindex_failed + just_autoindexed flags in server struct,
  REQUIRE_STORE captures pipeline return code, build_resource_status
  shows "indexing" state + failure detail + action_required hints

Additional fixes:
  G1: summary mode adds results=[] + results_suppressed=true
  CQ-3: Cypher + filter params produces warning

Tests: 2238 pass (11 new in test_input_validation.c covering F1,F6,F9,
F10,F15 edge cases, G1, CQ-3, IX-2). Updated test_store_nodes.c for
total degree. Updated test_token_reduction.c for G1 results key.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-03-26 01:59:37 -04:00