@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,778 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AutoSpec Hook: Environment Manager
|
|
5
|
+
*
|
|
6
|
+
* 环境知识管理 Hook,合并了验证和进化功能:
|
|
7
|
+
* - PreToolUse: 环境知识验证(原 environment-validator.js)
|
|
8
|
+
* - Stop: 环境进化检查(原 environment-evolution.js)
|
|
9
|
+
*
|
|
10
|
+
* 职责:
|
|
11
|
+
* 1. 验证环境知识 YAML 文件的结构和内容
|
|
12
|
+
* 2. 检测敏感信息(密码、API Key 等)
|
|
13
|
+
* 3. 会话结束时检查可沉淀的环境知识
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { PatternLoader } from './lib/detection-pattern-loader.js';
|
|
19
|
+
import { createHookLogger, safeJsonParse, handleHookError, readStdin, findProjectRootAsync, fileExists } from './lib/hook-logger.js';
|
|
20
|
+
import {
|
|
21
|
+
discoverSourceDirsSimple,
|
|
22
|
+
scanCodePatternsFast,
|
|
23
|
+
scanEnvVarPatternsFast,
|
|
24
|
+
scanDependenciesFast,
|
|
25
|
+
} from './lib/source-code-scanner.js';
|
|
26
|
+
|
|
27
|
+
const logger = createHookLogger('environment-manager');
|
|
28
|
+
|
|
29
|
+
// 动态加载 js-yaml(延迟加载,避免 top-level await 导致模块加载失败)
|
|
30
|
+
let yaml = null;
|
|
31
|
+
let yamlLoadAttempted = false;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 确保 js-yaml 已加载(延迟加载)
|
|
35
|
+
* 将 top-level await 改为函数内动态导入,避免模块初始化失败
|
|
36
|
+
*/
|
|
37
|
+
async function ensureYaml() {
|
|
38
|
+
if (yamlLoadAttempted) return yaml;
|
|
39
|
+
yamlLoadAttempted = true;
|
|
40
|
+
try {
|
|
41
|
+
yaml = await import('js-yaml');
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// js-yaml 不可用,使用降级方案 parseYamlSimple
|
|
44
|
+
logger.debug('js-yaml not available, using fallback YAML parser');
|
|
45
|
+
}
|
|
46
|
+
return yaml;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 简单的 YAML 解析器(降级方案)
|
|
51
|
+
* 支持多层嵌套、数组、行内数组等基本结构
|
|
52
|
+
*/
|
|
53
|
+
function parseYamlSimple(content) {
|
|
54
|
+
const lines = content.split('\n');
|
|
55
|
+
const result = {};
|
|
56
|
+
|
|
57
|
+
// 使用栈来跟踪嵌套层级
|
|
58
|
+
const stack = [{ obj: result, indent: -1, isArrayItem: false }];
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
const line = lines[i];
|
|
62
|
+
// 跳过空行和注释
|
|
63
|
+
const trimmed = line.trim();
|
|
64
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
65
|
+
|
|
66
|
+
const indent = line.search(/\S/);
|
|
67
|
+
|
|
68
|
+
// 弹出栈中缩进大于等于当前行的元素
|
|
69
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
70
|
+
stack.pop();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const top = stack[stack.length - 1];
|
|
74
|
+
let current = top.obj;
|
|
75
|
+
|
|
76
|
+
// 处理数组项
|
|
77
|
+
if (trimmed.startsWith('- ')) {
|
|
78
|
+
const itemStr = trimmed.substring(2).trim();
|
|
79
|
+
|
|
80
|
+
// 如果当前是对象但遇到数组项,需要转换为数组
|
|
81
|
+
if (!Array.isArray(current)) {
|
|
82
|
+
const parent = stack[stack.length - 2]?.obj;
|
|
83
|
+
if (parent && typeof parent === 'object') {
|
|
84
|
+
const keys = Object.keys(parent);
|
|
85
|
+
const lastKey = keys[keys.length - 1];
|
|
86
|
+
if (lastKey && typeof parent[lastKey] === 'object' && !Array.isArray(parent[lastKey])) {
|
|
87
|
+
const existing = parent[lastKey];
|
|
88
|
+
if (Object.keys(existing).length === 0) {
|
|
89
|
+
parent[lastKey] = [];
|
|
90
|
+
current = parent[lastKey];
|
|
91
|
+
stack[stack.length - 1] = { obj: current, indent: top.indent, isArrayItem: true };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (Array.isArray(current)) {
|
|
98
|
+
const colonIndex = itemStr.indexOf(':');
|
|
99
|
+
if (colonIndex > 0) {
|
|
100
|
+
const item = {};
|
|
101
|
+
const key = itemStr.substring(0, colonIndex).trim();
|
|
102
|
+
let value = itemStr.substring(colonIndex + 1).trim();
|
|
103
|
+
item[key] = parseYamlValue(value);
|
|
104
|
+
current.push(item);
|
|
105
|
+
stack.push({ obj: item, indent, isArrayItem: true });
|
|
106
|
+
} else {
|
|
107
|
+
current.push(parseYamlValue(itemStr));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 解析键值对
|
|
114
|
+
const colonIndex = trimmed.indexOf(':');
|
|
115
|
+
if (colonIndex > 0) {
|
|
116
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
117
|
+
let value = trimmed.substring(colonIndex + 1).trim();
|
|
118
|
+
|
|
119
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
120
|
+
current[key] = parseInlineArray(value);
|
|
121
|
+
} else if (value) {
|
|
122
|
+
current[key] = parseYamlValue(value);
|
|
123
|
+
} else {
|
|
124
|
+
current[key] = {};
|
|
125
|
+
stack.push({ obj: current[key], indent, isArrayItem: false });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 解析行内数组
|
|
135
|
+
*/
|
|
136
|
+
function parseInlineArray(value) {
|
|
137
|
+
const inner = value.slice(1, -1).trim();
|
|
138
|
+
if (!inner) return [];
|
|
139
|
+
|
|
140
|
+
const items = [];
|
|
141
|
+
let current = '';
|
|
142
|
+
let depth = 0;
|
|
143
|
+
|
|
144
|
+
for (const char of inner) {
|
|
145
|
+
if (char === '[') depth++;
|
|
146
|
+
if (char === ']') depth--;
|
|
147
|
+
if (char === ',' && depth === 0) {
|
|
148
|
+
items.push(parseYamlValue(current.trim()));
|
|
149
|
+
current = '';
|
|
150
|
+
} else {
|
|
151
|
+
current += char;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (current.trim()) {
|
|
155
|
+
items.push(parseYamlValue(current.trim()));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return items;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 解析 YAML 值
|
|
163
|
+
*/
|
|
164
|
+
function parseYamlValue(value) {
|
|
165
|
+
if (!value) return value;
|
|
166
|
+
|
|
167
|
+
// 行内数组
|
|
168
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
169
|
+
return parseInlineArray(value);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 移除引号
|
|
173
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
174
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
175
|
+
return value.slice(1, -1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 布尔值
|
|
179
|
+
if (value === 'true') return true;
|
|
180
|
+
if (value === 'false') return false;
|
|
181
|
+
|
|
182
|
+
// 数字
|
|
183
|
+
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
184
|
+
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
185
|
+
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 解析 YAML 内容(异步版本,推荐使用)
|
|
191
|
+
*/
|
|
192
|
+
async function parseYamlAsync(content) {
|
|
193
|
+
await ensureYaml();
|
|
194
|
+
if (yaml) {
|
|
195
|
+
return yaml.load(content);
|
|
196
|
+
}
|
|
197
|
+
return parseYamlSimple(content);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 解析 YAML 内容(同步版本,用于非 async 上下文)
|
|
202
|
+
*/
|
|
203
|
+
function parseYaml(content) {
|
|
204
|
+
if (yaml) {
|
|
205
|
+
return yaml.load(content);
|
|
206
|
+
}
|
|
207
|
+
return parseYamlSimple(content);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ========== 验证函数(PreToolUse)==========
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 验证环境知识结构
|
|
214
|
+
*/
|
|
215
|
+
function validateKnowledgeStructure(knowledge) {
|
|
216
|
+
const errors = [];
|
|
217
|
+
|
|
218
|
+
if (!knowledge.apiVersion) {
|
|
219
|
+
errors.push('缺少必需字段:apiVersion');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!knowledge.kind) {
|
|
223
|
+
errors.push('缺少必需字段:kind');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!knowledge.metadata && !knowledge.meta) {
|
|
227
|
+
errors.push('缺少必需字段:metadata 或 meta');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (!knowledge.spec) {
|
|
231
|
+
errors.push('缺少必需字段:spec');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return errors;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 验证中间件知识
|
|
239
|
+
*/
|
|
240
|
+
function validateMiddlewareKnowledge(knowledge) {
|
|
241
|
+
const errors = [];
|
|
242
|
+
const spec = knowledge.spec;
|
|
243
|
+
const meta = knowledge.metadata || knowledge.meta;
|
|
244
|
+
|
|
245
|
+
if (!spec.type) {
|
|
246
|
+
errors.push('中间件知识缺少 type 字段');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!meta?.name) {
|
|
250
|
+
errors.push('中间件知识缺少 metadata.name 字段');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (spec.connection && !spec.connection.template) {
|
|
254
|
+
errors.push('中间件知识缺少 connection.template 字段');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 检查危险命令
|
|
258
|
+
if (spec.validation?.healthCheck) {
|
|
259
|
+
const dangerousPatterns = [/rm\s+-rf/, /sudo/, /mkfs/, /dd\s+if=/];
|
|
260
|
+
for (const pattern of dangerousPatterns) {
|
|
261
|
+
if (pattern.test(spec.validation.healthCheck)) {
|
|
262
|
+
errors.push('healthCheck 包含潜在危险的命令');
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return errors;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 验证组件知识
|
|
273
|
+
*/
|
|
274
|
+
function validateComponentKnowledge(knowledge) {
|
|
275
|
+
const errors = [];
|
|
276
|
+
const spec = knowledge.spec;
|
|
277
|
+
const meta = knowledge.metadata || knowledge.meta;
|
|
278
|
+
|
|
279
|
+
if (!spec.type) {
|
|
280
|
+
errors.push('组件知识缺少 type 字段');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!meta?.name) {
|
|
284
|
+
errors.push('组件知识缺少 metadata.name 字段');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!spec.description) {
|
|
288
|
+
errors.push('组件知识缺少 description 字段');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (spec.interfaces && Array.isArray(spec.interfaces)) {
|
|
292
|
+
for (const iface of spec.interfaces) {
|
|
293
|
+
if (!iface.name) {
|
|
294
|
+
errors.push('接口定义缺少 name 字段');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return errors;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 验证集成知识
|
|
304
|
+
*/
|
|
305
|
+
function validateIntegrationKnowledge(knowledge) {
|
|
306
|
+
const errors = [];
|
|
307
|
+
const spec = knowledge.spec;
|
|
308
|
+
const meta = knowledge.metadata || knowledge.meta;
|
|
309
|
+
|
|
310
|
+
if (!spec.type) {
|
|
311
|
+
errors.push('集成知识缺少 type 字段');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!meta?.name) {
|
|
315
|
+
errors.push('集成知识缺少 metadata.name 字段');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return errors;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 检查硬编码敏感信息
|
|
323
|
+
*/
|
|
324
|
+
function checkSensitiveInfo(content) {
|
|
325
|
+
const sensitivePatterns = [
|
|
326
|
+
/password\s*[:=]\s*['"][^'"]+['"]/i,
|
|
327
|
+
/secret\s*[:=]\s*['"][^'"]+['"]/i,
|
|
328
|
+
/api[_-]?key\s*[:=]\s*['"][^'"]+['"]/i,
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
for (const pattern of sensitivePatterns) {
|
|
332
|
+
if (pattern.test(content)) {
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 处理 PreToolUse 事件:环境知识验证
|
|
341
|
+
*/
|
|
342
|
+
async function handlePreToolUse(input) {
|
|
343
|
+
// 确保 js-yaml 已加载(如果可用)
|
|
344
|
+
await ensureYaml();
|
|
345
|
+
|
|
346
|
+
const toolName = input.tool_name;
|
|
347
|
+
const toolInput = input.tool_input || {};
|
|
348
|
+
|
|
349
|
+
// 只检查 Write 和 Edit 操作
|
|
350
|
+
if (!['Write', 'Edit'].includes(toolName)) {
|
|
351
|
+
process.exit(0);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const filePath = toolInput.file_path || '';
|
|
355
|
+
|
|
356
|
+
// 只检查 environment 目录下的文件
|
|
357
|
+
if (!filePath.includes('/environment/') && !filePath.includes('\\environment\\')) {
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 只检查 YAML 文件
|
|
362
|
+
if (!filePath.endsWith('.yaml') && !filePath.endsWith('.yml')) {
|
|
363
|
+
process.exit(0);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 获取文件内容
|
|
367
|
+
const content = toolInput.content || toolInput.new_string || '';
|
|
368
|
+
|
|
369
|
+
if (!content) {
|
|
370
|
+
process.exit(0);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// 检查硬编码敏感信息
|
|
374
|
+
if (checkSensitiveInfo(content)) {
|
|
375
|
+
const output = {
|
|
376
|
+
hookSpecificOutput: {
|
|
377
|
+
hookEventName: 'PreToolUse',
|
|
378
|
+
permissionDecision: 'deny',
|
|
379
|
+
permissionDecisionReason:
|
|
380
|
+
'ENVIRONMENT SECURITY: 检测到可能的硬编码敏感信息。请使用环境变量替代。',
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
process.stdout.write(JSON.stringify(output));
|
|
384
|
+
process.exit(0);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// 解析 YAML
|
|
388
|
+
let knowledge;
|
|
389
|
+
try {
|
|
390
|
+
knowledge = parseYaml(content);
|
|
391
|
+
} catch (e) {
|
|
392
|
+
const output = {
|
|
393
|
+
hookSpecificOutput: {
|
|
394
|
+
hookEventName: 'PreToolUse',
|
|
395
|
+
permissionDecision: 'deny',
|
|
396
|
+
permissionDecisionReason: `ENVIRONMENT YAML ERROR: YAML 解析失败 - ${e.message}`,
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
process.stdout.write(JSON.stringify(output));
|
|
400
|
+
process.exit(0);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 验证基本结构
|
|
404
|
+
const structureErrors = validateKnowledgeStructure(knowledge);
|
|
405
|
+
if (structureErrors.length > 0) {
|
|
406
|
+
const output = {
|
|
407
|
+
hookSpecificOutput: {
|
|
408
|
+
hookEventName: 'PreToolUse',
|
|
409
|
+
permissionDecision: 'deny',
|
|
410
|
+
permissionDecisionReason: `ENVIRONMENT STRUCTURE ERROR: ${structureErrors.join('; ')}`,
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
process.stdout.write(JSON.stringify(output));
|
|
414
|
+
process.exit(0);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// 根据 kind 进行专项验证
|
|
418
|
+
let errors = [];
|
|
419
|
+
switch (knowledge.kind) {
|
|
420
|
+
case 'MiddlewareKnowledge':
|
|
421
|
+
errors = validateMiddlewareKnowledge(knowledge);
|
|
422
|
+
break;
|
|
423
|
+
case 'ComponentKnowledge':
|
|
424
|
+
errors = validateComponentKnowledge(knowledge);
|
|
425
|
+
break;
|
|
426
|
+
case 'IntegrationKnowledge':
|
|
427
|
+
errors = validateIntegrationKnowledge(knowledge);
|
|
428
|
+
break;
|
|
429
|
+
default:
|
|
430
|
+
// 未知类型,允许通过
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (errors.length > 0) {
|
|
435
|
+
const output = {
|
|
436
|
+
hookSpecificOutput: {
|
|
437
|
+
hookEventName: 'PreToolUse',
|
|
438
|
+
permissionDecision: 'deny',
|
|
439
|
+
permissionDecisionReason: `ENVIRONMENT VALIDATION ERROR: ${errors.join('; ')}`,
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
process.stdout.write(JSON.stringify(output));
|
|
443
|
+
process.exit(0);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// 验证通过
|
|
447
|
+
process.exit(0);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ========== 进化检查(Stop)==========
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* 使用 PatternLoader 检测中间件 - 使用公共模块
|
|
454
|
+
*/
|
|
455
|
+
async function detectMiddlewareWithPatternLoader(projectRoot, patternLoader) {
|
|
456
|
+
const middlewarePatterns = patternLoader.getMiddlewarePatterns();
|
|
457
|
+
const detected = [];
|
|
458
|
+
const sourceDirs = discoverSourceDirsSimple(projectRoot);
|
|
459
|
+
|
|
460
|
+
if (sourceDirs.length === 0) {
|
|
461
|
+
sourceDirs.push('src');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
for (const dir of sourceDirs) {
|
|
465
|
+
const dirPath = path.join(projectRoot, dir);
|
|
466
|
+
if (!fs.existsSync(dirPath)) continue;
|
|
467
|
+
|
|
468
|
+
for (const [name, pattern] of Object.entries(middlewarePatterns)) {
|
|
469
|
+
if (detected.includes(name)) continue;
|
|
470
|
+
|
|
471
|
+
// 检测代码模式
|
|
472
|
+
if (pattern.codePatterns) {
|
|
473
|
+
const found = scanCodePatternsFast(dirPath, pattern.codePatterns);
|
|
474
|
+
if (found) {
|
|
475
|
+
detected.push(name);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// 检测环境变量模式
|
|
481
|
+
if (pattern.envVarPatterns) {
|
|
482
|
+
const found = scanEnvVarPatternsFast(dirPath, pattern.envVarPatterns);
|
|
483
|
+
if (found) {
|
|
484
|
+
detected.push(name);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// 从依赖文件检测
|
|
491
|
+
const depDetected = scanDependenciesFast(projectRoot, middlewarePatterns);
|
|
492
|
+
for (const name of depDetected) {
|
|
493
|
+
if (!detected.includes(name)) {
|
|
494
|
+
detected.push(name);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return detected;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* 检查中间件是否已注册
|
|
503
|
+
*/
|
|
504
|
+
function checkRegisteredMiddleware(projectRoot, middlewareNames) {
|
|
505
|
+
const registryPath = path.join(projectRoot, '.autospec', 'environment', 'registry.json');
|
|
506
|
+
|
|
507
|
+
if (!fs.existsSync(registryPath)) {
|
|
508
|
+
return middlewareNames.map(name => ({ name, registered: false, active: false }));
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
try {
|
|
512
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
513
|
+
const registered = registry.environmentKnowledge?.middleware?.map(m => m.name) || [];
|
|
514
|
+
const active = registry.activeProfiles?.middleware || [];
|
|
515
|
+
|
|
516
|
+
return middlewareNames.map(name => ({
|
|
517
|
+
name,
|
|
518
|
+
registered: registered.includes(name),
|
|
519
|
+
active: active.includes(name),
|
|
520
|
+
}));
|
|
521
|
+
} catch (e) {
|
|
522
|
+
return middlewareNames.map(name => ({ name, registered: false, active: false }));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* 检测组件 - 扫描 package.json 中的组织内部组件
|
|
528
|
+
*/
|
|
529
|
+
function detectComponents(projectRoot) {
|
|
530
|
+
const detected = [];
|
|
531
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
532
|
+
|
|
533
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
534
|
+
return detected;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
try {
|
|
538
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
539
|
+
const allDeps = {
|
|
540
|
+
...pkg.dependencies,
|
|
541
|
+
...pkg.devDependencies
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// 检测以 @org/ 或组织特定前缀开头的包
|
|
545
|
+
const orgPrefix = '@'; // 可配置
|
|
546
|
+
for (const dep of Object.keys(allDeps)) {
|
|
547
|
+
if (dep.startsWith('@') && dep.includes('/')) {
|
|
548
|
+
// 跳过标准作用域包
|
|
549
|
+
const scope = dep.split('/')[0];
|
|
550
|
+
if (!['@types', '@babel', '@eslint', '@angular', '@vue', '@react'].includes(scope)) {
|
|
551
|
+
detected.push(dep);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
} catch (e) {
|
|
556
|
+
// 忽略解析错误
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return detected;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* 检测设计文档变更 - 扫描 specs/ 和 docs/ 目录
|
|
564
|
+
*/
|
|
565
|
+
function detectDesignDocChanges(projectRoot) {
|
|
566
|
+
const changes = [];
|
|
567
|
+
const dirsToCheck = [
|
|
568
|
+
'specs',
|
|
569
|
+
'docs/architecture',
|
|
570
|
+
'docs/api',
|
|
571
|
+
'docs/database',
|
|
572
|
+
'docs/product'
|
|
573
|
+
];
|
|
574
|
+
|
|
575
|
+
for (const dir of dirsToCheck) {
|
|
576
|
+
const dirPath = path.join(projectRoot, dir);
|
|
577
|
+
if (fs.existsSync(dirPath)) {
|
|
578
|
+
try {
|
|
579
|
+
const files = fs.readdirSync(dirPath);
|
|
580
|
+
if (files.length > 0) {
|
|
581
|
+
changes.push({ type: 'design', path: dir, count: files.length });
|
|
582
|
+
}
|
|
583
|
+
} catch (e) {
|
|
584
|
+
// 忽略错误
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return changes;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* 检测业务知识变更 - 扫描 knowledge/domain/ 目录
|
|
594
|
+
*/
|
|
595
|
+
function detectBusinessKnowledgeChanges(projectRoot) {
|
|
596
|
+
const changes = [];
|
|
597
|
+
const dirsToCheck = [
|
|
598
|
+
'knowledge/domain',
|
|
599
|
+
'docs/business'
|
|
600
|
+
];
|
|
601
|
+
|
|
602
|
+
for (const dir of dirsToCheck) {
|
|
603
|
+
const dirPath = path.join(projectRoot, dir);
|
|
604
|
+
if (fs.existsSync(dirPath)) {
|
|
605
|
+
try {
|
|
606
|
+
const files = fs.readdirSync(dirPath);
|
|
607
|
+
if (files.length > 0) {
|
|
608
|
+
changes.push({ type: 'business', path: dir, count: files.length });
|
|
609
|
+
}
|
|
610
|
+
} catch (e) {
|
|
611
|
+
// 忽略错误
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return changes;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* 检查组件是否已注册
|
|
621
|
+
*/
|
|
622
|
+
function checkRegisteredComponents(projectRoot, componentNames) {
|
|
623
|
+
const registryPath = path.join(projectRoot, '.autospec', 'environment', 'registry.json');
|
|
624
|
+
|
|
625
|
+
if (!fs.existsSync(registryPath)) {
|
|
626
|
+
return componentNames.map(name => ({ name, registered: false, active: false }));
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
631
|
+
const registered = registry.componentKnowledge?.components?.map(c => c.name) || [];
|
|
632
|
+
const active = registry.activeProfiles?.components || [];
|
|
633
|
+
|
|
634
|
+
return componentNames.map(name => ({
|
|
635
|
+
name,
|
|
636
|
+
registered: registered.includes(name),
|
|
637
|
+
active: active.includes(name),
|
|
638
|
+
}));
|
|
639
|
+
} catch (e) {
|
|
640
|
+
return componentNames.map(name => ({ name, registered: false, active: false }));
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* 处理 Stop 事件:环境进化检查
|
|
646
|
+
*/
|
|
647
|
+
async function handleStop(projectRoot) {
|
|
648
|
+
const envDir = path.join(projectRoot, '.autospec', 'environment');
|
|
649
|
+
|
|
650
|
+
// 如果环境目录不存在,跳过
|
|
651
|
+
if (!fs.existsSync(envDir)) {
|
|
652
|
+
process.exit(0);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// 使用 PatternLoader 检测中间件(异步版本,尝试加载 js-yaml)
|
|
656
|
+
const autospecDir = path.join(projectRoot, '.autospec');
|
|
657
|
+
const patternLoader = new PatternLoader(autospecDir);
|
|
658
|
+
await patternLoader.loadAsync();
|
|
659
|
+
|
|
660
|
+
// 1. 检测中间件
|
|
661
|
+
const detectedMiddleware = await detectMiddlewareWithPatternLoader(projectRoot, patternLoader);
|
|
662
|
+
const middlewareStatus = checkRegisteredMiddleware(projectRoot, detectedMiddleware);
|
|
663
|
+
const middlewareNeedAction = middlewareStatus.filter(m => !m.registered || !m.active);
|
|
664
|
+
|
|
665
|
+
// 2. 检测组件
|
|
666
|
+
const detectedComponents = detectComponents(projectRoot);
|
|
667
|
+
const componentStatus = checkRegisteredComponents(projectRoot, detectedComponents);
|
|
668
|
+
const componentNeedAction = componentStatus.filter(c => !c.registered || !c.active);
|
|
669
|
+
|
|
670
|
+
// 3. 检测设计文档变更
|
|
671
|
+
const designDocChanges = detectDesignDocChanges(projectRoot);
|
|
672
|
+
|
|
673
|
+
// 4. 检测业务知识变更
|
|
674
|
+
const businessKnowledgeChanges = detectBusinessKnowledgeChanges(projectRoot);
|
|
675
|
+
|
|
676
|
+
// 汇总需要处理的事项
|
|
677
|
+
const hasMiddlewareAction = middlewareNeedAction.length > 0;
|
|
678
|
+
const hasComponentAction = componentNeedAction.length > 0;
|
|
679
|
+
const hasDesignChanges = designDocChanges.length > 0;
|
|
680
|
+
const hasBusinessChanges = businessKnowledgeChanges.length > 0;
|
|
681
|
+
|
|
682
|
+
if (!hasMiddlewareAction && !hasComponentAction && !hasDesignChanges && !hasBusinessChanges) {
|
|
683
|
+
process.exit(0);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// 生成提醒消息
|
|
687
|
+
let message = '[AutoSpec Environment] ';
|
|
688
|
+
const actions = [];
|
|
689
|
+
|
|
690
|
+
// 中间件提醒
|
|
691
|
+
if (hasMiddlewareAction) {
|
|
692
|
+
const unregistered = middlewareNeedAction.filter(m => !m.registered).map(m => m.name);
|
|
693
|
+
const inactive = middlewareNeedAction.filter(m => m.registered && !m.active).map(m => m.name);
|
|
694
|
+
|
|
695
|
+
if (unregistered.length > 0) {
|
|
696
|
+
actions.push(`新中间件:${unregistered.join(', ')}`);
|
|
697
|
+
}
|
|
698
|
+
if (inactive.length > 0) {
|
|
699
|
+
actions.push(`未激活中间件:${inactive.join(', ')}`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// 组件提醒
|
|
704
|
+
if (hasComponentAction) {
|
|
705
|
+
const unregistered = componentNeedAction.filter(c => !c.registered).map(c => c.name);
|
|
706
|
+
const inactive = componentNeedAction.filter(c => c.registered && !c.active).map(c => c.name);
|
|
707
|
+
|
|
708
|
+
if (unregistered.length > 0) {
|
|
709
|
+
actions.push(`新组件:${unregistered.join(', ')}`);
|
|
710
|
+
}
|
|
711
|
+
if (inactive.length > 0) {
|
|
712
|
+
actions.push(`未激活组件:${inactive.join(', ')}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// 设计文档变更提醒
|
|
717
|
+
if (hasDesignChanges) {
|
|
718
|
+
const paths = designDocChanges.map(d => d.path).join(', ');
|
|
719
|
+
actions.push(`设计文档变更:${paths}`);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// 业务知识变更提醒
|
|
723
|
+
if (hasBusinessChanges) {
|
|
724
|
+
const paths = businessKnowledgeChanges.map(b => b.path).join(', ');
|
|
725
|
+
actions.push(`业务知识变更:${paths}`);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
message += actions.join(';') + '。运行 autospec env:sync 可同步最新知识。';
|
|
729
|
+
|
|
730
|
+
const output = {
|
|
731
|
+
hookSpecificOutput: {
|
|
732
|
+
hookEventName: 'Stop',
|
|
733
|
+
additionalContext: message,
|
|
734
|
+
},
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
process.stdout.write(JSON.stringify(output));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* 主函数
|
|
742
|
+
*/
|
|
743
|
+
async function main() {
|
|
744
|
+
try {
|
|
745
|
+
const inputRaw = await readStdin(1000);
|
|
746
|
+
const input = JSON.parse(inputRaw);
|
|
747
|
+
|
|
748
|
+
const eventName = input.event_name;
|
|
749
|
+
|
|
750
|
+
// 根据事件类型分发
|
|
751
|
+
if (eventName === 'PreToolUse') {
|
|
752
|
+
await handlePreToolUse(input);
|
|
753
|
+
} else if (eventName === 'Stop') {
|
|
754
|
+
const cwd = input.cwd || process.cwd();
|
|
755
|
+
const projectRoot = await findProjectRootAsync(cwd);
|
|
756
|
+
|
|
757
|
+
if (!projectRoot) {
|
|
758
|
+
process.exit(0);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
await handleStop(projectRoot);
|
|
762
|
+
} else {
|
|
763
|
+
logger.debug('Unknown event type, skipping', { eventName });
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
process.exit(0);
|
|
767
|
+
|
|
768
|
+
} catch (e) {
|
|
769
|
+
// 输出错误信息,使用 handleHookError
|
|
770
|
+
const result = handleHookError('environment-manager', e, {
|
|
771
|
+
eventName: 'Unknown'
|
|
772
|
+
});
|
|
773
|
+
process.stdout.write(JSON.stringify(result.hookSpecificOutput));
|
|
774
|
+
process.exit(0);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
main();
|