Repository navigation
fix(mcp): match search_code file_pattern with a directory against the repo-relative path - #7
halindrome wants to merge 56 commits into
Conversation
cbm_store_fetch_call_edges, cbm_leiden/cbm_louvain, and
cbm_layout_to_json document caller-owned memory ("caller frees
both" / "the caller frees it" / "Caller must free()") but exported
no matching free function, unlike the rest of the API
(cbm_store_find_nodes -> cbm_store_free_nodes,
cbm_layout_compute -> cbm_layout_free, etc.). From C a bare free()
works via static linking, but a caller across an FFI boundary (e.g.
C# P/Invoke) has no way to reach the DLL's internal free() unless
it is exported by name.
Adds three paired free functions following the existing pattern:
- cbm_store_free_call_edges(src, tgt)
- cbm_leiden_free(result)
- cbm_layout_free_json(json)
Updates the one internal production caller (mcp.c SCC analysis) to
use cbm_store_free_call_edges instead of a bare free(), and the
existing unit tests for cbm_leiden/cbm_louvain/cbm_layout_to_json
to use the new paired frees. Adds fetch_call_edges_basic,
fetch_call_edges_truncated, and fetch_call_edges_empty_project to
test_store_arch.c, covering cbm_store_fetch_call_edges /
cbm_store_free_call_edges, which had no prior test coverage.
Fixes DeusData#1762
Signed-off-by: Jumaga2015 <jumaga2015@gmail.com>
Signed-off-by: lorenzozanee <wyz0707@proton.me>
A record's accessors are implicit, so `record Point(double x, double y, double z)` declares no `x()` and extraction emitted no node for one. The call `p.x()` had nothing to resolve to, fell through to the short-name registry, and bound to the first `x` in the project — typically an unrelated class's private field. Measured on two indexed Java trees, that was 278 of 2444 and 2649 of 29509 CALLS edges. Extraction now emits one Method definition per record component, skipping any accessor the record declares itself (JLS 8.10.3), so the accessor has a node and the call resolves to it. And a Java call may no longer bind to a Variable or a Field. A Java call expression never names data, so such an edge is a spelling collision, not a call. The guard follows cbm_go_suppress_bare_field_ref — a pure, label-keyed predicate — and is called from both pass_calls.c and pass_parallel.c so the sequential and parallel resolvers stay identical. Java-gated: Kotlin properties hold function values and C has function pointers, so both legitimately call through data. Signed-off-by: MopicMP <obshiq123@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…y build The shared tier-2 registry registered Python defs one at a time without a mid-build finalize, so every Method's receiver probe scanned all registered types linearly: O(methods x types), single-threaded in lsp_cross_prepare. Keep the per-def registration order and back only the existence probe with a cbm_ht set of type QNs, as the Go and Rust builders do; the registry is identical by construction. On the Python 3.14 stdlib + site-packages the builder drops from 67-75 s to 0.20-0.25 s, with a byte-identical graph. Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
Brings the branch up to main at a222715. Two conflicts, both resolved by keeping both sides: - src/ui/layout3d.h: the new cbm_layout_free_json declaration sits before main's region-level layout section. - tests/test_store_arch.c: the Leiden/Louvain tests release their results through the new cbm_leiden_free; main's leiden_result_memory_core_ownership test is kept. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The three exported release functions added for DeusData#1762 freed raw heap blocks. Since the memory-core ratchet landed, the allocating side has to go through the core as well, and on current main cbm_leiden already allocates its result in CBM_MEM_CLASS_OTHER, so a raw free() in cbm_leiden_free left the class counters drifting. - cbm_store_fetch_call_edges: allocate and grow the source/target arrays with cbm_alloc/cbm_realloc in CBM_MEM_CLASS_STORE. An allocation failure now finalizes the statement, releases both arrays and returns CBM_STORE_ERR instead of writing through a NULL array. - cbm_store_free_call_edges, cbm_leiden_free and cbm_layout_free_json release with cbm_free in the class the matching allocation used. - cbm_layout_to_json writes its output through cbm_json_writer_allocator, so the string is an OTHER-class core block; the UI server's five release sites now call cbm_layout_free_json. - The header comments name the paired free instead of "caller frees". - Tests: ownership checks for all three pairs measure the class counters across the allocating call and the paired free, so a raw allocation on either side fails; the call-edge check grows past the initial capacity. Refs DeusData#1762 Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings DeusData#2420 (O(1) type membership in the shared Python cross-registry build, DeusData#2413) up to date with main a222715. Conflicts in internal/cbm/lsp/py_lsp.c, resolved as follows: - Includes: main already includes src/foundation/hash_table.h (module re-exports) and added lsp_node_iter.h. Main's lines are kept; the branch's duplicate include is dropped. - py_register_lsp_defs: main changed it to return bool (false when the package scope cannot be normalized). The merged signature keeps main's bool return and adds this branch's PyKnownTypes *known parameter. - cbm_run_py_lsp_cross_status: main's failure handling is kept and passes known = NULL, as the branch did for the per-file path. - cbm_py_build_cross_registry: main added an early `return NULL` when a def fails to register. The merged loop passes &known and frees the type-QN set before that early return, so the set does not leak on the failure path. - The test seam cbm_py_lsp_test_register_defs (added on main) passes known = NULL. tests/test_py_lsp.c and tests/test_py_lsp_scale.c merged cleanly. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Replace the wall-clock ratio test pylsp_scale_cross_registry_build_linear (py_lsp_scale suite) with pylsp_cross_registry_build_work_is_linear in the py_lsp suite. The new test builds the same generated input (n classes, each followed by two methods, in extraction order) and reads the seam-build CBM_LSP_WORK counter instead of a clock, so its verdict does not depend on host load, and it runs in PR CI, which skips the perf suites. 4x the classes must cost at most ~4x the counted work. With the type-QN set the build examines no registry candidates (0 at 1000 and 4000 classes); with the scanning receiver probe the work grows from 2,809,000 to 23,236,000 and the test fails. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings DeusData#2244 (agent detection through the UTF-8 path wrapper) up to date with main a222715. The branch was based on 45c45be, 1,323 commits behind. The merge is clean. The merged tree adds exactly this branch's 22 lines to main and removes 6, in src/cli/cli.c and tests/test_cli.c. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The Windows regression test for DeusData#2145 created its profile directory with test_mkdirp and read the result as a boolean. test_mkdirp returns 0 on success, so the test failed as soon as the directory existed and never reached the detection it was written to check. Inverting the check is not enough on its own. test_mkdirp uses cbm_mkdir, which is the narrow _mkdir on Windows. Under an ANSI code page such as 1252 it does not create the intended name: it creates a directory whose name is the UTF-8 bytes read as code-page characters. The old narrow stat finds that mangled directory and the new wide lookup correctly does not, so the test would pass without the fix and fail with it. th_mkdir_p goes through the wide API and creates the real name, the way a non-ASCII Windows profile actually looks. Since 2b329fe, Claude Code is detected from settings.json, .claude.json or the claude CLI, through cbm_file_exists, which is already wide on Windows; a bare .claude directory no longer marks it as detected. The test now asserts Cursor, which is detected by its directory alone, so the assertion goes through dir_exists, the function this branch changes. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Make this repository a Claude Code plugin marketplace so users can find and add the server from inside Claude Code: claude plugin marketplace add DeusData/codebase-memory-mcp claude plugin install codebase-memory@codebase-memory This is slice 1 of DeusData#1188: discovery only. The plugin registers the MCP server and nothing else; the skill, the graph agents and the context hooks still come from `codebase-memory-mcp install`. - .claude-plugin/marketplace.json: one plugin, sourced from ./plugin. - plugin/.claude-plugin/plugin.json: version pinned to the current release. Without a pinned version every commit to main would be a new plugin version for installed users. - plugin/.mcp.json: stdio server `npx -y codebase-memory-mcp@0.12.1`. The pin keeps main from advertising a server version npm does not have; the npm package downloads the release binary on first start and verifies it against the release checksums. - .gitignore: the root .mcp.json stays ignored; plugin/.mcp.json is published. - README: install section, and "use one or the other": with both the plugin and `install`, Claude Code runs the server twice under two tool namespaces. - tests/test_version_metadata_contract.sh: both plugin files are release-tracking surfaces; a new claim form reads the npm package spec (`codebase-memory-mcp@X.Y.Z`), and the unregistered-surface sweep now covers plugin/ and .claude-plugin/. The post-release sync that bumps server.json must bump these pins too, or this contract goes red. No language count is stated anywhere in the plugin metadata, so the language-count contract has nothing new to track. Refs DeusData#1188 Co-authored-by: Kody <kaidi.shi.1121@gmail.com> Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Preserve the current C/C++ field-binding policy while integrating the Java record accessor change. Handle varargs record components with array return types and reject data targets before sequential LSP call emission, matching the parallel path. Add focused regressions for both cases. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Pick up the shared CI contract repair while preserving the approved plugin changes. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
A record's implicit accessors are members the compiler writes and the source never spells. Their nodes now carry the property generated=record, and every registry registration path files them in the exact (qualified-name) map only, never in the short-name index. The Java type-aware resolver still binds p.y() on a Point to Point.y; a call such as v.y() on a type from outside the project no longer binds the accessor through a unique_name / suffix_match / field_type_hint / fuzzy guess. - extract_defs.c: each implicit accessor carries extra_props generated=record (key CBM_PROP_GENERATED, cbm.h). - registry.c: cbm_registry_add_qn_only, and the pure predicates cbm_def_props_generated / cbm_node_props_generated. - pass_definitions.c, pass_parallel.c, pipeline_incremental.c: the three registration sites route a generated member to the exact map only. - pipeline_delta.c: the incremental preseed proxy keeps the mark, so an incremental re-index registers the member exactly as a full one does (the proxy carried only a few properties and the accessor came back into the short-name index). Tests: pipeline_java_record_accessors_generated_qn_only (sequential, parallel and incremental venues) and two registry unit tests. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The import resolver's symbol-name fallback tries the trailing segments of an import path that names no project symbol, and a package segment that merely spells a record accessor bound the import to it. On elasticsearch, `import javax.crypto.spec.PBEKeySpec` bound a record's spec() accessor, and `new PBEKeySpec(...)`, annotations and type uses then followed the import onto it: 6,302 IMPORTS edges into accessors, plus CALLS, USAGE, DECORATES and THROWS through the import map. A generated member is registered by qualified name only, and no Java import can name an instance accessor, so the fallback now skips it exactly as it skips a Python def-local (DeusData#1548). The record pipeline fixture gains an import whose package segment spells an accessor. Without the change it fails in the sequential, parallel and incremental venues: an IMPORTS edge into Point.y, and make() -> Point.y. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
When a JVM LSP callee has no exact node and no Class.method tail match, cbm_pipeline_lsp_target_node takes the one project callable with that leaf name. A record accessor became such a callable: on elasticsearch `Objects.nonNull(x)` (37 CALLS) and `filter(Objects::nonNull)` (230 CALL_REFERENCE) bound a test record's nonNull() accessor. A generated member is registered by qualified name only. It still joins through its owner's Class.method tail, but the leaf-name fallback no longer considers it, either as the unique callable or as a rival that makes a same-named project callable ambiguous, so every other bind is decided exactly as before the accessors existed. The record pipeline fixture gains a record Seen(boolean nonNull) with `Objects.nonNull(o)` and `filter(Objects::nonNull)` callers. Without the change it fails in the sequential, parallel and incremental venues. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Windows headers define near and far as empty macros, so the locals named near and far in the generated-accessor pipeline test did not compile on the Windows leg. Rename them to near_m and far_m; the node names stay. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Conflict in src/pipeline/pass_calls.c: DeusData#2305 moved the inline LSP-target emission of resolve_single_call into calls_emit_lsp_target() and added the unresolved-call evidence around emit_classified_edge. The Java data-member veto of this branch is ported into that structure: - calls_emit_lsp_target: the veto runs once the target node is known, before the resolution, the evidence and any spawn or edge. It returns true (handled), so as before no edge is emitted and no registry fallback runs after it. - resolve_single_call: the veto stays in front of main's candidate_qn/calls_emitted bookkeeping, matching the parallel resolver, whose veto also precedes that bookkeeping. pass_parallel.c and the remaining files merged cleanly. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Recognize explicit C-family code in .inc files before extraction while preserving BitBake recipe bodies and ObjectScript markers. Exercise disk and byte classifiers, callback/linkage extraction and fast/full indexing. Signed-off-by: knewstimek <knewstimek@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
QA Round 1Scope: Contract Verification
Finding 1 — path_filter hint claims file_pattern "matched nothing" when files matched but the search text did not
Finding 2 — Hint rendering in the default tree/toon output is untested
Summary
No blocking findings. Contract: 6 criteria pass. 1 is partial, because suite-green is left to CI. ObservationsNone. SAST review not applicableTarget QA performed by Claude Code (claude-opus-5-5), manager + 3-lens panel Dispositions (orchestrator, Step 3B)
Verification
QA-Fix-Commit: 73ff216 Posted by the developer account (QA-agent token unavailable). Review by Claude Opus 5.5 (Anthropic) in Claude Code via qa-panel. |
QA Round 2Scope: Contract Verification
Summary
No new findings. The round-1 fix ( Observations
SAST review not applicableTarget QA performed by Claude Code (claude-opus-5-5), manager + 3-lens panel Cycle outcome: round 2 is clean. There are no findings, all contract criteria pass, and the round-1 fix is reviewed. Carried forward: no tree/toon-format test for the hint. Not approved: no QA-agent token is available, and approval is the human reviewer's call. Posted by the developer account (QA-agent token unavailable). Review by Claude Opus 5.5 (Anthropic) in Claude Code via qa-panel. |
… repo-relative path search_code matched file_pattern against the file basename only, so any pattern containing a directory (src/cli/*.c, apps/web/src/**/*.ts) silently returned 0 results, while search_graph's file_pattern matches the full repo-relative path. A file_pattern containing '/' is now matched in C against the repo-relative path of every indexed file, on all platforms, with search_graph-equivalent semantics: '*' and '**' match across '/', '**/' also matches zero directories, '?' matches one character, and a pattern without wildcards is a substring match. Patterns without '/' keep the existing basename behaviour. On Windows such patterns are scanned in process from the already-filtered list instead of the PowerShell -like post-filter. A search with a '/' pattern that returns nothing now carries a hint explaining whole-path matching and pointing at path_filter, and the schema describes both parameters. Refs DeusData#2836 Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
73ff216 to
5a666f2
Compare
…e-agent-detection fix(cli): support non-ASCII Windows agent paths
…-functions fix(store,ui): export paired free functions for FFI callers (DeusData#1762)
…n-marketplace feat(plugin): Claude Code plugin marketplace, discovery slice (DeusData#1188 slice 1)
…r-calls fix(java): resolve record accessor calls, and never bind a call to data
…gistry-index perf(py-lsp): O(1) type membership in the shared Python cross-registry build
…ting fix(discover): select C++ for C-family .inc fragments
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Summary
search_codematchedfile_patternagainst the file basename only. Any pattern containing a directory, such assrc/cli/*.corapps/web/src/**/*.ts, silently returned 0 results, whilesearch_graphmatches the same pattern against the full repo-relative path.Upstream issue: DeusData#2836
Change (
src/mcp/mcp.c)search_code_path_glob_match. It runs in linear time and doesn't usefnmatch, which Windows lacks.*and**match across/, and**/also matches zero directories.?matches one character.search_graph.write_scoped_filelist: afile_patterncontaining/is matched against the repo-relative path on every platform. Windows separators are normalised first. Patterns without/keep the existing basename behaviour./patterns are scanned in process from the already-filtered list, not by the PowerShell-likepost-filter.cbm_search_code_file_pattern_can_prefilteralready rejects/, so nothing is filtered twice./pattern yields zero matches, the response carries ahintpointing atpath_filter, in both tree and JSON output.file_pattern("Glob on file name; with '/', on repo-relative path (** = any dirs).") andpath_filter("Regex on repo-relative path.").Acceptance criteria
file_patterncontaining/is matched against the repo-relative path, with**crossing directories (including zero directories).file_patternwithout/keeps the current basename semantics./pattern that yields 0 results returns a hint pointing atpath_filter.file_patternandpath_filter.mcpsuite tests stay green.Known limits
find -name, so a/pattern there returns 0. The hint still fires.*crosses/, and matching is case-sensitive. Both are consistent withsearch_graphand with the existing basename match.Testing
search_code_file_pattern_matches_repo_relative_path_issue2836intests/test_mcp.c, with fixture filessrc/main.candsrc/cli/watcher.c:src/cli/*.c→ 1 matchsrc/**/*.c→ 2**/cli/*.c→ 1src/cli/→ 1watcher.c→ 1*.c→ 2lib/*.c→ 0, with the hintmcp.c, the new test fails, because the four directory patterns return 0 and there is no hint.scripts/test.sh --suites mcp: 397 passed, 4 skipped, 0 failed.🤖 Generated with Claude Code
https://claude.ai/code/session_01CDDUoWEZMNZ9fLvzp2pPGT