Commit Graph
11 Commits
Author SHA1 Message Date
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