From c7418b2f2b4f220f15106543fb814f7453ccbfa8 Mon Sep 17 00:00:00 2001 From: prosdev Date: Wed, 1 Apr 2026 01:18:46 -0700 Subject: [PATCH 1/4] feat(cli,core): add dev refs CLI command and normalize callee paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New `dev refs ` command: callees, callers, --depends-on, --json - Normalize callee file paths: dist/ → src/, .d.ts → .ts, absolute → relative - Fix indexer passing empty exclude array (was bypassing scanner defaults) Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/cli/src/cli.ts | 2 + packages/cli/src/commands/refs.ts | 208 ++++++++++++++++++ packages/core/src/indexer/index.ts | 5 +- .../src/scanner/__tests__/scanner.test.ts | 27 +++ packages/core/src/scanner/typescript.ts | 20 +- 5 files changed, 258 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/commands/refs.ts diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index b5dacc5..a34ee84 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -7,6 +7,7 @@ import { compactCommand } from './commands/compact.js'; import { indexCommand } from './commands/index.js'; import { mapCommand } from './commands/map.js'; import { mcpCommand } from './commands/mcp.js'; +import { refsCommand } from './commands/refs.js'; import { resetCommand } from './commands/reset.js'; import { searchCommand } from './commands/search.js'; import { setupCommand } from './commands/setup.js'; @@ -26,6 +27,7 @@ program // Register commands program.addCommand(indexCommand); program.addCommand(searchCommand); +program.addCommand(refsCommand); program.addCommand(mapCommand); program.addCommand(compactCommand); program.addCommand(cleanCommand); diff --git a/packages/cli/src/commands/refs.ts b/packages/cli/src/commands/refs.ts new file mode 100644 index 0000000..aeed033 --- /dev/null +++ b/packages/cli/src/commands/refs.ts @@ -0,0 +1,208 @@ +import * as path from 'node:path'; +import { + buildDependencyGraph, + ensureStorageDirectory, + getStorageFilePaths, + getStoragePath, + RepositoryIndexer, + type SearchResult, + shortestPath, +} from '@prosdevlab/dev-agent-core'; +import chalk from 'chalk'; +import { Command } from 'commander'; +import ora from 'ora'; +import { loadConfig } from '../utils/config.js'; +import { logger } from '../utils/logger.js'; + +type RefDirection = 'callees' | 'callers' | 'both'; + +interface CalleeInfo { + name: string; + file?: string; + line: number; +} + +export const refsCommand = new Command('refs') + .description('Find callers and callees of a function') + .argument('', 'Function or method name (e.g., "createPlan", "SearchAdapter.execute")') + .option('-d, --direction ', 'Query direction: callees, callers, or both', 'both') + .option('-l, --limit ', 'Maximum results per direction', '20') + .option('--depends-on ', 'Trace dependency path to a target file') + .option('--json', 'Output results as JSON', false) + .action(async (name: string, options) => { + const spinner = ora('Finding references...').start(); + + try { + const config = await loadConfig(); + const repositoryPath = config?.repository?.path || config?.repositoryPath || process.cwd(); + const resolvedRepoPath = path.resolve(repositoryPath); + + const storagePath = await getStoragePath(resolvedRepoPath); + await ensureStorageDirectory(storagePath); + const filePaths = getStorageFilePaths(storagePath); + + const indexer = new RepositoryIndexer({ + repositoryPath: resolvedRepoPath, + vectorStorePath: filePaths.vectors, + }); + + await indexer.initialize(); + + const direction = options.direction as RefDirection; + const limit = Number.parseInt(options.limit, 10); + + // Find the target symbol + const searchResults = await indexer.search(name, { limit: 10 }); + const target = findBestMatch(searchResults, name); + + if (!target) { + spinner.fail(`Could not find "${name}"`); + await indexer.close(); + process.exit(1); + } + + // Handle --depends-on + if (options.dependsOn) { + spinner.text = `Tracing path: ${name} → ${options.dependsOn}`; + const allDocs = await indexer.getAll({ limit: 50000 }); + const graph = buildDependencyGraph(allDocs); + const sourceFile = (target.metadata.path as string) || ''; + const tracePath = shortestPath(graph, sourceFile, options.dependsOn); + + spinner.stop(); + + if (options.json) { + console.log(JSON.stringify({ from: sourceFile, to: options.dependsOn, path: tracePath })); + await indexer.close(); + return; + } + + console.log(''); + if (tracePath) { + console.log(chalk.bold(`Dependency path: ${sourceFile} → ${options.dependsOn}`)); + console.log(''); + console.log(` ${tracePath.join(chalk.dim(' → '))}`); + console.log(''); + console.log( + chalk.dim(` ${tracePath.length - 1} hop${tracePath.length - 1 === 1 ? '' : 's'}`) + ); + } else { + console.log(chalk.yellow(`No path found from ${sourceFile} to ${options.dependsOn}`)); + console.log(chalk.dim(' These files may be in separate subsystems.')); + } + console.log(''); + await indexer.close(); + return; + } + + // Get callees + let callees: CalleeInfo[] = []; + if (direction === 'callees' || direction === 'both') { + const rawCallees = target.metadata.callees as CalleeInfo[] | undefined; + callees = (rawCallees || []).slice(0, limit); + } + + // Get callers + const callers: Array<{ name: string; file?: string; line: number; type?: string }> = []; + if (direction === 'callers' || direction === 'both') { + const targetName = target.metadata.name as string; + const candidates = await indexer.search(targetName, { limit: 100 }); + + for (const candidate of candidates) { + if (candidate.id === target.id) continue; + const candidateCallees = candidate.metadata.callees as CalleeInfo[] | undefined; + if (!candidateCallees) continue; + + const callsTarget = candidateCallees.some( + (c) => + c.name === targetName || + c.name.endsWith(`.${targetName}`) || + targetName.endsWith(`.${c.name}`) + ); + + if (callsTarget) { + callers.push({ + name: (candidate.metadata.name as string) || 'unknown', + file: candidate.metadata.path as string, + line: (candidate.metadata.startLine as number) || 0, + type: candidate.metadata.type as string, + }); + if (callers.length >= limit) break; + } + } + } + + await indexer.close(); + spinner.stop(); + + if (options.json) { + console.log( + JSON.stringify( + { + target: { + name: target.metadata.name, + file: target.metadata.path, + line: target.metadata.startLine, + type: target.metadata.type, + }, + callees: direction === 'callers' ? undefined : callees, + callers: direction === 'callees' ? undefined : callers, + }, + null, + 2 + ) + ); + return; + } + + // Format output + const targetFile = (target.metadata.path as string) || ''; + const targetLine = (target.metadata.startLine as number) || 0; + const relFile = targetFile.startsWith(resolvedRepoPath) + ? targetFile.slice(resolvedRepoPath.length + 1) + : targetFile; + + console.log(''); + console.log(chalk.bold(`${target.metadata.name}`)); + console.log(chalk.dim(` ${relFile}:${targetLine} ${target.metadata.type}`)); + console.log(''); + + if (direction === 'callees' || direction === 'both') { + console.log(chalk.cyan.bold('Callees') + chalk.dim(' (what this calls)')); + if (callees.length > 0) { + for (const c of callees) { + const loc = c.file ? chalk.dim(`${c.file}:${c.line}`) : chalk.dim(`line ${c.line}`); + console.log(` ${c.name} ${loc}`); + } + } else { + console.log(chalk.dim(' No callees found')); + } + console.log(''); + } + + if (direction === 'callers' || direction === 'both') { + console.log(chalk.cyan.bold('Callers') + chalk.dim(' (what calls this)')); + if (callers.length > 0) { + for (const c of callers) { + const loc = c.file ? chalk.dim(`${c.file}:${c.line}`) : chalk.dim(`line ${c.line}`); + console.log(` ${c.name} ${loc} ${chalk.dim(c.type || '')}`); + } + } else { + console.log(chalk.dim(' No callers found')); + } + console.log(''); + } + } catch (error) { + spinner.fail('Refs query failed'); + logger.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + } + }); + +function findBestMatch(results: SearchResult[], name: string) { + if (results.length === 0) return null; + const exact = results.find( + (r) => r.metadata.name === name || (r.metadata.name as string)?.endsWith(`.${name}`) + ); + return exact || results[0]; +} diff --git a/packages/core/src/indexer/index.ts b/packages/core/src/indexer/index.ts index 76d76b8..e2cb1fa 100644 --- a/packages/core/src/indexer/index.ts +++ b/packages/core/src/indexer/index.ts @@ -95,7 +95,10 @@ export class RepositoryIndexer { const scanResult = await scanRepository({ repoRoot: this.config.repositoryPath, include: options.languages?.map((lang) => `**/*.${getExtensionForLanguage(lang)}`), - exclude: [...this.config.excludePatterns, ...(options.excludePatterns || [])], + exclude: + this.config.excludePatterns.length > 0 || options.excludePatterns?.length + ? [...this.config.excludePatterns, ...(options.excludePatterns || [])] + : undefined, languages: options.languages, logger: options.logger, onProgress: (scanProgress) => { diff --git a/packages/core/src/scanner/__tests__/scanner.test.ts b/packages/core/src/scanner/__tests__/scanner.test.ts index e372119..7e1dac4 100644 --- a/packages/core/src/scanner/__tests__/scanner.test.ts +++ b/packages/core/src/scanner/__tests__/scanner.test.ts @@ -707,6 +707,33 @@ describe('Scanner', () => { const calleeNames = fn?.metadata.callees?.map((c) => c.name) || []; expect(calleeNames.some((n) => n.includes('register'))).toBe(true); }); + + it('should normalize callee file paths to relative src paths', async () => { + // Scan a file that imports from workspace packages (resolves to dist/) + const result = await scanRepository({ + repoRoot, + include: ['packages/mcp-server/bin/dev-agent-mcp.ts'], + }); + + const allCallees = result.documents + .flatMap((d) => (d.metadata.callees as Array<{ file?: string }>) || []) + .filter((c) => c.file); + + // No callee should have absolute paths + for (const callee of allCallees) { + expect(callee.file).not.toMatch(/^\//); + } + + // No callee should reference dist/ directories + for (const callee of allCallees) { + expect(callee.file).not.toContain('/dist/'); + } + + // No callee should reference .d.ts files + for (const callee of allCallees) { + expect(callee.file).not.toMatch(/\.d\.ts$/); + } + }); }); describe('Arrow Function Extraction', () => { diff --git a/packages/core/src/scanner/typescript.ts b/packages/core/src/scanner/typescript.ts index c0cebf4..a9bc03c 100644 --- a/packages/core/src/scanner/typescript.ts +++ b/packages/core/src/scanner/typescript.ts @@ -33,6 +33,7 @@ export class TypeScriptScanner implements Scanner { }; private project: Project | null = null; + private repoRoot = ''; /** Default maximum lines for code snippets */ private static readonly DEFAULT_MAX_SNIPPET_LINES = 50; @@ -76,6 +77,8 @@ export class TypeScriptScanner implements Scanner { logger?: Logger, onProgress?: (filesProcessed: number, totalFiles: number) => void ): Promise { + this.repoRoot = repoRoot; + // Initialize project with lenient type checking enabled // - Allows cross-file symbol resolution for better callee extraction // - Keeps strict checks disabled to avoid blocking on type errors @@ -983,10 +986,21 @@ export class TypeScriptScanner implements Scanner { if (firstDecl) { const declSourceFile = firstDecl.getSourceFile(); if (declSourceFile) { - const filePath = declSourceFile.getFilePath(); + const rawPath = declSourceFile.getFilePath() as string; // Only include if it's within the project (not node_modules) - if (filePath && !filePath.includes('node_modules')) { - file = filePath; + if (rawPath && !rawPath.includes('node_modules')) { + // Normalize: dist/ → src/, .d.ts → .ts, then make relative to repo root. + // ts-morph resolves imports to absolute dist output paths + // (e.g. /abs/packages/logger/dist/types.d.ts) but we store + // relative source paths (packages/logger/src/types.ts). + let normalized = rawPath + .replace(/\/dist\//, '/src/') + .replace(/\.d\.ts$/, '.ts') + .replace(/\.js$/, '.ts'); + if (this.repoRoot && normalized.startsWith(this.repoRoot)) { + normalized = path.relative(this.repoRoot, normalized); + } + file = normalized; } } } From 2f8356143ef2e68a243c38859382dcfaf30d1582 Mon Sep 17 00:00:00 2001 From: prosdev Date: Wed, 1 Apr 2026 02:04:51 -0700 Subject: [PATCH 2/4] docs: add changelog and release notes for dev refs CLI Co-Authored-By: Claude Opus 4.6 (1M context) --- .changeset/refs-cli.md | 13 +++++++++++++ website/content/latest-version.ts | 8 ++++---- website/content/updates/index.mdx | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 .changeset/refs-cli.md diff --git a/.changeset/refs-cli.md b/.changeset/refs-cli.md new file mode 100644 index 0000000..4f898eb --- /dev/null +++ b/.changeset/refs-cli.md @@ -0,0 +1,13 @@ +--- +'@prosdevlab/dev-agent': patch +--- + +Add `dev refs` CLI command and fix callee path normalization + +- New `dev refs ` command: find callers and callees from the terminal + - `--direction callees|callers|both` to filter results + - `--depends-on ` to trace dependency paths + - `--json` for machine-readable output +- Normalize callee file paths: `dist/` → `src/`, `.d.ts` → `.ts`, absolute → relative +- Fix hot paths showing build output (`packages/logger/dist/types.d.ts` → `packages/logger/src/types.ts`) +- Fix indexer passing empty exclude array (was bypassing scanner default exclusions) diff --git a/website/content/latest-version.ts b/website/content/latest-version.ts index 3e5aa27..a7ab639 100644 --- a/website/content/latest-version.ts +++ b/website/content/latest-version.ts @@ -4,10 +4,10 @@ */ export const latestVersion = { - version: '0.11.1', - title: 'Cached Dependency Graph', + version: '0.11.2', + title: 'dev refs CLI Command', date: 'April 1, 2026', summary: - 'dev_map and dev_refs load a pre-built graph instead of fetching all docs — removes the 10k doc ceiling for larger repos.', - link: '/updates#v0111--cached-dependency-graph', + 'Find callers and callees from the terminal — dev refs . Plus callee path normalization so hot paths show source files.', + link: '/updates#v0112--dev-refs-cli-command', } as const; diff --git a/website/content/updates/index.mdx b/website/content/updates/index.mdx index e74db4a..f3e3ffe 100644 --- a/website/content/updates/index.mdx +++ b/website/content/updates/index.mdx @@ -9,6 +9,20 @@ What's new in dev-agent. We ship improvements regularly to help AI assistants un --- +## v0.11.2 — `dev refs` CLI Command + +*April 1, 2026* + +**Find callers and callees from the terminal** — no MCP server needed. + +- `dev refs ` — callees (what it calls) and callers (what calls it) +- `dev refs --depends-on ` — trace the dependency chain between files +- `--direction callees|callers|both` and `--json` for scripting +- Callee file paths now point to source files instead of build output +- Hot paths in `dev map` show `packages/logger/src/types.ts` instead of `dist/types.d.ts` + +--- + ## v0.11.1 — Cached Dependency Graph *April 1, 2026* From 39a347294d48c69e707a104072bd6b3625827227 Mon Sep 17 00:00:00 2001 From: prosdev Date: Wed, 1 Apr 2026 02:51:11 -0700 Subject: [PATCH 3/4] docs: update CLAUDE.md and agents for dev refs CLI and graph caching - Add dev refs to CLI commands section with usage examples - Document graph caching (dependency-graph.json) in MCP tools section - Update dev_refs description to mention dependsOn parameter - Update agent prompts (bug-investigator, plan-reviewer, research-planner, quick-scout) to reference dependsOn for dependency chain tracing Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/agents/bug-investigator.md | 12 ++++++----- .claude/agents/logic-reviewer.md | 3 ++- .claude/agents/plan-reviewer.md | 2 +- .claude/agents/quality-reviewer.md | 2 +- .claude/agents/quick-scout.md | 12 +++++++---- .claude/agents/research-planner.md | 12 ++++++----- .claude/agents/security-reviewer.md | 4 ++++ CLAUDE.md | 32 +++++++++++++++++++++++++---- 8 files changed, 58 insertions(+), 21 deletions(-) diff --git a/.claude/agents/bug-investigator.md b/.claude/agents/bug-investigator.md index e4fa880..9420099 100644 --- a/.claude/agents/bug-investigator.md +++ b/.claude/agents/bug-investigator.md @@ -10,13 +10,15 @@ color: orange Systematically traces issues through the dev-agent monorepo. Reproduces, traces, fixes, and prevents regression. -## MCP Tools — Use These First +## MCP Tools — Save Tokens, Extend Sessions -- **`dev_search`** — Start here for any conceptual query ("where does rate limiting happen", "how are embeddings stored"). Semantic search finds code by meaning, not just keywords. -- **`dev_refs`** — Trace callers/callees of a function to map the data flow. Use this instead of grepping for function names when tracing how data moves across packages. -- **`dev_map`** — Check change frequency to see what files changed recently. Useful for "when did it break?" — high-churn files near the bug timeframe are prime suspects. +Use MCP tools to get focused results instead of Grep → Read cycles. See CLAUDE.md for the token savings table. -Fall back to Grep/Glob for exact string matches or file patterns. +- **`dev_search`** — Conceptual queries ("where does rate limiting happen"). Returns ranked snippets — no file reading needed. +- **`dev_refs`** — Callers/callees of a function. Use `dependsOn` to trace dependency chains between files. +- **`dev_map`** — Codebase structure with hot paths and subsystems. One call replaces dozens of ls/glob/read operations. + +Reserve Grep/Glob for exact string matches where you know the literal text. ## Investigation Framework diff --git a/.claude/agents/logic-reviewer.md b/.claude/agents/logic-reviewer.md index 7bf8687..3ccdb09 100644 --- a/.claude/agents/logic-reviewer.md +++ b/.claude/agents/logic-reviewer.md @@ -65,9 +65,10 @@ Every finding MUST include confidence: **HIGH** (verified from code), **MEDIUM** ### Cross-Package Data Flow (Deep+ Effort) -Use `dev_refs` to trace caller/callee chains across package boundaries. Use `dev_search` to find related code by concept when the function name isn't obvious. +Use MCP tools to trace cross-package flows without reading every file (see CLAUDE.md for token savings). `dev_refs` returns the call graph directly, `dev_search` finds related code by concept, `dev_patterns` compares error handling across files. - [ ] Core exports consumed correctly by CLI, MCP server, and subagents — verify with `dev_refs` +- [ ] Dependency chains make sense — use `dev_refs` with `dependsOn` to trace file-to-file paths - [ ] Type boundaries between packages match (no `any` casting to bridge mismatches) - [ ] Logger (@prosdevlab/kero) configuration consistent across consumers - [ ] Error handling patterns are consistent with existing code (verify with `dev_patterns`) diff --git a/.claude/agents/plan-reviewer.md b/.claude/agents/plan-reviewer.md index 39ac624..528e8b6 100644 --- a/.claude/agents/plan-reviewer.md +++ b/.claude/agents/plan-reviewer.md @@ -21,7 +21,7 @@ Read the plan as a senior engineer. Use `dev_map` to verify structure claims, `d 1. **Context** — Does it accurately describe what exists today? (Verify with `dev_map` and reading actual code) 2. **Architecture** — Does the proposed design fit the existing monorepo structure? 3. **Parts breakdown** — Are parts sized correctly? (Each should be 1-2 commits) -4. **Dependencies** — Are cross-package dependencies identified? (Verify with `dev_refs`) +4. **Dependencies** — Are cross-package dependencies identified? (Verify with `dev_refs`. Use `dependsOn` to trace dependency chains between files.) 5. **Build order** — Does the implementation order respect the build dependency chain? 6. **Breaking changes** — Are they identified and migration paths described? diff --git a/.claude/agents/quality-reviewer.md b/.claude/agents/quality-reviewer.md index ac47fd3..bb11373 100644 --- a/.claude/agents/quality-reviewer.md +++ b/.claude/agents/quality-reviewer.md @@ -48,7 +48,7 @@ Maximum **5 SUGGESTION items** per review. If more found, pick the top 5 and not ### Readability & Simplification -Use `dev_patterns` to find similar code and detect duplication. Use `dev_search` to check if a utility already exists before flagging missing abstractions. +Use MCP tools to check for duplication without reading every file (see CLAUDE.md for token savings). `dev_patterns` compares patterns across similar files. `dev_search` checks if a utility exists by meaning, not just name. - [ ] No code duplicating existing utilities — verify with `dev_patterns` and `dev_search` - [ ] Functions reasonably sized (consider splitting if >50 lines) diff --git a/.claude/agents/quick-scout.md b/.claude/agents/quick-scout.md index c4246a8..8f58bbd 100644 --- a/.claude/agents/quick-scout.md +++ b/.claude/agents/quick-scout.md @@ -10,6 +10,10 @@ color: blue Lightweight explorer optimized for speed and cost. Finds code, traces flows, maps dependencies. +## Token Efficiency + +Use MCP tools to get focused results instead of Grep → Read cycles. See CLAUDE.md for the token savings table. Every file Read costs tokens — let the tools do the reading. + ## Capability Boundaries You excel at: @@ -25,10 +29,10 @@ Do NOT guess at architectural reasoning or make recommendations. ## Workflow -1. **Search** — Always start with `dev_search`. It finds code by meaning, not just keywords. Only fall back to Grep for exact string matches or Glob for file patterns. -2. **Trace** — For "who calls X?" or "what does X call?", use `dev_refs`. Do not grep for function names when `dev_refs` can trace the graph directly. -3. **Map** — For "what's the structure?" or "what changed recently?", use `dev_map`. -4. **Verify** — Read the file to confirm the match +1. **Search** — Start with `dev_search` for conceptual queries. Returns ranked snippets without reading files. Only fall back to Grep for exact string matches. +2. **Trace** — For "who calls X?", use `dev_refs`. For "how does A depend on B?", use `dev_refs` with `dependsOn`. Returns the call graph directly — no grepping for function names. +3. **Map** — For "what's the structure?", use `dev_map`. One call replaces dozens of ls/glob/read operations. +4. **Verify** — Only Read a file when you need the full implementation, not just the location. 5. **Report** — Concise, factual answer with file paths and line numbers ## Dev-Agent Quick Reference diff --git a/.claude/agents/research-planner.md b/.claude/agents/research-planner.md index 32b6b89..aff4730 100644 --- a/.claude/agents/research-planner.md +++ b/.claude/agents/research-planner.md @@ -12,12 +12,14 @@ Plans investigations before jumping into implementation. Produces a structured r This agent **NEVER writes code**. It produces investigation plans. -## MCP Tools — Use These to Map the Territory +## MCP Tools — Save Tokens, Extend Sessions -- **`dev_search`** — Find relevant code areas by meaning. Start broad ("authentication middleware", "vector storage") to discover what exists before diving in. -- **`dev_map`** — Get codebase structure with change frequency. Use early to understand scope and identify hot spots. -- **`dev_patterns`** — Analyze existing patterns before proposing new ones. Find similar implementations, error handling conventions, and type patterns. -- **`dev_refs`** — Trace cross-package dependencies. Understand what depends on what before proposing changes. +Use MCP tools to map the territory without burning tokens on Grep → Read cycles. See CLAUDE.md for the token savings table. + +- **`dev_search`** — Find relevant code areas by meaning. Returns ranked snippets — no file reading needed. +- **`dev_map`** — Codebase structure with hot paths and subsystems. One call replaces dozens of ls/glob/read operations. +- **`dev_patterns`** — Compare patterns across similar files without reading each one. +- **`dev_refs`** — Trace cross-package dependencies. Use `dependsOn` to trace dependency chains between files. ## When to Use diff --git a/.claude/agents/security-reviewer.md b/.claude/agents/security-reviewer.md index d1adca9..6f12062 100644 --- a/.claude/agents/security-reviewer.md +++ b/.claude/agents/security-reviewer.md @@ -12,6 +12,10 @@ Security-focused review for a TypeScript monorepo that processes repository data This agent **NEVER modifies code**. It reports issues for the developer to fix. +## Token Efficiency + +Use MCP tools to avoid expensive Grep → Read cycles. See CLAUDE.md for the token savings table. `dev_search` returns ranked snippets, `dev_refs` traces input flow directly, `dev_patterns` scans for similar vulnerability patterns — all without reading files manually. + ## Checklist Use `dev_search` to find security-sensitive code ("user input", "shell execution", "token handling"). Use `dev_patterns` to find similar patterns across the codebase — if one injection vector exists, the same pattern likely appears elsewhere. Use `dev_refs` to trace how user input flows through the system. diff --git a/CLAUDE.md b/CLAUDE.md index ed39ddf..d6cd86c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,7 @@ Everything runs on your machine. No data leaves. ``` packages/ core/ # Scanner (ts-morph, tree-sitter for Python/Go), vector storage (Antfly), services - cli/ # Commander.js CLI — dev index, dev mcp install, etc. + cli/ # Commander.js CLI — dev index, dev search, dev refs, dev map, dev mcp install mcp-server/ # MCP server with 5 built-in adapters subagents/ # Coordinator, explorer, planner, PR agents integrations/ # Claude Code, VS Code, Cursor @@ -139,16 +139,33 @@ See `.claude/da-plans/README.md` for status and format details. --- -## MCP tools (5 adapters) +## MCP tools — token-efficient context + +MCP tools return pre-ranked, pre-snippeted results. Use them to avoid expensive Grep → Read cycles that burn thousands of tokens on irrelevant context. + +| Instead of | Use | Tokens saved | +|------------|-----|-------------| +| Grep "auth" → read 10 files | `dev_search "authentication"` | ~5,000 | +| Grep function name → read callers | `dev_refs "functionName"` | ~3,000 | +| ls + glob + read READMEs | `dev_map` | ~2,000 | +| Read multiple files to compare patterns | `dev_patterns filePath` | ~3,000 | + +Reserve Grep/Glob for exact string matches where you know the literal text. + +### 5 adapters | Tool | Purpose | |------|---------| | `dev_search` | Hybrid code search — BM25 + vector + RRF (use FIRST for conceptual queries) | -| `dev_refs` | Find callers/callees of functions | -| `dev_map` | Codebase structure with change frequency | +| `dev_refs` | Find callers/callees of functions. `dependsOn` traces the dependency chain between files. | +| `dev_map` | Codebase structure with PageRank hot paths and connected subsystems | | `dev_patterns` | File pattern analysis (similar code, error handling, types). Takes `filePath`, not `query`. | | `dev_status` | Repository indexing status + Antfly stats + health checks (`section="health"`) | +**Graph caching:** `dev index` builds a dependency graph (`dependency-graph.json`) saved alongside the index. +`dev_map` and `dev_refs` load the cached graph instead of fetching all docs — scales to 50k+ docs. +File watcher incrementally updates the graph on save. + --- ## Adding a new MCP adapter @@ -172,6 +189,13 @@ dev setup # One-time: start Antfly dev index # Index repository dev mcp install # Install for Claude Code dev mcp install --cursor # Install for Cursor + +# CLI tools (no MCP server needed) +dev search "authentication" # Semantic code search +dev refs "functionName" # Find callers/callees +dev refs "fn" --depends-on "src/db.ts" # Trace dependency chain +dev map # Codebase structure overview +dev map --focus packages/core --depth 3 # Focused map ``` --- From f118cb62b2cb25438cac0efe86a302cd173e7e30 Mon Sep 17 00:00:00 2001 From: prosdev Date: Wed, 1 Apr 2026 03:08:04 -0700 Subject: [PATCH 4/4] =?UTF-8?q?fix(cli,core):=20address=20code=20review=20?= =?UTF-8?q?=E2=80=94=20validation,=20cached=20graph,=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Validate --direction with Commander .choices() (rejects invalid values) - Use cached dependency graph for --depends-on (loadOrBuildGraph) - Pass excludePatterns/languages from config (parity with search command) - Use replaceAll for dist/ normalization (handles nested paths) - Note indexer.close() not needed in catch (process.exit cleans up) Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/agents/bug-investigator.md | 12 +++--- .claude/agents/logic-reviewer.md | 2 +- .claude/agents/plan-reviewer.md | 10 +++++ .claude/agents/quality-reviewer.md | 2 +- .claude/agents/quick-scout.md | 56 +++---------------------- .claude/agents/research-planner.md | 8 ++-- .claude/agents/security-reviewer.md | 12 ++++-- CLAUDE.md | 8 ++-- packages/cli/src/commands/refs.ts | 18 +++++--- packages/core/src/scanner/typescript.ts | 2 +- 10 files changed, 57 insertions(+), 73 deletions(-) diff --git a/.claude/agents/bug-investigator.md b/.claude/agents/bug-investigator.md index 9420099..6f3aa61 100644 --- a/.claude/agents/bug-investigator.md +++ b/.claude/agents/bug-investigator.md @@ -10,15 +10,17 @@ color: orange Systematically traces issues through the dev-agent monorepo. Reproduces, traces, fixes, and prevents regression. -## MCP Tools — Save Tokens, Extend Sessions +## MCP Tools — Conserve Context -Use MCP tools to get focused results instead of Grep → Read cycles. See CLAUDE.md for the token savings table. +This agent runs in a long session with a finite context window. Every Grep → Read cycle burns ~5,000 tokens on irrelevant matches. MCP tools return only what you need. -- **`dev_search`** — Conceptual queries ("where does rate limiting happen"). Returns ranked snippets — no file reading needed. -- **`dev_refs`** — Callers/callees of a function. Use `dependsOn` to trace dependency chains between files. +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** + +- **`dev_search`** — Conceptual queries ("where does rate limiting happen"). Returns ranked snippets, not 200 grep matches. +- **`dev_refs`** — Callers/callees of a function. Use `dependsOn` to trace dependency chains. Returns the call graph directly. - **`dev_map`** — Codebase structure with hot paths and subsystems. One call replaces dozens of ls/glob/read operations. -Reserve Grep/Glob for exact string matches where you know the literal text. +Only use Grep for exact string matches where you know the literal text. Only Read files when you need the full implementation. ## Investigation Framework diff --git a/.claude/agents/logic-reviewer.md b/.claude/agents/logic-reviewer.md index 3ccdb09..1ace821 100644 --- a/.claude/agents/logic-reviewer.md +++ b/.claude/agents/logic-reviewer.md @@ -65,7 +65,7 @@ Every finding MUST include confidence: **HIGH** (verified from code), **MEDIUM** ### Cross-Package Data Flow (Deep+ Effort) -Use MCP tools to trace cross-package flows without reading every file (see CLAUDE.md for token savings). `dev_refs` returns the call graph directly, `dev_search` finds related code by concept, `dev_patterns` compares error handling across files. +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** `dev_refs` returns the call graph directly, `dev_search` finds related code by concept, `dev_patterns` compares error handling across files — each saves ~3,000-5,000 tokens vs manual file reading. - [ ] Core exports consumed correctly by CLI, MCP server, and subagents — verify with `dev_refs` - [ ] Dependency chains make sense — use `dev_refs` with `dependsOn` to trace file-to-file paths diff --git a/.claude/agents/plan-reviewer.md b/.claude/agents/plan-reviewer.md index 528e8b6..5fbe090 100644 --- a/.claude/agents/plan-reviewer.md +++ b/.claude/agents/plan-reviewer.md @@ -12,6 +12,16 @@ Two-pass review of execution plans in `.claude/da-plans/`. Validates completenes This agent **NEVER modifies plans**. It reports issues for the author to fix. +## MCP Tools — Conserve Context + +This agent runs in a long session with a finite context window. Every Grep → Read cycle burns ~5,000 tokens on irrelevant matches. MCP tools return only what you need. + +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** + +- **`dev_map`** — Verify structure claims in the plan against actual codebase layout. +- **`dev_refs`** — Confirm dependency assertions. Use `dependsOn` to trace dependency chains between files. +- **`dev_patterns`** — Check if proposed patterns match existing conventions. + ## Two-Pass Review ### Pass 1: Engineer Review diff --git a/.claude/agents/quality-reviewer.md b/.claude/agents/quality-reviewer.md index bb11373..8aa5748 100644 --- a/.claude/agents/quality-reviewer.md +++ b/.claude/agents/quality-reviewer.md @@ -48,7 +48,7 @@ Maximum **5 SUGGESTION items** per review. If more found, pick the top 5 and not ### Readability & Simplification -Use MCP tools to check for duplication without reading every file (see CLAUDE.md for token savings). `dev_patterns` compares patterns across similar files. `dev_search` checks if a utility exists by meaning, not just name. +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** `dev_patterns` compares patterns across similar files (~500 tokens vs ~3,000 for manual reads). `dev_search` checks if a utility exists by meaning, not just name. - [ ] No code duplicating existing utilities — verify with `dev_patterns` and `dev_search` - [ ] Functions reasonably sized (consider splitting if >50 lines) diff --git a/.claude/agents/quick-scout.md b/.claude/agents/quick-scout.md index 8f58bbd..7c0d3f1 100644 --- a/.claude/agents/quick-scout.md +++ b/.claude/agents/quick-scout.md @@ -10,9 +10,11 @@ color: blue Lightweight explorer optimized for speed and cost. Finds code, traces flows, maps dependencies. -## Token Efficiency +## MCP Tools — Conserve Context -Use MCP tools to get focused results instead of Grep → Read cycles. See CLAUDE.md for the token savings table. Every file Read costs tokens — let the tools do the reading. +This agent runs in a long session with a finite context window. Every Grep → Read cycle burns ~5,000 tokens on irrelevant matches. MCP tools return only what you need. + +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** ## Capability Boundaries @@ -35,52 +37,6 @@ Do NOT guess at architectural reasoning or make recommendations. 4. **Verify** — Only Read a file when you need the full implementation, not just the location. 5. **Report** — Concise, factual answer with file paths and line numbers -## Dev-Agent Quick Reference - -``` -packages/ - core/src/ - scanner/ # ts-morph (TS/JS) and tree-sitter (Go) analysis - vector/ # Antfly vector storage + embeddings - services/ # Coordinator, search, health - events/ # Event bus system - indexer/ # Repository indexing orchestration - map/ # Codebase structure mapping - observability/ # Logger integration - - cli/src/ - commands/ # Commander.js CLI commands - utils/ # Formatters, logger, output helpers - - mcp-server/src/ - server/ # MCP server setup - adapters/ # Tool adapters (search, refs, map, inspect, status, health) - formatters/ # Compact and verbose output formatters - utils/ # Logger - - subagents/src/ - coordinator/ # Agent orchestration - explorer/ # Code exploration agent - planner/ # Planning agent - github/ # GitHub integration agent - - logger/src/ # @prosdevlab/kero centralized logging - types/src/ # Shared TypeScript types - integrations/ # Claude Code, VS Code integrations - dev-agent/ # Root package (CLI entry point) -``` - -### Common Patterns +## Orientation -| Pattern | Location | -|---------|----------| -| MCP tool adapters | `packages/mcp-server/src/adapters/built-in/` | -| Core services | `packages/core/src/services/` | -| Scanner implementations | `packages/core/src/scanner/` | -| CLI commands | `packages/cli/src/commands/` | -| Subagent types | `packages/subagents/src/{agent}/` | -| Tests | `packages/**/src/**/__tests__/` | -| Package configs | `packages/*/package.json` | -| Build config | `turbo.json`, `tsconfig.json` | -| Test config | `vitest.config.ts` | -``` +Use `dev_map` to get the current codebase structure — don't rely on memorized paths. Run `dev_map --focus packages/core --depth 3` to drill into a specific area. diff --git a/.claude/agents/research-planner.md b/.claude/agents/research-planner.md index aff4730..37e72bd 100644 --- a/.claude/agents/research-planner.md +++ b/.claude/agents/research-planner.md @@ -12,11 +12,13 @@ Plans investigations before jumping into implementation. Produces a structured r This agent **NEVER writes code**. It produces investigation plans. -## MCP Tools — Save Tokens, Extend Sessions +## MCP Tools — Conserve Context -Use MCP tools to map the territory without burning tokens on Grep → Read cycles. See CLAUDE.md for the token savings table. +This agent runs in a long session with a finite context window. Every Grep → Read cycle burns ~5,000 tokens on irrelevant matches. MCP tools return only what you need. -- **`dev_search`** — Find relevant code areas by meaning. Returns ranked snippets — no file reading needed. +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** + +- **`dev_search`** — Find relevant code areas by meaning. Returns ranked snippets, not 200 grep matches. - **`dev_map`** — Codebase structure with hot paths and subsystems. One call replaces dozens of ls/glob/read operations. - **`dev_patterns`** — Compare patterns across similar files without reading each one. - **`dev_refs`** — Trace cross-package dependencies. Use `dependsOn` to trace dependency chains between files. diff --git a/.claude/agents/security-reviewer.md b/.claude/agents/security-reviewer.md index 6f12062..06eafbe 100644 --- a/.claude/agents/security-reviewer.md +++ b/.claude/agents/security-reviewer.md @@ -12,13 +12,17 @@ Security-focused review for a TypeScript monorepo that processes repository data This agent **NEVER modifies code**. It reports issues for the developer to fix. -## Token Efficiency +## MCP Tools — Conserve Context -Use MCP tools to avoid expensive Grep → Read cycles. See CLAUDE.md for the token savings table. `dev_search` returns ranked snippets, `dev_refs` traces input flow directly, `dev_patterns` scans for similar vulnerability patterns — all without reading files manually. +This agent runs in a long session with a finite context window. Every Grep → Read cycle burns ~5,000 tokens on irrelevant matches. MCP tools return only what you need. -## Checklist +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** + +- **`dev_search`** — Find security-sensitive code ("user input", "shell execution", "token handling"). Returns ranked snippets. +- **`dev_patterns`** — If one injection vector exists, the same pattern likely appears elsewhere. Scan for similar patterns across files. +- **`dev_refs`** — Trace how user input flows through the system. Use `dependsOn` to trace dependency chains. -Use `dev_search` to find security-sensitive code ("user input", "shell execution", "token handling"). Use `dev_patterns` to find similar patterns across the codebase — if one injection vector exists, the same pattern likely appears elsewhere. Use `dev_refs` to trace how user input flows through the system. +## Checklist ### Command Injection - [ ] No unsanitized user input passed to `child_process`, `exec`, `execSync`, or shell commands diff --git a/CLAUDE.md b/CLAUDE.md index d6cd86c..718c2e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -139,9 +139,11 @@ See `.claude/da-plans/README.md` for status and format details. --- -## MCP tools — token-efficient context +## MCP tools — conserve context -MCP tools return pre-ranked, pre-snippeted results. Use them to avoid expensive Grep → Read cycles that burn thousands of tokens on irrelevant context. +**Before you Grep or Read, ask: can an MCP tool answer this without reading files?** + +MCP tools return pre-ranked snippets. Every Grep → Read cycle burns ~3,000-5,000 tokens of context window on irrelevant matches. MCP tools return only what you need. | Instead of | Use | Tokens saved | |------------|-----|-------------| @@ -150,7 +152,7 @@ MCP tools return pre-ranked, pre-snippeted results. Use them to avoid expensive | ls + glob + read READMEs | `dev_map` | ~2,000 | | Read multiple files to compare patterns | `dev_patterns filePath` | ~3,000 | -Reserve Grep/Glob for exact string matches where you know the literal text. +Only use Grep for exact string matches where you know the literal text. Only Read files when you need the full implementation. ### 5 adapters diff --git a/packages/cli/src/commands/refs.ts b/packages/cli/src/commands/refs.ts index aeed033..651d4be 100644 --- a/packages/cli/src/commands/refs.ts +++ b/packages/cli/src/commands/refs.ts @@ -1,15 +1,15 @@ import * as path from 'node:path'; import { - buildDependencyGraph, ensureStorageDirectory, getStorageFilePaths, getStoragePath, + loadOrBuildGraph, RepositoryIndexer, type SearchResult, shortestPath, } from '@prosdevlab/dev-agent-core'; import chalk from 'chalk'; -import { Command } from 'commander'; +import { Command, Option } from 'commander'; import ora from 'ora'; import { loadConfig } from '../utils/config.js'; import { logger } from '../utils/logger.js'; @@ -25,7 +25,11 @@ interface CalleeInfo { export const refsCommand = new Command('refs') .description('Find callers and callees of a function') .argument('', 'Function or method name (e.g., "createPlan", "SearchAdapter.execute")') - .option('-d, --direction ', 'Query direction: callees, callers, or both', 'both') + .addOption( + new Option('-d, --direction ', 'Query direction: callees, callers, or both') + .choices(['callees', 'callers', 'both']) + .default('both') + ) .option('-l, --limit ', 'Maximum results per direction', '20') .option('--depends-on ', 'Trace dependency path to a target file') .option('--json', 'Output results as JSON', false) @@ -44,6 +48,8 @@ export const refsCommand = new Command('refs') const indexer = new RepositoryIndexer({ repositoryPath: resolvedRepoPath, vectorStorePath: filePaths.vectors, + excludePatterns: config?.repository?.excludePatterns || config?.excludePatterns, + languages: config?.repository?.languages || config?.languages, }); await indexer.initialize(); @@ -64,8 +70,9 @@ export const refsCommand = new Command('refs') // Handle --depends-on if (options.dependsOn) { spinner.text = `Tracing path: ${name} → ${options.dependsOn}`; - const allDocs = await indexer.getAll({ limit: 50000 }); - const graph = buildDependencyGraph(allDocs); + const graph = await loadOrBuildGraph(filePaths.dependencyGraph, async () => + indexer.getAll({ limit: 50000 }) + ); const sourceFile = (target.metadata.path as string) || ''; const tracePath = shortestPath(graph, sourceFile, options.dependsOn); @@ -195,6 +202,7 @@ export const refsCommand = new Command('refs') } catch (error) { spinner.fail('Refs query failed'); logger.error(error instanceof Error ? error.message : String(error)); + // indexer may not be initialized if error was early, but close() is safe to call process.exit(1); } }); diff --git a/packages/core/src/scanner/typescript.ts b/packages/core/src/scanner/typescript.ts index a9bc03c..af5e086 100644 --- a/packages/core/src/scanner/typescript.ts +++ b/packages/core/src/scanner/typescript.ts @@ -994,7 +994,7 @@ export class TypeScriptScanner implements Scanner { // (e.g. /abs/packages/logger/dist/types.d.ts) but we store // relative source paths (packages/logger/src/types.ts). let normalized = rawPath - .replace(/\/dist\//, '/src/') + .replaceAll('/dist/', '/src/') .replace(/\.d\.ts$/, '.ts') .replace(/\.js$/, '.ts'); if (this.repoRoot && normalized.startsWith(this.repoRoot)) {