code-abyss 1.6.16 → 1.7.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/README.md +8 -6
- package/bin/install.js +59 -163
- package/bin/lib/ccline.js +82 -0
- package/bin/lib/utils.js +61 -0
- package/package.json +5 -2
- package/skills/SKILL.md +24 -16
- package/skills/domains/ai/SKILL.md +2 -2
- package/skills/domains/ai/prompt-and-eval.md +279 -0
- package/skills/domains/architecture/SKILL.md +2 -3
- package/skills/domains/architecture/security-arch.md +87 -0
- package/skills/domains/data-engineering/SKILL.md +188 -26
- package/skills/domains/development/SKILL.md +1 -4
- package/skills/domains/devops/SKILL.md +3 -5
- package/skills/domains/devops/performance.md +63 -0
- package/skills/domains/devops/testing.md +97 -0
- package/skills/domains/frontend-design/SKILL.md +12 -3
- package/skills/domains/frontend-design/claymorphism/SKILL.md +117 -0
- package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
- package/skills/domains/frontend-design/engineering.md +287 -0
- package/skills/domains/frontend-design/glassmorphism/SKILL.md +138 -0
- package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
- package/skills/domains/frontend-design/liquid-glass/SKILL.md +135 -0
- package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
- package/skills/domains/frontend-design/neubrutalism/SKILL.md +141 -0
- package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
- package/skills/domains/infrastructure/SKILL.md +174 -34
- package/skills/domains/mobile/SKILL.md +211 -21
- package/skills/domains/orchestration/SKILL.md +1 -0
- package/skills/domains/security/SKILL.md +4 -6
- package/skills/domains/security/blue-team.md +57 -0
- package/skills/domains/security/red-team.md +54 -0
- package/skills/domains/security/threat-intel.md +50 -0
- package/skills/orchestration/multi-agent/SKILL.md +195 -46
- package/skills/run_skill.js +139 -0
- package/skills/tools/gen-docs/SKILL.md +6 -4
- package/skills/tools/gen-docs/scripts/doc_generator.js +363 -0
- package/skills/tools/lib/shared.js +98 -0
- package/skills/tools/verify-change/SKILL.md +8 -6
- package/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
- package/skills/tools/verify-module/SKILL.md +6 -4
- package/skills/tools/verify-module/scripts/module_scanner.js +171 -0
- package/skills/tools/verify-quality/SKILL.md +5 -3
- package/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
- package/skills/tools/verify-security/SKILL.md +7 -5
- package/skills/tools/verify-security/scripts/security_scanner.js +283 -0
- package/skills/__pycache__/run_skill.cpython-312.pyc +0 -0
- package/skills/domains/COVERAGE_PLAN.md +0 -232
- package/skills/domains/ai/model-evaluation.md +0 -790
- package/skills/domains/ai/prompt-engineering.md +0 -703
- package/skills/domains/architecture/compliance.md +0 -299
- package/skills/domains/architecture/data-security.md +0 -184
- package/skills/domains/data-engineering/data-pipeline.md +0 -762
- package/skills/domains/data-engineering/data-quality.md +0 -894
- package/skills/domains/data-engineering/stream-processing.md +0 -791
- package/skills/domains/development/dart.md +0 -963
- package/skills/domains/development/kotlin.md +0 -834
- package/skills/domains/development/php.md +0 -659
- package/skills/domains/development/swift.md +0 -755
- package/skills/domains/devops/e2e-testing.md +0 -914
- package/skills/domains/devops/performance-testing.md +0 -734
- package/skills/domains/devops/testing-strategy.md +0 -667
- package/skills/domains/frontend-design/build-tools.md +0 -743
- package/skills/domains/frontend-design/performance.md +0 -734
- package/skills/domains/frontend-design/testing.md +0 -699
- package/skills/domains/infrastructure/gitops.md +0 -735
- package/skills/domains/infrastructure/iac.md +0 -855
- package/skills/domains/infrastructure/kubernetes.md +0 -1018
- package/skills/domains/mobile/android-dev.md +0 -979
- package/skills/domains/mobile/cross-platform.md +0 -795
- package/skills/domains/mobile/ios-dev.md +0 -931
- package/skills/domains/security/secrets-management.md +0 -834
- package/skills/domains/security/supply-chain.md +0 -931
- package/skills/domains/security/threat-modeling.md +0 -828
- package/skills/run_skill.py +0 -153
- package/skills/tests/README.md +0 -225
- package/skills/tests/SUMMARY.md +0 -362
- package/skills/tests/__init__.py +0 -3
- package/skills/tests/__pycache__/test_change_analyzer.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_doc_generator.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_module_scanner.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_quality_checker.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_security_scanner.cpython-312.pyc +0 -0
- package/skills/tests/test_change_analyzer.py +0 -558
- package/skills/tests/test_doc_generator.py +0 -538
- package/skills/tests/test_module_scanner.py +0 -376
- package/skills/tests/test_quality_checker.py +0 -516
- package/skills/tests/test_security_scanner.py +0 -426
- package/skills/tools/gen-docs/scripts/__pycache__/doc_generator.cpython-312.pyc +0 -0
- package/skills/tools/gen-docs/scripts/doc_generator.py +0 -520
- package/skills/tools/verify-change/scripts/__pycache__/change_analyzer.cpython-312.pyc +0 -0
- package/skills/tools/verify-change/scripts/change_analyzer.py +0 -529
- package/skills/tools/verify-module/scripts/__pycache__/module_scanner.cpython-312.pyc +0 -0
- package/skills/tools/verify-module/scripts/module_scanner.py +0 -321
- package/skills/tools/verify-quality/scripts/__pycache__/quality_checker.cpython-312.pyc +0 -0
- package/skills/tools/verify-quality/scripts/quality_checker.py +0 -481
- package/skills/tools/verify-security/scripts/__pycache__/security_scanner.cpython-312.pyc +0 -0
- package/skills/tools/verify-security/scripts/security_scanner.py +0 -374
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 文档生成器
|
|
4
|
+
* 自动生成/更新 README.md 和 DESIGN.md 骨架
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
// --- Utilities ---
|
|
11
|
+
|
|
12
|
+
function rglob(dir, filter) {
|
|
13
|
+
const results = [];
|
|
14
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
15
|
+
const full = path.join(dir, entry.name);
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
results.push(...rglob(full, filter));
|
|
18
|
+
} else if (!filter || filter(entry.name, full)) {
|
|
19
|
+
results.push(full);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return results;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// --- Language Detection ---
|
|
26
|
+
|
|
27
|
+
const LANG_MAP = {
|
|
28
|
+
'.py': 'Python', '.go': 'Go', '.rs': 'Rust', '.ts': 'TypeScript',
|
|
29
|
+
'.js': 'JavaScript', '.java': 'Java', '.c': 'C', '.cpp': 'C++',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function detectLanguage(modPath) {
|
|
33
|
+
const exts = {};
|
|
34
|
+
try {
|
|
35
|
+
for (const f of rglob(modPath)) {
|
|
36
|
+
const ext = path.extname(f).toLowerCase();
|
|
37
|
+
if (ext) exts[ext] = (exts[ext] || 0) + 1;
|
|
38
|
+
}
|
|
39
|
+
} catch { return 'Unknown'; }
|
|
40
|
+
const codeExts = Object.entries(exts).filter(([k]) => k in LANG_MAP);
|
|
41
|
+
if (codeExts.length) {
|
|
42
|
+
const best = codeExts.reduce((a, b) => b[1] > a[1] ? b : a);
|
|
43
|
+
return LANG_MAP[best[0]] || 'Unknown';
|
|
44
|
+
}
|
|
45
|
+
return 'Unknown';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// --- Python AST-lite extraction via regex ---
|
|
49
|
+
|
|
50
|
+
function analyzePythonModule(modPath) {
|
|
51
|
+
const info = makeInfo(modPath, 'Python');
|
|
52
|
+
const pyFiles = rglob(modPath, (name) => name.endsWith('.py'));
|
|
53
|
+
info.files = pyFiles.map(f => path.relative(modPath, f));
|
|
54
|
+
|
|
55
|
+
for (const pyFile of pyFiles) {
|
|
56
|
+
const basename = path.basename(pyFile);
|
|
57
|
+
if (basename.startsWith('test_') || basename.includes('_test')) continue;
|
|
58
|
+
let content;
|
|
59
|
+
try { content = fs.readFileSync(pyFile, 'utf-8'); } catch { continue; }
|
|
60
|
+
|
|
61
|
+
// Module docstring (triple-quoted at top)
|
|
62
|
+
if (!info.description) {
|
|
63
|
+
const docM = content.match(/^(?:#[^\n]*\n)*\s*(?:"""([\s\S]*?)"""|'''([\s\S]*?)''')/);
|
|
64
|
+
if (docM) info.description = (docM[1] || docM[2]).split('\n')[0].trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const rel = path.relative(modPath, pyFile);
|
|
68
|
+
|
|
69
|
+
// Functions
|
|
70
|
+
for (const m of content.matchAll(/^def\s+([A-Za-z]\w*)\s*\(/gm)) {
|
|
71
|
+
info.functions.push({ name: m[1], file: rel, doc: '' });
|
|
72
|
+
}
|
|
73
|
+
// Classes
|
|
74
|
+
for (const m of content.matchAll(/^class\s+([A-Za-z]\w*)\s*[:(]/gm)) {
|
|
75
|
+
info.classes.push({ name: m[1], file: rel, doc: '' });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Entry points
|
|
79
|
+
if (['main.py', '__main__.py', 'cli.py', 'app.py'].includes(basename)) {
|
|
80
|
+
info.entry_points.push(rel);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Dependencies
|
|
85
|
+
const reqPath = path.join(modPath, 'requirements.txt');
|
|
86
|
+
try {
|
|
87
|
+
const content = fs.readFileSync(reqPath, 'utf-8');
|
|
88
|
+
for (const line of content.split('\n')) {
|
|
89
|
+
const trimmed = line.trim();
|
|
90
|
+
if (trimmed && !trimmed.startsWith('#')) {
|
|
91
|
+
info.dependencies.push(trimmed.split(/[=><]/)[0]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch {}
|
|
95
|
+
|
|
96
|
+
return info;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- Generic analysis (regex fallback) ---
|
|
100
|
+
|
|
101
|
+
const LANG_PATTERNS = {
|
|
102
|
+
'Go': [/^\s*func\s+(\w+)/, /^\s*type\s+(\w+)\s+struct\b/],
|
|
103
|
+
'Rust': [/^\s*(?:pub\s+)?fn\s+(\w+)/, /^\s*(?:pub\s+)?struct\s+(\w+)/],
|
|
104
|
+
'TypeScript': [/^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/, /^\s*(?:export\s+)?class\s+(\w+)/],
|
|
105
|
+
'JavaScript': [/^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/, /^\s*(?:export\s+)?class\s+(\w+)/],
|
|
106
|
+
'Java': [/^\s*(?:public|private|protected)?\s*(?:static\s+)?\w+\s+(\w+)\s*\(/,
|
|
107
|
+
/^\s*(?:public\s+)?class\s+(\w+)/],
|
|
108
|
+
'C++': [/^\s*(?:\w+\s+)+(\w+)\s*\([^;]*$/, /^\s*class\s+(\w+)/],
|
|
109
|
+
'C': [/^\s*(?:\w+\s+)+(\w+)\s*\([^;]*$/, null],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const CODE_EXTS = new Set(['.py', '.go', '.rs', '.ts', '.js', '.java', '.c', '.cpp']);
|
|
113
|
+
|
|
114
|
+
function analyzeModule(modPath) {
|
|
115
|
+
const language = detectLanguage(modPath);
|
|
116
|
+
if (language === 'Python') return analyzePythonModule(modPath);
|
|
117
|
+
|
|
118
|
+
const info = makeInfo(modPath, language);
|
|
119
|
+
const [funcPat, clsPat] = LANG_PATTERNS[language] || [null, null];
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
for (const f of rglob(modPath)) {
|
|
123
|
+
if (!CODE_EXTS.has(path.extname(f).toLowerCase())) continue;
|
|
124
|
+
const rel = path.relative(modPath, f);
|
|
125
|
+
info.files.push(rel);
|
|
126
|
+
|
|
127
|
+
if (!funcPat && !clsPat) continue;
|
|
128
|
+
let content;
|
|
129
|
+
try { content = fs.readFileSync(f, 'utf-8'); } catch { continue; }
|
|
130
|
+
for (const line of content.split('\n')) {
|
|
131
|
+
if (funcPat) {
|
|
132
|
+
const m = line.match(funcPat);
|
|
133
|
+
if (m && !m[1].startsWith('_')) info.functions.push({ name: m[1], file: rel, doc: '' });
|
|
134
|
+
}
|
|
135
|
+
if (clsPat) {
|
|
136
|
+
const m = line.match(clsPat);
|
|
137
|
+
if (m && !m[1].startsWith('_')) info.classes.push({ name: m[1], file: rel, doc: '' });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch {}
|
|
142
|
+
|
|
143
|
+
return info;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function makeInfo(modPath, language) {
|
|
147
|
+
return {
|
|
148
|
+
name: path.basename(modPath), path: modPath, description: '', language,
|
|
149
|
+
files: [], functions: [], classes: [], dependencies: [], entry_points: [],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// --- README Generation ---
|
|
154
|
+
|
|
155
|
+
function generateReadme(info) {
|
|
156
|
+
const L = [];
|
|
157
|
+
L.push(`# ${info.name}`, '');
|
|
158
|
+
if (info.description) {
|
|
159
|
+
L.push(info.description);
|
|
160
|
+
} else {
|
|
161
|
+
L.push('> 请在此描述模块的核心功能、解决的问题和主要用途。');
|
|
162
|
+
L.push('> 例如:本模块提供 X 功能,用于解决 Y 问题。');
|
|
163
|
+
}
|
|
164
|
+
L.push('', '## 概述', '', '<!-- 描述这个模块是什么,解决什么问题 -->', '');
|
|
165
|
+
L.push('## 特性', '', '<!-- 列出模块的主要特性,每项应包含简短描述 -->', '');
|
|
166
|
+
L.push('- **特性1**: 请描述第一个主要特性');
|
|
167
|
+
L.push('- **特性2**: 请描述第二个主要特性');
|
|
168
|
+
L.push('- **特性3**: 请描述第三个主要特性', '');
|
|
169
|
+
|
|
170
|
+
if (info.dependencies.length) {
|
|
171
|
+
L.push('## 依赖', '', '```');
|
|
172
|
+
info.dependencies.slice(0, 10).forEach(d => L.push(d));
|
|
173
|
+
if (info.dependencies.length > 10) L.push(`# ... 及其他 ${info.dependencies.length - 10} 个依赖`);
|
|
174
|
+
L.push('```', '');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
L.push('## 使用方法', '');
|
|
178
|
+
if (info.entry_points.length) {
|
|
179
|
+
L.push('### 运行', '', '```bash');
|
|
180
|
+
const cmds = {
|
|
181
|
+
Python: `python -m ${info.name}`, Go: 'go run ./cmd/main.go',
|
|
182
|
+
Rust: 'cargo run', TypeScript: 'npm start', JavaScript: 'npm start'
|
|
183
|
+
};
|
|
184
|
+
L.push(cmds[info.language] || `# 请根据 ${info.language} 项目结构添加运行命令`);
|
|
185
|
+
L.push('```', '');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
L.push('### 示例', '');
|
|
189
|
+
const EXAMPLES = {
|
|
190
|
+
Python: `from ${info.name.toLowerCase()} import main\n\n` +
|
|
191
|
+
`# 初始化\nobj = main()\n\n# 执行操作\nresult = obj.process()\nprint(result)`,
|
|
192
|
+
Go: `package main\n\nimport "${info.name.toLowerCase()}"\n\nfunc main() {\n` +
|
|
193
|
+
` // 初始化\n obj := ${info.name.toLowerCase()}.New()\n` +
|
|
194
|
+
`\n // 执行操作\n result := obj.Process()\n println(result)\n}`,
|
|
195
|
+
Rust: `use ${info.name.toLowerCase()}::*;\n\nfn main() {\n` +
|
|
196
|
+
` // 初始化\n let obj = Object::new();\n\n` +
|
|
197
|
+
` // 执行操作\n let result = obj.process();\n` +
|
|
198
|
+
` println!("{}", result);\n}`,
|
|
199
|
+
TypeScript: `import { main } from "./${info.name.toLowerCase()}";\n\n` +
|
|
200
|
+
`// 初始化\nconst obj = new main();\n\n` +
|
|
201
|
+
`// 执行操作\nconst result = obj.process();\nconsole.log(result);`,
|
|
202
|
+
JavaScript: `const { main } = require("./${info.name.toLowerCase()}");\n\n` +
|
|
203
|
+
`// 初始化\nconst obj = new main();\n\n` +
|
|
204
|
+
`// 执行操作\nconst result = obj.process();\nconsole.log(result);`,
|
|
205
|
+
};
|
|
206
|
+
if (EXAMPLES[info.language]) {
|
|
207
|
+
L.push('```' + info.language.toLowerCase(), EXAMPLES[info.language], '```');
|
|
208
|
+
} else {
|
|
209
|
+
L.push('```' + info.language.toLowerCase());
|
|
210
|
+
L.push(`<!-- 请根据 ${info.language} 语言特性提供使用示例 -->`);
|
|
211
|
+
L.push(`<!-- 示例应包含:初始化、基本操作、结果处理 -->`);
|
|
212
|
+
L.push('```');
|
|
213
|
+
}
|
|
214
|
+
L.push('');
|
|
215
|
+
|
|
216
|
+
if (info.classes.length || info.functions.length) {
|
|
217
|
+
L.push('## API 概览', '');
|
|
218
|
+
if (info.classes.length) {
|
|
219
|
+
L.push('### 类', '', '| 类名 | 描述 |', '|------|------|');
|
|
220
|
+
info.classes.slice(0, 10).forEach(c => L.push(`| \`${c.name}\` | ${c.doc || '请补充此类的功能描述'} |`));
|
|
221
|
+
L.push('');
|
|
222
|
+
}
|
|
223
|
+
if (info.functions.length) {
|
|
224
|
+
L.push('### 函数', '', '| 函数 | 描述 |', '|------|------|');
|
|
225
|
+
info.functions.slice(0, 10).forEach(f => L.push(`| \`${f.name}()\` | ${f.doc || '请补充此函数的功能描述'} |`));
|
|
226
|
+
L.push('');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
L.push('## 目录结构', '', '```', `${info.name}/`);
|
|
231
|
+
info.files.sort().slice(0, 15).forEach(f => L.push(`├── ${f}`));
|
|
232
|
+
if (info.files.length > 15) L.push(`└── ... (${info.files.length - 15} more files)`);
|
|
233
|
+
L.push('```', '');
|
|
234
|
+
L.push('## 相关文档', '', '- [设计文档](DESIGN.md)', '');
|
|
235
|
+
return L.join('\n');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// --- DESIGN Generation ---
|
|
239
|
+
|
|
240
|
+
function generateDesign(info) {
|
|
241
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
242
|
+
const L = [];
|
|
243
|
+
L.push(`# ${info.name} 设计文档`, '');
|
|
244
|
+
L.push('## 设计概述', '', '### 目标', '', '<!-- 这个模块要解决什么问题? -->', '');
|
|
245
|
+
L.push('### 非目标', '', '<!-- 这个模块明确不做什么? -->', '');
|
|
246
|
+
L.push('## 架构设计', '', '### 整体架构', '', '```');
|
|
247
|
+
L.push('┌─────────────────────────────────────┐');
|
|
248
|
+
L.push('│ 请在此绘制模块的整体架构图 │');
|
|
249
|
+
L.push('│ 包括主要组件、数据流、依赖关系 │');
|
|
250
|
+
L.push('│ 可使用 ASCII 图或 Mermaid 图表 │');
|
|
251
|
+
L.push('└─────────────────────────────────────┘');
|
|
252
|
+
L.push('```', '');
|
|
253
|
+
L.push('### 核心组件', '');
|
|
254
|
+
if (info.classes.length) {
|
|
255
|
+
info.classes.slice(0, 5).forEach(c => L.push(`- **${c.name}**: ${c.doc || '请描述此组件的职责和功能'}`));
|
|
256
|
+
} else {
|
|
257
|
+
L.push('<!-- 列出模块的核心组件及其职责 -->');
|
|
258
|
+
L.push('- **组件1**: 请描述第一个核心组件的职责');
|
|
259
|
+
L.push('- **组件2**: 请描述第二个核心组件的职责');
|
|
260
|
+
L.push('- **组件3**: 请描述第三个核心组件的职责');
|
|
261
|
+
}
|
|
262
|
+
L.push('');
|
|
263
|
+
L.push('## 设计决策', '', '### 决策记录', '');
|
|
264
|
+
L.push('| 日期 | 决策 | 理由 | 影响 |', '|------|------|------|------|');
|
|
265
|
+
L.push(`| ${today} | 初始设计 | - | - |`, '');
|
|
266
|
+
L.push('### 技术选型', '', `- **语言**: ${info.language}`);
|
|
267
|
+
if (info.dependencies.length) L.push(`- **主要依赖**: ${info.dependencies.slice(0, 5).join(', ')}`);
|
|
268
|
+
L.push('- **理由**: <!-- 请说明为什么选择这些技术栈,包括性能、可维护性、生态等考量 -->', '');
|
|
269
|
+
L.push('## 权衡取舍', '', '### 已知限制', '');
|
|
270
|
+
L.push('<!-- 列出模块的已知限制和约束条件 -->');
|
|
271
|
+
L.push('- **限制1**: 请描述第一个已知限制及其原因');
|
|
272
|
+
L.push('- **限制2**: 请描述第二个已知限制及其原因', '');
|
|
273
|
+
L.push('### 技术债务', '');
|
|
274
|
+
L.push('<!-- 记录有意引入的技术债务、临时方案及其原因 -->');
|
|
275
|
+
L.push('- **债务1**: 描述 | 原因:性能优先 | 计划偿还时间:v2.0', '');
|
|
276
|
+
L.push('## 安全考量', '', '### 威胁模型', '');
|
|
277
|
+
L.push('<!-- 识别潜在的安全威胁,如认证、授权、数据泄露等 -->');
|
|
278
|
+
L.push('- **威胁1**: 请描述潜在威胁及其影响');
|
|
279
|
+
L.push('- **威胁2**: 请描述潜在威胁及其影响', '');
|
|
280
|
+
L.push('### 安全措施', '');
|
|
281
|
+
L.push('<!-- 列出已实施的安全措施,如输入验证、加密、访问控制等 -->');
|
|
282
|
+
L.push('- **措施1**: 请描述已实施的安全措施');
|
|
283
|
+
L.push('- **措施2**: 请描述已实施的安全措施', '');
|
|
284
|
+
L.push('## 变更历史', '', `### ${today} - 初始版本`, '');
|
|
285
|
+
L.push('**变更内容**: 创建模块', '', '**变更理由**: 初始开发', '');
|
|
286
|
+
return L.join('\n');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// --- Core: generate_docs ---
|
|
290
|
+
|
|
291
|
+
function generateDocs(targetPath, force) {
|
|
292
|
+
const modPath = path.resolve(targetPath);
|
|
293
|
+
const result = { readme: null, design: null, status: 'success', messages: [] };
|
|
294
|
+
|
|
295
|
+
if (!fs.existsSync(modPath)) {
|
|
296
|
+
result.status = 'error';
|
|
297
|
+
result.messages.push(`路径不存在: ${modPath}`);
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const info = analyzeModule(modPath);
|
|
302
|
+
|
|
303
|
+
const readmePath = path.join(modPath, 'README.md');
|
|
304
|
+
if (fs.existsSync(readmePath) && !force) {
|
|
305
|
+
result.messages.push('README.md 已存在,跳过(使用 --force 覆盖)');
|
|
306
|
+
} else {
|
|
307
|
+
fs.writeFileSync(readmePath, generateReadme(info));
|
|
308
|
+
result.readme = readmePath;
|
|
309
|
+
result.messages.push('已生成 README.md');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const designPath = path.join(modPath, 'DESIGN.md');
|
|
313
|
+
if (fs.existsSync(designPath) && !force) {
|
|
314
|
+
result.messages.push('DESIGN.md 已存在,跳过(使用 --force 覆盖)');
|
|
315
|
+
} else {
|
|
316
|
+
fs.writeFileSync(designPath, generateDesign(info));
|
|
317
|
+
result.design = designPath;
|
|
318
|
+
result.messages.push('已生成 DESIGN.md');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// --- CLI ---
|
|
325
|
+
|
|
326
|
+
function parseArgs(argv) {
|
|
327
|
+
const args = { path: '.', force: false, json: false, readmeOnly: false, designOnly: false };
|
|
328
|
+
const rest = argv.slice(2);
|
|
329
|
+
const positional = [];
|
|
330
|
+
for (const a of rest) {
|
|
331
|
+
if (a === '-f' || a === '--force') args.force = true;
|
|
332
|
+
else if (a === '--json') args.json = true;
|
|
333
|
+
else if (a === '--readme-only') args.readmeOnly = true;
|
|
334
|
+
else if (a === '--design-only') args.designOnly = true;
|
|
335
|
+
else if (a === '-h' || a === '--help') {
|
|
336
|
+
console.log('Usage: doc_generator.js [path] [-f|--force] [--json] [--readme-only] [--design-only]');
|
|
337
|
+
process.exit(0);
|
|
338
|
+
} else positional.push(a);
|
|
339
|
+
}
|
|
340
|
+
if (positional.length) args.path = positional[0];
|
|
341
|
+
return args;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function main() {
|
|
345
|
+
const args = parseArgs(process.argv);
|
|
346
|
+
const result = generateDocs(args.path, args.force);
|
|
347
|
+
|
|
348
|
+
if (args.json) {
|
|
349
|
+
console.log(JSON.stringify(result, null, 2));
|
|
350
|
+
} else {
|
|
351
|
+
console.log('='.repeat(50));
|
|
352
|
+
console.log('文档生成报告');
|
|
353
|
+
console.log('='.repeat(50));
|
|
354
|
+
for (const msg of result.messages) {
|
|
355
|
+
console.log(` \u2022 ${msg}`);
|
|
356
|
+
}
|
|
357
|
+
console.log('='.repeat(50));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
process.exit(result.status === 'success' ? 0 : 1);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
main();
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 验证工具共享库
|
|
5
|
+
* 消灭 verify-* 脚本间的重复代码
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// --- CLI 参数解析 ---
|
|
9
|
+
|
|
10
|
+
function parseCliArgs(argv, extraFlags) {
|
|
11
|
+
const args = argv.slice(2);
|
|
12
|
+
const result = { target: '.', verbose: false, json: false };
|
|
13
|
+
if (extraFlags) Object.assign(result, extraFlags);
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < args.length; i++) {
|
|
16
|
+
if (args[i] === '-v' || args[i] === '--verbose') result.verbose = true;
|
|
17
|
+
else if (args[i] === '--json') result.json = true;
|
|
18
|
+
else if (args[i] === '-h' || args[i] === '--help') { result.help = true; }
|
|
19
|
+
else if (args[i] === '--mode' && args[i + 1]) { result.mode = args[++i]; }
|
|
20
|
+
else if (args[i] === '--exclude') {
|
|
21
|
+
result.exclude = result.exclude || [];
|
|
22
|
+
while (i + 1 < args.length && !args[i + 1].startsWith('-')) result.exclude.push(args[++i]);
|
|
23
|
+
}
|
|
24
|
+
else if (!args[i].startsWith('-')) result.target = args[i];
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// --- 报告格式化 ---
|
|
30
|
+
|
|
31
|
+
const SEP = '='.repeat(60);
|
|
32
|
+
const DASH = '-'.repeat(40);
|
|
33
|
+
const ICONS = {
|
|
34
|
+
error: '\u2717', warning: '\u26A0', info: '\u2139',
|
|
35
|
+
critical: '\u{1F534}', high: '\u{1F7E0}', medium: '\u{1F7E1}', low: '\u{1F535}'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function reportHeader(title, fields) {
|
|
39
|
+
const lines = [SEP, title, SEP];
|
|
40
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
41
|
+
lines.push(`\n${k}: ${v}`);
|
|
42
|
+
}
|
|
43
|
+
return lines;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function reportIssues(issues, verbose, groupBy) {
|
|
47
|
+
if (!issues.length) return [];
|
|
48
|
+
const lines = ['\n' + DASH, '问题列表:', DASH];
|
|
49
|
+
|
|
50
|
+
if (groupBy) {
|
|
51
|
+
const groups = {};
|
|
52
|
+
for (const i of issues) (groups[i[groupBy]] || (groups[i[groupBy]] = [])).push(i);
|
|
53
|
+
for (const cat of Object.keys(groups).sort()) {
|
|
54
|
+
const items = groups[cat];
|
|
55
|
+
lines.push(`\n【${cat}】(${items.length} 个)`);
|
|
56
|
+
for (const i of items.slice(0, 10)) {
|
|
57
|
+
lines.push(` ${ICONS[i.severity] || '\u2139'} ` +
|
|
58
|
+
`${i.file_path || ''}${i.line_number ? ':' + i.line_number : ''}`);
|
|
59
|
+
lines.push(` ${i.message}`);
|
|
60
|
+
if (verbose && i.suggestion) lines.push(` \u{1F4A1} ${i.suggestion}`);
|
|
61
|
+
if (verbose && i.recommendation) lines.push(` \u{1F4A1} ${i.recommendation}`);
|
|
62
|
+
}
|
|
63
|
+
if (items.length > 10) lines.push(` ... 及其他 ${items.length - 10} 个问题`);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
for (const i of issues) {
|
|
67
|
+
const icon = ICONS[i.severity] || '\u2139';
|
|
68
|
+
lines.push(` ${icon} [${i.severity.toUpperCase()}] ${i.message}`);
|
|
69
|
+
if (i.path && verbose) lines.push(` 路径: ${i.path}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return lines;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function reportFooter() { return ['\n' + SEP]; }
|
|
76
|
+
|
|
77
|
+
function buildReport(title, fields, issues, verbose, groupBy) {
|
|
78
|
+
return [...reportHeader(title, fields), ...reportIssues(issues, verbose, groupBy), ...reportFooter()].join('\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- 通用计数 ---
|
|
82
|
+
|
|
83
|
+
function countBySeverity(issues, field) {
|
|
84
|
+
field = field || 'severity';
|
|
85
|
+
const counts = {};
|
|
86
|
+
for (const i of issues) counts[i[field]] = (counts[i[field]] || 0) + 1;
|
|
87
|
+
return counts;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function hasFatal(issues, fatalLevels) {
|
|
91
|
+
fatalLevels = fatalLevels || ['error'];
|
|
92
|
+
return issues.some(i => fatalLevels.includes(i.severity));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
parseCliArgs, buildReport, reportHeader, reportIssues,
|
|
97
|
+
reportFooter, countBySeverity, hasFatal, SEP, DASH, ICONS
|
|
98
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: verify-change
|
|
3
3
|
description: 变更校验关卡。分析代码变更,检测文档同步状态,评估变更影响范围。当魔尊提到变更检查、文档同步、代码审查、提交前检查、diff分析时使用。在设计级变更、重构完成时自动触发。
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: node>=18
|
|
4
6
|
user-invocable: true
|
|
5
7
|
disable-model-invocation: false
|
|
6
8
|
allowed-tools: Bash, Read, Grep
|
|
@@ -24,11 +26,11 @@ argument-hint: [--mode working|staged|committed]
|
|
|
24
26
|
|
|
25
27
|
```bash
|
|
26
28
|
# 在 skill 目录下运行
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
node scripts/change_analyzer.js # 分析工作区变更(默认)
|
|
30
|
+
node scripts/change_analyzer.js --mode staged # 分析暂存区变更
|
|
31
|
+
node scripts/change_analyzer.js --mode committed # 分析已提交变更
|
|
32
|
+
node scripts/change_analyzer.js -v # 详细模式
|
|
33
|
+
node scripts/change_analyzer.js --json # JSON 输出
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
## 检测能力
|
|
@@ -107,7 +109,7 @@ python scripts/change_analyzer.py --json # JSON 输出
|
|
|
107
109
|
## 校验流程
|
|
108
110
|
|
|
109
111
|
```
|
|
110
|
-
1. 运行 change_analyzer.
|
|
112
|
+
1. 运行 change_analyzer.js 自动分析
|
|
111
113
|
2. 识别变更文件和受影响模块
|
|
112
114
|
3. 检查文档同步状态
|
|
113
115
|
4. 评估变更影响
|