Version
codebase-memory-mcp 0.9.0 (Linux x86_64, standalone binary)
Platform
Linux (x64)
Install channel
GitHub release archive / install.sh / install.ps1
Binary variant
standard
What happened, and what did you expect?
Summary
.vue files are discovered and registered as File and Module nodes, and
get_architecture reports them under languages. But their <script setup>
contents are never parsed: no Function/Method nodes are created for
functions defined in a .vue file, and no CALLS edges are created for calls
originating from one.
The index therefore looks healthy while being structurally empty for every
.vue file in the repo.
Impact
This makes search_graph(max_degree=0, exclude_entry_points=true) unsafe on any
Vue project. Every utility called only from components reports in_degree: 0
and is presented as dead code.
On a 9.2k-line Vue/TS codebase the dead-code query returned 29 functions.
6 of the 11 I verified were false positives — all called from .vue files:
| Reported dead |
Actual call sites |
showModal |
5 |
hideModal |
6 |
copyToClipboard |
2 |
showToast |
1 |
hideToast |
1 |
now |
1 |
Acting on that list deletes working code. fan_in in get_architecture
hotspots is understated for the same reason (reported clone at fan_in: 3;
actual is 6).
Reproduction
Minimal reproduction
Four files. The .ts caller and the .vue caller do the same thing.
repro/
package.json
tsconfig.json
src/util.ts
src/caller.ts
src/components/Caller.vue
src/util.ts
export function fnRelTs(x: number): number { return x + 1; }
export function fnAliasTs(x: number): number { return x + 2; }
export function fnRelVue(x: number): number { return x + 3; }
export function fnAliasVue(x: number): number { return x + 4; }
export default { fnRelTs, fnAliasTs, fnRelVue, fnAliasVue };
src/caller.ts
import uRel from './util';
import uAlias from '~/util';
export function runTs(): number {
return uRel.fnRelTs(1) + uAlias.fnAliasTs(2);
}
src/components/Caller.vue
<script setup lang="ts">
import uRel from '../util';
import uAlias from '~/util';
const runVue = (): number => {
return uRel.fnRelVue(1) + uAlias.fnAliasVue(2);
};
</script>
<template>
<div>{{ runVue() }}</div>
</template>
tsconfig.json
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "baseUrl": ".", "paths": { "~/*": ["src/*"] } } }
package.json
{ "name": "repro", "version": "1.0.0", "type": "module" }
Then:
index_repository(repo_path="…/repro", mode="full", name="repro")
query_graph(project="repro",
query="MATCH (f) WHERE (f:Function OR f:Method) RETURN f.name, f.file_path, f.in_degree ORDER BY f.name")
Actual
fnAliasTs src/util.ts 1
fnAliasVue src/util.ts 0 ← called from Caller.vue
fnRelTs src/util.ts 1
fnRelVue src/util.ts 0 ← called from Caller.vue
runTs src/caller.ts 0
runVue is absent from the graph entirely — no node is created for it.
MATCH (a)-[r:CALLS]->(b) RETURN a.name, a.file_path, b.name
→ runTs, src/caller.ts, fnRelTs
runTs, src/caller.ts, fnAliasTs
Only two edges. Nothing originates from Caller.vue.
Expected
fnRelVue and fnAliasVue at in_degree: 1, a Function node for runVue
at src/components/Caller.vue, and two additional CALLS edges.
What this is not
I first suspected namespace default imports (import u from './util' then
u.fn()) or path-alias resolution (~/util). Both work correctly — the
2×2 above isolates it: caller.ts resolves relative and aliased namespace
calls fine. The only failing variable is the caller being a .vue file.
Why it is easy to miss
get_architecture reports the files as present, which reads as success:
"languages": [ { "language": "Vue", "file_count": 34 }, … ]
Aggregate edge density is the tell — on the same machine:
| Repo |
Nodes |
Edges |
Edges/node |
| Python, 3.7k LOC |
409 |
1665 |
4.07 |
| Vue+TS, 9.2k LOC |
480 |
924 |
1.93 |
And on the Vue repo:
MATCH (f) WHERE (f:Function OR f:Method) AND f.file_path ENDS WITH '.vue'
RETURN count(f) → 0
MATCH (a)-[r:CALLS]->(b) WHERE a.file_path ENDS WITH '.vue'
RETURN count(r) → 0
MATCH (f:File) WHERE f.path ENDS WITH '.vue' RETURN count(f) → 34
34 file nodes, zero structural content.
Suggested direction
Extract the <script> / <script setup> block from the SFC before handing it
to the TS parser, preserving a line offset so file_path/line attribution stays
correct. Template-block references (@click="handler", {{ fn() }}) are a
larger job and could be a follow-up — script-block extraction alone would
recover the bulk of the missing edges.
Two smaller asks, if useful:
- Until this lands, consider having
index_repository warn when a language is
detected but yields zero function nodes — that single signal would have made
this obvious immediately.
exclude_entry_points=true in search_graph(max_degree=0, …) implies a
confidence the index cannot support for partially-parsed languages. A
per-language "structural coverage" figure in index_status would let callers
know when not to trust a dead-code result.
Environment
codebase-memory-mcp 0.9.0, standalone binary
- Linux 7.0.0-28-generic, x86_64
- Repos: Vue 3 + TypeScript (Vite,
~/* path alias), and Python 3 for the
comparison baseline
mode="full" in all runs; skipped_count: 0, expected_nodes matched
nodes in every index (no reported extraction failures)
Logs
Diagnostics trajectory (memory / performance / leak issues)
Project scale (if relevant)
No response
Confirmations
Version
codebase-memory-mcp 0.9.0(Linux x86_64, standalone binary)Platform
Linux (x64)
Install channel
GitHub release archive / install.sh / install.ps1
Binary variant
standard
What happened, and what did you expect?
Summary
.vuefiles are discovered and registered asFileandModulenodes, andget_architecturereports them underlanguages. But their<script setup>contents are never parsed: no
Function/Methodnodes are created forfunctions defined in a
.vuefile, and noCALLSedges are created for callsoriginating from one.
The index therefore looks healthy while being structurally empty for every
.vuefile in the repo.Impact
This makes
search_graph(max_degree=0, exclude_entry_points=true)unsafe on anyVue project. Every utility called only from components reports
in_degree: 0and is presented as dead code.
On a 9.2k-line Vue/TS codebase the dead-code query returned 29 functions.
6 of the 11 I verified were false positives — all called from
.vuefiles:showModalhideModalcopyToClipboardshowToasthideToastnowActing on that list deletes working code.
fan_ininget_architecturehotspots is understated for the same reason (reported
cloneatfan_in: 3;actual is 6).
Reproduction
Minimal reproduction
Four files. The
.tscaller and the.vuecaller do the same thing.src/util.tssrc/caller.tssrc/components/Caller.vuetsconfig.json{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "baseUrl": ".", "paths": { "~/*": ["src/*"] } } }package.json{ "name": "repro", "version": "1.0.0", "type": "module" }Then:
Actual
runVueis absent from the graph entirely — no node is created for it.Only two edges. Nothing originates from
Caller.vue.Expected
fnRelVueandfnAliasVueatin_degree: 1, aFunctionnode forrunVueat
src/components/Caller.vue, and two additionalCALLSedges.What this is not
I first suspected namespace default imports (
import u from './util'thenu.fn()) or path-alias resolution (~/util). Both work correctly — the2×2 above isolates it:
caller.tsresolves relative and aliased namespacecalls fine. The only failing variable is the caller being a
.vuefile.Why it is easy to miss
get_architecturereports the files as present, which reads as success:Aggregate edge density is the tell — on the same machine:
And on the Vue repo:
34 file nodes, zero structural content.
Suggested direction
Extract the
<script>/<script setup>block from the SFC before handing itto the TS parser, preserving a line offset so
file_path/line attribution stayscorrect. Template-block references (
@click="handler",{{ fn() }}) are alarger job and could be a follow-up — script-block extraction alone would
recover the bulk of the missing edges.
Two smaller asks, if useful:
index_repositorywarn when a language isdetected but yields zero function nodes — that single signal would have made
this obvious immediately.
exclude_entry_points=trueinsearch_graph(max_degree=0, …)implies aconfidence the index cannot support for partially-parsed languages. A
per-language "structural coverage" figure in
index_statuswould let callersknow when not to trust a dead-code result.
Environment
codebase-memory-mcp0.9.0, standalone binary~/*path alias), and Python 3 for thecomparison baseline
mode="full"in all runs;skipped_count: 0,expected_nodesmatchednodesin every index (no reported extraction failures)Logs
Diagnostics trajectory (memory / performance / leak issues)
Project scale (if relevant)
No response
Confirmations