@chongyan/autospec 1.0.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/LICENSE +21 -0
- package/README.en.md +472 -0
- package/README.md +476 -0
- package/bin/autospec.js +3 -0
- package/knowledge/README.md +144 -0
- package/knowledge/checklists/code.md +182 -0
- package/knowledge/checklists/design.md +196 -0
- package/knowledge/checklists/release.md +70 -0
- package/knowledge/checklists/requirement.md +169 -0
- package/knowledge/checklists/test.md +46 -0
- package/knowledge/config/README.en.md +44 -0
- package/knowledge/config/README.md +44 -0
- package/knowledge/config/role-composition.yaml +98 -0
- package/knowledge/config/role-extensions.yaml +140 -0
- package/knowledge/config/skill-compositions.yaml +142 -0
- package/knowledge/config/team-stage.yaml +95 -0
- package/knowledge/config/team-tasks.yaml +139 -0
- package/knowledge/config/team-triggers.yaml +198 -0
- package/knowledge/config/validation-patterns.yaml +137 -0
- package/knowledge/domain/README.md +115 -0
- package/knowledge/domain/flows/README.md +194 -0
- package/knowledge/domain/glossary.md +143 -0
- package/knowledge/domain/rules.md +138 -0
- package/knowledge/environment/README.en.md +36 -0
- package/knowledge/environment/README.md +87 -0
- package/knowledge/environment/component-knowledge.md +316 -0
- package/knowledge/environment/detection-patterns.yaml +502 -0
- package/knowledge/environment/middleware-knowledge.md +237 -0
- package/knowledge/environment/template-registry.md +321 -0
- package/knowledge/guides/domain-driven-design.md +345 -0
- package/knowledge/guides/knowledge-management.md +369 -0
- package/knowledge/guides/requirement-engineering.md +329 -0
- package/knowledge/guides/stages/ai-effect-evaluator.md +93 -0
- package/knowledge/guides/stages/code-implementer.md +205 -0
- package/knowledge/guides/stages/code-reviewer.md +111 -0
- package/knowledge/guides/stages/consistency-checker.md +177 -0
- package/knowledge/guides/stages/design-planner.md +401 -0
- package/knowledge/guides/stages/design-reviewer.md +83 -0
- package/knowledge/guides/stages/integration-test-runner.md +105 -0
- package/knowledge/guides/stages/release-checker.md +205 -0
- package/knowledge/guides/stages/requirement-analyzer.md +195 -0
- package/knowledge/guides/stages/requirement-reviewer.md +83 -0
- package/knowledge/guides/stages/security-reviewer.md +89 -0
- package/knowledge/guides/stages/test-context-analyzer.md +250 -0
- package/knowledge/guides/stages/test-generator.md +241 -0
- package/knowledge/guides/stages/test-planner.md +183 -0
- package/knowledge/guides/stages/test-reviewer.md +76 -0
- package/knowledge/guides/stages/unit-test-runner.md +83 -0
- package/knowledge/guides/support/ai-agent-analyzer.md +362 -0
- package/knowledge/guides/support/ai-anomaly-analyzer.md +213 -0
- package/knowledge/guides/support/ai-artifact-evaluator.md +192 -0
- package/knowledge/guides/support/ai-capability-analyzer.md +193 -0
- package/knowledge/guides/support/ai-component-analyzer.md +169 -0
- package/knowledge/guides/support/ai-data-validator.md +276 -0
- package/knowledge/guides/support/ai-evaluation-planner.md +374 -0
- package/knowledge/guides/support/ai-path-evaluator.md +274 -0
- package/knowledge/guides/support/ai-pipeline-evaluator.md +219 -0
- package/knowledge/guides/support/ai-rag-analyzer.md +339 -0
- package/knowledge/guides/support/ai-task-assessor.md +418 -0
- package/knowledge/guides/support/ai-test-diagnostics.md +133 -0
- package/knowledge/guides/support/complexity-assessor.md +268 -0
- package/knowledge/guides/support/component-discovery.md +183 -0
- package/knowledge/guides/support/environment-scanner.md +207 -0
- package/knowledge/guides/support/environment-validator.md +207 -0
- package/knowledge/guides/support/knowledge-generator.md +234 -0
- package/knowledge/guides/support/methodology-extractor.md +55 -0
- package/knowledge/guides/support/pipeline-protocol.md +438 -0
- package/knowledge/guides/support/practice-logger.md +359 -0
- package/knowledge/guides/support/scope-inference.md +174 -0
- package/knowledge/guides/support/skill-distiller.md +91 -0
- package/knowledge/guides/support/skill-updater.md +45 -0
- package/knowledge/guides/support/skill-validator.md +72 -0
- package/knowledge/guides/support/team-orchestrator.md +323 -0
- package/knowledge/guides/support/tech-stack-analyzer.md +139 -0
- package/knowledge/guides/support/test-runner.md +254 -0
- package/knowledge/guides/system-design.md +352 -0
- package/knowledge/organization/ai-native-team.md +318 -0
- package/knowledge/organization/team-metrics.md +228 -0
- package/knowledge/principles/constitution.md +134 -0
- package/knowledge/principles/core-principles.md +368 -0
- package/knowledge/principles/design-philosophy.md +877 -0
- package/knowledge/principles/evolution.md +553 -0
- package/knowledge/process/01-requirement.md +113 -0
- package/knowledge/process/02-design.md +123 -0
- package/knowledge/process/03-implementation.md +90 -0
- package/knowledge/process/04-review.md +80 -0
- package/knowledge/process/05-testing.md +90 -0
- package/knowledge/process/06-delivery.md +88 -0
- package/knowledge/process/README.en.md +38 -0
- package/knowledge/process/README.md +48 -0
- package/knowledge/process/ai-sdlc.md +475 -0
- package/knowledge/process/overview.md +319 -0
- package/knowledge/standards/code-review.md +876 -0
- package/knowledge/standards/coding-style.md +940 -0
- package/knowledge/standards/data-consistency.md +1085 -0
- package/knowledge/standards/document-versioning.md +210 -0
- package/knowledge/standards/risk-detection.md +186 -0
- package/knowledge/templates/ai-evaluation.md +150 -0
- package/knowledge/templates/api-design.md +117 -0
- package/knowledge/templates/database-design.md +132 -0
- package/knowledge/templates/domain-driven-design.md +321 -0
- package/knowledge/templates/product-proposal.md +201 -0
- package/knowledge/templates/system-design.md +227 -0
- package/knowledge/templates/task-breakdown.md +107 -0
- package/knowledge/templates/test-case.md +170 -0
- package/package.json +53 -0
- package/plugins/.claude-plugin/plugin.json +134 -0
- package/plugins/agents/roles/ai-engineer.md +129 -0
- package/plugins/agents/roles/backend-engineer.md +165 -0
- package/plugins/agents/roles/ceo.md +94 -0
- package/plugins/agents/roles/data-engineer.md +135 -0
- package/plugins/agents/roles/devops-engineer.md +181 -0
- package/plugins/agents/roles/frontend-engineer.md +129 -0
- package/plugins/agents/roles/product-owner.md +98 -0
- package/plugins/agents/roles/quality-engineer.md +129 -0
- package/plugins/agents/roles/security-engineer.md +180 -0
- package/plugins/agents/roles/tech-lead.md +97 -0
- package/plugins/agents/support/blind-comparator.md +88 -0
- package/plugins/agents/support/consistency-checker.md +103 -0
- package/plugins/agents/support/failure-diagnostician.md +141 -0
- package/plugins/agents/support/independent-reviewer.md +80 -0
- package/plugins/agents/support/safety-auditor.md +121 -0
- package/plugins/agents/support/skill-benchmarker.md +86 -0
- package/plugins/agents/support/skill-forger.md +105 -0
- package/plugins/agents/support/stage-gate-evaluator.md +121 -0
- package/plugins/agents/support/test-coverage-reviewer.md +73 -0
- package/plugins/benchmarks/templates/README.md +44 -0
- package/plugins/benchmarks/templates/commands/explore-template.yaml +48 -0
- package/plugins/benchmarks/templates/pipeline/agile-template.yaml +84 -0
- package/plugins/benchmarks/templates/pipeline/waterfall-template.yaml +106 -0
- package/plugins/benchmarks/templates/skills/requirement-analyzer-template.yaml +48 -0
- package/plugins/commands/README.en.md +96 -0
- package/plugins/commands/README.md +96 -0
- package/plugins/commands/apply.md +191 -0
- package/plugins/commands/archive.md +76 -0
- package/plugins/commands/env-export.md +79 -0
- package/plugins/commands/env-sync.md +640 -0
- package/plugins/commands/env-template.md +223 -0
- package/plugins/commands/env-update.md +264 -0
- package/plugins/commands/env-validate.md +176 -0
- package/plugins/commands/env.md +79 -0
- package/plugins/commands/explore.md +76 -0
- package/plugins/commands/field-evolve.md +536 -0
- package/plugins/commands/memory.md +249 -0
- package/plugins/commands/project-evolve.md +821 -0
- package/plugins/commands/propose.md +93 -0
- package/plugins/commands/review.md +140 -0
- package/plugins/commands/run.md +224 -0
- package/plugins/commands/status.md +62 -0
- package/plugins/commands/validate.md +108 -0
- package/plugins/hooks/README.en.md +56 -0
- package/plugins/hooks/README.md +56 -0
- package/plugins/hooks/ai-project-guard.js +329 -0
- package/plugins/hooks/artifact-evaluation-hook.js +237 -0
- package/plugins/hooks/constitution-guard.js +211 -0
- package/plugins/hooks/environment-autocommit.js +264 -0
- package/plugins/hooks/environment-manager.js +778 -0
- package/plugins/hooks/execution-tracker.js +354 -0
- package/plugins/hooks/frozen-zone-guard.js +140 -0
- package/plugins/hooks/layer1-validator.js +423 -0
- package/plugins/hooks/lib/artifact-evaluator.js +414 -0
- package/plugins/hooks/lib/benchmarks/change-detector.js +390 -0
- package/plugins/hooks/lib/benchmarks/evaluator.js +605 -0
- package/plugins/hooks/lib/benchmarks/integration-example.js +169 -0
- package/plugins/hooks/lib/data-and-ai-detector.js +275 -0
- package/plugins/hooks/lib/detection-pattern-loader.js +865 -0
- package/plugins/hooks/lib/directory-discovery.js +395 -0
- package/plugins/hooks/lib/environment-config-loader.js +341 -0
- package/plugins/hooks/lib/environment-detector.js +553 -0
- package/plugins/hooks/lib/environment-evolver.js +564 -0
- package/plugins/hooks/lib/environment-registry.js +813 -0
- package/plugins/hooks/lib/execution-path.js +427 -0
- package/plugins/hooks/lib/hook-error-recorder.js +245 -0
- package/plugins/hooks/lib/hook-logger.js +538 -0
- package/plugins/hooks/lib/hook-runner.js +97 -0
- package/plugins/hooks/lib/hook-runner.sh +44 -0
- package/plugins/hooks/lib/hook-state-manager.js +480 -0
- package/plugins/hooks/lib/memory-extractor.js +377 -0
- package/plugins/hooks/lib/memory-manager.js +673 -0
- package/plugins/hooks/lib/metrics-analyzer.js +489 -0
- package/plugins/hooks/lib/project-evolution/auto-fixer.js +511 -0
- package/plugins/hooks/lib/project-evolution/memory-manager.js +346 -0
- package/plugins/hooks/lib/project-evolution/pattern-detector.js +476 -0
- package/plugins/hooks/lib/project-evolution/semantic-indexer.js +480 -0
- package/plugins/hooks/lib/project-structure-detector.js +326 -0
- package/plugins/hooks/lib/rollback-tracker.js +346 -0
- package/plugins/hooks/lib/source-code-scanner.js +596 -0
- package/plugins/hooks/lib/technology-stack-detector.js +374 -0
- package/plugins/hooks/lib/test-failure-analyzer.js +375 -0
- package/plugins/hooks/lib/test-failure-fixer.js +268 -0
- package/plugins/hooks/lib/trace-context.js +277 -0
- package/plugins/hooks/lib/validation-patterns.js +415 -0
- package/plugins/hooks/memory-sync.js +171 -0
- package/plugins/hooks/pipeline-observer.js +413 -0
- package/plugins/hooks/scope-sentinel.js +204 -0
- package/plugins/hooks/trace-initialization.js +169 -0
- package/plugins/memory/templates/code-quality.yaml +149 -0
- package/plugins/memory/templates/multi-system.yaml +155 -0
- package/plugins/memory/templates/team-habits.yaml +119 -0
- package/plugins/memory/templates/testing.yaml +121 -0
- package/plugins/skills/README.en.md +47 -0
- package/plugins/skills/README.md +104 -0
- package/plugins/skills/benchmark-executor/README.md +93 -0
- package/plugins/skills/benchmark-executor/SKILL.md +647 -0
- package/plugins/skills/benchmark-generator/SKILL.md +349 -0
- package/plugins/skills/delivery-stage/SKILL.md +203 -0
- package/plugins/skills/design-stage/SKILL.md +216 -0
- package/plugins/skills/evolution-process/SKILL.md +291 -0
- package/plugins/skills/exploration-phase/SKILL.md +133 -0
- package/plugins/skills/implementation-stage/SKILL.md +179 -0
- package/plugins/skills/layer1-validation/SKILL.md +79 -0
- package/plugins/skills/pending-dashboard/SKILL.md +109 -0
- package/plugins/skills/project-evolution/SKILL.md +847 -0
- package/plugins/skills/requirement-stage/SKILL.md +183 -0
- package/plugins/skills/skill-forge/SKILL.md +223 -0
- package/plugins/skills/skill-forge/references/description-guide.md +92 -0
- package/plugins/skills/skill-forge/references/quality-rubric.md +104 -0
- package/plugins/skills/skill-forge/references/skill-template.md +106 -0
- package/plugins/skills/startup-guard/SKILL.md +38 -0
- package/plugins/skills/testing-stage/SKILL.md +195 -0
- package/scripts/cli/global-init.js +288 -0
- package/scripts/cli/global.js +324 -0
- package/scripts/cli/index.js +55 -0
- package/scripts/cli/init.js +382 -0
- package/scripts/cli/list.js +69 -0
- package/scripts/cli/org.js +340 -0
- package/scripts/cli/update.js +44 -0
- package/scripts/config/commands.config.js +145 -0
- package/scripts/config/hooks.config.js +197 -0
- package/scripts/evolution/evolution-router.js +273 -0
- package/scripts/evolution/evolution-signal-collector.js +307 -0
- package/scripts/evolution/knowledge-loader.js +346 -0
- package/scripts/evolution/marketplace.js +317 -0
- package/scripts/evolution/version-manager.js +371 -0
- package/scripts/install/agents.js +106 -0
- package/scripts/install/commands.js +133 -0
- package/scripts/install/constants.js +424 -0
- package/scripts/install/hook-logger.js +536 -0
- package/scripts/install/hooks.js +110 -0
- package/scripts/install/index.js +39 -0
- package/scripts/install/skills.js +95 -0
- package/scripts/postinstall.js +25 -0
- package/scripts/state.js +376 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Scanner - 统一的代码扫描模块
|
|
3
|
+
*
|
|
4
|
+
* 合并了 evolver.js 和 environment-manager.js 中的重复扫描逻辑
|
|
5
|
+
* 提供统一的代码模式扫描、环境变量扫描、依赖检测和源码目录发现功能
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
// 默认支持的代码文件扩展名
|
|
12
|
+
const DEFAULT_CODE_EXTENSIONS = ['.py', '.js', '.ts', '.jsx', '.tsx', '.java', '.go', '.rs'];
|
|
13
|
+
|
|
14
|
+
// 默认支持的环境变量文件扩展名
|
|
15
|
+
const DEFAULT_ENV_EXTENSIONS = ['.py', '.js', '.ts', '.jsx', '.tsx', '.java', '.go', '.rs', '.env', '.yaml', '.yml', '.json'];
|
|
16
|
+
|
|
17
|
+
// 需要跳过的目录
|
|
18
|
+
const SKIP_DIRS = ['node_modules', '.git', 'dist', 'build', '__pycache__', 'venv', '.venv', 'target', '.idea', '.vscode'];
|
|
19
|
+
|
|
20
|
+
// 常见源码目录
|
|
21
|
+
const COMMON_SOURCE_DIRS = ['src', 'lib', 'app', 'server', 'backend', 'frontend', 'api', 'web', 'client', 'mobile', 'ml', 'ai', 'data'];
|
|
22
|
+
|
|
23
|
+
// 目录到类型的映射
|
|
24
|
+
const DIR_TYPE_MAP = {
|
|
25
|
+
'backend': 'backend',
|
|
26
|
+
'server': 'backend',
|
|
27
|
+
'api': 'backend',
|
|
28
|
+
'frontend': 'frontend',
|
|
29
|
+
'web': 'frontend',
|
|
30
|
+
'client': 'frontend',
|
|
31
|
+
'mobile': 'mobile',
|
|
32
|
+
'app': 'mobile',
|
|
33
|
+
'ml': 'ai',
|
|
34
|
+
'ai': 'ai',
|
|
35
|
+
'model': 'ai',
|
|
36
|
+
'data': 'data',
|
|
37
|
+
'warehouse': 'data',
|
|
38
|
+
'datalake': 'data',
|
|
39
|
+
'pipeline': 'data',
|
|
40
|
+
'etl': 'data',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// 技术栈检测指示器
|
|
44
|
+
const TECH_INDICATORS = [
|
|
45
|
+
{ file: 'package.json', tech: 'Node.js' },
|
|
46
|
+
{ file: 'pom.xml', tech: 'Java Maven' },
|
|
47
|
+
{ file: 'build.gradle', tech: 'Java Gradle' },
|
|
48
|
+
{ file: 'go.mod', tech: 'Go' },
|
|
49
|
+
{ file: 'requirements.txt', tech: 'Python' },
|
|
50
|
+
{ file: 'pyproject.toml', tech: 'Python' },
|
|
51
|
+
{ file: 'Cargo.toml', tech: 'Rust' },
|
|
52
|
+
{ file: 'composer.json', tech: 'PHP' },
|
|
53
|
+
{ file: 'Gemfile', tech: 'Ruby' },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 扫描代码模式 - 统一版本
|
|
58
|
+
* @param {string} dirPath - 要扫描的目录路径
|
|
59
|
+
* @param {Array} codePatterns - 代码模式数组,每项包含 pattern, language, confidence
|
|
60
|
+
* @param {Object} options - 扫描选项
|
|
61
|
+
* @param {number} options.maxDepth - 最大扫描深度,默认4
|
|
62
|
+
* @param {Array} options.extensions - 要扫描的文件扩展名
|
|
63
|
+
* @param {string} projectRoot - 项目根目录(用于计算相对路径)
|
|
64
|
+
* @returns {Array} 匹配的证据数组
|
|
65
|
+
*/
|
|
66
|
+
export function scanCodePatterns(dirPath, codePatterns, options = {}, projectRoot = dirPath) {
|
|
67
|
+
const evidence = [];
|
|
68
|
+
const extensions = options.extensions || DEFAULT_CODE_EXTENSIONS;
|
|
69
|
+
const maxDepth = options.maxDepth ?? 4;
|
|
70
|
+
|
|
71
|
+
const scanDir = (dir, depth = 0) => {
|
|
72
|
+
if (depth > maxDepth) return;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
76
|
+
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (entry.name.startsWith('.') || SKIP_DIRS.includes(entry.name)) continue;
|
|
79
|
+
|
|
80
|
+
const fullPath = path.join(dir, entry.name);
|
|
81
|
+
|
|
82
|
+
if (entry.isDirectory()) {
|
|
83
|
+
scanDir(fullPath, depth + 1);
|
|
84
|
+
} else if (entry.isFile()) {
|
|
85
|
+
const ext = path.extname(entry.name);
|
|
86
|
+
if (extensions.includes(ext)) {
|
|
87
|
+
try {
|
|
88
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
89
|
+
|
|
90
|
+
for (const cp of codePatterns) {
|
|
91
|
+
try {
|
|
92
|
+
const regex = new RegExp(cp.pattern, 'gi');
|
|
93
|
+
if (regex.test(content)) {
|
|
94
|
+
evidence.push({
|
|
95
|
+
file: path.relative(projectRoot, fullPath),
|
|
96
|
+
type: 'code-pattern',
|
|
97
|
+
pattern: cp.pattern,
|
|
98
|
+
language: cp.language,
|
|
99
|
+
confidence: cp.confidence || 0.8,
|
|
100
|
+
});
|
|
101
|
+
break; // 每个文件只记录一次
|
|
102
|
+
}
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// 忽略无效正则
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
// 忽略读取错误
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
// 忽略目录读取错误
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
scanDir(dirPath);
|
|
119
|
+
return evidence;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 快速扫描代码模式 - 返回布尔值版本(用于快速检测)
|
|
124
|
+
* @param {string} dirPath - 要扫描的目录路径
|
|
125
|
+
* @param {Array} codePatterns - 代码模式数组
|
|
126
|
+
* @param {Object} options - 扫描选项
|
|
127
|
+
* @returns {boolean} 是否找到匹配
|
|
128
|
+
*/
|
|
129
|
+
export function scanCodePatternsFast(dirPath, codePatterns, options = {}) {
|
|
130
|
+
const extensions = options.extensions || DEFAULT_CODE_EXTENSIONS;
|
|
131
|
+
const maxDepth = options.maxDepth ?? 4;
|
|
132
|
+
|
|
133
|
+
const scan = (dir, depth = 0) => {
|
|
134
|
+
if (depth > maxDepth) return false;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
138
|
+
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
if (entry.isDirectory()) {
|
|
141
|
+
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
142
|
+
if (scan(path.join(dir, entry.name), depth + 1)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
} else if (entry.isFile()) {
|
|
146
|
+
const ext = path.extname(entry.name);
|
|
147
|
+
if (extensions.includes(ext)) {
|
|
148
|
+
try {
|
|
149
|
+
const content = fs.readFileSync(path.join(dir, entry.name), 'utf-8');
|
|
150
|
+
for (const cp of codePatterns) {
|
|
151
|
+
try {
|
|
152
|
+
const regex = new RegExp(cp.pattern, 'i');
|
|
153
|
+
if (regex.test(content)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
// 忽略无效正则
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
// 忽略读取错误
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch (e) {
|
|
167
|
+
// 忽略目录读取错误
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return scan(dirPath);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 扫描环境变量模式 - 统一版本
|
|
178
|
+
* @param {string} dirPath - 要扫描的目录路径
|
|
179
|
+
* @param {Array} envVarPatterns - 环境变量模式数组
|
|
180
|
+
* @param {Object} options - 扫描选项
|
|
181
|
+
* @param {string} projectRoot - 项目根目录
|
|
182
|
+
* @returns {Array} 匹配的证据数组
|
|
183
|
+
*/
|
|
184
|
+
export function scanEnvVarPatterns(dirPath, envVarPatterns, options = {}, projectRoot = dirPath) {
|
|
185
|
+
const evidence = [];
|
|
186
|
+
const extensions = options.extensions || DEFAULT_ENV_EXTENSIONS;
|
|
187
|
+
const maxDepth = options.maxDepth ?? 4;
|
|
188
|
+
|
|
189
|
+
const scanDir = (dir, depth = 0) => {
|
|
190
|
+
if (depth > maxDepth) return;
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
194
|
+
|
|
195
|
+
for (const entry of entries) {
|
|
196
|
+
if (entry.name.startsWith('.') || SKIP_DIRS.includes(entry.name)) continue;
|
|
197
|
+
|
|
198
|
+
const fullPath = path.join(dir, entry.name);
|
|
199
|
+
|
|
200
|
+
if (entry.isDirectory()) {
|
|
201
|
+
scanDir(fullPath, depth + 1);
|
|
202
|
+
} else if (entry.isFile()) {
|
|
203
|
+
const ext = path.extname(entry.name);
|
|
204
|
+
if (extensions.includes(ext) || entry.name.includes('.env')) {
|
|
205
|
+
try {
|
|
206
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
207
|
+
|
|
208
|
+
for (const evp of envVarPatterns) {
|
|
209
|
+
try {
|
|
210
|
+
const regex = new RegExp(evp.pattern, 'g');
|
|
211
|
+
const matches = content.match(regex);
|
|
212
|
+
if (matches) {
|
|
213
|
+
evidence.push({
|
|
214
|
+
file: path.relative(projectRoot, fullPath),
|
|
215
|
+
type: 'env-var',
|
|
216
|
+
pattern: evp.pattern,
|
|
217
|
+
envVars: [...new Set(matches)],
|
|
218
|
+
extract: evp.extract,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
// 忽略无效正则
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} catch (e) {
|
|
226
|
+
// 忽略读取错误
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} catch (e) {
|
|
232
|
+
// 忽略目录读取错误
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
scanDir(dirPath);
|
|
237
|
+
return evidence;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 快速扫描环境变量模式 - 返回布尔值版本
|
|
242
|
+
* @param {string} dirPath - 要扫描的目录路径
|
|
243
|
+
* @param {Array} envVarPatterns - 环境变量模式数组
|
|
244
|
+
* @param {Object} options - 扫描选项
|
|
245
|
+
* @returns {boolean} 是否找到匹配
|
|
246
|
+
*/
|
|
247
|
+
export function scanEnvVarPatternsFast(dirPath, envVarPatterns, options = {}) {
|
|
248
|
+
const extensions = options.extensions || DEFAULT_ENV_EXTENSIONS;
|
|
249
|
+
const maxDepth = options.maxDepth ?? 4;
|
|
250
|
+
|
|
251
|
+
const scan = (dir, depth = 0) => {
|
|
252
|
+
if (depth > maxDepth) return false;
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
256
|
+
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
if (entry.isDirectory()) {
|
|
259
|
+
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
260
|
+
if (scan(path.join(dir, entry.name), depth + 1)) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
} else if (entry.isFile()) {
|
|
264
|
+
const ext = path.extname(entry.name);
|
|
265
|
+
if (extensions.includes(ext) || entry.name.includes('.env')) {
|
|
266
|
+
try {
|
|
267
|
+
const content = fs.readFileSync(path.join(dir, entry.name), 'utf-8');
|
|
268
|
+
for (const evp of envVarPatterns) {
|
|
269
|
+
try {
|
|
270
|
+
const regex = new RegExp(evp.pattern, 'g');
|
|
271
|
+
if (regex.test(content)) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
} catch (e) {
|
|
275
|
+
// 忽略无效正则
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
} catch (e) {
|
|
279
|
+
// 忽略读取错误
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} catch (e) {
|
|
285
|
+
// 忽略目录读取错误
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return false;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
return scan(dirPath);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 扫描依赖文件 - 统一版本(返回详细证据)
|
|
296
|
+
* @param {string} projectRoot - 项目根目录
|
|
297
|
+
* @param {Array} dependencies - 依赖模式数组
|
|
298
|
+
* @returns {Array} 检测到的依赖证据数组
|
|
299
|
+
*/
|
|
300
|
+
export function scanDependencies(projectRoot, dependencies) {
|
|
301
|
+
const evidence = [];
|
|
302
|
+
|
|
303
|
+
for (const dep of dependencies) {
|
|
304
|
+
if (dep.packageManager === 'npm') {
|
|
305
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
306
|
+
if (fs.existsSync(pkgPath)) {
|
|
307
|
+
try {
|
|
308
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
309
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
310
|
+
|
|
311
|
+
for (const [depName] of Object.entries(allDeps)) {
|
|
312
|
+
if (matchDependency(dep.pattern, depName)) {
|
|
313
|
+
evidence.push({
|
|
314
|
+
file: 'package.json',
|
|
315
|
+
type: 'dependency',
|
|
316
|
+
dependency: depName,
|
|
317
|
+
packageManager: 'npm',
|
|
318
|
+
pattern: dep.pattern,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} catch (e) {
|
|
323
|
+
// 忽略解析错误
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} else if (dep.packageManager === 'pip') {
|
|
327
|
+
const reqPath = path.join(projectRoot, 'requirements.txt');
|
|
328
|
+
if (fs.existsSync(reqPath)) {
|
|
329
|
+
try {
|
|
330
|
+
const content = fs.readFileSync(reqPath, 'utf-8');
|
|
331
|
+
const lines = content.split('\n');
|
|
332
|
+
|
|
333
|
+
for (const line of lines) {
|
|
334
|
+
const depName = line.split(/[=<>#\[]/)[0].trim();
|
|
335
|
+
if (depName && matchDependency(dep.pattern, depName)) {
|
|
336
|
+
evidence.push({
|
|
337
|
+
file: 'requirements.txt',
|
|
338
|
+
type: 'dependency',
|
|
339
|
+
dependency: depName,
|
|
340
|
+
packageManager: 'pip',
|
|
341
|
+
pattern: dep.pattern,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} catch (e) {
|
|
346
|
+
// 忽略解析错误
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} else if (dep.packageManager === 'maven') {
|
|
350
|
+
const pomPath = path.join(projectRoot, 'pom.xml');
|
|
351
|
+
if (fs.existsSync(pomPath)) {
|
|
352
|
+
try {
|
|
353
|
+
const content = fs.readFileSync(pomPath, 'utf-8');
|
|
354
|
+
if (content.includes(dep.pattern)) {
|
|
355
|
+
evidence.push({
|
|
356
|
+
file: 'pom.xml',
|
|
357
|
+
type: 'dependency',
|
|
358
|
+
dependency: dep.pattern,
|
|
359
|
+
packageManager: 'maven',
|
|
360
|
+
pattern: dep.pattern,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
} catch (e) {
|
|
364
|
+
// 忽略解析错误
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return evidence;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* 快速扫描依赖 - 返回检测到的名称数组
|
|
375
|
+
* @param {string} projectRoot - 项目根目录
|
|
376
|
+
* @param {Object} middlewarePatterns - 中间件模式对象
|
|
377
|
+
* @returns {Array} 检测到的中间件名称数组
|
|
378
|
+
*/
|
|
379
|
+
export function scanDependenciesFast(projectRoot, middlewarePatterns) {
|
|
380
|
+
const detected = [];
|
|
381
|
+
|
|
382
|
+
// package.json
|
|
383
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
384
|
+
if (fs.existsSync(pkgPath)) {
|
|
385
|
+
try {
|
|
386
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
387
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
388
|
+
|
|
389
|
+
for (const [depName] of Object.entries(deps)) {
|
|
390
|
+
for (const [mwName, pattern] of Object.entries(middlewarePatterns)) {
|
|
391
|
+
if (detected.includes(mwName)) continue;
|
|
392
|
+
if (pattern.dependencies) {
|
|
393
|
+
for (const dep of pattern.dependencies) {
|
|
394
|
+
if (dep.packageManager === 'npm') {
|
|
395
|
+
const regex = new RegExp('^' + dep.pattern.replace(/\*/g, '.*') + '$');
|
|
396
|
+
if (regex.test(depName) || depName.includes(dep.pattern)) {
|
|
397
|
+
detected.push(mwName);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
} catch (e) {
|
|
406
|
+
// 忽略
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// requirements.txt
|
|
411
|
+
const reqPath = path.join(projectRoot, 'requirements.txt');
|
|
412
|
+
if (fs.existsSync(reqPath)) {
|
|
413
|
+
try {
|
|
414
|
+
const content = fs.readFileSync(reqPath, 'utf-8');
|
|
415
|
+
const lines = content.split('\n');
|
|
416
|
+
|
|
417
|
+
for (const line of lines) {
|
|
418
|
+
const depName = line.split(/[=<>#\[]/)[0].trim();
|
|
419
|
+
if (!depName) continue;
|
|
420
|
+
|
|
421
|
+
for (const [mwName, pattern] of Object.entries(middlewarePatterns)) {
|
|
422
|
+
if (detected.includes(mwName)) continue;
|
|
423
|
+
if (pattern.dependencies) {
|
|
424
|
+
for (const dep of pattern.dependencies) {
|
|
425
|
+
if (dep.packageManager === 'pip') {
|
|
426
|
+
const regex = new RegExp('^' + dep.pattern.replace(/\*/g, '.*') + '$');
|
|
427
|
+
if (regex.test(depName) || depName.includes(dep.pattern)) {
|
|
428
|
+
detected.push(mwName);
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
} catch (e) {
|
|
437
|
+
// 忽略
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return detected;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* 匹配依赖名称
|
|
446
|
+
* @param {string} pattern - 匹配模式
|
|
447
|
+
* @param {string} depName - 依赖名称
|
|
448
|
+
* @returns {boolean} 是否匹配
|
|
449
|
+
*/
|
|
450
|
+
function matchDependency(pattern, depName) {
|
|
451
|
+
if (pattern.startsWith('^') || pattern.includes('*')) {
|
|
452
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
453
|
+
return regex.test(depName);
|
|
454
|
+
}
|
|
455
|
+
return depName === pattern || depName.includes(pattern);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* 发现源码目录 - 统一版本(带类型信息)
|
|
460
|
+
* @param {string} projectRoot - 项目根目录
|
|
461
|
+
* @param {Object} config - 项目配置(可选,包含 subsystems)
|
|
462
|
+
* @returns {Array} 源码目录数组,每项包含 path, type, techStack
|
|
463
|
+
*/
|
|
464
|
+
export function discoverSourceDirs(projectRoot, config = {}) {
|
|
465
|
+
const dirs = [];
|
|
466
|
+
|
|
467
|
+
// 1. 从 config.json 的 subsystems 中获取
|
|
468
|
+
if (config.subsystems) {
|
|
469
|
+
for (const sub of config.subsystems) {
|
|
470
|
+
dirs.push({
|
|
471
|
+
path: sub.path,
|
|
472
|
+
type: sub.type,
|
|
473
|
+
techStack: sub.techStack,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 2. 自动扫描常见源码目录
|
|
479
|
+
for (const dir of COMMON_SOURCE_DIRS) {
|
|
480
|
+
const fullPath = path.join(projectRoot, dir);
|
|
481
|
+
if (fs.existsSync(fullPath) && !dirs.find(d => d.path === dir)) {
|
|
482
|
+
dirs.push({
|
|
483
|
+
path: dir,
|
|
484
|
+
type: inferType(dir),
|
|
485
|
+
techStack: detectTechStack(fullPath),
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return dirs;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* 简单发现源码目录 - 只返回目录名数组
|
|
495
|
+
* @param {string} projectRoot - 项目根目录
|
|
496
|
+
* @returns {Array} 目录名数组
|
|
497
|
+
*/
|
|
498
|
+
export function discoverSourceDirsSimple(projectRoot) {
|
|
499
|
+
const dirs = [];
|
|
500
|
+
|
|
501
|
+
for (const dir of COMMON_SOURCE_DIRS) {
|
|
502
|
+
const fullPath = path.join(projectRoot, dir);
|
|
503
|
+
if (fs.existsSync(fullPath)) {
|
|
504
|
+
dirs.push(dir);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return dirs;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* 推断目录类型
|
|
513
|
+
* @param {string} dirName - 目录名称
|
|
514
|
+
* @returns {string} 类型
|
|
515
|
+
*/
|
|
516
|
+
function inferType(dirName) {
|
|
517
|
+
return DIR_TYPE_MAP[dirName.toLowerCase()] || 'unknown';
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* 检测技术栈
|
|
522
|
+
* @param {string} dirPath - 目录路径
|
|
523
|
+
* @returns {string} 技术栈名称
|
|
524
|
+
*/
|
|
525
|
+
function detectTechStack(dirPath) {
|
|
526
|
+
for (const ind of TECH_INDICATORS) {
|
|
527
|
+
if (fs.existsSync(path.join(dirPath, ind.file))) {
|
|
528
|
+
return ind.tech;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return 'Unknown';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* 综合扫描 - 使用 PatternLoader 检测中间件(用于 environment-manager)
|
|
536
|
+
* @param {string} projectRoot - 项目根目录
|
|
537
|
+
* @param {Object} patternLoader - PatternLoader 实例
|
|
538
|
+
* @returns {Array} 检测到的中间件名称数组
|
|
539
|
+
*/
|
|
540
|
+
export function detectMiddlewareWithPatterns(projectRoot, patternLoader) {
|
|
541
|
+
const middlewarePatterns = patternLoader.getMiddlewarePatterns();
|
|
542
|
+
const detected = [];
|
|
543
|
+
const sourceDirs = discoverSourceDirsSimple(projectRoot);
|
|
544
|
+
|
|
545
|
+
if (sourceDirs.length === 0) {
|
|
546
|
+
sourceDirs.push('src');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
for (const dir of sourceDirs) {
|
|
550
|
+
const dirPath = path.join(projectRoot, dir);
|
|
551
|
+
if (!fs.existsSync(dirPath)) continue;
|
|
552
|
+
|
|
553
|
+
for (const [name, pattern] of Object.entries(middlewarePatterns)) {
|
|
554
|
+
if (detected.includes(name)) continue;
|
|
555
|
+
|
|
556
|
+
// 检测代码模式
|
|
557
|
+
if (pattern.codePatterns) {
|
|
558
|
+
const found = scanCodePatternsFast(dirPath, pattern.codePatterns);
|
|
559
|
+
if (found) {
|
|
560
|
+
detected.push(name);
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// 检测环境变量模式
|
|
566
|
+
if (pattern.envVarPatterns) {
|
|
567
|
+
const found = scanEnvVarPatternsFast(dirPath, pattern.envVarPatterns);
|
|
568
|
+
if (found) {
|
|
569
|
+
detected.push(name);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// 从依赖文件检测
|
|
576
|
+
const depDetected = scanDependenciesFast(projectRoot, middlewarePatterns);
|
|
577
|
+
for (const name of depDetected) {
|
|
578
|
+
if (!detected.includes(name)) {
|
|
579
|
+
detected.push(name);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return detected;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
export default {
|
|
587
|
+
scanCodePatterns,
|
|
588
|
+
scanCodePatternsFast,
|
|
589
|
+
scanEnvVarPatterns,
|
|
590
|
+
scanEnvVarPatternsFast,
|
|
591
|
+
scanDependencies,
|
|
592
|
+
scanDependenciesFast,
|
|
593
|
+
discoverSourceDirs,
|
|
594
|
+
discoverSourceDirsSimple,
|
|
595
|
+
detectMiddlewareWithPatterns,
|
|
596
|
+
};
|