Skip to content

feat(scanner): Track CUE package dependency edges - #146

Merged
JordanCoin merged 3 commits into
JordanCoin:mainfrom
reneleonhardt:feat/cue-package-dependency-edges
Aug 27, 2026
Merged

feat(scanner): Track CUE package dependency edges#146
JordanCoin merged 3 commits into
JordanCoin:mainfrom
reneleonhardt:feat/cue-package-dependency-edges

Conversation

@reneleonhardt

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds bounded support for CUE package dependencies:

  • Detects .cue files and includes them in the tree, skyline, colors, and dependency graph.
  • Extracts CUE imports lexically, covering comments, aliases, grouped imports, malformed tokens, and duplicate imports without evaluating CUE.
  • Resolves imports within the declared CUE module, including nested cue.mod/module.cue roots in monorepos; external imports, suffix-only matches, and imports without a declared module remain unresolved.
  • Preserves caller-selected filters during graph rendering and retains CUE results when ast-grep is unavailable.

Why it matters

CUE files were previously invisible to Codemap's language-aware dependency output. Codemap now reports CUE package edges within the repository without executing CUE or presenting external imports as repository dependencies.

CLI / MCP surface

No new CLI commands, arguments, or MCP tools. Existing surfaces now behave as follows:

  • codemap --deps <path> includes resolved CUE package imports within the repository.
  • MCP get_dependencies returns the same resolved edges.
  • CUE files participate in language detection, tree output, skyline, colors, and dependency graphs.
  • The ast-grep fallback retains CUE analysis when no ast-grep binary is available.

Coverage provenance notes

CUE edges are reported from the cue-imports source. White-box tests cover lexical extraction, module detection, filter cloning, module-scoped resolution, and the no-CUE path. Black-box tests cover the public file-graph API and rendered edges for nested modules and caller-selected filters. External imports and imports without a declared CUE module are not reported as resolved repository edges.

Developed with carefully directed, manually reviewed AI assistance.

Co-Authored-By: GPT-5.6 Sol codex@openai.com

Copy link
Copy Markdown
Owner

Reviewed in depth (adversarial pass, all failure claims demonstrated end-to-end with a built binary). The guard rails are well conceived — module-prefix fail-closed resolution, external/no-module imports honestly unresolved, and the EffectiveFilters change genuinely fixes a pre-existing bug where CLI --only/--exclude were ignored during graph rendering. Three items block merge; all have small, local fixes.

1. BLOCKING — CUE presence disables the #123 fallback and fabricates complete coverage

scanner/walker.go:389-394 intercepts ErrAstGrepNotFound and returns the CUE-only outcome as success, so scanForGraphOutcomeWithFilters never runs the Go-parser/Cargo fallbacks. Demonstrated on a Go+CUE fixture with ast-grep hidden: base main reports coverage partial with ast-grep unavailable + go-parser fallback recovered 2 of 2 Go files; this PR reports complete with only cue-imports — the Go files silently vanish and --importers util/util.go answers a confident "No files import" with no caveat. The degraded-primary variant is also defeated: appended CUE analyses make degradedAstGrepError see non-empty analyses, so recovery never fires in any repo containing a .cue file. (TestScanForDepsOutcomeUsesGoFallbackWithoutAstGrep uses a CUE-free repo, which is why this slipped through.)

Fix: don't intercept in scanForDepsPrimaryOutcome — let the incomplete error propagate, run the existing fallback chain, then append CUE analyses/sources to whatever outcome results (and exclude CUE analyses from the emptiness check), keeping the ast-grep source in provenance.

2. BLOCKING — the lexer extracts imports from inside CUE string literals → false edges

text/scanner lexes Go string forms, not CUE's: """…""", '''…''', and '…' are mislexed so their contents tokenize as code. Demonstrated: a docs.cue whose only content is a doc field usage: """ … import "example.com/acme/templates" … """ produces a real docs → templates edge in --deps. Embedded snippets in multiline strings are everyday CUE (timoni templates, YAML blobs). Simplest strong fix: CUE only allows imports in the file preamble, so stop scanning at the first non-import declaration; alternatively teach the lexer CUE's string forms. Either kills the class.

3. BLOCKING — single global module: nested cue.mod doesn't shadow

One (cueModule, cueRoot) is chosen for the whole repo (alphabetically first parseable module.cue). A file under a nested module importing a path that happens to match the OUTER module resolves across the boundary — false cross-module edge (demonstrated). Fix: resolve each .cue file against its nearest enclosing cue.mod root only.

Non-blocking, worth fixing while in here

  • Directory edges ignore package clauses: an import resolves to every .cue file in the target dir, including ones in a different package (wrong-edge class, demonstrated); the :name selector suffix is neither stripped nor matched. The bytes are already read in scanCUEFiles — record the package clause and filter.
  • module: "example.com/acme@v0" (what cue mod init writes today) never matches sub-path imports — strip a trailing @vN. As-is the feature no-ops on registry-era modules.
  • scanCUEFiles adds a third full-tree walk per scan, even for repos with zero .cue files, and walks --excluded trees (filtered only later).
  • render/colors.go files .cue with config formats while skyline treats it as code — pick one.

Sequencing

#143/#144 merged (no overlap). #145 (Dart) is planned to land first; after it does, this branch has two one-line unions to resolve — render/depgraph.go langOrder (keep both languages) and the README language sentence (keep #145's "21 ast-grep rules" count and add the CUE lexical sentence, matching your own wording that CUE is not an ast-grep rule).

What held up under attack, for the record: comments (//, /* */ multiline), import as a field name, attributes, interpolation, grouped imports with interleaved comments, aliases, malformed strings, CRLF/unicode, missing package clause — all correctly handled; filter cloning is genuinely aliasing-safe.


Generated by Claude Code

@reneleonhardt
reneleonhardt force-pushed the feat/cue-package-dependency-edges branch from 6ca2832 to d552101 Compare August 27, 2026 06:55
@reneleonhardt

Copy link
Copy Markdown
Contributor Author

Complied

  • Fixed all three blocking findings: fallback/provenance, CUE string false edges, and nested-module shadowing.
  • Added package/selector filtering, @vN normalization, exact-file resolution, regression coverage, and raw-import exclusion.
  • Reused the graph builder’s existing filtered file inventory, removing the redundant CUE tree walk from the normal graph path.

Not fully complied

Additional changes

  • Added focused inventory-reuse and graph-path regression coverage.
  • Clarified the CUE module resolver comment.
  • Retained both Dart and CUE documentation/order during rebase resolution.

@JordanCoin
JordanCoin merged commit 6087bd2 into JordanCoin:main Aug 27, 2026
12 checks passed

Copy link
Copy Markdown
Owner

Verified and merged. Re-ran all three blocking scenarios against a binary built from the new head: with ast-grep hidden, a Go+CUE repo now recovers Go edges through the fallback with honest partial provenance alongside cue-imports; the string-literal import in a """ doc field produces no edge; and a file under a nested cue.mod no longer resolves against the outer module. The #145 unions came through exactly right (both languages in langOrder, README count intact), the inventory-reuse drops the extra tree walk, and the full suite is clean. CUE lands as language 22 — thanks for the thorough turnaround on all four of your PRs in this batch.


Generated by Claude Code

@reneleonhardt
reneleonhardt deleted the feat/cue-package-dependency-edges branch August 27, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants