Skip to content

Commit 8dbae7d

Browse files
committed
fix: Fix depth parameter defaults for single vs multi queries
1 parent 10fadba commit 8dbae7d

6 files changed

Lines changed: 254 additions & 8 deletions

File tree

CLAUDE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,13 @@ codebase outline "src/**/*.ts" --dry-run # 预览匹配的文件
111111
codebase outline --clear-cache # 清除摘要缓存
112112

113113
# 调用图分析
114-
codebase call --query="functionA,functionB" # 查询函数调用关系
114+
codebase call --query="functionA,functionB" # 查询函数调用关系(多函数连接分析,默认深度10)
115+
codebase call --query="main" # 查询单个函数的调用树(默认深度3)
115116
codebase call src/commands # 分析指定目录
116117
codebase call --output=graph.json # 导出分析结果
117118
codebase call --open # 打开可视化图表查看器
118-
codebase call --depth=3 # 设置分析深度
119+
codebase call --query="main" --depth=5 # 自定义调用树深度
120+
codebase call --query="app,addUser" --depth=15 # 自定义路径搜索深度
119121
codebase call --path=/workspace # 指定工作空间路径
120122

121123
# stdio 适配器

CONFIG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ codebase --force index
9090
- `--json` - Output search results in JSON format
9191
- `call [path]` - Analyze code dependencies (file or directory)
9292
- `--query <names>` - Query dependencies for specific names (comma-separated, for call command)
93-
- `--depth <number>` - Query depth for dependency traversal (default: 10, for call command)
93+
- `--depth <number>` - Query depth for dependency traversal (default: 3 for single query, 10 for multi-query, for call command)
9494
- `--output <file>` - Export dependency data to JSON file (for call command)
9595
- `--open` - Open HTML visualization in browser (for call command)
9696
- `--clear-cache` - Clear dependency analysis cache (for call command)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ codebase call --path=/my/project
164164
- **Wildcards**: `*` (any characters), `?` (single character)
165165
- Examples: `--query="get*"`, `--query="*User*"`, `--query="*.*.get*"`
166166
- **Single pattern**: `--query="main"` - Shows dependency tree (what it calls, who calls it)
167-
- Use `--depth` to control tree depth (default: 10)
167+
- Use `--depth` to control tree depth (default: 3)
168168
- **Multiple patterns**: `--query="main,helper"` - Analyzes connections between functions
169-
- Connection search depth is fixed at 10 (--depth is ignored)
169+
- Use `--depth` to control path search depth (default: 10)
170170
171171
**Supported Languages:**
172172
- **TypeScript/JavaScript** (.ts, .tsx, .js, .jsx)

docs/plans/260117-dependency-cli.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,4 +1011,67 @@ analyzeConnections(result.nodes, 'functionA,functionB', 10)
10111011
**总结:**
10121012
本次修订实现了 depth 参数在单函数和多函数查询中的统一控制,同时根据不同查询类型的特点设置了合理的默认值。提升了 CLI 的灵活性和易用性。
10131013
1014+
### 修订6:Summary 模式支持 JSON 输出(2026-01-23)
1015+
1016+
**问题:**
1017+
用户执行 `npx tsx src/cli.ts call --demo --json` 时,`--json` 参数未生效,仍然输出格式化文本而非 JSON。
1018+
1019+
**原因:**
1020+
- Summary 模式(默认模式)的 `displaySummary` 函数未实现 JSON 输出
1021+
- `--json` 参数仅在 Query 模式(需要 `--query` 参数)下工作
1022+
- 命令进入 Summary 模式时忽略了 `--json` 参数
1023+
1024+
**修复:**
1025+
1026+
1. 修改 `displaySummary` 函数签名,添加 `asJson` 参数:
1027+
```typescript
1028+
function displaySummary(result: AnalysisResult, asJson: boolean = false): void
1029+
```
1030+
1031+
2. 添加 JSON 输出逻辑(src/commands/call.ts:114-158):
1032+
```typescript
1033+
if (asJson) {
1034+
const componentTypesObj: Record<string, any> = {};
1035+
for (const [type, count] of componentTypes.entries()) {
1036+
const examples = Array.from(nodes.entries())
1037+
.filter(([_, node]) => node.componentType === type)
1038+
.slice(0, MAX_EXAMPLES)
1039+
.map(([id, _]) => id);
1040+
componentTypesObj[type] = { count, examples };
1041+
}
1042+
1043+
const jsonOutput = {
1044+
summary: {
1045+
totalFiles: summary.totalFiles,
1046+
totalNodes: summary.totalNodes,
1047+
totalRelationships: summary.totalRelationships,
1048+
languages: summary.languages,
1049+
cycleCount: cycles.length,
1050+
},
1051+
componentTypes: componentTypesObj,
1052+
topModules: topModules.map(([module, count]) => ({ module, dependencies: count })),
1053+
relationships: {
1054+
resolved: { count, examples: [...] },
1055+
unresolved: { count, examples: [...] }
1056+
},
1057+
};
1058+
1059+
console.log(JSON.stringify(jsonOutput, null, 2));
1060+
return;
1061+
}
1062+
```
1063+
1064+
3. 修改 `callHandler` 传递 `options.json` 参数(src/commands/call.ts:513):
1065+
```typescript
1066+
displaySummary(result, options.json);
1067+
```
1068+
1069+
**验证:**
1070+
- ✅ `npx tsx src/cli.ts call --demo --json` - 输出 JSON 格式
1071+
- ✅ `npx tsx src/cli.ts call --demo` - 输出格式化文本(保持兼容)
1072+
- ✅ `npx tsx src/cli.ts call --demo --json --query="greetUser"` - Query 模式 JSON 输出正常
1073+
1074+
**总结:**
1075+
本次修订使 `--json` 参数在所有模式下保持一致,提升了 CLI 的用户体验和可预测性。JSON 输出格式与现有的格式化文本输出保持了信息对等。
1076+
10141077
## 总结
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# 调用图边检测限制问题
2+
3+
## 主题
4+
5+
记录 `codebase call` 多函数查询时静态分析无法追踪属性访问导致的边丢失问题。
6+
7+
## 代码背景
8+
9+
`codebase call` 命令用于分析函数之间的调用关系,支持:
10+
- 单函数查询:显示完整的调用树(被谁调用 + 调用谁)
11+
- 多函数查询:查找多个函数之间的连接关系
12+
13+
核心实现位于 `src/dependency/query.ts``src/dependency/analyzers/base.ts`
14+
15+
## 问题描述
16+
17+
### 现象
18+
19+
```bash
20+
# 单函数查询 - 正常显示调用树
21+
codebase call --query="indexHandler" --depth=10
22+
# 显示:indexHandler → initializeManager → startIndexing → ...
23+
24+
# 多函数查询 - 找不到边
25+
codebase call --query="scanDirectory,indexHandler"
26+
# 输出:
27+
# Found 2 matching node(s):
28+
# - src/code-index/processors/scanner.DirectoryScanner.scanDirectory
29+
# - src/commands/index.indexHandler
30+
# Direct connections: (none)
31+
# Chains found: (none)
32+
```
33+
34+
### 实际调用链
35+
36+
```
37+
indexHandler (src/commands/index.ts:232-385)
38+
└── initializeManager (src/commands/shared.ts:118-155)
39+
└── CodeIndexManager.startIndexing (src/code-index/manager.ts:199-216)
40+
└── CodeIndexOrchestrator.startIndexing (src/code-index/orchestrator.ts:142-375)
41+
└── this.scanner.scanDirectory() ← 调用链在此断裂!
42+
```
43+
44+
## 根本原因
45+
46+
**静态分析无法追踪属性访问(property access)**
47+
48+
### 调用类型与识别能力
49+
50+
| 调用类型 | 示例 | 静态分析能否识别 |
51+
|---------|------|-----------------|
52+
| 直接调用 | `func()` | ✅ 能 |
53+
| 成员调用 | `obj.method()` | ✅ 能 |
54+
| 属性访问调用 | `this.scanner.scanDirectory()` | ❌ 不能 |
55+
56+
### 技术细节
57+
58+
依赖分析基于 AST 静态解析,核心逻辑在 `src/dependency/analyzers/base.ts:208-248`
59+
60+
```typescript
61+
// 能识别的调用
62+
if (callee.type === 'identifier') {
63+
// 全局直接调用
64+
this.addEdge(caller, calleeInfo.name, ...)
65+
}
66+
67+
// 成员调用也能识别
68+
if (callee.type === 'member_expression') {
69+
// console.log() 等
70+
this.addEdge(caller, calleeInfo.fullPath, ...)
71+
}
72+
```
73+
74+
但对于 `this.scanner.scanDirectory()`
75+
- `this.scanner` 是实例属性,AST 中表示为 `member_expression`
76+
- `this.scanner` 的运行时类型无法通过静态分析确定
77+
- 工具不知道 `this.scanner` 指向 `DirectoryScanner` 实例
78+
79+
### 相关代码位置
80+
81+
1. **调用信息提取**`src/dependency/analyzers/base.ts:644-695` (`extractCallInfo`)
82+
2. **调用边添加**`src/dependency/analyzers/base.ts:208-248` (`traverseForCalls`)
83+
3. **多函数连接分析**`src/dependency/query.ts:332-380` (`findShortestPath`, `findChains`)
84+
85+
## 实施计划
86+
87+
### 方案 1:属性访问追踪(复杂)
88+
89+
`traverseForCalls` 中识别 `this.property.method()` 模式:
90+
- 记录 `this.property` 的赋值来源
91+
- 通过数据流分析追踪属性指向
92+
- **缺点**:实现复杂,可能影响性能
93+
94+
### 方案 2:注册表映射(折中)
95+
96+
在类初始化时记录实例属性类型:
97+
- `orchestrator.scanner``DirectoryScanner`
98+
- 解析时查询映射表
99+
- **缺点**:需要手动维护映射
100+
101+
### 方案 3:文档说明(简单)
102+
103+
在帮助文档中说明限制:
104+
- 告知用户静态分析的局限性
105+
- 提供变通方案(如使用单函数查询)
106+
- **优点**:实现简单,无副作用
107+
108+
## 实施记录
109+
110+
### 2026-01-17
111+
112+
- 创建本文档记录问题
113+
- 分析根本原因:静态分析无法追踪属性访问
114+
- 评估三种解决方案
115+
116+
## 总结
117+
118+
### 经验教训
119+
120+
1. **静态分析有固有局限**:AST 解析只能看到语法结构,无法推断运行时类型
121+
2. **成员调用 vs 属性访问**`obj.method()` 能识别,但 `this.prop.method()` 难以追踪
122+
3. **工具定位要清晰**:依赖分析工具应明确定位为"静态调用图分析"
123+
124+
### 后续优化建议
125+
126+
1. 短期:在 CLI 帮助文档中说明静态分析的限制
127+
2. 中期:实现方案 2(注册表映射),提升常见模式的识别率
128+
3. 长期:考虑集成 TypeScript 编译器 API 进行更精确的分析
129+
130+
### 参考资源
131+
132+
- AST 解析基础:[tree-sitter 文档](https://tree-sitter.github.io/tree-sitter/)
133+
- TypeScript 编译器 API:[typescript-eslint](https://typescript-eslint.io/)
134+
```
135+
136+
如需调整内容或格式,请告知。

src/commands/call.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async function openGraphViewer(fileSystem: any): Promise<void> {
6969
/**
7070
* Format and display dependency analysis summary
7171
*/
72-
function displaySummary(result: AnalysisResult): void {
72+
function displaySummary(result: AnalysisResult, asJson: boolean = false): void {
7373
const { summary, nodes, relationships, cycles } = result;
7474

7575
// Maximum number of examples to display for each category
@@ -110,7 +110,52 @@ function displaySummary(result: AnalysisResult): void {
110110
.slice(0, MAX_EXAMPLES)
111111
.filter(([_, count]) => count > 0);
112112

113-
// Output summary
113+
// JSON output mode
114+
if (asJson) {
115+
const componentTypesObj: Record<string, any> = {};
116+
for (const [type, count] of componentTypes.entries()) {
117+
const examples = Array.from(nodes.entries())
118+
.filter(([_, node]) => node.componentType === type)
119+
.slice(0, MAX_EXAMPLES)
120+
.map(([id, _]) => id);
121+
componentTypesObj[type] = { count, examples };
122+
}
123+
124+
const jsonOutput = {
125+
summary: {
126+
totalFiles: summary.totalFiles,
127+
totalNodes: summary.totalNodes,
128+
totalRelationships: summary.totalRelationships,
129+
languages: summary.languages,
130+
cycleCount: cycles.length,
131+
},
132+
componentTypes: componentTypesObj,
133+
topModules: topModules.map(([module, count]) => ({ module, dependencies: count })),
134+
relationships: {
135+
resolved: {
136+
count: resolvedEdges.length,
137+
examples: resolvedEdges.slice(0, MAX_EXAMPLES).map(edge => ({
138+
caller: edge.caller,
139+
callee: edge.callee,
140+
callLine: edge.callLine,
141+
})),
142+
},
143+
unresolved: {
144+
count: unresolvedEdges.length,
145+
examples: unresolvedEdges.slice(0, MAX_EXAMPLES).map(edge => ({
146+
caller: edge.caller,
147+
callee: edge.callee,
148+
callLine: edge.callLine,
149+
})),
150+
},
151+
},
152+
};
153+
154+
console.log(JSON.stringify(jsonOutput, null, 2));
155+
return;
156+
}
157+
158+
// Text output mode (original)
114159
console.log('\nDependency Analysis Summary');
115160
console.log('==========================');
116161
console.log(`Files: ${summary.totalFiles}`);
@@ -466,7 +511,7 @@ async function callHandler(targetPath: string | undefined, options: CommandOptio
466511
}
467512
} else {
468513
// Summary mode (default) - Task 2
469-
displaySummary(result);
514+
displaySummary(result, options.json);
470515
}
471516

472517
// Display errors if any

0 commit comments

Comments
 (0)