Commit Graph
51 Commits
Author SHA1 Message Date
Martin VogelandMatt Van Horn bb7f2246d6 fix(discover): .cbmignore negation can un-skip built-in dirs (safety core excluded)
A .cbmignore negation pattern (e.g. "!obj/") whose rule is the last
match for a directory now un-skips built-in skip-list dirs, so projects
can index sources that live under names like obj/, target/, or docs/
(issue #500). Distills the design and test intents of PR #543 onto the
current 3-state cbm_gitignore_match_result API: instead of a second
inverted-polarity matcher, should_skip_directory consults the existing
last-match-wins result (<0 = negated) inside the built-in branch and
falls through so .gitignore/global/local rules still apply.

Non-negatable safety core: .git, node_modules, and the worktree-internal
dirs (.worktrees, .claude-worktrees) can never be un-skipped — a
repo-committed .cbmignore must not defeat OOM/safety skips (#489, #802).
Un-skipped dirs are walked normally and therefore no longer appear in
the excluded-subtree list (#411 coherence).

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-03 19:25:00 +02:00
Martin Vogel e82f6da0a9 fix(discover): use cbm_fopen for worktree gitlink + commondir reads
Follow-up to #683 (@Naam). The worktree info/exclude fix read the `.git` gitlink
file and its `commondir` with raw fopen(); on Windows raw fopen uses the ANSI
code page and fails to open non-ASCII (e.g. CJK) repo paths, so a worktree
indexed under such a path would silently not honor info/exclude /
core.excludesFile. Switch both reads to cbm_fopen() (the UTF-8 -> _wfopen
wrapper), matching the rest of discover.c (e.g. read_core_excludes_file at 248).

No behavior change on POSIX (raw fopen already opens UTF-8 byte paths there);
this is a Windows-only correctness/consistency fix, verified by the full test
suite staying green and cross-platform CI. discover.c now has no raw fopen.

Refs #682, #683

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-02 00:40:31 +02:00
Martin Vogel 46c11ff189 Merge pull request #683 from Naam/fix/worktree-info-exclude
fix(discover): honor info/exclude and core.excludesFile in linked worktrees
2026-07-02 00:31:29 +02:00
ShauryaaSharma 98218a282b fix(windows): use _wfopen for non-ASCII project paths (#700)
Standard fopen on Windows uses the ANSI/system codepage and silently
fails to open files whose absolute path contains non-ASCII characters
(accented, CJK, etc.). This causes the pipeline to skip parsing those
files entirely — File nodes are created by directory traversal (which
already uses wide Win32 APIs) but tree-sitter never receives the source,
so no Function/Class/Method nodes are extracted.

Add cbm_fopen(path, mode) to compat_fs:
- Windows: converts the UTF-8 path to UTF-16LE via cbm_utf8_to_wide
  (existing helper in win_utf8.h) and calls _wfopen.
- POSIX: delegates to fopen (no-op change, paths are already UTF-8).

Replace every fopen call that operates on user project file paths:
- src/pipeline/pass_parallel.c    (primary indexing path)
- src/pipeline/pass_definitions.c
- src/pipeline/pass_calls.c
- src/pipeline/pass_semantic.c
- src/pipeline/pass_usages.c
- src/pipeline/pass_lsp_cross.c
- src/pipeline/pass_pkgmap.c
- src/pipeline/pass_k8s.c
- src/pipeline/pass_envscan.c
- src/pipeline/path_alias.c       (tsconfig detection)
- src/pipeline/artifact.c
- src/discover/language.c         (content-sniff for language detection)
- src/discover/gitignore.c
- src/discover/discover.c
- src/discover/userconfig.c
- src/mcp/mcp.c                   (read_file_lines, adr.md reader)

Fixes #700

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
2026-06-30 01:03:09 +05:30
Nahim El Atmani f1b93829dc fix(discover): honor info/exclude and excludesfile in linked worktrees
In a linked worktree, <repo>/.git is a regular file ("gitdir: <path>")
rather than a directory. cbm_discover_ex gated all per-clone ignore
sources on .git being a directory, so inside a worktree it skipped
.git/info/exclude and the global core.excludesfile entirely and walked
into directories the user had excluded there (e.g. build/, twister-out/).
Committed .gitignore still applied via the issue #510 path, which is why
the gap was easy to miss.

Resolve the git common dir up front: when .git is a gitlink file, parse
'gitdir:', then follow <gitdir>/commondir to the shared dir where git
keeps info/exclude and config (falling back to the gitdir when no
commondir file is present). info/exclude and config are then read from
that common dir, so a worktree behaves like an ordinary checkout.

Adds discover_worktree_info_exclude (fails before this change) and
discover_worktree_committed_gitignore.

Signed-off-by: Nahim El Atmani <2959826+Naam@users.noreply.github.com>
2026-06-28 21:05:07 -07:00
Martin Vogel 3f754cf449 fix: 17 reproduce-first issue + new-bug fixes (per-case verified)
Each was RED (reproduce-first). Verdicts: production bug unless noted.

- #510 nested .gitignore: load root .gitignore unconditionally (discover.c).
- #403 IDE dir: add "Antigravity" to ALWAYS_SKIP_DIRS.
- #571 CJK project name: transliterate non-ASCII bytes to hex in fqn.c (was -> "-").
- #520 detect_changes: include untracked files (git status --porcelain -uno) (mcp.c).
- #363 CBM_MEM_BUDGET_MB env override (mem.c).
- #570 Codex dual-hook: write the hook to hooks.json when present (cli.c).
- #431 VSCode per-profile mcp.json (cli.c).
- #521 routes from config URLs: deny upstream/jwks/healthcheck/... keys (pipeline.c).
- #548 D:\ browse: normalize sep before is_dir + drive-root self-parent (http_server.c);
  TEST A was a broken fixture (called is_dir on backslash) -> corrected to the
  production sequence; assertion unchanged.
- #495 Rust cfg-gated twins: fold #[cfg(...)] into the QN (extract_defs.c).
- new_py_tuple_unpack: handle pattern_list/tuple_pattern LHS -> Variable per id.
- new_ts_class_field_arrow: class arrow-field -> Method + scope (extract_defs.c).
- repro_extraction/#554 C++ out-of-line: compute_func_qn class-qualifies the call's
  enclosing QN via cbm_cpp_out_of_line_parent_class (extract_unified.c + helpers.h).
- #523 cross-repo HTTP_CALLS: emit service-pattern edge with target=NULL when the
  client lib is unindexed (pass_calls.c, pass_parallel.c).
- #514 data_flow arg expressions: serialize "args" on CALLS edges + propagate
  through store BFS + mcp data_flow (pass_calls.c, store.h/.c, mcp.c).
- #627 query_graph: size_t malloc + NULL-check; OPTIONAL MATCH from bound terminal
  so WHERE c IS NULL returns orphans correctly (cypher.c).
- #546/#480 trace_path: union BFS across same-name nodes (.d.ts dup) (mcp.c).

DEFERRED (need a maintainer decision, untouched): #56 cross-crate Rust (needs Cargo
workspace plumbing / Rust cross-LSP — architectural), #607 reinstall-deletes-index
(cbm_remove_indexes is shared with uninstall/update where deletion is intended).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-27 11:59:59 +02:00
Martin Vogel 9c17b79aba fix(discover): skip .claude-worktrees during indexing
QUALITY_ANALYSIS gap #1: .claude-worktrees was indexed (nearly tripling this
repo's indexed surface + polluting queries with stale duplicate symbols).
Discovery skipped .claude and .worktrees but not .claude-worktrees. Add it to
ALWAYS_SKIP_DIRS. Flips the discovery-hygiene invariant green.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 22:10:02 +02:00
Martin Vogel 090a1a72d5 Merge pull request #542 from mvanhorn/fix/499-global-core-excludesfile-ignore-source
fix: honor git global core.excludesFile as an ignore source
2026-06-23 23:46:24 +02:00
Martin Vogel 5ea5579d2b fix(discover): make cbm_gitignore_merge atomic on allocation failure
cbm_gitignore_merge copied src patterns into dst one strdup at a time and
returned void on failure, leaving dst with a partial set of patterns and no
signal to the caller. If the dropped pattern was the critical exclude (the
.git/info/exclude entry that prevents an OOM walk), the bug could silently
recur with some patterns applied and others dropped.

Make the merge atomic: on any allocation failure roll back the partial copies
so dst is left exactly as it was, and return bool so callers can tell. The
.gitignore caller now degrades gracefully (exclude patterns skipped, as if the
file were absent) instead of ending up with a half-merged matcher.

Adds a reproduce-first test that injects a strdup failure mid-merge via a test
seam and asserts dst is unchanged; it is red without the rollback.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-22 23:51:49 +02:00
Matt Van Horn 9f66e428f8 fix: resolve core.excludesFile only from user-owned global git configs
Drop the repo-local .git/config pass in resolve_global_excludes_path() so
an untrusted repo cannot point core.excludesFile at an arbitrary file (e.g.
~/.ssh/id_rsa) and have cbm open and read it merely by being indexed. git is
safe here because it only trusts a repo's .git/config after a deliberate
clone/init; the indexer has no such trust step. Global excludes now resolve
only from the user-owned ~/.gitconfig and $XDG_CONFIG_HOME/git/config. A
repo's own ignores remain handled by .gitignore / .cbmignore.

Add a guard test asserting a repo-local .git/config whose excludesFile points
outside the repo is ignored, not read.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-06-22 14:39:26 -07:00
Martin Vogel 0ad4d2d029 Merge pull request #448 from isc-tdyar/fix/deploy-dir-always-skip
fix(discover): deploy/ directories indexed as duplicate source in full mode
2026-06-22 23:27:09 +02:00
Shaurya 11009f2d94 fix(discover): honor .git/info/exclude during file discovery (#493)
* fix(discover): honor .git/info/exclude during file discovery

index_repository only loaded .gitignore when building the exclusion
matcher. .git/info/exclude — the per-clone exclude file that Git treats
as authoritative — was never read, so paths excluded only there were
walked in full. On repos with Sandcastle worktrees this caused the
indexer to traverse 661K files / 170 GB and OOM (issue #489).

Add cbm_gitignore_merge() to append patterns from one matcher into
another. In cbm_discover_ex, after loading .gitignore, also load
.git/info/exclude and merge its patterns in. If only the exclude file
exists, use it directly. No downstream call paths change.

Add three unit tests for cbm_gitignore_merge and two integration tests
that reproduce the issue scenario (exclude-only and exclude-stacked-
with-gitignore).

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>

* style(discover): fix clang-format alignment in cbm_gitignore_merge

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>

---------

Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
2026-06-22 22:04:42 +02:00
Matt Van Horn 3975d7950d fix: honor git global core.excludesFile as an ignore source
Implements 2026-06-20-013-fix-global-core-excludesfile-ignore-source.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-06-20 18:48:47 -07:00
Thomas Dyar bfd53f33ec fix(discover): deploy/ directories indexed as duplicate source in full mode
deploy/ and deployed/ build artifact directories were in FAST_SKIP_DIRS,
which only applies in fast/moderate indexing modes. In the default full
mode they were indexed alongside source files, creating duplicate
Class/Method rows with stale data. Any MATCH (c:Class {name:'Foo'})
without a file_path filter returned multiple rows — one from source,
one from the build copy.

Move deploy and deployed to ALWAYS_SKIP_DIRS so they are excluded in all
indexing modes.

Regression test: discover_deploy_excluded_full_mode in test_discover.c.

Signed-off-by: Thomas Dyar <tdyar@intersystems.com>
2026-06-12 14:55:46 -04:00
Martin Vogel 3ee9d5d112 Drop the nim grammar and refresh language counts
Removes the heaviest vendored grammar (66 MB) along with its language
wiring and test fixtures; the supported-language count moves to 158
across README, site, and manifest. The grammar manifest additionally
records the canonical-source decisions for the five
registry-disagreement grammars and the per-directory license files
restored earlier this week.
2026-06-12 02:17:39 +02:00
Martin Vogel 861b9d917e Resolve grammar-only IMPORTS edges for sibling-file imports
pass_pkgmap.c: sibling-file resolution (direct, SCSS _partial, meson subdir/meson.build). language.c: map .just/.inc extensions. extract_imports.c: strip quotes from path imports (pony/func), parse_meson_imports (subdir()), generalized lisp parser for emacslisp/fennel/commonlisp node kinds. Fixes scss/just/bitbake/func/pony/emacslisp/meson import edges.
2026-06-08 14:24:39 +02:00
Martin Vogel 2d772b01bf Fix extraction/resolution across languages: routing, Go methods, cross-LSP, inheritance, constructors, node kinds
WIP checkpoint (local). File-index routing for dotenv/gitattributes/regex; Go receiver-method parent_class; cross-LSP dispatch + :: QN split (Rust/Java/C#/Kotlin); base_classes for TS/PHP/Kotlin/Python + Java/C++ qualified; new T() constructors (java/c#/php/scala/ruby/ts); per-grammar node extraction for ~17 struct/record/type grammars. Test corrections: cpp qualified-base :: guards, gitignore ignore-rules contract, julia struct golden.
2026-06-07 22:56:12 +02:00
Martin Vogel ba52d6ba56 Report excluded subtrees in the index_repository result (#411)
index_repository silently dropped entire subtrees (ALWAYS_SKIP dirs in all modes; FAST_SKIP dirs like tools/scripts/bin in moderate/fast) with no record — users couldn't tell what wasn't indexed. The discover walk now records skipped directory rel-paths; cbm_discover_ex returns them; the pipeline holds + exposes them; and the index response carries a compact summary: "excluded":{"dirs":[up to 25],"count":N,"truncated":bool}. Behavior-neutral for indexing. Adds a reproduction in test_integration.c.
2026-06-06 22:16:04 +02:00
Martin Vogel a8fc901129 feat(helm): extract Helm/Go-template named templates and include calls
Helm chart helpers (.tpl) and Go templates produced no semantic graph — just a
flat key flood. Wire the existing gotemplate grammar for real structure:

- .tpl files map to the gotemplate language (Helm _helpers.tpl).
- {{ define "chart.fullname" }} becomes a Function node named after the
  template (the name is a string literal, not an identifier, so it's resolved
  in a dedicated walker branch).
- {{ include "x" . }} and {{ template "x" . }} resolve to the referenced
  template name instead of the bare `include` builtin, so they form CALLS
  edges to the define'd Function. template_action is added to the call set.

Lets you trace which charts include which named templates. Tests cover define
-> Function and include/template -> CALLS, plus the .tpl mapping.

Refs #338
2026-06-01 18:41:49 +02:00
Martin Vogel 1e1d40851c feat(lang): add CFML / ColdFusion support (cfscript + tag dialects)
Vendor the MIT-licensed cfmleditor/tree-sitter-cfml grammars and wire both
CFML dialects through the pipeline:

- .cfc components -> cfscript grammar (CBM_LANG_CFSCRIPT): a JS-like grammar, so
  function/method declarations, calls and imports extract with the shared JS/TS
  arrays. Components' functions become Function nodes.
- .cfm templates -> cfml tag grammar (CBM_LANG_CFML): tag-based (HTML-derived).
  Embedded <cfscript> functions extract via function_declaration; tag-based
  <cffunction name="..."> nodes are handled by a dedicated walker branch that
  reads the name attribute (cf_function_tag has no name field). Shared scanner
  headers are vendored at vendored/common/ so the grammar's ../../common/
  includes resolve without editing vendored sources.

Adds an ASCII case-insensitive compare for CFML's case-insensitive attributes.
Tests cover both dialects and the extension mapping. LICENSEs vendored.

Closes #38
2026-06-01 18:28:36 +02:00
Martin Vogel f16f9dcf01 feat(lang): add Qt QML language support
Vendor the MIT-licensed tree-sitter-qmljs grammar (a TypeScript superset with
declarative ui_* nodes) and wire .qml files through the full pipeline: language
enum, extension + display-name mapping, grammar shim, and a language spec that
reuses the JS/TS function/call/branch arrays and adds QML class/field/import
types (ui_inline_component, ui_property, ui_signal, ui_import).

QML files now produce Function nodes (incl. JS functions inside QML objects),
class/interface/enum nodes, imports and CALLS edges, so call chains through a
Qt project's .qml files can be traced. LICENSE vendored alongside the grammar.

Closes #42
2026-06-01 18:07:05 +02:00
Martin Vogel db6e99c0ea fix(discover): map .blade.php to Blade by default (#258)
Laravel Blade templates (*.blade.php) fell through to the single-extension
lookup and were mis-classified as PHP, so the PHP extractor tried to parse
Blade syntax. Add a built-in compound-extension table (checked before the
single-extension fallback and alongside user config) mapping .blade.php to
CBM_LANG_BLADE, so Blade files are discovered and parsed with the Blade
grammar. Combined with the now-non-blocking discovery gate (c29e6d5), this
resolves the "agent stuck on Blade files" report. Plain .php is unaffected.
2026-05-31 21:15:57 +02:00
Martin Vogel 2de984bdb6 fix(discover): map .mjs/.cjs (JS) and .mts/.cts (TS) extensions (#197)
.mjs (ES modules) and .cjs (CommonJS) were absent from the extension
table, so those files were never assigned a language — they weren't
indexed (no graph nodes) and search_code returned 0 for any pattern in
them. Add .mjs/.cjs → JavaScript and .mts/.cts → TypeScript.

Verified end-to-end: a function in a .mjs file is now extracted and found
by search_code. Closes #197.
2026-05-31 12:10:29 +02:00
Ivan Porto Carrero 9e2bb92895 fix(arena): NULL-arena guard in cbm_arena_alloc; skip vendored/ dirs
- cbm_arena_alloc returns NULL on a NULL arena (both arena.c copies)
  instead of dereferencing it — defense-in-depth against the NULL-arena
  type-allocation path that could crash the LSP type layer.
- discover: add "vendored" to the always-skip directory list.

Distilled from #374, taking the parts not already on main. The PR's
ts_lsp tuple-arena allocation, the C/C++ template formal-count clamp,
and the type_rep unbound-param preservation it also carried all landed
independently in v0.7.0 / via #322 / #360, so only these two
defensive improvements remain. Relates to #390.
2026-05-30 18:26:44 +02:00
jjserenity 2389d82214 fix(windows): use wide-char API for non-ASCII path support (#386)
Replace ANSI Windows APIs (FindFirstFileA, CreateFileA, etc.) with
wide-char (W) counterparts. Add UTF-8/UTF-16 conversion helpers.
Fix stat() calls in discover pipeline to use _wstat64.

Fixes #357
2026-05-30 18:05:03 +02:00
Martin Vogel bb1411620a refactor(pipeline): always run LSP; remove opt-in advanced mode
The per-file and cross-file LSP type-aware call/usage resolution now runs in every index mode (full/moderate/fast) instead of being gated behind a separate CBM_MODE_ADVANCED. Remove the mode from both index-mode enums, the g_lsp_enabled toggle and its save/restore dance, and the per-mode gating in discover, pipeline and the incremental path.

Now that the LSP walkers are O(n), the callee-attribution accuracy (e.g. ~91k call overrides on roslyn, ~60k on kubernetes) is worth the latency in every mode. Fast mode still filters files and skips similarity/semantic edges, so it stays faster than full while now also benefiting from LSP resolution.

A 'mode':'advanced' request falls back to the full default — identical behaviour to the old advanced mode, so existing callers are unaffected. This also fixes the test_incremental.c failures, which were asserting LSP-resolved results that the gated incremental path never produced.
2026-05-28 19:06:09 +02:00
Martin Vogel b61861eb5a feat(pipeline): add advanced index mode — gate ALL LSP behind it
New CBM_MODE_ADVANCED=3. Both per-file LSP (type-aware resolution
during extract) and the cross-file LSP pass (during resolve) are now
OPT-IN: FULL and below run tree-sitter extraction + registry textual
resolution only; ADVANCED adds the full LSP layer.

Rationale: profiling showed LSP changes the kubernetes graph by only
~0.01% of edges (it mostly re-confirms textual resolution) while
costing significant memory + time. Making it opt-in keeps the default
FULL path lean (3.0 GB vs 5.6 GB peak RSS on K8s) without meaningfully
worse output, and lets users pay for LSP precision only when wanted.

Mechanism:
- cbm.c: g_lsp_enabled atomic toggle wraps the entire per-file LSP
  block (Go/C/C++/PHP/Python/TS/JS/C#) + the C/C++ preprocessor-pass
  LSP call. Default true so ad-hoc extraction / tests keep historical
  behaviour. cbm_set_lsp_enabled / cbm_get_lsp_enabled in cbm.h.
- pipeline.c: sets the toggle (save→set→restore so it never leaks to
  other callers) from p->mode before extraction; gates the cross-LSP
  all_defs collection + registry build on advanced; gates the
  sequential lsp_cross dispatch. Fixed the moderate_only comparison
  (== FAST, not > MODERATE) so ADVANCED is not treated as lighter.
- pipeline_incremental.c: same toggle save/set/restore.
- discover.h/.c: added CBM_MODE_ADVANCED to discover's enum + all 4
  filter comparisons treat ADVANCED like FULL (full discovery, no
  fast-filtering). Without this, ADVANCED (numerically 3) was
  mis-detected as fast-mode and dropped ~half the files.
- mcp.c: parse mode "advanced" + tool schema enum/description.

Dual-mode K8s bench: FULL 46.7s (lsp_overrides=0, 3.0 GB) vs
ADVANCED 48.8s (lsp_overrides=80,818, 5.6 GB). Graphs within 0.01%.
All 3,615 tests pass.
2026-05-26 16:48:58 +02:00
KuaaMU 1a4973d01c fix: add .m extension to EXT_TABLE for content-based disambiguation
The .m extension was missing from EXT_TABLE, causing detect_file_language
to return CBM_LANG_COUNT (Unknown) before the content-based disambiguation
logic (cbm_disambiguate_m) could ever run. This made .m files invisible
to the indexer.

Add {".m", CBM_LANG_MATLAB} to EXT_TABLE so the initial lookup succeeds,
allowing the subsequent cbm_disambiguate_m call to correctly determine
whether the file is MATLAB, Objective-C, or Magma.

Fixes #296
2026-05-10 20:32:52 +02:00
vinay-veerappa 048f1b46c9 feat(lang): add Pine Script support via vendored kvarenzn/tree-sitter-pine (#273)
Adds Pine Script (TradingView's indicator/strategy DSL) as a first-class
language using the project's existing language-add pipeline:

- scripts/new-languages.json: register pine (display "PineScript",
  ts_func tree_sitter_pine, repo kvarenzn/tree-sitter-pine, extension
  .pine, has_scanner=true, module_root source_file)
- internal/cbm/vendored/grammars/pine/: vendored from
  https://github.com/kvarenzn/tree-sitter-pine via scripts/vendor-grammar.sh
  — parser.c, scanner.c, tree_sitter/ headers. License is ISC (declared
  in upstream package.json; written into vendored LICENSE for clarity
  since upstream has no LICENSE file)
- internal/cbm/grammar_pine.c: 4-line wrapper generated by
  scripts/generate-lang-code.py wrappers
- internal/cbm/cbm.h: CBM_LANG_PINE before CBM_LANG_COUNT
- internal/cbm/lang_specs.c: 7 node-type arrays (func / class /
  module / call / var / branch / assign) cross-checked against
  kvarenzn's grammar.js, plus the spec table entry
- src/discover/language.c: .pine -> CBM_LANG_PINE extension dispatch
  and "PineScript" name

scripts/audit-grammar-security.sh on the vendored grammar: no
dangerous includes (only string/stdint/stdbool/stdlib), no system /
popen / exec / socket / mmap / dlopen calls, no constructor/destructor
attributes, no inline asm. The 148-line scanner.c only handles
INDENT/DEDENT/NEWLINE tokens via the standard tree-sitter lexer API.

Distilled from #273 rather than vendored verbatim because the
original PR's parser.c was a plain 46k-line drop without provenance,
no LICENSE attribution, and bundled an unrelated Makefile change
(scripts/embed-frontend.sh -> sh scripts/embed-frontend.sh) that
didn't belong to a Pine Script feature. The node-type choices in
the lang spec match the original PR's selections, all of which were
verified to be real grammar.js rules.

Full suite: 2840 passed, 0 failed under ASan + UBSan.

Closes #273.

Co-authored-by: vinay-veerappa <vinay.veerappa@gmail.com>
2026-05-09 11:50:05 +02:00
test ea98c91cf5 Add 77 extension mapping tests for new languages
Add automated extension-to-language mapping tests for all new grammars
that have file extensions. Removes conflicting extensions:
- .blade.php: compound extension, handled by userconfig system
- .env*: filenames, not extensions (no auto-discovery for dotfiles)
- .sv/.svh: already mapped to Verilog, SystemVerilog uses same grammar
2026-04-15 22:28:31 +02:00
test b2b48f8b0d Add 89 new tree-sitter grammars (66 to 155 languages)
Vendor, wire up, and compile 89 new tree-sitter grammars, expanding
language support from 66 to 155 languages. All grammars pass security
audit (no dangerous patterns in scanner.c files).

New programming languages (31):
  Solidity, Typst, GDScript, Gleam, PowerShell, Pascal, D, Nim, Scheme,
  Fennel, Fish, AWK, Zsh, Tcl, Ada, Agda, Racket, Odin, ReScript,
  PureScript, Nickel, Crystal, Teal, Hare, Pony, Luau, Janet, Sway,
  NASM, Assembly, TLA+, Pkl, Cairo, Move, Squirrel, ISPC, FunC, Smali

New config/data/IDL formats (31):
  Just, Astro, Blade, Go Template, Templ, Liquid, Jinja2, Prisma,
  Hyprlang, DotEnv, Diff, WGSL, KDL, JSON5, Jsonnet, RON, Thrift,
  Cap'n Proto, Properties, SSH Config, BibTeX, Starlark, Bicep, CSV,
  Requirements, HLSL, VHDL, SystemVerilog, DeviceTree, Linker Script,
  GN, Kconfig, BitBake, TableGen, Slang, LLVM IR, Smithy, WIT,
  Go Mod, Mermaid, RST, Beancount, Puppet, PO, Regex, JSDoc,
  gitattributes, gitignore, Apex, SOQL, SOSL

Infrastructure:
  - scripts/new-languages.json: manifest for all new languages
  - scripts/generate-lang-code.py: generates boilerplate from manifest
  - scripts/audit-grammar-security.sh: pre-vendoring security scanner
  - Fixed angle-bracket includes in 18 grammars
  - Fixed PureScript scanner const mismatch
  - Fixed VHDL scanner void* API signatures
  - Fixed RST tree_sitter_rst/ subdirectory include paths
  - Copied crystal unicode.c (extra scanner dependency)

All new languages start with minimal lang specs (module_types only).
Function/class/call extraction specs to be refined incrementally.
2026-04-15 21:28:51 +02:00
Martin VogelanddLo999 6f268d0f91 Respect nested .gitignore files during indexing, security audit all variants
Nested .gitignore support (fixes #178):
- Load per-subdirectory .gitignore during walk, match paths relative to
  the gitignore's directory via local_rel_path()
- Root and nested gitignores stack independently
- Owned gitignores collected and freed at walk end (avoids use-after-free
  from borrowed pointers on the iterative stack)

Security:
- Run security-strings/install/network + ClamAV + Windows Defender on
  ALL binary variants (standard + UI), not just standard
- Whitelist UI bundle URLs (React, Three.js, Google Fonts, Tailwind, W3C)

Co-Authored-By: dLo999 <dLo999@users.noreply.github.com>
2026-04-06 19:44:16 +02:00
Martin Vogel 8a06d78ac7 Parallelize post-passes, fix mode filtering + semantic edge quality
- Parallelize pass_similarity and pass_semantic_edges via worker pool with
  thread-local edge buffers; sequential final merge since gbuf is not
  thread-safe. Adds cbm_lsh_query_into() as a thread-safe variant with
  caller-provided candidate buffer.

- Add activatable profiling subsystem (CBM_PROFILE=1 env or --profile flag)
  for step-level timing of extract, resolve, corpus build, vector phases,
  and sqlite dump. Zero overhead when disabled.

- Fix cbm_index_mode_t enum mismatch between pipeline.h (FULL=0, MODERATE=1,
  FAST=2) and discover.h (FULL=0, FAST=1). mode=fast silently no-op'd
  fast-discovery filtering because discover.c compared against the wrong
  value. Linux kernel fast mode went 1:40 -> 3:11 as a result; now back to
  1:40. Broaden the filter guard to mode != CBM_MODE_FULL so MODERATE and
  FAST both get aggressive discovery.

- Clamp cbm_sem_combined_score output to [0, 1]. The proximity multiplier
  returns up to 1.10 as a same-file boost which could push the final
  cosine score above 1.0.

- Short-circuit semantic scoring when MinHash jaccard >= 0.95. Exact
  near-clones are already emitted as SIMILAR_TO edges; returning 0 here
  avoids flooding SEMANTICALLY_RELATED with cross-service copy-paste
  boilerplate and frees the edge budget for genuine vocabulary-bridged
  relations.

- Validate search_graph semantic_query as an array of strings and return
  a clear error for a single-string input. Update the tool description
  to spell out the requirement explicitly with an example.

- JSON-escape user-controlled strings (callee names, call arguments,
  URL paths, import local_name) in call/argument properties. Introduces
  cbm_json_escape() in foundation/str_util.

- Skip SQLite pending_byte_page (file offset 0x40000000) during raw page
  writes in sqlite_writer to avoid corrupting databases that cross the
  1 GiB boundary.

- Migrate pretrained vector blob from UniXcoder (51K tokens) to
  nomic-embed-code (40856 tokens x 768d int8). Includes the extraction
  script under scripts/extract_nomic_vectors.py.
2026-04-06 12:41:30 +02:00
Martin Vogelandjimpark 9b302eeb80 Fix Zed and VS Code config paths on Windows
Add cbm_app_config_dir() and cbm_app_local_dir() platform helpers:
- Windows: %APPDATA% / %LOCALAPPDATA%
- macOS: $HOME for Library/Application Support paths
- Linux: $XDG_CONFIG_HOME or ~/.config

Update all 6 Zed/VS Code path locations (detect, install, uninstall).
macOS uses $HOME directly for Library paths; Windows/Linux use the
new helpers. Consolidates userconfig.c XDG logic into platform.c.

Co-Authored-By: jimpark <jimpark@users.noreply.github.com>
2026-04-03 15:20:49 +02:00
Martin Vogel 3908624fad Fix remaining lint: cppcheck variableScope, dead code, const correctness
- platform.c: move nlen into if(env) block (variableScope)
- userconfig.c: move home into else block (variableScope)
- pipeline.c: remove dead rc!=0 check after httplinks removal
- cli.c: rmdir_recursive propagates cbm_rmdir errors, remove redundant local
- clang-tidy: whitelist cosmetic-only checks, enable all safety checks
- Thread-safe getenv via environ iteration (concurrency-mt-unsafe fix)
- Multiple files: add constants.h includes for named constants
2026-04-02 12:46:39 +02:00
Martin Vogel d9bd071e77 Fix lint: magic numbers in cypher/sqlite_writer/configlink/gitignore, extract URL-in-args helpers 2026-04-02 01:08:51 +02:00
Martin Vogel 7fa3acd0c6 WIP: strict linting + RAM-first pipeline (lint fixes pending) 2026-04-01 23:22:44 +02:00
Martin Vogel 6e4ca93cf0 Split 168 functions to cognitive complexity 25, zero lint errors
Lower thresholds to industry defaults:
  cognitive-complexity: 25 (was 250)
  statements: 200 (was 400)
  lines: 400 (was 800)

All 168 functions split into smaller helpers across 44 files.
Zero NOLINTNEXTLINE suppressions remain. Zero clang-tidy errors.

Add clang-tidy to scripts/lint.sh (--ci to skip where unavailable).
Fix sqlite_writer B-tree PageRef initialization. Fix Terraform struct
parsing, Louvain null guards, const qualifiers, shadow variables.

2741 tests pass.
2026-03-31 20:04:12 +02:00
Martin Vogel 87c2e144c0 Set cognitive complexity to industry default (25), tag 168 functions
Lower thresholds to best-practice values:
  cognitive-complexity: 250 -> 25 (industry default)
  statements: 400 -> 200
  lines: 800 -> 400

Add NOLINTNEXTLINE to 168 functions that currently exceed these thresholds.
Each suppression will be removed as the function is split in subsequent
commits. Zero net behavioral changes.

Also: extract execute_with_clause from cypher execute_single.
2026-03-31 20:04:12 +02:00
Martin Vogel f26acd4ced Eliminate NOLINTNEXTLINE suppressions + iterative AST walkers
Remove ~480 dead per-line NOLINTNEXTLINE comments. Update .clang-tidy with
documented upstream bug references for disabled checks.

Convert key AST walkers to iterative with explicit stacks: walk_defs (with
context-aware stack frames for class nesting), walk_throws, walk_readwrites,
walk_calls, walk_usages, walk_env_accesses, extract_elixir_call, glob_match,
cbm_yaml_free, rmdir_recursive, resolve_func_name, walk_variables_iter,
count_branching. Reversed child push for left-to-right traversal.

Disable readability-implicit-bool-conversion globally — designed for C++,
flags all idiomatic C patterns (bool = x && y) with no config to allow them.

Fix clang-format, implicit-bool edge cases, add test_helpers.h.
2026-03-31 20:04:11 +02:00
Martin Vogel fad0ff7f90 Merge PR #87: feat(k8s) — Kubernetes and Kustomize indexing (66 languages)
Adds first-class indexing for K8s manifests and Kustomize overlays:
- Resource nodes for K8s kinds (Deployment/my-app, Service/my-svc)
- Module nodes for Kustomize overlays with IMPORTS edges
- Detection via name (kustomization.yaml) and content (apiVersion:)
- Reuses vendored YAML grammar — no new tree-sitter grammar needed

Fix lint: clang-format, implicit bool conversion, magic number.
Resolve CONTRIBUTING.md conflict. Update language count 64 → 66.
Add infrastructure indexing to README features.

Closes #86. Thanks @halindrome!
2026-03-23 13:06:37 +01:00
Martin Vogel 7f33e8b49b Fix lint issues in userconfig + add custom extensions docs to README
- clang-format: fix alignment in userconfig.c and userconfig.h
- Magic number 1280 → PATH_BUF_SZ named constant
- Remove dead g-- before break in dedup loop (cppcheck)
- Add "Custom File Extensions" section to README documenting
  .codebase-memory.json and global config
2026-03-23 11:52:39 +01:00
Shane McCarronandClaude Sonnet 4.6 94841e0542 fix(k8s): address QA round 1 findings
- [Major 1+2] pass_k8s: always re-extract K8s manifests with CBM_LANG_K8S,
  discarding any cached YAML result; pass already-read source buffer to
  handle_k8s_manifest to eliminate the double file read
- [Minor 3] CONTRIBUTING.md: fix extractor path to internal/cbm/extract_k8s.c
  and clarify tree-sitter YAML grammar usage
- [Minor 4] language.c: add comment documenting intentional case-sensitive
  FILENAME_TABLE and case-insensitive cbm_is_kustomize_file() split behaviour
- [Minor 5] test_pipeline.c: add k8s_extract_manifest_multidoc test pinning
  single-document-per-file extraction behaviour
- [Minor 6] pass_infrascan: replace strlen with strnlen(content, 4096) in
  cbm_is_k8s_manifest to bound the scan
- [Minor 7] extract_k8s: add "crds" to is_kustomize_list_key
- [Nit 8] extract_k8s: remove dead second block_node unwrap in
  extract_k8s_scalars metadata descent
- [Nit 10] extract_k8s: use cbm_arena_strdup for def.label "Resource"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 09:29:22 -05:00
Shane McCarronandClaude Sonnet 4.6 e696fcdf80 style(k8s): apply clang-format to k8s and infra-pass source files
- Fix alignment violations in extract_k8s.c, pass_k8s.c, pass_infrascan.c
- Fix spacing in language.c filename table rows (kustomization.yml entry)
- Fix alignment in cbm.h CBMLanguage enum comments
- Fix pipeline.c empty-body brace style

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 09:29:22 -05:00
Shane McCarronandClaude Sonnet 4.6 ee769d5806 feat(k8s): detection helpers, CBMLanguage enum, and language table entries
- Add CBM_LANG_KUSTOMIZE and CBM_LANG_K8S to CBMLanguage enum (before CBM_LANG_COUNT)
- Add kustomization.yaml/yml to FILENAME_TABLE mapped to CBM_LANG_KUSTOMIZE
- Add Kustomize and Kubernetes entries to LANG_NAMES
- Implement cbm_is_kustomize_file() with to_lower+strcmp pattern
- Implement cbm_is_k8s_manifest() scanning first 4KB for apiVersion: via ci_strstr()
- Declare both helpers in pipeline_internal.h Infrascan helpers section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 09:29:22 -05:00
Shane McCarronandClaude Sonnet 4.6 1bfe473a76 fix(discover): address QA round 1 findings
- pipeline.c: inline userconfig cleanup on early-exit paths (discover
  failure and cancel check) so userconfig is always freed, fixing the
  resource leak when pipeline exits before reaching the cleanup label
- userconfig.c: free partially-built entries array on global config
  parse failure to fix alloc-failure memory leak
- language.c: probe user config for compound extensions (e.g.
  ".blade.php") by scanning from the first dot to the last, falling back
  to built-in single-extension lookup — preserves built-in behavior
  while correctly resolving user-defined compound extensions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 17:25:33 -05:00
Shane McCarronandClaude Sonnet 4.6 1cdb98319a feat(discover): add user-defined extension→language mappings via config files
Read extra_extensions from ~/.config/codebase-memory-mcp/config.json
(global) and {repo}/.codebase-memory.json (project), merging them with
project config taking precedence. Unknown language values warn and skip.
Missing files are silently ignored (fail-open).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 17:25:33 -05:00
Martin Vogel 85fc74fc31 Full Windows portability: proper implementations, not stubs
- handle_processes: GetProcessMemoryInfo + GetProcessTimes (Windows),
  getrusage (POSIX) — real process metrics on both platforms
- handle_process_kill: TerminateProcess via OpenProcess (Windows),
  kill(SIGTERM) (POSIX)
- index_thread_fn: CreateProcess + WaitForSingleObject (Windows),
  fork+exec+waitpid (POSIX)
- cbm_clock_gettime: QueryPerformanceCounter (Windows) — replaces
  all bare clock_gettime calls across 6 files
- cbm_nanosleep, cbm_strcasestr, cbm_mkdir: compat shims
- CBM_TLS: _Thread_local (C11 standard, works on all compilers)
- Replace bare strndup with cbm_strndup, guard POSIX headers
- Add mingw-w64 cross-compile to Docker test infrastructure
2026-03-18 18:47:43 +01:00
Martin Vogel 18fa9979ee Replace vmem with mimalloc global allocator, add extraction-phase prescan, fix __init__.py QN collision
Memory management:
- Vendor mimalloc v2.1.9 as global allocator (MI_OVERRIDE=1 in prod)
- New mem.h/mem.c: RSS-based budget tracking via mi_process_info()
- Remove vmem.c/vmem.h (mmap-based budget tracking)
- Remove slab tier2 bump allocator (~300 LOC); >64B goes to mimalloc
- Slab tier1 pages from malloc (= mimalloc) instead of vmem
- Arena blocks from malloc instead of vmem
- Budget raised from 35% to 50% RAM (no more untracked C++ heap)

Extraction-phase prescan (eliminates disk re-reads):
- HTTP call sites: keyword check + URL extraction during extraction
- HTTP routes: decorator + source-based extraction during extraction
- Config file refs: regex scan during extraction
- httplinks: 41.8s → 13ms on Linux kernel (3,212x faster)
- configlink: 41.4s → 0.8s on Linux kernel (54x faster)
- Linux kernel fast-mode total: 2m38s → 1m18s

Bug fixes:
- __init__.py Module QN no longer collides with Folder QN
- index.ts same fix for JS/TS packages
- 13 regression tests for QN collision at FQN + extraction layers
- search_graph/search_code default limit raised from 10 to 500k
- Resolve all clang-tidy, cppcheck, and clang-format warnings

Repo cleanup:
- tree-sitter-form, tree-sitter-magma moved to tools/
- .gitignore: build/, node_modules/, graph-ui/dist/, TEST_PLAN.md
2026-03-17 22:05:57 +01:00
Martin Vogel 0d216c7936 Resolve all clang-tidy, cppcheck, and clang-format warnings across 94 files
Enforce WarningsAsErrors: '*' with zero suppressions of entire check categories.
All non-FP warnings fixed at source; true false positives get per-instance NOLINT
with reasoning. Fixes include: braces around statements (1679), isolate declarations
(75), explicit casts for multilevel pointer conversions, missing includes, function
parameter documentation via NOLINT, and proper NOLINTBEGIN/NOLINTEND nesting.
2026-03-15 22:37:34 +01:00