Originally created by: Avazbek22
Picking files by hand is DevProjex's strength — you see exactly what leaves your project. But it has a cost that grows with project size: the user has to know which files matter.
A typical situation. The user wants to ask an AI about AuthService.cs. They check it in the tree and export. The model answers poorly — it never saw IUserRepository, LoginRequest, AuthResult, the types the class is built from. The user didn't skip them on purpose; they just didn't hold the whole dependency picture in their head. Nobody does, past a few dozen files.
Today the user's options are bad: check whole folders (context bloats, tokens explode) or chase imports manually through the source (tedious, error-prone — the exact pain that pushes people back to CLI dump-everything tools).
Competing tools solve this by taking the human out: codesurf ranks files by query keywords and silently drops the rest; aider builds a repo map internally and never shows it. Both fail invisibly — when the ranking guesses wrong, the user gets a confidently bad answer and never learns why.
DevProjex should solve it the DevProjex way: the machine finds and explains, the human sees and decides.
Right-click a file in the tree → "Find related files".
DevProjex analyzes the project's code and shows a panel of connected files, each with a human-readable reason and a token cost. Nothing gets checked automatically — the user reviews, unchecks what they don't want, confirms. Confirmed files get checked in the tree like any manual selection.
Sketch of the interaction (final UI/UX is open — see the UI section):
right-click AuthService.cs → "Find related files"
┌─ Related to AuthService.cs ────────────────────────────┐
│ │
│ Dependencies (what this file uses) +1,840 tk │
│ ☑ IUserRepository.cs constructor parameter type │
│ ☑ IPasswordHasher.cs constructor parameter type │
│ ☑ ITokenService.cs field type │
│ ☑ AuthResult.cs return type │
│ ☑ LoginRequest.cs method parameter type │
│ │
│ Dependents (what uses this file) +4,120 tk │
│ ☐ AuthController.cs constructs AuthService │
│ ☐ AuthServiceTests.cs references AuthService │
│ │
│ Ambiguous │
│ ▸ User — 3 candidate declarations │
│ │
│ [Cancel] [Add selected] │
└────────────────────────────────────────────────────────┘
Both directions matter and serve different jobs:
This is the strongest objection to visual selection — "in a big repo you'll click forever" — answered without giving up visibility. It's also a combination no competitor has: graph suggestion + visible tree + manual confirmation + live token delta + the same Smart Ignore / Smart Secrets / Compression pipeline underneath. codesurf has the graph but hides the decision; aider has the map but locks it inside its own session; CLI packers have neither.
This section exists so you don't re-walk paths we already walked. The conclusions below came from real research and real measurement — treat them as strong defaults, not dogma. If you find a genuinely better way, say so and explain why.
We evaluated DeusData/codebase-memory-mcp (38k★, C, MIT, tree-sitter-based, real hybrid semantic resolvers including a serious C# binder model) and its fork network in depth. Conclusion: use nothing from it as a dependency. Reasons, in order of weight:
By the time this feature is built, DevProjex already has (from the Code Compression work):
bodies.scm, declarations.scm, manifests) — adding queries is adding files, not code;Three evidence layers. Internally richer than what the user sees; externally it collapses to simple confidence.
Layer A — explicit relations. Imports/includes/module references, per language: Python import/from, TS/JS import (respecting tsconfig paths / package.json / index files), Go import paths, Java imports + source roots, C/C++ #include, Rust use/mod. One query file per language.
Honesty requirement learned in review: an import names a module, package, or header — not always a file. Python imports may hit __init__ re-exports; Go imports a directory; a C++ header is not its implementation file; TS resolution depends on config. So model these as evidence (Import, Include, ModuleReference, ProjectReference) plus a resolution status (Resolved — unique target in the manifest, Ambiguous — multiple candidates, External — outside the project, Unresolved). Only Resolved explicit relations are top-confidence.
Layer B — syntactic type relations. This is what makes C# work (C# has no file import — using names a namespace smeared across files), and it improves every other language too. Build: declarations index with namespaces/packages × type references extracted only from type syntactic positions × import/using context of the referencing file.
Type positions, not bare identifiers — this distinction is load-bearing. Field/property/parameter/return types, base types and interfaces, generic type arguments and constraints, attributes/annotations/decorators, new T(...), typeof(T), casts and type patterns, fully-qualified names. Matching every identifier against the declarations index was proposed and rejected: local variables and method names collide with type names and drown the result in noise. Restricting to type positions kills the noise before resolution. Nice side effect: AddScoped<IUserRepository, UserRepository>() yields two type-reference facts for free, covering most DI-registration cases without any framework awareness.
Unique target → strong confidence. Multiple targets → an ambiguous group, never a guess.
Layer C — heuristics (bare identifier matches, framework conventions, reflection hints). Not in v1. If ever added: off by default, never pre-checked.
ScopeId + LanguageId + SymbolKind + QualifiedName + GenericArity, with declaration sites as a list of files. Two things were explicitly removed after review: file path in the key (breaks C# partial classes — one symbol, several files) and parameter arity (belongs to a call graph; this is a file graph, overloads don't participate).
DevProjex's scope isolation (from Smart Ignore) stays. But a flat "edges never cross scopes" rule was rejected: apps/web → packages/shared-types is the reason monorepos exist. Policy: an explicit, resolved relation (project reference, workspace dependency, resolved import) may cross scopes and is flagged CrossScope — shown, perhaps grouped separately. A heuristic relation without workspace evidence may not cross. Name coincidence between neighboring projects must never create an edge.
Facts are extracted from original source, before compression cuts bodies (identifiers live in bodies; the graph must see them even though layer B mostly reads signatures — attributes and new T(...) sit inside bodies too). Order: parse → graph facts → compression edits → secrets → preview/export.
Do not assume parsed trees are lying around: compression parses only selected files and frees each Tree immediately (a hard rule from that feature — trees cost ~80 bytes per source char). The dependents direction needs facts for the whole eligible scope, so the first "Find related" triggers a background indexing pass — one parse per file, extract compact facts, free the tree immediately, keep only the facts. Subsequent calls are cache hits. One changed file re-indexes one file.
This was a genuine design catch: file facts and graph resolution invalidate differently.
tsconfig.json re-resolves hundreds of files that were not re-parsed. The design must express that: re-resolve without re-parse.DevProjex owns discovery. Smart Ignore, Git mode, filters, and scopes produce the manifest; the graph engine consumes it. There is exactly one scanner in this application. A second one inside the graph would eventually disagree with the tree — the class of bug we refuse to create.
Entry point: context menu on a file (later perhaps on a multi-selection). Beyond that, the layout above is a sketch. What must hold, whatever the final design:
Program.cs) confuses both.Unresolved, diagnostics only.Ranking/PageRank, token-budget fitting, automatic inclusion or exclusion, embeddings, graph visualization, SQLite or any persistent store beyond the existing cache pattern, a second native library, SCIP integration, framework-specific analyzers.
Run CBM as a separate pinned process (comparator, not ground truth — it errs too) on 3 repositories: a C# solution, a mixed monorepo, a TS workspace. 10 seed files, ~50 hand-verified relationships. The question is not a precision percentage. The question is: which classes of edges does the layered scheme miss versus CBM, and how much noise does layer B produce on real C#? If the answer is "misses little, noise near zero" — build. If layer B drowns in ambiguity on real code — rethink before writing the feature.
Hard C# cases to include as fixtures (sourced from CBM's own issue tracker and review): DI registrations, overloads, alias/global usings, duplicate class names across namespaces, interface/implementation pairs, extension methods, nested types, partial classes.
AuthService.cs → "Find related files" → the panel lists its field/parameter/return types under Dependencies, each with a reason; nothing is applied until confirmed.IUserRepository.cs → Dependents lists the files that reference it.tsconfig.json re-resolves without re-parsing.apps/web → packages/shared-types crosses scopes and is flagged; a name coincidence between sibling projects never creates an edge.Test the mechanics, not a language × category matrix: resolution statuses, ambiguity grouping, cache invalidation on file change vs. config change, cross-scope policy, determinism, partial-class identity, the background indexing lifecycle (trees freed, facts kept). Human confirmation is the safety net for individual wrong suggestions; the fixtures guard the machinery that produces them. Roughly fifteen focused tests beat two hundred golden files.