@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,211 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AutoSpec PreToolUse Hook: Constitution Guard
|
|
5
|
+
*
|
|
6
|
+
* Enforces startup gate protocol (DP6 Constitution compliance) for development tasks.
|
|
7
|
+
* When code changes (Write/Edit) are about to be made, checks if the startup gate
|
|
8
|
+
* has been properly followed (reading constitution, overview, state tracking).
|
|
9
|
+
*
|
|
10
|
+
* This is a WARNING hook (does not block) to remind about constitutional requirements.
|
|
11
|
+
* See CLAUDE.md section "启动门禁(硬性规则)"
|
|
12
|
+
*
|
|
13
|
+
* 增强功能:
|
|
14
|
+
* - 门禁执行统计:记录每次门禁检查结果
|
|
15
|
+
* - 与 metrics.json 集成
|
|
16
|
+
*
|
|
17
|
+
* Security Level: HIGH - fail-open + alert
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createHookLogger, safeJsonParse, handleHookError, readStdin, findProjectRootAsync, fileExists } from './lib/hook-logger.js';
|
|
21
|
+
import { recordHookExecution } from './lib/hook-error-recorder.js';
|
|
22
|
+
import { getMetrics, updateMetrics } from './lib/hook-state-manager.js';
|
|
23
|
+
import { getTraceId, recordEvent } from './lib/trace-context.js';
|
|
24
|
+
import { recordGate } from './lib/execution-path.js';
|
|
25
|
+
|
|
26
|
+
const logger = createHookLogger('constitution-guard');
|
|
27
|
+
|
|
28
|
+
const DEVELOPMENT_PATTERNS = [
|
|
29
|
+
/\.(js|ts|jsx|tsx|py|java|go|rs|cpp|c|h|php|rb|swift|kt|scala)$/i,
|
|
30
|
+
/package\.json$/,
|
|
31
|
+
/pom\.xml$/,
|
|
32
|
+
/build\.gradle$/,
|
|
33
|
+
/go\.mod$/,
|
|
34
|
+
/Cargo\.toml$/,
|
|
35
|
+
/requirements\.txt$/,
|
|
36
|
+
/pyproject\.toml$/
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
async function main() {
|
|
40
|
+
let input;
|
|
41
|
+
try {
|
|
42
|
+
const rawInput = await readStdin(1000);
|
|
43
|
+
input = safeJsonParse(rawInput, null, 'hook input');
|
|
44
|
+
|
|
45
|
+
if (!input) {
|
|
46
|
+
logger.debug('Empty or invalid input, skipping');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
logger.error('Failed to parse input', { error: err.message });
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const toolName = input.tool_name;
|
|
55
|
+
if (toolName !== 'Write' && toolName !== 'Edit') {
|
|
56
|
+
logger.debug('Not a write/edit operation, skipping', { toolName });
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const fs = await import('fs');
|
|
61
|
+
const path = await import('path');
|
|
62
|
+
|
|
63
|
+
// Find project root
|
|
64
|
+
const cwd = input.cwd || process.cwd();
|
|
65
|
+
const projectRoot = await findProjectRootAsync(cwd);
|
|
66
|
+
|
|
67
|
+
if (!projectRoot) {
|
|
68
|
+
logger.debug('No .autospec directory found, skipping');
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const filePath = (input.tool_input?.file_path || '').replace(/\\/g, '/');
|
|
73
|
+
|
|
74
|
+
// Check if this is a code/development file
|
|
75
|
+
const isDevelopmentFile = DEVELOPMENT_PATTERNS.some(p => p.test(filePath));
|
|
76
|
+
if (!isDevelopmentFile) {
|
|
77
|
+
logger.debug('Not a development file, skipping', { filePath });
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
logger.debug('Development file detected', { filePath });
|
|
82
|
+
|
|
83
|
+
// Check if startup gate has been followed
|
|
84
|
+
const statePath = path.join(projectRoot, '.autospec', 'state.json');
|
|
85
|
+
|
|
86
|
+
// If state.json doesn't exist, startup gate wasn't followed
|
|
87
|
+
if (!(await fileExists(statePath))) {
|
|
88
|
+
logger.info('No state.json found - startup gate may not have been followed');
|
|
89
|
+
|
|
90
|
+
const output = {
|
|
91
|
+
hookSpecificOutput: {
|
|
92
|
+
hookEventName: 'PreToolUse',
|
|
93
|
+
additionalContext:
|
|
94
|
+
`[AutoSpec Constitution Guard] ⚠️ CONSTITUTION ALERT (DP6)\n` +
|
|
95
|
+
`Development activity detected but no state.json found.\n` +
|
|
96
|
+
`Startup gate protocol may not have been followed:\n` +
|
|
97
|
+
` 1. Read framework/pipeline/overview.md → 确定迭代模式\n` +
|
|
98
|
+
` 2. Read framework/constitution.md → 确认红线\n` +
|
|
99
|
+
` 3. 向用户确认任务范围\n` +
|
|
100
|
+
`Consider using /autospec:run to properly start the workflow.`
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
process.stdout.write(JSON.stringify(output));
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// State exists, check basic record
|
|
108
|
+
try {
|
|
109
|
+
const content = await fs.promises.readFile(statePath, 'utf-8');
|
|
110
|
+
const state = safeJsonParse(content, null, 'state.json');
|
|
111
|
+
|
|
112
|
+
if (!state) {
|
|
113
|
+
logger.debug('Failed to parse state.json');
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if there's any layer 1/2 execution record
|
|
118
|
+
const hasExecutionRecord = state.stages && Object.values(state.stages).some(
|
|
119
|
+
s => s.layer1Results || s.layer2Results
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Check if constitution was read (stored in metadata)
|
|
123
|
+
const hasConstitutionRead = state.metadata && state.metadata.constitutionRead;
|
|
124
|
+
|
|
125
|
+
logger.debug('State check complete', { hasExecutionRecord, hasConstitutionRead });
|
|
126
|
+
|
|
127
|
+
// 记录门禁执行结果
|
|
128
|
+
const gatePassed = hasExecutionRecord || hasConstitutionRead;
|
|
129
|
+
recordGateResult(projectRoot, 'constitution', gatePassed, gatePassed ? null : '缺少执行记录');
|
|
130
|
+
|
|
131
|
+
if (!hasExecutionRecord && !hasConstitutionRead) {
|
|
132
|
+
// File is being written but no verification records exist
|
|
133
|
+
// This might indicate that the startup steps weren't properly followed
|
|
134
|
+
// We don't block, just warn once per session
|
|
135
|
+
logger.info('No execution records found in state');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
process.exit(0);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
logger.debug('Failed to read state', { error: err.message });
|
|
141
|
+
process.exit(0);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main().catch((err) => {
|
|
146
|
+
const result = handleHookError('constitution-guard', err, {
|
|
147
|
+
eventName: 'PreToolUse'
|
|
148
|
+
});
|
|
149
|
+
process.stdout.write(JSON.stringify(result.hookSpecificOutput));
|
|
150
|
+
// High 优先级 hook:fail-open,但记录错误
|
|
151
|
+
process.exit(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 记录门禁执行结果
|
|
156
|
+
* @param {string} projectRoot - 项目根目录
|
|
157
|
+
* @param {string} gate - 门禁名称
|
|
158
|
+
* @param {boolean} passed - 是否通过
|
|
159
|
+
* @param {string|null} reason - 原因(未通过时)
|
|
160
|
+
*/
|
|
161
|
+
function recordGateResult(projectRoot, gate, passed, reason = null) {
|
|
162
|
+
try {
|
|
163
|
+
// 更新 metrics.json 的 gates 统计
|
|
164
|
+
const metrics = getMetrics(projectRoot, false);
|
|
165
|
+
|
|
166
|
+
if (!metrics.gates) {
|
|
167
|
+
metrics.gates = {};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!metrics.gates[gate]) {
|
|
171
|
+
metrics.gates[gate] = { passed: 0, blocked: 0, history: [] };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (passed) {
|
|
175
|
+
metrics.gates[gate].passed++;
|
|
176
|
+
} else {
|
|
177
|
+
metrics.gates[gate].blocked++;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 添加历史记录(保留最近 50 条)
|
|
181
|
+
metrics.gates[gate].history.push({
|
|
182
|
+
timestamp: new Date().toISOString(),
|
|
183
|
+
passed,
|
|
184
|
+
reason
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (metrics.gates[gate].history.length > 50) {
|
|
188
|
+
metrics.gates[gate].history = metrics.gates[gate].history.slice(-50);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
updateMetrics(projectRoot, metrics);
|
|
192
|
+
|
|
193
|
+
// 记录到执行路径
|
|
194
|
+
recordGate(projectRoot, gate, passed, reason);
|
|
195
|
+
|
|
196
|
+
// 记录到 trace 日志
|
|
197
|
+
const traceId = getTraceId(projectRoot);
|
|
198
|
+
if (traceId) {
|
|
199
|
+
recordEvent(projectRoot, passed ? 'gate_pass' : 'gate_block', {
|
|
200
|
+
source: 'constitution-guard',
|
|
201
|
+
action: 'gate_check',
|
|
202
|
+
data: {
|
|
203
|
+
gate,
|
|
204
|
+
reason
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
logger.debug('Failed to record gate result', { error: e.message });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AutoSpec Hook: Environment Autocommit
|
|
5
|
+
*
|
|
6
|
+
* 环境知识自动提交 Hook
|
|
7
|
+
* 触发时机:Stop 事件
|
|
8
|
+
* 功能:
|
|
9
|
+
* 1. 检测 .autospec/environment/ 目录的变化
|
|
10
|
+
* 2. 自动执行 git add + commit
|
|
11
|
+
* 3. 可配置是否自动 push
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import { createHookLogger, readStdin, findProjectRootAsync } from './lib/hook-logger.js';
|
|
17
|
+
|
|
18
|
+
const logger = createHookLogger('environment-autocommit');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 检查是否为 git 仓库
|
|
22
|
+
*/
|
|
23
|
+
function isGitRepo(dir) {
|
|
24
|
+
return fs.existsSync(path.join(dir, '.git'));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 获取 git 状态
|
|
29
|
+
*/
|
|
30
|
+
function getGitStatus(repoDir) {
|
|
31
|
+
const { execSync } = require('child_process');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const status = execSync('git status --porcelain', {
|
|
35
|
+
cwd: repoDir,
|
|
36
|
+
encoding: 'utf-8'
|
|
37
|
+
});
|
|
38
|
+
return status;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 获取当前分支名
|
|
46
|
+
*/
|
|
47
|
+
function getCurrentBranch(repoDir) {
|
|
48
|
+
const { execSync } = require('child_process');
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const branch = execSync('git branch --show-current', {
|
|
52
|
+
cwd: repoDir,
|
|
53
|
+
encoding: 'utf-8'
|
|
54
|
+
}).trim();
|
|
55
|
+
return branch;
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return 'main';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 执行 git add
|
|
63
|
+
*/
|
|
64
|
+
function gitAdd(repoDir, files) {
|
|
65
|
+
const { execSync } = require('child_process');
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
execSync(`git add ${files.join(' ')}`, {
|
|
69
|
+
cwd: repoDir,
|
|
70
|
+
encoding: 'utf-8'
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
logger.error('git add failed', { error: e.message });
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 执行 git commit
|
|
81
|
+
*/
|
|
82
|
+
function gitCommit(repoDir, message) {
|
|
83
|
+
const { execSync } = require('child_process');
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
execSync(`git commit -m "${message}"`, {
|
|
87
|
+
cwd: repoDir,
|
|
88
|
+
encoding: 'utf-8'
|
|
89
|
+
});
|
|
90
|
+
return true;
|
|
91
|
+
} catch (e) {
|
|
92
|
+
logger.error('git commit failed', { error: e.message });
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 执行 git push
|
|
99
|
+
*/
|
|
100
|
+
function gitPush(repoDir) {
|
|
101
|
+
const { execSync } = require('child_process');
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
execSync('git push', {
|
|
105
|
+
cwd: repoDir,
|
|
106
|
+
encoding: 'utf-8'
|
|
107
|
+
});
|
|
108
|
+
return true;
|
|
109
|
+
} catch (e) {
|
|
110
|
+
logger.error('git push failed', { error: e.message });
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 加载配置
|
|
117
|
+
*/
|
|
118
|
+
function loadConfig(projectRoot) {
|
|
119
|
+
const configPath = path.join(projectRoot, '.autospec', 'environment', 'autocommit.json');
|
|
120
|
+
|
|
121
|
+
if (!fs.existsSync(configPath)) {
|
|
122
|
+
// 返回默认配置
|
|
123
|
+
return {
|
|
124
|
+
enabled: false,
|
|
125
|
+
autoPush: false,
|
|
126
|
+
messageTemplate: 'chore: 更新环境知识 {date}',
|
|
127
|
+
paths: ['.autospec/environment/']
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
133
|
+
} catch (e) {
|
|
134
|
+
logger.warn('Failed to load autocommit config', { error: e.message });
|
|
135
|
+
return { enabled: false };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 生成提交消息
|
|
141
|
+
*/
|
|
142
|
+
function generateCommitMessage(template, changes) {
|
|
143
|
+
const date = new Date().toISOString().split('T')[0];
|
|
144
|
+
let message = template.replace('{date}', date);
|
|
145
|
+
|
|
146
|
+
// 如果有具体变更,添加到消息中
|
|
147
|
+
if (changes.length > 0) {
|
|
148
|
+
const summary = changes.map(c => path.basename(c)).join(', ');
|
|
149
|
+
message += `\n\n变更文件: ${summary}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return message;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 处理 Stop 事件
|
|
157
|
+
*/
|
|
158
|
+
async function handleStop(projectRoot) {
|
|
159
|
+
// 检查是否为 git 仓库
|
|
160
|
+
if (!isGitRepo(projectRoot)) {
|
|
161
|
+
logger.debug('Not a git repo, skipping autocommit');
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 加载配置
|
|
166
|
+
const config = loadConfig(projectRoot);
|
|
167
|
+
|
|
168
|
+
if (!config.enabled) {
|
|
169
|
+
logger.debug('Autocommit is disabled');
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 检查环境知识目录
|
|
174
|
+
const envDir = path.join(projectRoot, '.autospec', 'environment');
|
|
175
|
+
if (!fs.existsSync(envDir)) {
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 获取 git 状态
|
|
180
|
+
const status = getGitStatus(projectRoot);
|
|
181
|
+
|
|
182
|
+
// 过滤出环境知识目录的变更
|
|
183
|
+
const envPaths = config.paths || ['.autospec/environment/'];
|
|
184
|
+
const changedFiles = status.split('\n')
|
|
185
|
+
.filter(line => line.trim())
|
|
186
|
+
.map(line => line.substring(3).trim())
|
|
187
|
+
.filter(file => envPaths.some(p => file.startsWith(p)));
|
|
188
|
+
|
|
189
|
+
if (changedFiles.length === 0) {
|
|
190
|
+
logger.debug('No environment knowledge changes');
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
logger.info('Detected environment knowledge changes', { files: changedFiles });
|
|
195
|
+
|
|
196
|
+
// 执行 git add
|
|
197
|
+
const addResult = gitAdd(projectRoot, changedFiles);
|
|
198
|
+
if (!addResult) {
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 生成提交消息
|
|
203
|
+
const message = generateCommitMessage(config.messageTemplate || 'chore: 更新环境知识', changedFiles);
|
|
204
|
+
|
|
205
|
+
// 执行 git commit
|
|
206
|
+
const commitResult = gitCommit(projectRoot, message);
|
|
207
|
+
if (!commitResult) {
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
logger.info('Committed environment knowledge changes', { message });
|
|
212
|
+
|
|
213
|
+
// 如果配置了自动 push
|
|
214
|
+
if (config.autoPush) {
|
|
215
|
+
const pushResult = gitPush(projectRoot);
|
|
216
|
+
if (pushResult) {
|
|
217
|
+
logger.info('Pushed environment knowledge changes');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 输出结果
|
|
222
|
+
const branch = getCurrentBranch(projectRoot);
|
|
223
|
+
const output = {
|
|
224
|
+
hookSpecificOutput: {
|
|
225
|
+
hookEventName: 'Stop',
|
|
226
|
+
additionalContext: `[AutoSpec] 环境知识已自动提交到 ${branch} 分支${config.autoPush ? '并推送' : ''}。变更文件: ${changedFiles.length} 个`,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
process.stdout.write(JSON.stringify(output));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 主函数
|
|
235
|
+
*/
|
|
236
|
+
async function main() {
|
|
237
|
+
try {
|
|
238
|
+
const inputRaw = await readStdin(1000);
|
|
239
|
+
const input = JSON.parse(inputRaw);
|
|
240
|
+
|
|
241
|
+
const eventName = input.event_name;
|
|
242
|
+
|
|
243
|
+
if (eventName === 'Stop') {
|
|
244
|
+
const cwd = input.cwd || process.cwd();
|
|
245
|
+
const projectRoot = await findProjectRootAsync(cwd);
|
|
246
|
+
|
|
247
|
+
if (!projectRoot) {
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
await handleStop(projectRoot);
|
|
252
|
+
} else {
|
|
253
|
+
logger.debug('Unknown event type, skipping', { eventName });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
process.exit(0);
|
|
257
|
+
|
|
258
|
+
} catch (e) {
|
|
259
|
+
logger.error('Hook error', { error: e.message });
|
|
260
|
+
process.exit(0);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
main();
|