ccjk 12.2.2 → 12.3.2
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/dist/chunks/agent-teams.mjs +3 -3
- package/dist/chunks/agent.mjs +2 -2
- package/dist/chunks/agents.mjs +4 -5
- package/dist/chunks/api-cli.mjs +5 -5
- package/dist/chunks/api-providers.mjs +1 -1
- package/dist/chunks/api.mjs +4 -4
- package/dist/chunks/auto-bootstrap.mjs +1 -1
- package/dist/chunks/auto-fixer.mjs +3 -3
- package/dist/chunks/auto-init.mjs +3 -3
- package/dist/chunks/auto-updater.mjs +9 -9
- package/dist/chunks/banner.mjs +2 -2
- package/dist/chunks/boost.mjs +4 -4
- package/dist/chunks/ccjk-agents.mjs +3 -3
- package/dist/chunks/ccjk-all.mjs +6 -6
- package/dist/chunks/ccjk-config.mjs +2 -2
- package/dist/chunks/ccjk-hooks.mjs +4 -4
- package/dist/chunks/ccjk-mcp.mjs +5 -5
- package/dist/chunks/ccjk-setup.mjs +5 -5
- package/dist/chunks/ccjk-skills.mjs +5 -5
- package/dist/chunks/ccr.mjs +14 -14
- package/dist/chunks/ccu.mjs +2 -2
- package/dist/chunks/check-updates.mjs +6 -6
- package/dist/chunks/claude-code-config-manager.mjs +4 -4
- package/dist/chunks/claude-code-incremental-manager.mjs +6 -6
- package/dist/chunks/claude-config.mjs +1 -1
- package/dist/chunks/claude-wrapper.mjs +1 -1
- package/dist/chunks/cli-hook.mjs +9 -10
- package/dist/chunks/codex-config-switch.mjs +6 -6
- package/dist/chunks/codex-provider-manager.mjs +6 -6
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/codex.mjs +5 -5
- package/dist/chunks/commands.mjs +2 -2
- package/dist/chunks/commands2.mjs +1 -1
- package/dist/chunks/commit.mjs +2 -2
- package/dist/chunks/completion.mjs +2 -2
- package/dist/chunks/config-consolidator.mjs +2 -2
- package/dist/chunks/config-switch.mjs +7 -7
- package/dist/chunks/config.mjs +3 -3
- package/dist/chunks/config2.mjs +4 -4
- package/dist/chunks/config3.mjs +3 -3
- package/dist/chunks/constants.mjs +1 -1
- package/dist/chunks/context-opt.mjs +442 -0
- package/dist/chunks/convoy-manager.mjs +355 -3
- package/dist/chunks/dashboard.mjs +2 -2
- package/dist/chunks/doctor.mjs +4 -4
- package/dist/chunks/evolution.mjs +2 -2
- package/dist/chunks/health-alerts.mjs +530 -4
- package/dist/chunks/help.mjs +1 -1
- package/dist/chunks/index.mjs +0 -23
- package/dist/chunks/index10.mjs +571 -634
- package/dist/chunks/index11.mjs +569 -1061
- package/dist/chunks/index12.mjs +1076 -914
- package/dist/chunks/index13.mjs +951 -135
- package/dist/chunks/index14.mjs +184 -209
- package/dist/chunks/index15.mjs +218 -0
- package/dist/chunks/index2.mjs +24 -19
- package/dist/chunks/index3.mjs +12 -19085
- package/dist/chunks/index4.mjs +19092 -16
- package/dist/chunks/index5.mjs +16 -7602
- package/dist/chunks/index6.mjs +7590 -159
- package/dist/chunks/index7.mjs +171 -1602
- package/dist/chunks/index8.mjs +1602 -19
- package/dist/chunks/index9.mjs +15 -612
- package/dist/chunks/init.mjs +13 -13
- package/dist/chunks/installer.mjs +5 -5
- package/dist/chunks/installer2.mjs +1 -1
- package/dist/chunks/interview.mjs +4 -4
- package/dist/chunks/manager.mjs +1 -1
- package/dist/chunks/marketplace.mjs +2 -2
- package/dist/chunks/mcp-cli.mjs +9 -9
- package/dist/chunks/mcp.mjs +7 -7
- package/dist/chunks/memory.mjs +3 -3
- package/dist/chunks/menu-hierarchical.mjs +14 -14
- package/dist/chunks/menu.mjs +12 -12
- package/dist/chunks/metrics-display.mjs +1 -1
- package/dist/chunks/migrator.mjs +1 -1
- package/dist/chunks/monitor.mjs +2 -2
- package/dist/chunks/notification.mjs +4 -4
- package/dist/chunks/onboarding-wizard.mjs +2 -2
- package/dist/chunks/onboarding.mjs +4 -4
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/paradigm.mjs +1 -1
- package/dist/chunks/permission-manager.mjs +2 -2
- package/dist/chunks/permissions.mjs +3 -3
- package/dist/chunks/persistence-manager.mjs +3 -3
- package/dist/chunks/plugin.mjs +2 -2
- package/dist/chunks/prompts.mjs +5 -5
- package/dist/chunks/providers.mjs +2 -2
- package/dist/chunks/quick-actions.mjs +2 -2
- package/dist/chunks/quick-provider.mjs +6 -5
- package/dist/chunks/quick-setup.mjs +10 -10
- package/dist/chunks/remote.mjs +5 -5
- package/dist/chunks/session.mjs +2 -2
- package/dist/chunks/sessions.mjs +1 -1
- package/dist/chunks/silent-updater.mjs +1 -1
- package/dist/chunks/simple-config.mjs +1 -1
- package/dist/chunks/skill2.mjs +3 -3
- package/dist/chunks/skills-sync.mjs +4 -4
- package/dist/chunks/skills.mjs +3 -3
- package/dist/chunks/slash-commands.mjs +3 -3
- package/dist/chunks/startup.mjs +1 -1
- package/dist/chunks/stats.mjs +2 -2
- package/dist/chunks/status.mjs +2 -2
- package/dist/chunks/team.mjs +3 -3
- package/dist/chunks/thinking.mjs +4 -4
- package/dist/chunks/trace.mjs +2 -2
- package/dist/chunks/uninstall.mjs +8 -8
- package/dist/chunks/update.mjs +9 -9
- package/dist/chunks/upgrade-manager.mjs +3 -3
- package/dist/chunks/version-checker.mjs +4 -4
- package/dist/chunks/vim.mjs +3 -3
- package/dist/chunks/workflows.mjs +1 -1
- package/dist/chunks/wsl.mjs +1 -1
- package/dist/chunks/zero-config.mjs +3 -3
- package/dist/cli.mjs +56 -23
- package/dist/index.mjs +5 -5
- package/dist/shared/{ccjk.CCcQfbni.mjs → ccjk.B1TwPltj.mjs} +1 -1
- package/dist/shared/{ccjk.CePkJq2S.mjs → ccjk.BfIpomdz.mjs} +1 -1
- package/dist/shared/{ccjk.D8ZLYSZZ.mjs → ccjk.CXzjn01x.mjs} +1 -1
- package/dist/shared/{ccjk.Cjj8SVrn.mjs → ccjk.Cot9p9_n.mjs} +1 -1
- package/dist/shared/{ccjk.CvChMYvB.mjs → ccjk.DCw2WnZU.mjs} +1 -1
- package/dist/shared/{ccjk.DG_o24cZ.mjs → ccjk.DJdmgr2d.mjs} +1 -1
- package/dist/shared/{ccjk.BIxuVL3_.mjs → ccjk.DcKLglJQ.mjs} +2 -2
- package/dist/shared/{ccjk.DLLw-h4Y.mjs → ccjk.DfXjf8EC.mjs} +2 -2
- package/dist/shared/{ccjk.KpFl2RDA.mjs → ccjk.DpstNaeR.mjs} +3 -3
- package/dist/shared/{ccjk.DOBWBkFR.mjs → ccjk.XsJWJuQP.mjs} +5 -5
- package/dist/shared/{ccjk._dESH4Rk.mjs → ccjk.dYDLfmph.mjs} +1 -1
- package/dist/shared/{ccjk.DS7UESmF.mjs → ccjk.hrRv8G6j.mjs} +4 -4
- package/dist/shared/{ccjk.BWFpnOr3.mjs → ccjk.mJpVRDZ8.mjs} +1 -1
- package/dist/templates/claude-code/common/settings.json +3 -1
- package/package.json +20 -18
- package/templates/claude-code/common/settings.json +3 -1
- package/dist/chunks/context-loader.mjs +0 -351
- package/dist/chunks/context.mjs +0 -372
- package/dist/chunks/health-check.mjs +0 -532
package/dist/chunks/index11.mjs
CHANGED
|
@@ -1,1171 +1,679 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { j as join, d as dirname } from '../shared/ccjk.bQ7Dh1g4.mjs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
gitCmd += ` ${since}..${until || "HEAD"}`;
|
|
6
|
+
class ProjectAnalyzer {
|
|
7
|
+
projectRoot;
|
|
8
|
+
constructor(projectRoot = process.cwd()) {
|
|
9
|
+
this.projectRoot = projectRoot;
|
|
11
10
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Analyze project and return comprehensive analysis
|
|
13
|
+
*/
|
|
14
|
+
async analyze() {
|
|
15
|
+
const packageJson = this.readPackageJson();
|
|
16
|
+
const techStack = this.detectTechStack(packageJson);
|
|
17
|
+
const projectType = this.detectProjectType(techStack, packageJson);
|
|
18
|
+
const frameworks = this.detectFrameworks(packageJson);
|
|
19
|
+
const hasTests = this.detectTestFramework(packageJson);
|
|
20
|
+
const hasDatabase = this.detectDatabase(packageJson);
|
|
21
|
+
const hasApi = this.detectApiEndpoints();
|
|
22
|
+
const buildTool = this.detectBuildTool(packageJson);
|
|
23
|
+
const cicd = this.detectCICD();
|
|
24
|
+
const patterns = this.detectPatterns();
|
|
25
|
+
const confidence = this.calculateConfidence(techStack, frameworks);
|
|
26
|
+
return {
|
|
27
|
+
projectRoot: this.projectRoot,
|
|
28
|
+
projectType,
|
|
29
|
+
techStack,
|
|
30
|
+
frameworks,
|
|
31
|
+
hasTests,
|
|
32
|
+
hasDatabase,
|
|
33
|
+
hasApi,
|
|
34
|
+
buildTool,
|
|
35
|
+
cicd,
|
|
36
|
+
patterns,
|
|
37
|
+
confidence,
|
|
38
|
+
packageJson: packageJson ?? void 0
|
|
39
|
+
};
|
|
18
40
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const files = fileLines.filter((f) => f.trim());
|
|
33
|
-
if (isFixCommit(message)) {
|
|
34
|
-
commits.push({
|
|
35
|
-
hash,
|
|
36
|
-
shortHash,
|
|
37
|
-
message,
|
|
38
|
-
author,
|
|
39
|
-
date,
|
|
40
|
-
files
|
|
41
|
-
});
|
|
41
|
+
/**
|
|
42
|
+
* Read and parse package.json
|
|
43
|
+
*/
|
|
44
|
+
readPackageJson() {
|
|
45
|
+
const packageJsonPath = join(this.projectRoot, "package.json");
|
|
46
|
+
if (!existsSync(packageJsonPath)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
51
|
+
return JSON.parse(content);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/\bresolve\b/i,
|
|
55
|
-
/\bcorrect\b/i,
|
|
56
|
-
/修复/,
|
|
57
|
-
/修正/,
|
|
58
|
-
/解决/,
|
|
59
|
-
/bug/i
|
|
60
|
-
];
|
|
61
|
-
return fixPatterns.some((p) => p.test(message));
|
|
62
|
-
}
|
|
63
|
-
function analyzeFixCommit(commit, cwd = process.cwd()) {
|
|
64
|
-
const diff = getCommitDiff(commit.hash, cwd);
|
|
65
|
-
const bugType = detectBugType(commit.message, diff);
|
|
66
|
-
const severity = detectSeverity(commit.message, diff, commit.files);
|
|
67
|
-
const rootCause = extractRootCause(commit.message, diff);
|
|
68
|
-
const solution = extractSolution(diff);
|
|
69
|
-
const preventionSuggestions = generatePreventionSuggestions(bugType);
|
|
70
|
-
return {
|
|
71
|
-
commit,
|
|
72
|
-
bugType,
|
|
73
|
-
severity,
|
|
74
|
-
rootCause,
|
|
75
|
-
solution,
|
|
76
|
-
preventionSuggestions,
|
|
77
|
-
relatedPostmortems: []
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
function getCommitDiff(hash, cwd) {
|
|
81
|
-
try {
|
|
82
|
-
return execSync(`git show ${hash} --pretty="" --patch`, {
|
|
83
|
-
cwd,
|
|
84
|
-
encoding: "utf-8",
|
|
85
|
-
maxBuffer: 1024 * 1024 * 10
|
|
86
|
-
// 10MB
|
|
87
|
-
});
|
|
88
|
-
} catch {
|
|
89
|
-
return "";
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function detectBugType(message, diff) {
|
|
93
|
-
const content = `${message}
|
|
94
|
-
${diff}`.toLowerCase();
|
|
95
|
-
const patterns = [
|
|
96
|
-
{
|
|
97
|
-
category: "type-safety",
|
|
98
|
-
patterns: [
|
|
99
|
-
/null|undefined|cannot read|typeerror/,
|
|
100
|
-
/类型|空值|未定义/,
|
|
101
|
-
/optional chaining|\?\./,
|
|
102
|
-
/strict.*null|null.*check/
|
|
103
|
-
]
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
category: "error-handling",
|
|
107
|
-
patterns: [
|
|
108
|
-
/try.*catch|exception|throw|error.*handling/,
|
|
109
|
-
/unhandled.*rejection|promise.*reject/,
|
|
110
|
-
/异常|错误处理|捕获/
|
|
111
|
-
]
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
category: "performance",
|
|
115
|
-
patterns: [
|
|
116
|
-
/performance|slow|timeout|memory|leak/,
|
|
117
|
-
/optimize|optimization|cache/,
|
|
118
|
-
/性能|优化|缓存|内存/
|
|
119
|
-
]
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
category: "security",
|
|
123
|
-
patterns: [
|
|
124
|
-
/security|xss|csrf|injection|auth/,
|
|
125
|
-
/vulnerability|exploit|sanitize/,
|
|
126
|
-
/安全|漏洞|注入|认证/
|
|
127
|
-
]
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
category: "race-condition",
|
|
131
|
-
patterns: [
|
|
132
|
-
/race.*condition|concurrent|async.*await/,
|
|
133
|
-
/deadlock|mutex|lock/,
|
|
134
|
-
/竞态|并发|死锁/
|
|
135
|
-
]
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
category: "logic-error",
|
|
139
|
-
patterns: [
|
|
140
|
-
/logic|incorrect|wrong.*result/,
|
|
141
|
-
/逻辑|错误结果|计算错误/
|
|
142
|
-
]
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
category: "api-misuse",
|
|
146
|
-
patterns: [
|
|
147
|
-
/api.*usage|incorrect.*call|wrong.*parameter/,
|
|
148
|
-
/接口|调用错误|参数错误/
|
|
149
|
-
]
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
category: "configuration",
|
|
153
|
-
patterns: [
|
|
154
|
-
/config|setting|environment|env/,
|
|
155
|
-
/配置|环境|设置/
|
|
156
|
-
]
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
category: "dependency",
|
|
160
|
-
patterns: [
|
|
161
|
-
/dependency|package|version|upgrade/,
|
|
162
|
-
/依赖|版本|升级/
|
|
163
|
-
]
|
|
56
|
+
/**
|
|
57
|
+
* Detect technology stack from dependencies
|
|
58
|
+
*/
|
|
59
|
+
detectTechStack(packageJson) {
|
|
60
|
+
if (!packageJson) {
|
|
61
|
+
return {
|
|
62
|
+
languages: ["javascript"],
|
|
63
|
+
runtime: "node",
|
|
64
|
+
packageManager: this.detectPackageManager()
|
|
65
|
+
};
|
|
164
66
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
67
|
+
const deps = {
|
|
68
|
+
...packageJson.dependencies,
|
|
69
|
+
...packageJson.devDependencies
|
|
70
|
+
};
|
|
71
|
+
const languages = [];
|
|
72
|
+
let runtime = "node";
|
|
73
|
+
if (deps.typescript || existsSync(join(this.projectRoot, "tsconfig.json"))) {
|
|
74
|
+
languages.push("typescript");
|
|
75
|
+
} else {
|
|
76
|
+
languages.push("javascript");
|
|
169
77
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
function detectSeverity(message, diff, files) {
|
|
174
|
-
const content = `${message}
|
|
175
|
-
${diff}`.toLowerCase();
|
|
176
|
-
if (/critical|crash|data.*loss|security.*vuln/i.test(content) || files.some((f) => /auth|security|payment/i.test(f))) {
|
|
177
|
-
return "critical";
|
|
178
|
-
}
|
|
179
|
-
if (/breaking|major|important|urgent/i.test(content) || files.length > 10) {
|
|
180
|
-
return "high";
|
|
181
|
-
}
|
|
182
|
-
if (/moderate|minor.*issue/i.test(content) || files.length > 3) {
|
|
183
|
-
return "medium";
|
|
184
|
-
}
|
|
185
|
-
return "low";
|
|
186
|
-
}
|
|
187
|
-
function extractRootCause(message, diff) {
|
|
188
|
-
const causePatterns = [
|
|
189
|
-
/caused by[:\s]+(.+)/i,
|
|
190
|
-
/root cause[:\s]+(.+)/i,
|
|
191
|
-
/because[:\s]+(.+)/i,
|
|
192
|
-
/due to[:\s]+(.+)/i,
|
|
193
|
-
/原因[::]\s*(.+)/,
|
|
194
|
-
/由于\s*(.+)/
|
|
195
|
-
];
|
|
196
|
-
for (const pattern of causePatterns) {
|
|
197
|
-
const match = message.match(pattern);
|
|
198
|
-
if (match) {
|
|
199
|
-
return match[1].trim();
|
|
78
|
+
if (existsSync(join(this.projectRoot, "deno.json")) || existsSync(join(this.projectRoot, "deno.jsonc"))) {
|
|
79
|
+
runtime = "deno";
|
|
200
80
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const addedLines = diff.match(/^\+[^+].*/gm) || [];
|
|
204
|
-
if (removedLines.length > 0 && addedLines.length > 0) {
|
|
205
|
-
return `\u4EE3\u7801\u53D8\u66F4: \u79FB\u9664 ${removedLines.length} \u884C, \u65B0\u589E ${addedLines.length} \u884C`;
|
|
206
|
-
}
|
|
207
|
-
return "\u9700\u8981\u8FDB\u4E00\u6B65\u5206\u6790";
|
|
208
|
-
}
|
|
209
|
-
function extractSolution(diff) {
|
|
210
|
-
const addedLines = diff.match(/^\+[^+].*/gm) || [];
|
|
211
|
-
if (addedLines.length === 0) {
|
|
212
|
-
return "\u5220\u9664\u4E86\u95EE\u9898\u4EE3\u7801";
|
|
213
|
-
}
|
|
214
|
-
if (addedLines.length <= 5) {
|
|
215
|
-
return addedLines.map((l) => l.substring(1)).join("\n");
|
|
216
|
-
}
|
|
217
|
-
return `\u65B0\u589E ${addedLines.length} \u884C\u4EE3\u7801\u4FEE\u590D\u95EE\u9898`;
|
|
218
|
-
}
|
|
219
|
-
function generatePreventionSuggestions(bugType, _diff) {
|
|
220
|
-
const suggestions = {
|
|
221
|
-
"type-safety": [
|
|
222
|
-
"\u542F\u7528 TypeScript strict \u6A21\u5F0F",
|
|
223
|
-
"\u4F7F\u7528\u53EF\u9009\u94FE\u64CD\u4F5C\u7B26 (?.) \u8FDB\u884C\u7A7A\u503C\u68C0\u67E5",
|
|
224
|
-
"\u4E3A\u6240\u6709\u5916\u90E8\u6570\u636E\u6DFB\u52A0\u8FD0\u884C\u65F6\u9A8C\u8BC1",
|
|
225
|
-
"\u4F7F\u7528 zod \u6216 io-ts \u8FDB\u884C\u7C7B\u578B\u9A8C\u8BC1"
|
|
226
|
-
],
|
|
227
|
-
"error-handling": [
|
|
228
|
-
"\u4E3A\u6240\u6709\u5F02\u6B65\u64CD\u4F5C\u6DFB\u52A0 try-catch",
|
|
229
|
-
"\u5B9E\u73B0\u5168\u5C40\u9519\u8BEF\u5904\u7406\u4E2D\u95F4\u4EF6",
|
|
230
|
-
"\u6DFB\u52A0\u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6 (React)",
|
|
231
|
-
"\u4F7F\u7528 Result \u7C7B\u578B\u66FF\u4EE3\u5F02\u5E38"
|
|
232
|
-
],
|
|
233
|
-
"performance": [
|
|
234
|
-
"\u6DFB\u52A0\u6027\u80FD\u76D1\u63A7\u548C\u544A\u8B66",
|
|
235
|
-
"\u5B9E\u73B0\u7F13\u5B58\u7B56\u7565",
|
|
236
|
-
"\u4F7F\u7528\u61D2\u52A0\u8F7D\u548C\u4EE3\u7801\u5206\u5272",
|
|
237
|
-
"\u5B9A\u671F\u8FDB\u884C\u6027\u80FD\u6D4B\u8BD5"
|
|
238
|
-
],
|
|
239
|
-
"security": [
|
|
240
|
-
"\u5B9E\u65BD\u5B89\u5168\u4EE3\u7801\u5BA1\u67E5",
|
|
241
|
-
"\u4F7F\u7528\u5B89\u5168\u626B\u63CF\u5DE5\u5177",
|
|
242
|
-
"\u9075\u5FAA OWASP \u5B89\u5168\u6307\u5357",
|
|
243
|
-
"\u5B9A\u671F\u66F4\u65B0\u4F9D\u8D56"
|
|
244
|
-
],
|
|
245
|
-
"race-condition": [
|
|
246
|
-
"\u4F7F\u7528\u9002\u5F53\u7684\u9501\u673A\u5236",
|
|
247
|
-
"\u907F\u514D\u5171\u4EAB\u53EF\u53D8\u72B6\u6001",
|
|
248
|
-
"\u4F7F\u7528\u539F\u5B50\u64CD\u4F5C",
|
|
249
|
-
"\u6DFB\u52A0\u5E76\u53D1\u6D4B\u8BD5"
|
|
250
|
-
],
|
|
251
|
-
"logic-error": [
|
|
252
|
-
"\u589E\u52A0\u5355\u5143\u6D4B\u8BD5\u8986\u76D6",
|
|
253
|
-
"\u5B9E\u65BD\u4EE3\u7801\u5BA1\u67E5",
|
|
254
|
-
"\u6DFB\u52A0\u65AD\u8A00\u548C\u4E0D\u53D8\u91CF\u68C0\u67E5",
|
|
255
|
-
"\u4F7F\u7528\u5F62\u5F0F\u5316\u9A8C\u8BC1\u5DE5\u5177"
|
|
256
|
-
],
|
|
257
|
-
"api-misuse": [
|
|
258
|
-
"\u5B8C\u5584 API \u6587\u6863",
|
|
259
|
-
"\u6DFB\u52A0\u53C2\u6570\u9A8C\u8BC1",
|
|
260
|
-
"\u63D0\u4F9B\u4F7F\u7528\u793A\u4F8B",
|
|
261
|
-
"\u5B9E\u73B0 API \u7248\u672C\u63A7\u5236"
|
|
262
|
-
],
|
|
263
|
-
"configuration": [
|
|
264
|
-
"\u4F7F\u7528\u914D\u7F6E\u9A8C\u8BC1",
|
|
265
|
-
"\u63D0\u4F9B\u9ED8\u8BA4\u914D\u7F6E",
|
|
266
|
-
"\u6587\u6863\u5316\u6240\u6709\u914D\u7F6E\u9879",
|
|
267
|
-
"\u5B9E\u73B0\u914D\u7F6E\u70ED\u91CD\u8F7D"
|
|
268
|
-
],
|
|
269
|
-
"dependency": [
|
|
270
|
-
"\u9501\u5B9A\u4F9D\u8D56\u7248\u672C",
|
|
271
|
-
"\u5B9A\u671F\u66F4\u65B0\u4F9D\u8D56",
|
|
272
|
-
"\u4F7F\u7528\u4F9D\u8D56\u626B\u63CF\u5DE5\u5177",
|
|
273
|
-
"\u6D4B\u8BD5\u4F9D\u8D56\u5347\u7EA7"
|
|
274
|
-
],
|
|
275
|
-
"memory-leak": [
|
|
276
|
-
"\u5B9E\u73B0\u8D44\u6E90\u6E05\u7406",
|
|
277
|
-
"\u4F7F\u7528\u5185\u5B58\u5206\u6790\u5DE5\u5177",
|
|
278
|
-
"\u907F\u514D\u5FAA\u73AF\u5F15\u7528",
|
|
279
|
-
"\u5B9A\u671F\u8FDB\u884C\u5185\u5B58\u6D4B\u8BD5"
|
|
280
|
-
],
|
|
281
|
-
"other": [
|
|
282
|
-
"\u589E\u52A0\u6D4B\u8BD5\u8986\u76D6",
|
|
283
|
-
"\u5B9E\u65BD\u4EE3\u7801\u5BA1\u67E5",
|
|
284
|
-
"\u5B8C\u5584\u6587\u6863"
|
|
285
|
-
]
|
|
286
|
-
};
|
|
287
|
-
return suggestions[bugType] || suggestions.other;
|
|
288
|
-
}
|
|
289
|
-
function generatePostmortem(analyses, existingIds) {
|
|
290
|
-
const grouped = groupByCategory(analyses);
|
|
291
|
-
const reports = [];
|
|
292
|
-
let nextId = getNextId(existingIds);
|
|
293
|
-
for (const [category, categoryAnalyses] of Object.entries(grouped)) {
|
|
294
|
-
const merged = mergeAnalyses(categoryAnalyses);
|
|
295
|
-
for (const analysis of merged) {
|
|
296
|
-
const id = `PM-${String(nextId++).padStart(3, "0")}`;
|
|
297
|
-
const report = {
|
|
298
|
-
id,
|
|
299
|
-
title: generateTitle(analysis),
|
|
300
|
-
severity: analysis.severity,
|
|
301
|
-
category: analysis.bugType,
|
|
302
|
-
status: "active",
|
|
303
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
304
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
305
|
-
relatedCommits: [analysis.commit],
|
|
306
|
-
affectedVersions: {
|
|
307
|
-
from: "unknown",
|
|
308
|
-
to: "unknown"
|
|
309
|
-
},
|
|
310
|
-
description: generateDescription(analysis),
|
|
311
|
-
rootCause: [analysis.rootCause],
|
|
312
|
-
solution: {
|
|
313
|
-
description: analysis.solution
|
|
314
|
-
},
|
|
315
|
-
preventionMeasures: analysis.preventionSuggestions,
|
|
316
|
-
aiDirectives: generateAiDirectives(analysis),
|
|
317
|
-
detectionPatterns: generateDetectionPatterns(analysis),
|
|
318
|
-
relatedFiles: analysis.commit.files,
|
|
319
|
-
tags: [category, analysis.severity],
|
|
320
|
-
metadata: {
|
|
321
|
-
generatedBy: "ccjk-postmortem",
|
|
322
|
-
version: "1.0.0"
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
reports.push(report);
|
|
81
|
+
if (existsSync(join(this.projectRoot, "bun.lockb"))) {
|
|
82
|
+
runtime = "bun";
|
|
326
83
|
}
|
|
84
|
+
return {
|
|
85
|
+
languages,
|
|
86
|
+
runtime,
|
|
87
|
+
packageManager: this.detectPackageManager()
|
|
88
|
+
};
|
|
327
89
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (!grouped[category]) {
|
|
335
|
-
grouped[category] = [];
|
|
90
|
+
/**
|
|
91
|
+
* Detect package manager
|
|
92
|
+
*/
|
|
93
|
+
detectPackageManager() {
|
|
94
|
+
if (existsSync(join(this.projectRoot, "pnpm-lock.yaml"))) {
|
|
95
|
+
return "pnpm";
|
|
336
96
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
return grouped;
|
|
340
|
-
}
|
|
341
|
-
function mergeAnalyses(analyses) {
|
|
342
|
-
const merged = [];
|
|
343
|
-
for (const analysis of analyses) {
|
|
344
|
-
const similar = merged.find(
|
|
345
|
-
(m) => calculateFileOverlap(m.commit.files, analysis.commit.files) > 0.5
|
|
346
|
-
);
|
|
347
|
-
if (similar) {
|
|
348
|
-
similar.commit.files = Array.from(/* @__PURE__ */ new Set([...similar.commit.files, ...analysis.commit.files]));
|
|
349
|
-
similar.preventionSuggestions = Array.from(/* @__PURE__ */ new Set([...similar.preventionSuggestions, ...analysis.preventionSuggestions]));
|
|
350
|
-
} else {
|
|
351
|
-
merged.push({ ...analysis });
|
|
97
|
+
if (existsSync(join(this.projectRoot, "yarn.lock"))) {
|
|
98
|
+
return "yarn";
|
|
352
99
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
function calculateFileOverlap(files1, files2) {
|
|
357
|
-
const set1 = new Set(files1);
|
|
358
|
-
const set2 = new Set(files2);
|
|
359
|
-
const intersection = Array.from(set1).filter((f) => set2.has(f));
|
|
360
|
-
const union = /* @__PURE__ */ new Set([...files1, ...files2]);
|
|
361
|
-
return intersection.length / union.size;
|
|
362
|
-
}
|
|
363
|
-
function getNextId(existingIds) {
|
|
364
|
-
const numbers = existingIds.map((id) => Number.parseInt(id.replace("PM-", ""), 10)).filter((n) => !Number.isNaN(n));
|
|
365
|
-
return numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
|
|
366
|
-
}
|
|
367
|
-
function generateTitle(analysis) {
|
|
368
|
-
const categoryTitles = {
|
|
369
|
-
"type-safety": "\u7C7B\u578B\u5B89\u5168\u95EE\u9898",
|
|
370
|
-
"error-handling": "\u9519\u8BEF\u5904\u7406\u7F3A\u5931",
|
|
371
|
-
"performance": "\u6027\u80FD\u95EE\u9898",
|
|
372
|
-
"security": "\u5B89\u5168\u6F0F\u6D1E",
|
|
373
|
-
"race-condition": "\u7ADE\u6001\u6761\u4EF6",
|
|
374
|
-
"logic-error": "\u903B\u8F91\u9519\u8BEF",
|
|
375
|
-
"api-misuse": "API \u4F7F\u7528\u4E0D\u5F53",
|
|
376
|
-
"configuration": "\u914D\u7F6E\u95EE\u9898",
|
|
377
|
-
"dependency": "\u4F9D\u8D56\u95EE\u9898",
|
|
378
|
-
"memory-leak": "\u5185\u5B58\u6CC4\u6F0F",
|
|
379
|
-
"other": "\u5176\u4ED6\u95EE\u9898"
|
|
380
|
-
};
|
|
381
|
-
const baseTitle = categoryTitles[analysis.bugType] || "\u672A\u5206\u7C7B\u95EE\u9898";
|
|
382
|
-
const message = analysis.commit.message;
|
|
383
|
-
const specificPart = message.replace(/^(fix|bugfix|hotfix)[(:]\s*/i, "").split("\n")[0];
|
|
384
|
-
if (specificPart && specificPart.length < 50) {
|
|
385
|
-
return `${baseTitle}: ${specificPart}`;
|
|
386
|
-
}
|
|
387
|
-
return baseTitle;
|
|
388
|
-
}
|
|
389
|
-
function generateDescription(analysis) {
|
|
390
|
-
return `
|
|
391
|
-
\u5728 ${analysis.commit.date} \u53D1\u73B0\u5E76\u4FEE\u590D\u4E86\u4E00\u4E2A ${analysis.bugType} \u7C7B\u578B\u7684\u95EE\u9898\u3002
|
|
392
|
-
|
|
393
|
-
**\u63D0\u4EA4\u4FE1\u606F**: ${analysis.commit.message}
|
|
394
|
-
|
|
395
|
-
**\u5F71\u54CD\u6587\u4EF6**:
|
|
396
|
-
${analysis.commit.files.map((f) => `- ${f}`).join("\n")}
|
|
397
|
-
|
|
398
|
-
**\u6839\u672C\u539F\u56E0**: ${analysis.rootCause}
|
|
399
|
-
`.trim();
|
|
400
|
-
}
|
|
401
|
-
function generateAiDirectives(analysis) {
|
|
402
|
-
const directives = [];
|
|
403
|
-
const categoryDirectives = {
|
|
404
|
-
"type-safety": [
|
|
405
|
-
"\u5904\u7406\u5916\u90E8\u6570\u636E\u65F6\u5FC5\u987B\u8FDB\u884C\u7A7A\u503C\u68C0\u67E5",
|
|
406
|
-
"\u4F7F\u7528 TypeScript \u4E25\u683C\u6A21\u5F0F",
|
|
407
|
-
"\u907F\u514D\u4F7F\u7528 any \u7C7B\u578B"
|
|
408
|
-
],
|
|
409
|
-
"error-handling": [
|
|
410
|
-
"\u6240\u6709\u5F02\u6B65\u64CD\u4F5C\u5FC5\u987B\u6709\u9519\u8BEF\u5904\u7406",
|
|
411
|
-
"\u63D0\u4F9B\u6709\u610F\u4E49\u7684\u9519\u8BEF\u6D88\u606F",
|
|
412
|
-
"\u5B9E\u73B0\u4F18\u96C5\u964D\u7EA7"
|
|
413
|
-
],
|
|
414
|
-
"performance": [
|
|
415
|
-
"\u907F\u514D\u5728\u5FAA\u73AF\u4E2D\u8FDB\u884C I/O \u64CD\u4F5C",
|
|
416
|
-
"\u4F7F\u7528\u9002\u5F53\u7684\u7F13\u5B58\u7B56\u7565",
|
|
417
|
-
"\u6CE8\u610F\u5927\u6570\u636E\u96C6\u7684\u5904\u7406"
|
|
418
|
-
],
|
|
419
|
-
"security": [
|
|
420
|
-
"\u9A8C\u8BC1\u6240\u6709\u7528\u6237\u8F93\u5165",
|
|
421
|
-
"\u4F7F\u7528\u53C2\u6570\u5316\u67E5\u8BE2",
|
|
422
|
-
"\u4E0D\u8981\u5728\u65E5\u5FD7\u4E2D\u8BB0\u5F55\u654F\u611F\u4FE1\u606F"
|
|
423
|
-
],
|
|
424
|
-
"race-condition": [
|
|
425
|
-
"\u6CE8\u610F\u5F02\u6B65\u64CD\u4F5C\u7684\u6267\u884C\u987A\u5E8F",
|
|
426
|
-
"\u4F7F\u7528\u9002\u5F53\u7684\u540C\u6B65\u673A\u5236",
|
|
427
|
-
"\u907F\u514D\u5171\u4EAB\u53EF\u53D8\u72B6\u6001"
|
|
428
|
-
],
|
|
429
|
-
"logic-error": [
|
|
430
|
-
"\u6DFB\u52A0\u8FB9\u754C\u6761\u4EF6\u68C0\u67E5",
|
|
431
|
-
"\u4F7F\u7528\u65AD\u8A00\u9A8C\u8BC1\u5047\u8BBE",
|
|
432
|
-
"\u7F16\u5199\u5355\u5143\u6D4B\u8BD5\u8986\u76D6\u8FB9\u754C\u60C5\u51B5"
|
|
433
|
-
],
|
|
434
|
-
"api-misuse": [
|
|
435
|
-
"\u67E5\u9605 API \u6587\u6863\u786E\u8BA4\u6B63\u786E\u7528\u6CD5",
|
|
436
|
-
"\u68C0\u67E5\u53C2\u6570\u7C7B\u578B\u548C\u8303\u56F4",
|
|
437
|
-
"\u5904\u7406\u6240\u6709\u53EF\u80FD\u7684\u8FD4\u56DE\u503C"
|
|
438
|
-
],
|
|
439
|
-
"configuration": [
|
|
440
|
-
"\u63D0\u4F9B\u5408\u7406\u7684\u9ED8\u8BA4\u503C",
|
|
441
|
-
"\u9A8C\u8BC1\u914D\u7F6E\u503C\u7684\u6709\u6548\u6027",
|
|
442
|
-
"\u6587\u6863\u5316\u914D\u7F6E\u9009\u9879"
|
|
443
|
-
],
|
|
444
|
-
"dependency": [
|
|
445
|
-
"\u68C0\u67E5\u4F9D\u8D56\u7684\u517C\u5BB9\u6027",
|
|
446
|
-
"\u9605\u8BFB\u66F4\u65B0\u65E5\u5FD7",
|
|
447
|
-
"\u5728\u5347\u7EA7\u524D\u8FDB\u884C\u6D4B\u8BD5"
|
|
448
|
-
],
|
|
449
|
-
"memory-leak": [
|
|
450
|
-
"\u53CA\u65F6\u6E05\u7406\u4E0D\u518D\u4F7F\u7528\u7684\u8D44\u6E90",
|
|
451
|
-
"\u907F\u514D\u5FAA\u73AF\u5F15\u7528",
|
|
452
|
-
"\u4F7F\u7528 WeakMap/WeakSet"
|
|
453
|
-
],
|
|
454
|
-
"other": [
|
|
455
|
-
"\u4ED4\u7EC6\u5BA1\u67E5\u4EE3\u7801\u53D8\u66F4",
|
|
456
|
-
"\u6DFB\u52A0\u9002\u5F53\u7684\u6D4B\u8BD5"
|
|
457
|
-
]
|
|
458
|
-
};
|
|
459
|
-
directives.push(...categoryDirectives[analysis.bugType] || categoryDirectives.other);
|
|
460
|
-
for (const file of analysis.commit.files) {
|
|
461
|
-
if (file.includes("api") || file.includes("service")) {
|
|
462
|
-
directives.push(`\u4FEE\u6539 ${path.basename(file)} \u65F6\u6CE8\u610F API \u517C\u5BB9\u6027`);
|
|
100
|
+
if (existsSync(join(this.projectRoot, "bun.lockb"))) {
|
|
101
|
+
return "bun";
|
|
463
102
|
}
|
|
464
|
-
if (
|
|
465
|
-
|
|
103
|
+
if (existsSync(join(this.projectRoot, "package-lock.json"))) {
|
|
104
|
+
return "npm";
|
|
466
105
|
}
|
|
106
|
+
return "npm";
|
|
467
107
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
description: "Promise \u672A\u5904\u7406 rejection",
|
|
501
|
-
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
502
|
-
severity: "medium"
|
|
503
|
-
}
|
|
504
|
-
],
|
|
505
|
-
"performance": [
|
|
506
|
-
{
|
|
507
|
-
type: "regex",
|
|
508
|
-
pattern: "for\\s*\\([^)]+\\)\\s*\\{[^}]*await",
|
|
509
|
-
description: "\u5FAA\u73AF\u4E2D\u4F7F\u7528 await",
|
|
510
|
-
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
511
|
-
severity: "medium"
|
|
512
|
-
}
|
|
513
|
-
],
|
|
514
|
-
"security": [
|
|
515
|
-
{
|
|
516
|
-
type: "regex",
|
|
517
|
-
pattern: "innerHTML\\s*=",
|
|
518
|
-
description: "\u76F4\u63A5\u8BBE\u7F6E innerHTML \u53EF\u80FD\u5BFC\u81F4 XSS",
|
|
519
|
-
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
520
|
-
severity: "high"
|
|
521
|
-
},
|
|
522
|
-
{
|
|
523
|
-
type: "regex",
|
|
524
|
-
pattern: "eval\\s*\\(",
|
|
525
|
-
description: "\u4F7F\u7528 eval \u5B58\u5728\u5B89\u5168\u98CE\u9669",
|
|
526
|
-
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
527
|
-
severity: "critical"
|
|
528
|
-
}
|
|
529
|
-
],
|
|
530
|
-
"race-condition": [],
|
|
531
|
-
"logic-error": [],
|
|
532
|
-
"api-misuse": [],
|
|
533
|
-
"configuration": [],
|
|
534
|
-
"dependency": [],
|
|
535
|
-
"memory-leak": [
|
|
536
|
-
{
|
|
537
|
-
type: "regex",
|
|
538
|
-
pattern: "addEventListener\\([^)]+\\)(?![\\s\\S]*removeEventListener)",
|
|
539
|
-
description: "\u6DFB\u52A0\u4E8B\u4EF6\u76D1\u542C\u5668\u4F46\u672A\u79FB\u9664",
|
|
540
|
-
fileTypes: [".ts", ".tsx", ".js", ".jsx"],
|
|
541
|
-
severity: "medium"
|
|
542
|
-
}
|
|
543
|
-
],
|
|
544
|
-
"other": []
|
|
545
|
-
};
|
|
546
|
-
patterns.push(...categoryPatterns[analysis.bugType] || []);
|
|
547
|
-
return patterns;
|
|
548
|
-
}
|
|
549
|
-
const PostmortemAnalyzer = {
|
|
550
|
-
getFixCommits,
|
|
551
|
-
analyzeFixCommit,
|
|
552
|
-
generatePostmortem
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
const DEFAULT_CONFIG = {
|
|
556
|
-
enabled: true,
|
|
557
|
-
directory: "./postmortem",
|
|
558
|
-
autoSyncToClaudeMd: true,
|
|
559
|
-
maxSyncItems: 10,
|
|
560
|
-
minSyncSeverity: "medium",
|
|
561
|
-
detection: {
|
|
562
|
-
enabled: true,
|
|
563
|
-
excludePatterns: ["node_modules/**", "dist/**", "*.test.*", "*.spec.*"],
|
|
564
|
-
includePatterns: ["src/**/*.ts", "src/**/*.tsx"]
|
|
565
|
-
},
|
|
566
|
-
aiAnalysis: {
|
|
567
|
-
provider: "claude"
|
|
568
|
-
}
|
|
569
|
-
};
|
|
570
|
-
const INDEX_FILE = "index.json";
|
|
571
|
-
const CLAUDE_MD_SECTION_START = "<!-- POSTMORTEM_START -->";
|
|
572
|
-
const CLAUDE_MD_SECTION_END = "<!-- POSTMORTEM_END -->";
|
|
573
|
-
class PostmortemManager {
|
|
574
|
-
config;
|
|
575
|
-
projectRoot;
|
|
576
|
-
postmortemDir;
|
|
577
|
-
constructor(projectRoot = process.cwd(), config = {}) {
|
|
578
|
-
this.projectRoot = projectRoot;
|
|
579
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
580
|
-
this.postmortemDir = path.join(projectRoot, this.config.directory);
|
|
108
|
+
/**
|
|
109
|
+
* Detect project type
|
|
110
|
+
*/
|
|
111
|
+
detectProjectType(techStack, packageJson) {
|
|
112
|
+
if (!packageJson) {
|
|
113
|
+
return "unknown";
|
|
114
|
+
}
|
|
115
|
+
const deps = {
|
|
116
|
+
...packageJson.dependencies,
|
|
117
|
+
...packageJson.devDependencies
|
|
118
|
+
};
|
|
119
|
+
const hasFrontend = deps.react || deps.vue || deps["@angular/core"] || deps.svelte;
|
|
120
|
+
const hasBackend = deps.express || deps.fastify || deps.koa || deps["@nestjs/core"] || deps.hapi;
|
|
121
|
+
if (deps.next || deps.nuxt || deps["@remix-run/react"] || deps["@sveltejs/kit"]) {
|
|
122
|
+
return "fullstack";
|
|
123
|
+
}
|
|
124
|
+
if (packageJson.bin || deps.commander || deps.yargs || deps.cac) {
|
|
125
|
+
return "cli";
|
|
126
|
+
}
|
|
127
|
+
if (packageJson.main && !hasFrontend && !hasBackend) {
|
|
128
|
+
return "library";
|
|
129
|
+
}
|
|
130
|
+
if (hasFrontend && hasBackend) {
|
|
131
|
+
return "fullstack";
|
|
132
|
+
}
|
|
133
|
+
if (hasFrontend) {
|
|
134
|
+
return "frontend";
|
|
135
|
+
}
|
|
136
|
+
if (hasBackend) {
|
|
137
|
+
return "backend";
|
|
138
|
+
}
|
|
139
|
+
return "unknown";
|
|
581
140
|
}
|
|
582
|
-
// ==========================================================================
|
|
583
|
-
// Initialization
|
|
584
|
-
// ==========================================================================
|
|
585
141
|
/**
|
|
586
|
-
*
|
|
142
|
+
* Detect frameworks
|
|
587
143
|
*/
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
limit: 200,
|
|
592
|
-
cwd: this.projectRoot
|
|
593
|
-
});
|
|
594
|
-
if (commits.length === 0) {
|
|
595
|
-
this.saveIndex(this.createEmptyIndex());
|
|
596
|
-
return { created: 0, directory: this.postmortemDir };
|
|
144
|
+
detectFrameworks(packageJson) {
|
|
145
|
+
if (!packageJson) {
|
|
146
|
+
return [];
|
|
597
147
|
}
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
148
|
+
const deps = {
|
|
149
|
+
...packageJson.dependencies,
|
|
150
|
+
...packageJson.devDependencies
|
|
151
|
+
};
|
|
152
|
+
const frameworks = [];
|
|
153
|
+
if (deps.react)
|
|
154
|
+
frameworks.push("react");
|
|
155
|
+
if (deps.vue)
|
|
156
|
+
frameworks.push("vue");
|
|
157
|
+
if (deps["@angular/core"])
|
|
158
|
+
frameworks.push("angular");
|
|
159
|
+
if (deps.svelte)
|
|
160
|
+
frameworks.push("svelte");
|
|
161
|
+
if (deps.next)
|
|
162
|
+
frameworks.push("next.js");
|
|
163
|
+
if (deps.nuxt)
|
|
164
|
+
frameworks.push("nuxt");
|
|
165
|
+
if (deps["@remix-run/react"])
|
|
166
|
+
frameworks.push("remix");
|
|
167
|
+
if (deps["@sveltejs/kit"])
|
|
168
|
+
frameworks.push("sveltekit");
|
|
169
|
+
if (deps.express)
|
|
170
|
+
frameworks.push("express");
|
|
171
|
+
if (deps.fastify)
|
|
172
|
+
frameworks.push("fastify");
|
|
173
|
+
if (deps.koa)
|
|
174
|
+
frameworks.push("koa");
|
|
175
|
+
if (deps["@nestjs/core"])
|
|
176
|
+
frameworks.push("nestjs");
|
|
177
|
+
if (deps.hapi)
|
|
178
|
+
frameworks.push("hapi");
|
|
179
|
+
return frameworks;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Detect test framework
|
|
183
|
+
*/
|
|
184
|
+
detectTestFramework(packageJson) {
|
|
185
|
+
if (!packageJson) {
|
|
186
|
+
return false;
|
|
604
187
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
188
|
+
const deps = {
|
|
189
|
+
...packageJson.dependencies,
|
|
190
|
+
...packageJson.devDependencies
|
|
191
|
+
};
|
|
192
|
+
return !!(deps.vitest || deps.jest || deps.mocha || deps.ava || deps["@playwright/test"] || deps.cypress);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Detect database usage
|
|
196
|
+
*/
|
|
197
|
+
detectDatabase(packageJson) {
|
|
198
|
+
if (!packageJson) {
|
|
199
|
+
return false;
|
|
608
200
|
}
|
|
609
|
-
|
|
201
|
+
const deps = {
|
|
202
|
+
...packageJson.dependencies,
|
|
203
|
+
...packageJson.devDependencies
|
|
204
|
+
};
|
|
205
|
+
return !!(deps.prisma || deps["@prisma/client"] || deps.drizzle || deps["drizzle-orm"] || deps.typeorm || deps.sequelize || deps.mongoose || deps.pg || deps.mysql || deps.mysql2 || deps.sqlite3 || deps["better-sqlite3"]);
|
|
610
206
|
}
|
|
611
207
|
/**
|
|
612
|
-
*
|
|
208
|
+
* Detect API endpoints
|
|
613
209
|
*/
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
210
|
+
detectApiEndpoints() {
|
|
211
|
+
const apiDirs = [
|
|
212
|
+
"src/api",
|
|
213
|
+
"src/routes",
|
|
214
|
+
"api",
|
|
215
|
+
"routes",
|
|
216
|
+
"pages/api",
|
|
217
|
+
// Next.js
|
|
218
|
+
"app/api"
|
|
219
|
+
// Next.js App Router
|
|
619
220
|
];
|
|
620
|
-
|
|
621
|
-
if (!fs.existsSync(dir)) {
|
|
622
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
623
|
-
}
|
|
624
|
-
}
|
|
221
|
+
return apiDirs.some((dir) => existsSync(join(this.projectRoot, dir)));
|
|
625
222
|
}
|
|
626
|
-
// ==========================================================================
|
|
627
|
-
// Report Management
|
|
628
|
-
// ==========================================================================
|
|
629
223
|
/**
|
|
630
|
-
*
|
|
224
|
+
* Detect build tool
|
|
631
225
|
*/
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
226
|
+
detectBuildTool(packageJson) {
|
|
227
|
+
if (!packageJson) {
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
const deps = {
|
|
231
|
+
...packageJson.dependencies,
|
|
232
|
+
...packageJson.devDependencies
|
|
233
|
+
};
|
|
234
|
+
if (deps.vite || existsSync(join(this.projectRoot, "vite.config.ts"))) {
|
|
235
|
+
return "vite";
|
|
236
|
+
}
|
|
237
|
+
if (deps.webpack || existsSync(join(this.projectRoot, "webpack.config.js"))) {
|
|
238
|
+
return "webpack";
|
|
239
|
+
}
|
|
240
|
+
if (deps.rollup || existsSync(join(this.projectRoot, "rollup.config.js"))) {
|
|
241
|
+
return "rollup";
|
|
242
|
+
}
|
|
243
|
+
if (deps.esbuild) {
|
|
244
|
+
return "esbuild";
|
|
245
|
+
}
|
|
246
|
+
if (deps.turbo || existsSync(join(this.projectRoot, "turbo.json"))) {
|
|
247
|
+
return "turbo";
|
|
248
|
+
}
|
|
249
|
+
return void 0;
|
|
640
250
|
}
|
|
641
251
|
/**
|
|
642
|
-
*
|
|
252
|
+
* Detect CI/CD configuration
|
|
643
253
|
*/
|
|
644
|
-
|
|
645
|
-
const
|
|
646
|
-
if (
|
|
647
|
-
|
|
254
|
+
detectCICD() {
|
|
255
|
+
const cicd = [];
|
|
256
|
+
if (existsSync(join(this.projectRoot, ".github", "workflows"))) {
|
|
257
|
+
cicd.push("github-actions");
|
|
648
258
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
259
|
+
if (existsSync(join(this.projectRoot, ".gitlab-ci.yml"))) {
|
|
260
|
+
cicd.push("gitlab-ci");
|
|
261
|
+
}
|
|
262
|
+
if (existsSync(join(this.projectRoot, ".circleci", "config.yml"))) {
|
|
263
|
+
cicd.push("circleci");
|
|
654
264
|
}
|
|
265
|
+
if (existsSync(join(this.projectRoot, ".travis.yml"))) {
|
|
266
|
+
cicd.push("travis-ci");
|
|
267
|
+
}
|
|
268
|
+
return cicd;
|
|
655
269
|
}
|
|
656
270
|
/**
|
|
657
|
-
*
|
|
271
|
+
* Detect common patterns in the project
|
|
658
272
|
*/
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
273
|
+
detectPatterns() {
|
|
274
|
+
const patterns = [];
|
|
275
|
+
if (existsSync(join(this.projectRoot, "packages")) || existsSync(join(this.projectRoot, "apps"))) {
|
|
276
|
+
patterns.push("monorepo");
|
|
277
|
+
}
|
|
278
|
+
if (existsSync(join(this.projectRoot, "tsconfig.json"))) {
|
|
279
|
+
patterns.push("typescript");
|
|
280
|
+
}
|
|
281
|
+
if (existsSync(join(this.projectRoot, ".eslintrc.js")) || existsSync(join(this.projectRoot, "eslint.config.js"))) {
|
|
282
|
+
patterns.push("eslint");
|
|
283
|
+
}
|
|
284
|
+
if (existsSync(join(this.projectRoot, ".prettierrc")) || existsSync(join(this.projectRoot, "prettier.config.js"))) {
|
|
285
|
+
patterns.push("prettier");
|
|
286
|
+
}
|
|
287
|
+
if (existsSync(join(this.projectRoot, "Dockerfile")) || existsSync(join(this.projectRoot, "docker-compose.yml"))) {
|
|
288
|
+
patterns.push("docker");
|
|
289
|
+
}
|
|
290
|
+
if (existsSync(join(this.projectRoot, ".env.example")) || existsSync(join(this.projectRoot, ".env.local"))) {
|
|
291
|
+
patterns.push("env-config");
|
|
292
|
+
}
|
|
293
|
+
return patterns;
|
|
662
294
|
}
|
|
663
295
|
/**
|
|
664
|
-
*
|
|
296
|
+
* Calculate confidence score based on detected information
|
|
665
297
|
*/
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
- **\u72B6\u6001**: ${report.status}
|
|
680
|
-
- **\u521B\u5EFA\u65F6\u95F4**: ${report.createdAt}
|
|
681
|
-
- **\u66F4\u65B0\u65F6\u95F4**: ${report.updatedAt}
|
|
682
|
-
|
|
683
|
-
## \u76F8\u5173\u63D0\u4EA4
|
|
684
|
-
${report.relatedCommits.map((c) => `- \`${c.shortHash}\` - ${c.message} (${c.author}, ${c.date})`).join("\n")}
|
|
685
|
-
|
|
686
|
-
## \u5F71\u54CD\u7248\u672C
|
|
687
|
-
- **\u4ECE**: ${report.affectedVersions.from}
|
|
688
|
-
- **\u5230**: ${report.affectedVersions.to}
|
|
689
|
-
|
|
690
|
-
## \u95EE\u9898\u63CF\u8FF0
|
|
691
|
-
${report.description}
|
|
692
|
-
|
|
693
|
-
## \u6839\u672C\u539F\u56E0
|
|
694
|
-
${report.rootCause.map((c) => `- ${c}`).join("\n")}
|
|
695
|
-
|
|
696
|
-
## \u4FEE\u590D\u65B9\u6848
|
|
697
|
-
${report.solution.description}
|
|
698
|
-
|
|
699
|
-
${report.solution.codeExample ? `
|
|
700
|
-
### \u4EE3\u7801\u793A\u4F8B
|
|
701
|
-
|
|
702
|
-
**\u274C \u9519\u8BEF\u5199\u6CD5**
|
|
703
|
-
\`\`\`typescript
|
|
704
|
-
${report.solution.codeExample.bad}
|
|
705
|
-
\`\`\`
|
|
706
|
-
|
|
707
|
-
**\u2705 \u6B63\u786E\u5199\u6CD5**
|
|
708
|
-
\`\`\`typescript
|
|
709
|
-
${report.solution.codeExample.good}
|
|
710
|
-
\`\`\`
|
|
711
|
-
` : ""}
|
|
712
|
-
|
|
713
|
-
## \u9884\u9632\u63AA\u65BD
|
|
714
|
-
${report.preventionMeasures.map((m, i) => `${i + 1}. ${m}`).join("\n")}
|
|
715
|
-
|
|
716
|
-
## AI \u5F00\u53D1\u6307\u4EE4
|
|
717
|
-
> \u4EE5\u4E0B\u6307\u4EE4\u4F1A\u81EA\u52A8\u6CE8\u5165\u5230 CLAUDE.md \u4E2D\uFF0C\u6307\u5BFC AI \u5728\u5F00\u53D1\u65F6\u907F\u514D\u7C7B\u4F3C\u95EE\u9898
|
|
718
|
-
|
|
719
|
-
${report.aiDirectives.map((d) => `- ${d}`).join("\n")}
|
|
720
|
-
|
|
721
|
-
## \u68C0\u6D4B\u6A21\u5F0F
|
|
722
|
-
${report.detectionPatterns.length > 0 ? report.detectionPatterns.map((p) => `
|
|
723
|
-
### ${p.description}
|
|
724
|
-
- **\u7C7B\u578B**: ${p.type}
|
|
725
|
-
- **\u6A21\u5F0F**: \`${p.pattern}\`
|
|
726
|
-
- **\u9002\u7528\u6587\u4EF6**: ${p.fileTypes.join(", ")}
|
|
727
|
-
- **\u4E25\u91CD\u7A0B\u5EA6**: ${p.severity}
|
|
728
|
-
`).join("\n") : "\u6682\u65E0\u81EA\u52A8\u68C0\u6D4B\u6A21\u5F0F"}
|
|
729
|
-
|
|
730
|
-
## \u76F8\u5173\u6587\u4EF6
|
|
731
|
-
${report.relatedFiles.map((f) => `- \`${f}\``).join("\n")}
|
|
732
|
-
|
|
733
|
-
## \u6807\u7B7E
|
|
734
|
-
${report.tags.map((t) => `\`${t}\``).join(" ")}
|
|
735
|
-
|
|
736
|
-
---
|
|
737
|
-
*\u7531 CCJK Postmortem System \u81EA\u52A8\u751F\u6210*
|
|
738
|
-
`;
|
|
298
|
+
calculateConfidence(techStack, frameworks) {
|
|
299
|
+
let confidence = 0.5;
|
|
300
|
+
if (techStack.languages.length > 0)
|
|
301
|
+
confidence += 0.1;
|
|
302
|
+
if (techStack.runtime !== "unknown")
|
|
303
|
+
confidence += 0.1;
|
|
304
|
+
if (techStack.packageManager)
|
|
305
|
+
confidence += 0.1;
|
|
306
|
+
if (frameworks.length > 0)
|
|
307
|
+
confidence += 0.1;
|
|
308
|
+
if (frameworks.length > 2)
|
|
309
|
+
confidence += 0.1;
|
|
310
|
+
return Math.min(confidence, 1);
|
|
739
311
|
}
|
|
740
312
|
/**
|
|
741
|
-
*
|
|
313
|
+
* Get file count in directory
|
|
742
314
|
*/
|
|
743
|
-
|
|
744
|
-
|
|
315
|
+
getFileCount(dir) {
|
|
316
|
+
try {
|
|
317
|
+
if (!existsSync(dir)) {
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
let count = 0;
|
|
321
|
+
const items = readdirSync(dir);
|
|
322
|
+
for (const item of items) {
|
|
323
|
+
const fullPath = join(dir, item);
|
|
324
|
+
const stat = statSync(fullPath);
|
|
325
|
+
if (stat.isDirectory()) {
|
|
326
|
+
count += this.getFileCount(fullPath);
|
|
327
|
+
} else {
|
|
328
|
+
count++;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return count;
|
|
332
|
+
} catch {
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function analyzeProject(projectRoot) {
|
|
338
|
+
const analyzer = new ProjectAnalyzer(projectRoot);
|
|
339
|
+
return analyzer.analyze();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
343
|
+
const __dirname$1 = dirname(__filename$1);
|
|
344
|
+
class ConfigGenerator {
|
|
345
|
+
templatesDir;
|
|
346
|
+
outputDir;
|
|
347
|
+
constructor(templatesDir, outputDir) {
|
|
348
|
+
this.templatesDir = templatesDir || join(__dirname$1, "..", "templates");
|
|
349
|
+
this.outputDir = outputDir || join(homedir(), ".config", "claude");
|
|
745
350
|
}
|
|
746
|
-
// ==========================================================================
|
|
747
|
-
// Index Management
|
|
748
|
-
// ==========================================================================
|
|
749
351
|
/**
|
|
750
|
-
*
|
|
352
|
+
* Generate configurations from template selection
|
|
751
353
|
*/
|
|
752
|
-
|
|
354
|
+
async generate(selection) {
|
|
355
|
+
const agentConfigs = await this.generateAgents(selection.agents);
|
|
356
|
+
const skillConfigs = await this.generateSkills(selection.skills);
|
|
753
357
|
return {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
total: 0,
|
|
758
|
-
bySeverity: { critical: 0, high: 0, medium: 0, low: 0 },
|
|
759
|
-
byCategory: {
|
|
760
|
-
"type-safety": 0,
|
|
761
|
-
"error-handling": 0,
|
|
762
|
-
"performance": 0,
|
|
763
|
-
"security": 0,
|
|
764
|
-
"logic-error": 0,
|
|
765
|
-
"race-condition": 0,
|
|
766
|
-
"memory-leak": 0,
|
|
767
|
-
"api-misuse": 0,
|
|
768
|
-
"configuration": 0,
|
|
769
|
-
"dependency": 0,
|
|
770
|
-
"other": 0
|
|
771
|
-
},
|
|
772
|
-
byStatus: { active: 0, resolved: 0, monitoring: 0, archived: 0 }
|
|
773
|
-
},
|
|
774
|
-
reports: []
|
|
358
|
+
agents: agentConfigs,
|
|
359
|
+
skills: skillConfigs,
|
|
360
|
+
summary: this.generateSummary(agentConfigs, skillConfigs)
|
|
775
361
|
};
|
|
776
362
|
}
|
|
777
363
|
/**
|
|
778
|
-
*
|
|
364
|
+
* Generate agent configurations
|
|
779
365
|
*/
|
|
780
|
-
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
366
|
+
async generateAgents(agents) {
|
|
367
|
+
const configs = [];
|
|
368
|
+
for (const agent of agents) {
|
|
369
|
+
const templatePath = join(this.templatesDir, "agents", agent.file);
|
|
370
|
+
if (!existsSync(templatePath)) {
|
|
371
|
+
console.warn(`Agent template not found: ${agent.file}`);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const content = readFileSync(templatePath, "utf-8");
|
|
375
|
+
const outputPath = join(this.outputDir, "agents", agent.file);
|
|
376
|
+
configs.push({
|
|
377
|
+
id: agent.id,
|
|
378
|
+
path: outputPath,
|
|
379
|
+
content
|
|
380
|
+
});
|
|
790
381
|
}
|
|
382
|
+
return configs;
|
|
791
383
|
}
|
|
792
384
|
/**
|
|
793
|
-
*
|
|
385
|
+
* Generate skill configurations
|
|
794
386
|
*/
|
|
795
|
-
|
|
796
|
-
const
|
|
797
|
-
|
|
387
|
+
async generateSkills(skills) {
|
|
388
|
+
const configs = [];
|
|
389
|
+
for (const skill of skills) {
|
|
390
|
+
const templatePath = join(this.templatesDir, "skills", skill.file);
|
|
391
|
+
if (!existsSync(templatePath)) {
|
|
392
|
+
console.warn(`Skill template not found: ${skill.file}`);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const content = readFileSync(templatePath, "utf-8");
|
|
396
|
+
const outputPath = join(this.outputDir, "skills", skill.file);
|
|
397
|
+
configs.push({
|
|
398
|
+
id: skill.id,
|
|
399
|
+
path: outputPath,
|
|
400
|
+
content
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return configs;
|
|
798
404
|
}
|
|
799
405
|
/**
|
|
800
|
-
*
|
|
406
|
+
* Write configurations to disk
|
|
801
407
|
*/
|
|
802
|
-
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
});
|
|
822
|
-
} catch {
|
|
408
|
+
async write(config) {
|
|
409
|
+
const agentsDir = join(this.outputDir, "agents");
|
|
410
|
+
const skillsDir = join(this.outputDir, "skills");
|
|
411
|
+
if (!existsSync(agentsDir)) {
|
|
412
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
413
|
+
}
|
|
414
|
+
if (!existsSync(skillsDir)) {
|
|
415
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
416
|
+
}
|
|
417
|
+
for (const agent of config.agents) {
|
|
418
|
+
const dir = join(agent.path, "..");
|
|
419
|
+
if (!existsSync(dir)) {
|
|
420
|
+
mkdirSync(dir, { recursive: true });
|
|
421
|
+
}
|
|
422
|
+
writeFileSync(agent.path, agent.content, "utf-8");
|
|
423
|
+
}
|
|
424
|
+
for (const skill of config.skills) {
|
|
425
|
+
const dir = join(skill.path, "..");
|
|
426
|
+
if (!existsSync(dir)) {
|
|
427
|
+
mkdirSync(dir, { recursive: true });
|
|
823
428
|
}
|
|
429
|
+
writeFileSync(skill.path, skill.content, "utf-8");
|
|
824
430
|
}
|
|
825
|
-
index.reports.sort((a, b) => {
|
|
826
|
-
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
827
|
-
const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
|
|
828
|
-
if (severityDiff !== 0)
|
|
829
|
-
return severityDiff;
|
|
830
|
-
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
831
|
-
});
|
|
832
|
-
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
833
|
-
this.saveIndex(index);
|
|
834
|
-
return index;
|
|
835
431
|
}
|
|
836
|
-
// ==========================================================================
|
|
837
|
-
// CLAUDE.md Integration
|
|
838
|
-
// ==========================================================================
|
|
839
432
|
/**
|
|
840
|
-
*
|
|
433
|
+
* Generate summary
|
|
841
434
|
*/
|
|
842
|
-
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
435
|
+
generateSummary(agents, skills) {
|
|
436
|
+
const lines = [];
|
|
437
|
+
lines.push("# Generated Configuration Summary\n");
|
|
438
|
+
lines.push(`Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
439
|
+
`);
|
|
440
|
+
lines.push("## Agents\n");
|
|
441
|
+
for (const agent of agents) {
|
|
442
|
+
lines.push(`- ${agent.id}: ${agent.path}`);
|
|
848
443
|
}
|
|
849
|
-
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
content = content.substring(0, startIndex) + content.substring(endIndex + CLAUDE_MD_SECTION_END.length);
|
|
444
|
+
lines.push("\n## Skills\n");
|
|
445
|
+
for (const skill of skills) {
|
|
446
|
+
lines.push(`- ${skill.id}: ${skill.path}`);
|
|
853
447
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
448
|
+
lines.push("\n## Usage\n");
|
|
449
|
+
lines.push("1. Agents are automatically loaded by Claude Code");
|
|
450
|
+
lines.push("2. Skills can be triggered using their command triggers");
|
|
451
|
+
lines.push("3. Use `claude` command to start coding with AI assistance");
|
|
452
|
+
return lines.join("\n");
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function generateConfigs(selection) {
|
|
456
|
+
const generator = new ConfigGenerator();
|
|
457
|
+
return generator.generate(selection);
|
|
458
|
+
}
|
|
459
|
+
async function writeConfigs(config) {
|
|
460
|
+
const generator = new ConfigGenerator();
|
|
461
|
+
await generator.write(config);
|
|
462
|
+
}
|
|
860
463
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
464
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
465
|
+
const __dirname = dirname(__filename);
|
|
466
|
+
class TemplateSelector {
|
|
467
|
+
templatesDir;
|
|
468
|
+
constructor(templatesDir) {
|
|
469
|
+
this.templatesDir = templatesDir || join(__dirname, "..", "..", "..", "templates");
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Select templates based on project analysis
|
|
473
|
+
*/
|
|
474
|
+
async select(analysis) {
|
|
475
|
+
const agents = await this.selectAgents(analysis);
|
|
476
|
+
const skills = await this.selectSkills(analysis);
|
|
864
477
|
return {
|
|
865
|
-
|
|
866
|
-
|
|
478
|
+
agents,
|
|
479
|
+
skills,
|
|
480
|
+
reasoning: this.generateReasoning(analysis, agents, skills)
|
|
867
481
|
};
|
|
868
482
|
}
|
|
869
483
|
/**
|
|
870
|
-
*
|
|
484
|
+
* Select agent templates
|
|
871
485
|
*/
|
|
872
|
-
|
|
873
|
-
const
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
486
|
+
async selectAgents(analysis) {
|
|
487
|
+
const selectedAgents = [];
|
|
488
|
+
const agentIndex = this.loadAgentIndex();
|
|
489
|
+
switch (analysis.projectType) {
|
|
490
|
+
case "frontend":
|
|
491
|
+
selectedAgents.push(
|
|
492
|
+
...this.findAgentsByCategory(agentIndex, "frontend")
|
|
493
|
+
);
|
|
494
|
+
break;
|
|
495
|
+
case "backend":
|
|
496
|
+
selectedAgents.push(
|
|
497
|
+
...this.findAgentsByCategory(agentIndex, "backend")
|
|
498
|
+
);
|
|
499
|
+
break;
|
|
500
|
+
case "fullstack":
|
|
501
|
+
selectedAgents.push(
|
|
502
|
+
...this.findAgentsByCategory(agentIndex, "fullstack")
|
|
503
|
+
);
|
|
504
|
+
selectedAgents.push(
|
|
505
|
+
...this.findAgentsByCategory(agentIndex, "frontend").slice(0, 1),
|
|
506
|
+
...this.findAgentsByCategory(agentIndex, "backend").slice(0, 1)
|
|
507
|
+
);
|
|
508
|
+
break;
|
|
509
|
+
case "cli":
|
|
510
|
+
case "library":
|
|
511
|
+
selectedAgents.push(
|
|
512
|
+
...this.findAgentsByCategory(agentIndex, "backend")
|
|
513
|
+
);
|
|
514
|
+
break;
|
|
894
515
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
""
|
|
900
|
-
];
|
|
901
|
-
if (reports.length === 0) {
|
|
902
|
-
lines.push("\u6682\u65E0\u9700\u8981\u5173\u6CE8\u7684\u95EE\u9898\u3002");
|
|
903
|
-
} else {
|
|
904
|
-
const critical = reports.filter((r) => r.severity === "critical");
|
|
905
|
-
const high = reports.filter((r) => r.severity === "high");
|
|
906
|
-
const medium = reports.filter((r) => r.severity === "medium");
|
|
907
|
-
if (critical.length > 0) {
|
|
908
|
-
lines.push("### \u{1F534} \u4E25\u91CD");
|
|
909
|
-
for (const r of critical) {
|
|
910
|
-
lines.push(`- **${r.id}**: ${r.title}`);
|
|
911
|
-
lines.push(` - ${r.aiDirectives[0] || r.preventionMeasures[0]}`);
|
|
912
|
-
}
|
|
913
|
-
lines.push("");
|
|
914
|
-
}
|
|
915
|
-
if (high.length > 0) {
|
|
916
|
-
lines.push("### \u{1F7E0} \u9AD8\u4F18\u5148\u7EA7");
|
|
917
|
-
for (const r of high) {
|
|
918
|
-
lines.push(`- **${r.id}**: ${r.title}`);
|
|
919
|
-
lines.push(` - ${r.aiDirectives[0] || r.preventionMeasures[0]}`);
|
|
920
|
-
}
|
|
921
|
-
lines.push("");
|
|
922
|
-
}
|
|
923
|
-
if (medium.length > 0) {
|
|
924
|
-
lines.push("### \u{1F7E1} \u4E2D\u4F18\u5148\u7EA7");
|
|
925
|
-
for (const r of medium) {
|
|
926
|
-
lines.push(`- **${r.id}**: ${r.title}`);
|
|
927
|
-
}
|
|
928
|
-
lines.push("");
|
|
929
|
-
}
|
|
930
|
-
lines.push("### \u{1F4CB} \u5F00\u53D1\u6307\u4EE4");
|
|
931
|
-
const allDirectives = /* @__PURE__ */ new Set();
|
|
932
|
-
for (const r of reports.slice(0, 5)) {
|
|
933
|
-
for (const d of r.aiDirectives.slice(0, 2)) {
|
|
934
|
-
allDirectives.add(d);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
for (const d of allDirectives) {
|
|
938
|
-
lines.push(`- ${d}`);
|
|
939
|
-
}
|
|
940
|
-
lines.push("");
|
|
941
|
-
lines.push(`> \u8BE6\u7EC6\u4FE1\u606F\u8BF7\u67E5\u770B \`${this.config.directory}/\` \u76EE\u5F55`);
|
|
516
|
+
if (analysis.hasTests) {
|
|
517
|
+
selectedAgents.push(
|
|
518
|
+
...this.findAgentsByCategory(agentIndex, "testing")
|
|
519
|
+
);
|
|
942
520
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
521
|
+
if (analysis.cicd && analysis.cicd.length > 0) {
|
|
522
|
+
selectedAgents.push(
|
|
523
|
+
...this.findAgentsByCategory(agentIndex, "devops")
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
if (analysis.hasApi || analysis.hasDatabase) {
|
|
527
|
+
selectedAgents.push(
|
|
528
|
+
...this.findAgentsByCategory(agentIndex, "security")
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
return this.deduplicateAndSort(selectedAgents);
|
|
951
532
|
}
|
|
952
|
-
// ==========================================================================
|
|
953
|
-
// Code Checking
|
|
954
|
-
// ==========================================================================
|
|
955
533
|
/**
|
|
956
|
-
*
|
|
534
|
+
* Select skill templates
|
|
957
535
|
*/
|
|
958
|
-
async
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
536
|
+
async selectSkills(analysis) {
|
|
537
|
+
const selectedSkills = [];
|
|
538
|
+
const skillIndex = this.loadSkillIndex();
|
|
539
|
+
selectedSkills.push(
|
|
540
|
+
...this.findSkillsByCategory(skillIndex, "git")
|
|
541
|
+
);
|
|
542
|
+
selectedSkills.push(
|
|
543
|
+
...this.findSkillsByCategory(skillIndex, "code-quality")
|
|
544
|
+
);
|
|
545
|
+
if (analysis.hasTests) {
|
|
546
|
+
selectedSkills.push(
|
|
547
|
+
...this.findSkillsByIds(skillIndex, ["generate-tests"])
|
|
548
|
+
);
|
|
967
549
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
550
|
+
if (analysis.hasApi) {
|
|
551
|
+
selectedSkills.push(
|
|
552
|
+
...this.findSkillsByIds(skillIndex, ["api-docs"])
|
|
553
|
+
);
|
|
972
554
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
const report = this.getReport(meta.id);
|
|
978
|
-
if (!report)
|
|
979
|
-
continue;
|
|
980
|
-
for (const pattern of report.detectionPatterns) {
|
|
981
|
-
patterns.push({ pattern, postmortemId: report.id });
|
|
982
|
-
}
|
|
555
|
+
if (analysis.hasDatabase) {
|
|
556
|
+
selectedSkills.push(
|
|
557
|
+
...this.findSkillsByIds(skillIndex, ["database-migration"])
|
|
558
|
+
);
|
|
983
559
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
const content = fs.readFileSync(fullPath, "utf-8");
|
|
989
|
-
const lines = content.split("\n");
|
|
990
|
-
for (const { pattern, postmortemId } of patterns) {
|
|
991
|
-
if (!pattern.fileTypes.some((ft) => file.endsWith(ft))) {
|
|
992
|
-
continue;
|
|
993
|
-
}
|
|
994
|
-
if (pattern.type === "regex") {
|
|
995
|
-
try {
|
|
996
|
-
const regex = new RegExp(pattern.pattern, "g");
|
|
997
|
-
for (let i = 0; i < lines.length; i++) {
|
|
998
|
-
const line = lines[i];
|
|
999
|
-
const matches = line.match(regex);
|
|
1000
|
-
if (matches) {
|
|
1001
|
-
issues.push({
|
|
1002
|
-
file,
|
|
1003
|
-
line: i + 1,
|
|
1004
|
-
column: line.indexOf(matches[0]) + 1,
|
|
1005
|
-
pattern,
|
|
1006
|
-
postmortemId,
|
|
1007
|
-
message: `\u53EF\u80FD\u89E6\u53D1 ${postmortemId}: ${pattern.description}`,
|
|
1008
|
-
suggestion: `\u53C2\u8003 ${this.config.directory}/${postmortemId}.md`
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
} catch {
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
560
|
+
if (analysis.projectType === "frontend" || analysis.projectType === "fullstack") {
|
|
561
|
+
selectedSkills.push(
|
|
562
|
+
...this.findSkillsByIds(skillIndex, ["performance-optimization"])
|
|
563
|
+
);
|
|
1016
564
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
issuesFound: issues,
|
|
1027
|
-
summary,
|
|
1028
|
-
passed: summary.critical === 0 && summary.high === 0
|
|
1029
|
-
};
|
|
565
|
+
if (analysis.packageJson) {
|
|
566
|
+
selectedSkills.push(
|
|
567
|
+
...this.findSkillsByIds(skillIndex, ["dependency-update"])
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
selectedSkills.push(
|
|
571
|
+
...this.findSkillsByIds(skillIndex, ["refactor-code"])
|
|
572
|
+
);
|
|
573
|
+
return this.deduplicateAndSort(selectedSkills);
|
|
1030
574
|
}
|
|
1031
575
|
/**
|
|
1032
|
-
*
|
|
576
|
+
* Load agent index
|
|
1033
577
|
*/
|
|
1034
|
-
|
|
578
|
+
loadAgentIndex() {
|
|
1035
579
|
try {
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
});
|
|
1040
|
-
return output.trim().split("\n").filter(Boolean);
|
|
580
|
+
const indexPath = join(this.templatesDir, "agents", "index.json");
|
|
581
|
+
const content = readFileSync(indexPath, "utf-8");
|
|
582
|
+
return JSON.parse(content);
|
|
1041
583
|
} catch {
|
|
1042
|
-
return [];
|
|
584
|
+
return { templates: [] };
|
|
1043
585
|
}
|
|
1044
586
|
}
|
|
1045
587
|
/**
|
|
1046
|
-
*
|
|
588
|
+
* Load skill index
|
|
1047
589
|
*/
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
continue;
|
|
1057
|
-
}
|
|
1058
|
-
if (entry.isDirectory()) {
|
|
1059
|
-
walk(fullPath);
|
|
1060
|
-
} else if (entry.isFile()) {
|
|
1061
|
-
if (this.config.detection.includePatterns.some((p) => this.matchGlob(relativePath, p))) {
|
|
1062
|
-
files.push(relativePath);
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
};
|
|
1067
|
-
walk(this.projectRoot);
|
|
1068
|
-
return files;
|
|
590
|
+
loadSkillIndex() {
|
|
591
|
+
try {
|
|
592
|
+
const indexPath = join(this.templatesDir, "skills", "index.json");
|
|
593
|
+
const content = readFileSync(indexPath, "utf-8");
|
|
594
|
+
return JSON.parse(content);
|
|
595
|
+
} catch {
|
|
596
|
+
return { templates: [] };
|
|
597
|
+
}
|
|
1069
598
|
}
|
|
1070
599
|
/**
|
|
1071
|
-
*
|
|
600
|
+
* Find agents by category
|
|
1072
601
|
*/
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
return new RegExp(`^${regexPattern}$`).test(filepath);
|
|
602
|
+
findAgentsByCategory(index, category) {
|
|
603
|
+
return index.templates.filter((agent) => agent.category === category);
|
|
1076
604
|
}
|
|
1077
605
|
/**
|
|
1078
|
-
*
|
|
606
|
+
* Find skills by category
|
|
1079
607
|
*/
|
|
1080
|
-
|
|
1081
|
-
return
|
|
1082
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1083
|
-
filesChecked,
|
|
1084
|
-
issuesFound: [],
|
|
1085
|
-
summary: { critical: 0, high: 0, medium: 0, low: 0 },
|
|
1086
|
-
passed: true
|
|
1087
|
-
};
|
|
608
|
+
findSkillsByCategory(index, category) {
|
|
609
|
+
return index.templates.filter((skill) => skill.category === category);
|
|
1088
610
|
}
|
|
1089
|
-
// ==========================================================================
|
|
1090
|
-
// Release Summary
|
|
1091
|
-
// ==========================================================================
|
|
1092
611
|
/**
|
|
1093
|
-
*
|
|
612
|
+
* Find skills by IDs
|
|
1094
613
|
*/
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
const commits = PostmortemAnalyzer.getFixCommits({
|
|
1098
|
-
since,
|
|
1099
|
-
until,
|
|
1100
|
-
cwd: this.projectRoot
|
|
1101
|
-
});
|
|
1102
|
-
const analyses = commits.map(
|
|
1103
|
-
(c) => PostmortemAnalyzer.analyzeFixCommit(c, this.projectRoot)
|
|
1104
|
-
);
|
|
1105
|
-
const existingIds = this.listReports().map((r) => r.id);
|
|
1106
|
-
const newReports = PostmortemAnalyzer.generatePostmortem(analyses, existingIds);
|
|
1107
|
-
const newIds = [];
|
|
1108
|
-
for (const report of newReports) {
|
|
1109
|
-
report.affectedVersions = { from: since || "unknown", to: version };
|
|
1110
|
-
this.saveReport(report);
|
|
1111
|
-
newIds.push(report.id);
|
|
1112
|
-
}
|
|
1113
|
-
this.updateIndex();
|
|
1114
|
-
const summary = {
|
|
1115
|
-
version,
|
|
1116
|
-
releaseDate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1117
|
-
fixCommitCount: commits.length,
|
|
1118
|
-
newPostmortems: newIds,
|
|
1119
|
-
updatedPostmortems: [],
|
|
1120
|
-
summary: this.generateReleaseSummaryText(commits, newReports),
|
|
1121
|
-
keyLessons: this.extractKeyLessons(newReports)
|
|
1122
|
-
};
|
|
1123
|
-
const summaryPath = path.join(this.postmortemDir, "summaries", `${version}.json`);
|
|
1124
|
-
fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
1125
|
-
if (this.config.autoSyncToClaudeMd) {
|
|
1126
|
-
await this.syncToClaudeMd();
|
|
1127
|
-
}
|
|
1128
|
-
return summary;
|
|
614
|
+
findSkillsByIds(index, ids) {
|
|
615
|
+
return index.templates.filter((skill) => ids.includes(skill.id));
|
|
1129
616
|
}
|
|
1130
617
|
/**
|
|
1131
|
-
*
|
|
618
|
+
* Deduplicate and sort templates by priority
|
|
1132
619
|
*/
|
|
1133
|
-
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
const categories = /* @__PURE__ */ new Map();
|
|
1141
|
-
for (const r of reports) {
|
|
1142
|
-
categories.set(r.category, (categories.get(r.category) || 0) + 1);
|
|
1143
|
-
}
|
|
1144
|
-
for (const [cat, count] of categories) {
|
|
1145
|
-
lines.push(`- ${cat}: ${count} \u4E2A`);
|
|
620
|
+
deduplicateAndSort(templates) {
|
|
621
|
+
const seen = /* @__PURE__ */ new Set();
|
|
622
|
+
const unique = [];
|
|
623
|
+
for (const template of templates) {
|
|
624
|
+
if (!seen.has(template.id)) {
|
|
625
|
+
seen.add(template.id);
|
|
626
|
+
unique.push(template);
|
|
1146
627
|
}
|
|
1147
628
|
}
|
|
1148
|
-
return
|
|
629
|
+
return unique.sort((a, b) => b.priority - a.priority);
|
|
1149
630
|
}
|
|
1150
631
|
/**
|
|
1151
|
-
*
|
|
632
|
+
* Generate reasoning for template selection
|
|
1152
633
|
*/
|
|
1153
|
-
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
634
|
+
generateReasoning(analysis, agents, skills) {
|
|
635
|
+
const reasons = [];
|
|
636
|
+
reasons.push(`Detected ${analysis.projectType} project`);
|
|
637
|
+
if (analysis.frameworks.length > 0) {
|
|
638
|
+
reasons.push(`Using frameworks: ${analysis.frameworks.join(", ")}`);
|
|
639
|
+
}
|
|
640
|
+
reasons.push(`Tech stack: ${analysis.techStack.languages.join(", ")} on ${analysis.techStack.runtime}`);
|
|
641
|
+
if (analysis.hasTests) {
|
|
642
|
+
reasons.push("Tests detected - including test engineer and test generation");
|
|
643
|
+
}
|
|
644
|
+
if (analysis.hasDatabase) {
|
|
645
|
+
reasons.push("Database detected - including migration skills");
|
|
646
|
+
}
|
|
647
|
+
if (analysis.hasApi) {
|
|
648
|
+
reasons.push("API endpoints detected - including API documentation");
|
|
1159
649
|
}
|
|
1160
|
-
|
|
650
|
+
if (analysis.cicd && analysis.cicd.length > 0) {
|
|
651
|
+
reasons.push(`CI/CD detected (${analysis.cicd.join(", ")}) - including DevOps engineer`);
|
|
652
|
+
}
|
|
653
|
+
reasons.push(`
|
|
654
|
+
Selected ${agents.length} agents and ${skills.length} skills`);
|
|
655
|
+
return reasons.join("\n");
|
|
1161
656
|
}
|
|
1162
657
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
658
|
+
async function selectTemplates(analysis) {
|
|
659
|
+
const selector = new TemplateSelector();
|
|
660
|
+
return selector.select(analysis);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async function smartGenerate(projectRoot) {
|
|
664
|
+
const analysis = await analyzeProject(projectRoot);
|
|
665
|
+
const selection = await selectTemplates(analysis);
|
|
666
|
+
const config = await generateConfigs(selection);
|
|
667
|
+
return {
|
|
668
|
+
analysis,
|
|
669
|
+
selection,
|
|
670
|
+
config
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
async function smartGenerateAndInstall(projectRoot) {
|
|
674
|
+
const result = await smartGenerate(projectRoot);
|
|
675
|
+
await writeConfigs(result.config);
|
|
676
|
+
return result;
|
|
1169
677
|
}
|
|
1170
678
|
|
|
1171
|
-
export {
|
|
679
|
+
export { ConfigGenerator, ProjectAnalyzer, TemplateSelector, analyzeProject, generateConfigs, selectTemplates, smartGenerate, smartGenerateAndInstall, writeConfigs };
|