@mycodemap/mycodemap 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -6
- package/README.md +172 -80
- package/dist/cli/commands/cycles.d.ts.map +1 -1
- package/dist/cli/commands/cycles.js +2 -0
- package/dist/cli/commands/cycles.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +5 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +189 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/report.d.ts +12 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +158 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/watch-foreground.d.ts.map +1 -1
- package/dist/cli/commands/watch-foreground.js +2 -0
- package/dist/cli/commands/watch-foreground.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +2 -0
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/first-run-guide.d.ts +23 -0
- package/dist/cli/first-run-guide.d.ts.map +1 -0
- package/dist/cli/first-run-guide.js +83 -0
- package/dist/cli/first-run-guide.js.map +1 -0
- package/dist/cli/index.js +63 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/platform-check.d.ts +21 -0
- package/dist/cli/platform-check.d.ts.map +1 -0
- package/dist/cli/platform-check.js +94 -0
- package/dist/cli/platform-check.js.map +1 -0
- package/dist/cli/tree-sitter-check.d.ts +35 -0
- package/dist/cli/tree-sitter-check.d.ts.map +1 -0
- package/dist/cli/tree-sitter-check.js +133 -0
- package/dist/cli/tree-sitter-check.js.map +1 -0
- package/dist/cli/utils/sanitize.d.ts +54 -0
- package/dist/cli/utils/sanitize.d.ts.map +1 -0
- package/dist/cli/utils/sanitize.js +131 -0
- package/dist/cli/utils/sanitize.js.map +1 -0
- package/docs/AI_ASSISTANT_SETUP.md +743 -0
- package/docs/CI_GATEWAY_DESIGN.md +784 -0
- package/docs/OMC_TEAM_DEBUG_REPORT.md +285 -0
- package/docs/PUBLISH_NPM_DESIGN_FINAL.md +485 -0
- package/docs/REFACTOR_ARCHITECTURE_OVERVIEW.md +552 -0
- package/docs/REFACTOR_CONFIDENCE_DESIGN.md +244 -0
- package/docs/REFACTOR_GIT_ANALYZER_DESIGN.md +785 -0
- package/docs/REFACTOR_ORCHESTRATOR_DESIGN.md +1065 -0
- package/docs/REFACTOR_REQUIREMENTS.md +970 -0
- package/docs/REFACTOR_RESULT_FUSION_DESIGN.md +315 -0
- package/docs/REFACTOR_TEST_LINKER_DESIGN.md +311 -0
- package/docs/SETUP_GUIDE.md +407 -0
- package/docs/archive/AI_INTEGRATION_GUIDE_ARCHIVED.md +385 -0
- package/docs/archive/PUBLISH_NPM_DESIGN_V1.md +1693 -0
- package/docs/archive/PUBLISH_NPM_DESIGN_V2.md +390 -0
- package/docs/archive/TASK_DESIGN_COVERAGE_REPORT.md +314 -0
- package/docs/plans/POST_TASK_PLAN.md +129 -0
- package/docs/plans/archive/2026-03-03-deps-path-extension-fix.md +186 -0
- package/examples/README.md +61 -0
- package/examples/claude/codemap-skill.md +94 -0
- package/examples/codex/codemap-agent.md +66 -0
- package/examples/copilot/copilot-instructions.md +24 -0
- package/examples/kimi/codemap-skill.md +92 -0
- package/package.json +5 -3
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
# Git 分析器详细设计
|
|
2
|
+
|
|
3
|
+
> 版本: 2.5
|
|
4
|
+
> 所属模块: 编排层 - Git 分析器
|
|
5
|
+
> 更新日期: 2026-02-28
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 功能定位
|
|
10
|
+
|
|
11
|
+
在影响分析场景中,分析文件的修改历史,评估修改风险。
|
|
12
|
+
|
|
13
|
+
**新增功能 (v2.4)**:
|
|
14
|
+
- 极简 Commit 格式验证 `[TAG]` 格式
|
|
15
|
+
- AI 饲料生成器 (`.mycodemap/ai-feed.txt`)
|
|
16
|
+
- 文件头注释扫描 `[META]`/`[WHY]`/`[DEPS]`
|
|
17
|
+
- 基于 GRAVITY/HEAT/IMPACT 的危险置信度计算
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. 数据结构设计
|
|
22
|
+
|
|
23
|
+
### 2.1 提交信息
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// src/orchestrator/git-analyzer.ts
|
|
27
|
+
|
|
28
|
+
interface CommitInfo {
|
|
29
|
+
hash: string;
|
|
30
|
+
message: string;
|
|
31
|
+
date: Date;
|
|
32
|
+
author: string;
|
|
33
|
+
files: string[];
|
|
34
|
+
tag?: CommitTag; // [TAG] 解析结果 (v2.4新增)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// v2.4 新增: 极简 Commit Tag
|
|
38
|
+
interface CommitTag {
|
|
39
|
+
type: 'BUGFIX' | 'FEATURE' | 'REFACTOR' | 'CONFIG' | 'DOCS' | 'DELETE' | 'UNKNOWN';
|
|
40
|
+
scope: string; // 模块名,如 "git-analyzer"
|
|
41
|
+
subject: string; // 提交描述
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2.2 风险评分 (v2.4 重构)
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
interface RiskScore {
|
|
49
|
+
level: 'high' | 'medium' | 'low';
|
|
50
|
+
score: number; // 0-1 综合分数
|
|
51
|
+
|
|
52
|
+
// 维度评分
|
|
53
|
+
gravity: number; // 依赖复杂度 (出度+入度)
|
|
54
|
+
heat: HeatScore; // 时间维度活跃度
|
|
55
|
+
impact: number; // 影响面 (被依赖数)
|
|
56
|
+
|
|
57
|
+
riskFactors: string[]; // 风险因素说明
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// v2.4 新增: 热度评分
|
|
61
|
+
interface HeatScore {
|
|
62
|
+
freq30d: number; // 30天内修改次数
|
|
63
|
+
lastType: string; // 最后提交标签 [BUGFIX]等
|
|
64
|
+
lastDate: Date | null; // 最后修改日期
|
|
65
|
+
stability: boolean; // 是否稳定 (沉积岩 vs 火山灰)
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2.3 AI 饲料数据结构 (v2.4 新增)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// src/orchestrator/ai-feed-generator.ts
|
|
73
|
+
|
|
74
|
+
interface AIFeed {
|
|
75
|
+
file: string;
|
|
76
|
+
gravity: number; // 依赖复杂度分数
|
|
77
|
+
heat: HeatScore; // 时间维度
|
|
78
|
+
meta: FileMeta; // 文件元数据
|
|
79
|
+
deps: string[]; // 依赖的文件
|
|
80
|
+
dependents: string[]; // 被哪些文件依赖
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface FileMeta {
|
|
84
|
+
since?: string; // 创建时间 2024-01
|
|
85
|
+
owner?: string; // 负责人/团队
|
|
86
|
+
stable?: boolean; // 是否稳定
|
|
87
|
+
why?: string; // 存在理由 (苏格拉底问题)
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2.4 文件头注释格式 (v2.4 新增)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// src/orchestrator/file-header-scanner.ts
|
|
95
|
+
|
|
96
|
+
interface FileHeader {
|
|
97
|
+
meta?: {
|
|
98
|
+
since?: string;
|
|
99
|
+
owner?: string;
|
|
100
|
+
stable?: boolean;
|
|
101
|
+
};
|
|
102
|
+
why?: string; // [WHY] 回答
|
|
103
|
+
deps?: string[]; // [DEPS] 关键依赖
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 扫描规则:
|
|
107
|
+
// - 检查文件前10行
|
|
108
|
+
// - 正则匹配: \/\/ \[META\] (.+)
|
|
109
|
+
// - 正则匹配: \/\/ \[WHY\] (.+)
|
|
110
|
+
// - 正则匹配: \/\/ \[DEPS\] (.+)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 3. 接口设计
|
|
116
|
+
|
|
117
|
+
### 3.1 Git 分析器类
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
class GitAnalyzer {
|
|
121
|
+
/**
|
|
122
|
+
* 查找相关提交
|
|
123
|
+
* 支持两种模式:关键词搜索 + 文件搜索
|
|
124
|
+
*/
|
|
125
|
+
async findRelatedCommits(
|
|
126
|
+
keywords: string[],
|
|
127
|
+
files: string[],
|
|
128
|
+
options: { maxCommits: number; projectRoot: string }
|
|
129
|
+
): Promise<CommitInfo[]> {
|
|
130
|
+
const commits: CommitInfo[] = [];
|
|
131
|
+
|
|
132
|
+
// 1. 关键词搜索(提交信息)
|
|
133
|
+
if (keywords.length > 0) {
|
|
134
|
+
const keywordResults = await this.searchByKeywords(keywords, options.maxCommits);
|
|
135
|
+
commits.push(...keywordResults);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 2. 文件搜索(修改过的文件)
|
|
139
|
+
if (files.length > 0) {
|
|
140
|
+
const fileResults = await this.searchByFiles(files, options.maxCommits);
|
|
141
|
+
commits.push(...fileResults);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 3. 去重 + 排序(按日期)
|
|
145
|
+
const unique = new Map<string, CommitInfo>();
|
|
146
|
+
for (const commit of commits) {
|
|
147
|
+
if (!unique.has(commit.hash)) {
|
|
148
|
+
unique.set(commit.hash, commit);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return Array.from(unique.values())
|
|
153
|
+
.sort((a, b) => b.date.getTime() - a.date.getTime())
|
|
154
|
+
.slice(0, options.maxCommits);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// v2.4 新增: 解析极简 Commit Tag
|
|
158
|
+
parseCommitTag(message: string): CommitTag | undefined {
|
|
159
|
+
const match = message.match(/^\[(BUGFIX|FEATURE|REFACTOR|CONFIG|DOCS|DELETE)\]\s*(.+?)?:(.+)$/);
|
|
160
|
+
if (!match) return undefined;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
type: match[1] as CommitTag['type'],
|
|
164
|
+
scope: match[2]?.trim() || 'general',
|
|
165
|
+
subject: match[3].trim()
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 3.2 关键词搜索
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
private async searchByKeywords(
|
|
175
|
+
keywords: string[],
|
|
176
|
+
limit: number
|
|
177
|
+
): Promise<CommitInfo[]> {
|
|
178
|
+
// 使用 execFile + 参数数组避免命令注入
|
|
179
|
+
// 同时限制关键词长度防止过度匹配
|
|
180
|
+
const sanitizedKeywords = keywords
|
|
181
|
+
.slice(0, 5) // 最多 5 个关键词
|
|
182
|
+
.map(k => k.slice(0, 100)) // 每个关键词最多 100 字符
|
|
183
|
+
.filter(k => /^[a-zA-Z0-9_\-\.]+$/.test(k)); // 仅允许安全字符
|
|
184
|
+
|
|
185
|
+
if (sanitizedKeywords.length === 0) return [];
|
|
186
|
+
|
|
187
|
+
const pattern = sanitizedKeywords.join('|');
|
|
188
|
+
const { stdout } = await execFile('git', [
|
|
189
|
+
'log', '--all', `--grep=${pattern}`,
|
|
190
|
+
'--format=%H|%s|%ai|%an',
|
|
191
|
+
'-n', String(limit)
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
return this.parseGitLog(stdout);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 3.3 文件搜索
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
private async searchByFiles(
|
|
202
|
+
files: string[],
|
|
203
|
+
limit: number
|
|
204
|
+
): Promise<CommitInfo[]> {
|
|
205
|
+
// git --follow 语义仅支持单路径,多文件需逐个查询后合并
|
|
206
|
+
const allCommits: CommitInfo[] = [];
|
|
207
|
+
|
|
208
|
+
for (const file of files) {
|
|
209
|
+
try {
|
|
210
|
+
const { stdout } = await execFile('git', [
|
|
211
|
+
'log', '--follow', '--format=%H|%s|%ai|%an', '--name-only',
|
|
212
|
+
'-n', String(limit), '--', file
|
|
213
|
+
]);
|
|
214
|
+
const fileCommits = this.parseGitLogWithFiles(stdout);
|
|
215
|
+
allCommits.push(...fileCommits);
|
|
216
|
+
} catch {
|
|
217
|
+
// 单文件查询失败时跳过
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 按哈希去重
|
|
222
|
+
const unique = new Map<string, CommitInfo>();
|
|
223
|
+
for (const commit of allCommits) {
|
|
224
|
+
if (!unique.has(commit.hash)) {
|
|
225
|
+
unique.set(commit.hash, commit);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return Array.from(unique.values());
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### 3.4 风险评分计算 (v2.4 重构)
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
/**
|
|
237
|
+
* 计算风险评分 (v2.4 重构版)
|
|
238
|
+
* 基于 GRAVITY / HEAT / IMPACT 三维模型
|
|
239
|
+
*/
|
|
240
|
+
calculateRiskScore(
|
|
241
|
+
targetFiles: string[],
|
|
242
|
+
commits: CommitInfo[],
|
|
243
|
+
feedData: AIFeed[] // AI 饲料数据
|
|
244
|
+
): RiskScore {
|
|
245
|
+
// 1. 获取文件饲料数据
|
|
246
|
+
const fileFeeds = feedData.filter(f => targetFiles.includes(f.file));
|
|
247
|
+
|
|
248
|
+
// 2. 计算 GRAVITY (依赖复杂度)
|
|
249
|
+
const avgGravity = fileFeeds.reduce((sum, f) => sum + f.gravity, 0) /
|
|
250
|
+
(fileFeeds.length || 1);
|
|
251
|
+
const gravityScore = Math.min(avgGravity / 20, 1); // 归一化到 0-1
|
|
252
|
+
|
|
253
|
+
// 3. 计算 HEAT (时间维度)
|
|
254
|
+
const totalFreq = fileFeeds.reduce((sum, f) => sum + f.heat.freq30d, 0);
|
|
255
|
+
const avgFreq = totalFreq / (fileFeeds.length || 1);
|
|
256
|
+
const freqScore = Math.min(avgFreq / 10, 1);
|
|
257
|
+
|
|
258
|
+
// 标签风险权重
|
|
259
|
+
const tagWeights: Record<string, number> = {
|
|
260
|
+
'BUGFIX': 0.9, // 修复过的代码 = 有风险
|
|
261
|
+
'REFACTOR': 0.8, // 重构过的代码
|
|
262
|
+
'FEATURE': 0.7, // 新功能
|
|
263
|
+
'CONFIG': 0.5,
|
|
264
|
+
'DOCS': 0.2,
|
|
265
|
+
'DELETE': 0.1,
|
|
266
|
+
'UNKNOWN': 0.5
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const lastTypes = fileFeeds.map(f => f.heat.lastType);
|
|
270
|
+
const avgTagRisk = lastTypes.reduce((sum, t) => sum + (tagWeights[t] || 0.5), 0) /
|
|
271
|
+
(lastTypes.length || 1);
|
|
272
|
+
|
|
273
|
+
// 4. 计算 IMPACT (影响面)
|
|
274
|
+
const totalDependents = fileFeeds.reduce((sum, f) => sum + f.dependents.length, 0);
|
|
275
|
+
const impactScore = Math.min(totalDependents / 50, 1);
|
|
276
|
+
|
|
277
|
+
// 5. 稳定性调整(与需求文档一致:不稳定文件增加风险)
|
|
278
|
+
const unstableCount = fileFeeds.filter(f => f.meta.stable === false).length;
|
|
279
|
+
const unstableRatio = unstableCount / (fileFeeds.length || 1);
|
|
280
|
+
const stabilityBoost = unstableRatio * 0.15;
|
|
281
|
+
|
|
282
|
+
// 6. 综合评分(单一真源:REFACTOR_REQUIREMENTS.md 8.6)
|
|
283
|
+
const totalScore = Math.min(
|
|
284
|
+
Math.max(
|
|
285
|
+
gravityScore * 0.30 +
|
|
286
|
+
freqScore * 0.15 +
|
|
287
|
+
avgTagRisk * 0.10 +
|
|
288
|
+
stabilityBoost +
|
|
289
|
+
impactScore * 0.10,
|
|
290
|
+
0
|
|
291
|
+
),
|
|
292
|
+
1
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const riskFactors: string[] = [];
|
|
296
|
+
if (gravityScore > 0.7) riskFactors.push('高依赖复杂度');
|
|
297
|
+
if (freqScore > 0.7) riskFactors.push('近期频繁修改');
|
|
298
|
+
if (avgTagRisk > 0.7) riskFactors.push('历史问题较多(BUGFIX频繁)');
|
|
299
|
+
if (impactScore > 0.7) riskFactors.push('影响面广');
|
|
300
|
+
if (unstableCount > 0) riskFactors.push('模块标记为不稳定');
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
level: totalScore > 0.7 ? 'high' : totalScore > 0.4 ? 'medium' : 'low',
|
|
304
|
+
score: totalScore,
|
|
305
|
+
gravity: gravityScore,
|
|
306
|
+
heat: {
|
|
307
|
+
freq30d: Math.round(avgFreq),
|
|
308
|
+
lastType: lastTypes[0] || 'UNKNOWN',
|
|
309
|
+
lastDate: fileFeeds[0]?.heat.lastDate || null,
|
|
310
|
+
stability: unstableCount === 0
|
|
311
|
+
},
|
|
312
|
+
impact: impactScore,
|
|
313
|
+
riskFactors
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### 3.5 日志解析
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
private parseGitLog(stdout: string): CommitInfo[] {
|
|
322
|
+
return stdout.trim().split('\n')
|
|
323
|
+
.filter(line => line.trim())
|
|
324
|
+
.map(line => {
|
|
325
|
+
const [hash, message, date, author] = line.split('|');
|
|
326
|
+
return {
|
|
327
|
+
hash,
|
|
328
|
+
message,
|
|
329
|
+
date: new Date(date),
|
|
330
|
+
author,
|
|
331
|
+
files: [],
|
|
332
|
+
tag: this.parseCommitTag(message) // v2.4 新增
|
|
333
|
+
};
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 4. AI 饲料生成器 (v2.4 新增)
|
|
341
|
+
|
|
342
|
+
### 4.1 核心类设计
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// src/orchestrator/ai-feed-generator.ts
|
|
346
|
+
|
|
347
|
+
class AIFeedGenerator {
|
|
348
|
+
private gitAnalyzer: GitAnalyzer;
|
|
349
|
+
private headerScanner: FileHeaderScanner;
|
|
350
|
+
|
|
351
|
+
constructor(gitAnalyzer: GitAnalyzer) {
|
|
352
|
+
this.gitAnalyzer = gitAnalyzer;
|
|
353
|
+
this.headerScanner = new FileHeaderScanner();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* 生成 AI 饲料
|
|
358
|
+
* 集成到 codemap generate 命令
|
|
359
|
+
*/
|
|
360
|
+
async generate(projectRoot: string): Promise<AIFeed[]> {
|
|
361
|
+
const files = await globby(['src/**/*.ts'], { cwd: projectRoot });
|
|
362
|
+
const feed: AIFeed[] = [];
|
|
363
|
+
|
|
364
|
+
// 第一遍:收集基础信息
|
|
365
|
+
for (const file of files) {
|
|
366
|
+
const fullPath = path.join(projectRoot, file);
|
|
367
|
+
feed.push({
|
|
368
|
+
file,
|
|
369
|
+
gravity: 0,
|
|
370
|
+
heat: this.scanGitHistory(file, projectRoot),
|
|
371
|
+
meta: this.headerScanner.scan(fullPath),
|
|
372
|
+
deps: [],
|
|
373
|
+
dependents: []
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 第二遍:计算依赖关系
|
|
378
|
+
const fileMap = new Map(feed.map(f => [f.file, f]));
|
|
379
|
+
|
|
380
|
+
for (const item of feed) {
|
|
381
|
+
const fullPath = path.join(projectRoot, item.file);
|
|
382
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
383
|
+
|
|
384
|
+
// 扫描 import 语句
|
|
385
|
+
const imports = [...content.matchAll(/from ['"](\.\.?\/.+?)['"]/g)]
|
|
386
|
+
.map(m => m[1] + '.ts');
|
|
387
|
+
|
|
388
|
+
item.deps = imports;
|
|
389
|
+
item.gravity = imports.length;
|
|
390
|
+
|
|
391
|
+
// 反向建立 dependents
|
|
392
|
+
for (const imp of imports) {
|
|
393
|
+
const target = fileMap.get(imp);
|
|
394
|
+
if (target) target.dependents.push(item.file);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// 重新计算 gravity (出度 + 入度)
|
|
399
|
+
for (const item of feed) {
|
|
400
|
+
item.gravity = item.deps.length + item.dependents.length;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return feed.sort((a, b) => b.gravity - a.gravity);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* 输出 AI 饲料文件
|
|
408
|
+
*/
|
|
409
|
+
writeFeedFile(feed: AIFeed[], outputPath: string): void {
|
|
410
|
+
let output = `# CODEMAP AI FEED\n`;
|
|
411
|
+
output += `# Generated: ${new Date().toISOString()}\n\n`;
|
|
412
|
+
|
|
413
|
+
for (const f of feed) {
|
|
414
|
+
output += `FILE: ${f.file}\n`;
|
|
415
|
+
output += `GRAVITY: ${f.gravity} | HEAT: ${f.heat.freq30d}/${f.heat.lastType}/${f.heat.lastDate || 'never'}\n`;
|
|
416
|
+
output += `META: since=${f.meta.since || 'unknown'} stable=${f.meta.stable} why=${f.meta.why || 'none'}\n`;
|
|
417
|
+
output += `IMPACT: ${f.dependents.length} files depend on this\n`;
|
|
418
|
+
output += `DEPS: ${f.deps.join(', ') || 'none'}\n`;
|
|
419
|
+
output += `---\n`;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
fs.writeFileSync(outputPath, output);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private scanGitHistory(filePath: string, projectRoot: string): HeatScore {
|
|
426
|
+
try {
|
|
427
|
+
const log = execSync(
|
|
428
|
+
`git log --since="30 days ago" --pretty=format:"%s" -- "${filePath}"`,
|
|
429
|
+
{ cwd: projectRoot, encoding: 'utf-8' }
|
|
430
|
+
);
|
|
431
|
+
const commits = log.split('\n').filter(Boolean);
|
|
432
|
+
|
|
433
|
+
const last = commits[0] || '';
|
|
434
|
+
const typeMatch = last.match(/^\[(\w+)\]/);
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
freq30d: commits.length,
|
|
438
|
+
lastType: typeMatch ? typeMatch[1] : 'UNKNOWN',
|
|
439
|
+
lastDate: execSync(
|
|
440
|
+
`git log -1 --pretty=format:"%ci" -- "${filePath}"`,
|
|
441
|
+
{ cwd: projectRoot, encoding: 'utf-8' }
|
|
442
|
+
).split(' ')[0] || null,
|
|
443
|
+
stability: commits.length < 3 // 30天内少于3次视为稳定
|
|
444
|
+
};
|
|
445
|
+
} catch {
|
|
446
|
+
return { freq30d: 0, lastType: 'NEW', lastDate: null, stability: true };
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### 4.2 文件头注释扫描器
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// src/orchestrator/file-header-scanner.ts
|
|
456
|
+
|
|
457
|
+
class FileHeaderScanner {
|
|
458
|
+
/**
|
|
459
|
+
* 扫描文件头注释
|
|
460
|
+
* 只读取文件前10行
|
|
461
|
+
*/
|
|
462
|
+
scan(filePath: string): FileMeta {
|
|
463
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
464
|
+
const lines = content.split('\n').slice(0, 10);
|
|
465
|
+
const header = lines.join('\n');
|
|
466
|
+
|
|
467
|
+
// 解析 [META]
|
|
468
|
+
const metaMatch = header.match(/\/\/ \[META\] (.+)/);
|
|
469
|
+
const meta: FileMeta = {};
|
|
470
|
+
|
|
471
|
+
if (metaMatch) {
|
|
472
|
+
const metaStr = metaMatch[1];
|
|
473
|
+
meta.since = metaStr.match(/since:(\S+)/)?.[1];
|
|
474
|
+
meta.owner = metaStr.match(/owner:(\S+)/)?.[1];
|
|
475
|
+
meta.stable = metaStr.includes('stable:true');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 解析 [WHY]
|
|
479
|
+
const whyMatch = header.match(/\/\/ \[WHY\] (.+)/);
|
|
480
|
+
if (whyMatch) {
|
|
481
|
+
meta.why = whyMatch[1].trim();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// 解析 [DEPS]
|
|
485
|
+
const depsMatch = header.match(/\/\/ \[DEPS\] (.+)/);
|
|
486
|
+
if (depsMatch) {
|
|
487
|
+
meta.deps = depsMatch[1].split(',').map(d => d.trim());
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return meta;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* 验证文件头是否完整
|
|
495
|
+
* CI 门禁使用
|
|
496
|
+
*/
|
|
497
|
+
validate(filePath: string): { valid: boolean; missing: string[] } {
|
|
498
|
+
const meta = this.scan(filePath);
|
|
499
|
+
const missing: string[] = [];
|
|
500
|
+
|
|
501
|
+
if (!meta.since) missing.push('[META] since');
|
|
502
|
+
if (!meta.why) missing.push('[WHY]');
|
|
503
|
+
|
|
504
|
+
return { valid: missing.length === 0, missing };
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## 5. 极简 Commit 格式验证 (v2.4 新增)
|
|
512
|
+
|
|
513
|
+
### 5.1 Commit Tag 正则
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// src/orchestrator/commit-validator.ts
|
|
517
|
+
|
|
518
|
+
const COMMIT_TAG_REGEX = /^\[(BUGFIX|FEATURE|REFACTOR|CONFIG|DOCS|DELETE)\]\s*(.+?)?:(.+)$/;
|
|
519
|
+
|
|
520
|
+
const TAG_DESCRIPTIONS: Record<string, string> = {
|
|
521
|
+
'BUGFIX': '修复问题',
|
|
522
|
+
'FEATURE': '新功能',
|
|
523
|
+
'REFACTOR': '重构',
|
|
524
|
+
'CONFIG': '配置变更',
|
|
525
|
+
'DOCS': '文档',
|
|
526
|
+
'DELETE': '删除代码'
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
class CommitValidator {
|
|
530
|
+
/**
|
|
531
|
+
* 验证 commit message 格式
|
|
532
|
+
* 用于 Git Hook 和 CI
|
|
533
|
+
*/
|
|
534
|
+
validate(message: string): { valid: boolean; error?: string } {
|
|
535
|
+
const lines = message.split('\n');
|
|
536
|
+
const firstLine = lines[0].trim();
|
|
537
|
+
|
|
538
|
+
// 检查第一行格式
|
|
539
|
+
const match = firstLine.match(COMMIT_TAG_REGEX);
|
|
540
|
+
if (!match) {
|
|
541
|
+
return {
|
|
542
|
+
valid: false,
|
|
543
|
+
error: this.formatError('提交信息必须以大写标签开头')
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const [, tag, scope, subject] = match;
|
|
548
|
+
|
|
549
|
+
// 验证标签有效性
|
|
550
|
+
if (!TAG_DESCRIPTIONS[tag]) {
|
|
551
|
+
return {
|
|
552
|
+
valid: false,
|
|
553
|
+
error: `无效的标签: [${tag}]`
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// scope 不能为空(极简方案要求强制 scope)
|
|
558
|
+
if (!scope || scope.trim() === '') {
|
|
559
|
+
return {
|
|
560
|
+
valid: false,
|
|
561
|
+
error: 'scope 不能为空,格式: [TAG] scope: message'
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// subject 不能为空
|
|
566
|
+
if (!subject || subject.trim() === '') {
|
|
567
|
+
return {
|
|
568
|
+
valid: false,
|
|
569
|
+
error: '提交描述不能为空'
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return { valid: true };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private formatError(msg: string): string {
|
|
577
|
+
const tags = Object.entries(TAG_DESCRIPTIONS)
|
|
578
|
+
.map(([tag, desc]) => ` [${tag}] ${desc}`)
|
|
579
|
+
.join('\n');
|
|
580
|
+
|
|
581
|
+
return `${msg}\n\n允许的格式:\n${tags}\n\n示例:\n [BUGFIX] git-analyzer: fix risk score calculation\n [FEATURE] orchestrator: add confidence scoring`;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## 6. 风险评分策略 (v2.4 更新)
|
|
589
|
+
|
|
590
|
+
### 6.1 三维评估模型
|
|
591
|
+
|
|
592
|
+
> **注意**: 风险评分公式以 `REFACTOR_REQUIREMENTS.md` 为单一真源。
|
|
593
|
+
> 此处实现应与需求文档保持一致。
|
|
594
|
+
|
|
595
|
+
| 维度 | 指标 | 计算方式 | 权重 |
|
|
596
|
+
|------|------|----------|------|
|
|
597
|
+
| **GRAVITY** | 依赖复杂度 | (出度 + 入度) / 20 | 30% |
|
|
598
|
+
| **HEAT.freq30d** | 修改频率 | 30天修改次数 / 10 | 15% |
|
|
599
|
+
| **HEAT.lastType** | 最后提交标签 | 标签风险值 | 10% |
|
|
600
|
+
| **IMPACT** | 影响面 | 被依赖文件数 / 50 | 10% |
|
|
601
|
+
| **STABILITY** | 稳定性 | 不稳定文件比例 * 0.15 | 15% |
|
|
602
|
+
|
|
603
|
+
### 6.2 标签风险权重
|
|
604
|
+
|
|
605
|
+
| 标签 | 风险值 | 说明 |
|
|
606
|
+
|------|--------|------|
|
|
607
|
+
| BUGFIX | 0.9 | 修复过的代码 = 曾经有问题 |
|
|
608
|
+
| REFACTOR | 0.8 | 重构过的代码 = 复杂度较高 |
|
|
609
|
+
| FEATURE | 0.7 | 新功能 = 可能不稳定 |
|
|
610
|
+
| CONFIG | 0.5 | 配置变更 = 中等风险 |
|
|
611
|
+
| DOCS | 0.2 | 文档 = 低风险 |
|
|
612
|
+
| DELETE | 0.1 | 删除代码 = 极低风险 |
|
|
613
|
+
| UNKNOWN | 0.5 | 未知 = 默认中等风险 |
|
|
614
|
+
|
|
615
|
+
### 6.3 风险等级
|
|
616
|
+
|
|
617
|
+
- `high`: 综合分数 > 0.7
|
|
618
|
+
- `medium`: 综合分数 > 0.4
|
|
619
|
+
- `low`: 综合分数 ≤ 0.4
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## 7. 模块依赖
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
Git 分析器 (git-analyzer.ts)
|
|
627
|
+
│
|
|
628
|
+
├── 依赖: execFile (子进程)
|
|
629
|
+
│
|
|
630
|
+
├── AI 饲料生成器 (ai-feed-generator.ts)
|
|
631
|
+
│ ├── 依赖: FileHeaderScanner
|
|
632
|
+
│ ├── 依赖: GitAnalyzer
|
|
633
|
+
│ └── 输出: .mycodemap/ai-feed.txt
|
|
634
|
+
│
|
|
635
|
+
├── Commit 验证器 (commit-validator.ts)
|
|
636
|
+
│ └── 用于: Git Hook / CI
|
|
637
|
+
│
|
|
638
|
+
└── 被以下模块使用:
|
|
639
|
+
└── AnalyzeCommand (analyze.ts)
|
|
640
|
+
└── CI Gateway (ci-gateway.ts)
|
|
641
|
+
└── WorkflowOrchestrator (workflow-orchestrator.ts) (v2.5 新增)
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## 10. 工作流上下文集成 (v2.5 规划)
|
|
647
|
+
|
|
648
|
+
### 10.1 Git 历史在工作流中的角色
|
|
649
|
+
|
|
650
|
+
在工作流编排中,Git 分析器提供每个阶段的上下文信息:
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
// 工作流阶段的 Git 分析配置
|
|
654
|
+
|
|
655
|
+
const PHASE_GIT_CONFIG: Record<WorkflowPhase, GitAnalysisConfig> = {
|
|
656
|
+
reference: {
|
|
657
|
+
// 参考搜索:查找相关提交作为参考
|
|
658
|
+
analysisType: 'find-similar-commits',
|
|
659
|
+
maxCommits: 10,
|
|
660
|
+
includeHistory: true,
|
|
661
|
+
extractPatterns: ['*.ts']
|
|
662
|
+
},
|
|
663
|
+
impact: {
|
|
664
|
+
// 影响分析:查找目标文件的修改历史
|
|
665
|
+
analysisType: 'file-history',
|
|
666
|
+
maxCommits: 20,
|
|
667
|
+
includeHistory: true,
|
|
668
|
+
extractPatterns: ['*.ts']
|
|
669
|
+
},
|
|
670
|
+
risk: {
|
|
671
|
+
// 风险评估:计算文件热度
|
|
672
|
+
analysisType: 'heat-analysis',
|
|
673
|
+
maxCommits: 30,
|
|
674
|
+
includeHistory: true,
|
|
675
|
+
timeWindow: '30d'
|
|
676
|
+
},
|
|
677
|
+
implementation: {
|
|
678
|
+
// 代码实现:记录实现意图
|
|
679
|
+
analysisType: 'none',
|
|
680
|
+
maxCommits: 0,
|
|
681
|
+
includeHistory: false
|
|
682
|
+
},
|
|
683
|
+
commit: {
|
|
684
|
+
// 提交:验证 Commit 格式
|
|
685
|
+
analysisType: 'validate-commit',
|
|
686
|
+
maxCommits: 1,
|
|
687
|
+
includeHistory: false
|
|
688
|
+
},
|
|
689
|
+
ci: {
|
|
690
|
+
// CI:完整历史分析
|
|
691
|
+
analysisType: 'full-analysis',
|
|
692
|
+
maxCommits: 50,
|
|
693
|
+
includeHistory: true
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### 10.2 工作流 Git 分析器集成
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
class WorkflowGitAnalyzer {
|
|
702
|
+
private analyzer: GitAnalyzer;
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* 根据当前阶段执行 Git 分析
|
|
706
|
+
*/
|
|
707
|
+
async analyzeForPhase(
|
|
708
|
+
phase: WorkflowPhase,
|
|
709
|
+
targetFiles: string[],
|
|
710
|
+
context: WorkflowContext
|
|
711
|
+
): Promise<GitAnalysisResult> {
|
|
712
|
+
const config = PHASE_GIT_CONFIG[phase];
|
|
713
|
+
|
|
714
|
+
if (config.analysisType === 'none') {
|
|
715
|
+
return { type: 'skipped', results: [] };
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// 从上下文获取历史结果(避免重复分析)
|
|
719
|
+
const cachedHistory = context.artifacts.get('impact')?.metadata?.gitHistory;
|
|
720
|
+
if (cachedHistory && phase !== 'risk') {
|
|
721
|
+
return { type: 'cached', results: cachedHistory };
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// 执行新的分析
|
|
725
|
+
switch (config.analysisType) {
|
|
726
|
+
case 'find-similar-commits':
|
|
727
|
+
return this.analyzer.findRelatedCommits(
|
|
728
|
+
context.task.split(' '),
|
|
729
|
+
targetFiles,
|
|
730
|
+
{ maxCommits: config.maxCommits, projectRoot: process.cwd() }
|
|
731
|
+
);
|
|
732
|
+
|
|
733
|
+
case 'file-history':
|
|
734
|
+
return this.analyzer.findRelatedCommits(
|
|
735
|
+
[],
|
|
736
|
+
targetFiles,
|
|
737
|
+
{ maxCommits: config.maxCommits, projectRoot: process.cwd() }
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
case 'heat-analysis':
|
|
741
|
+
return this.analyzer.calculateFileHeat(targetFiles);
|
|
742
|
+
|
|
743
|
+
case 'validate-commit':
|
|
744
|
+
return this.analyzer.validateCommit(context.task);
|
|
745
|
+
|
|
746
|
+
default:
|
|
747
|
+
return { type: 'none', results: [] };
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 8. 风险与缓解
|
|
756
|
+
|
|
757
|
+
| 风险 | 影响 | 缓解措施 |
|
|
758
|
+
|------|------|----------|
|
|
759
|
+
| Git 分析慢 | 大项目卡顿 | 限制提交数量 + 缓存 |
|
|
760
|
+
| git --follow 多文件问题 | 仅支持单文件 | 逐文件查询后合并 |
|
|
761
|
+
| 命令注入 | 安全漏洞 | 使用 execFile + 输入验证 |
|
|
762
|
+
| Commit 格式不兼容 | 迁移成本 | 提供 `codemap migrate` 命令批量转换 |
|
|
763
|
+
| 文件头注释遗漏 | CI 频繁失败 | `codemap generate --fix` 自动添加模板 |
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
## 9. 附录: AI 饲料示例
|
|
768
|
+
|
|
769
|
+
```text
|
|
770
|
+
# CODEMAP AI FEED
|
|
771
|
+
# Generated: 2026-02-28T19:30:00Z
|
|
772
|
+
|
|
773
|
+
FILE: src/orchestrator/git-analyzer.ts
|
|
774
|
+
GRAVITY: 15 | HEAT: 5/BUGFIX/2026-02-19
|
|
775
|
+
META: since=2024-03 stable=false why=分析Git历史,评估文件修改风险
|
|
776
|
+
IMPACT: 8 files depend on this
|
|
777
|
+
DEPS: src/types/index.ts, src/utils/exec.ts
|
|
778
|
+
---
|
|
779
|
+
FILE: src/utils/date.ts
|
|
780
|
+
GRAVITY: 0 | HEAT: 0/NEW/never
|
|
781
|
+
META: since=2023-06 stable=true why=日期工具函数,项目早期沉淀
|
|
782
|
+
IMPACT: 0 files depend on this
|
|
783
|
+
DEPS: none
|
|
784
|
+
---
|
|
785
|
+
```
|