@polymorphism-tech/morph-spec 7.9.0 → 7.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +6 -2
- package/docs/QUICKSTART.md +4 -4
- package/framework/CLAUDE.md +1 -1
- package/framework/MORPH.md +2 -2
- package/framework/commands/commit.md +166 -166
- package/framework/commands/morph-infra.md +215 -215
- package/framework/commands/morph-preflight.md +205 -205
- package/framework/commands/morph-troubleshoot.md +124 -124
- package/framework/hooks/claude-code/statusline.py +679 -679
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/activity-logger.js +160 -160
- package/framework/hooks/shared/hook-response.js +45 -45
- package/framework/hooks/shared/payload-utils.js +78 -78
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/rubrics/backend.json +41 -41
- package/framework/rules/ai-agents-standards.md +45 -45
- package/framework/rules/dotnet-templates.md +86 -86
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +27 -27
- package/framework/rules/neon-standards.md +25 -25
- package/framework/rules/nextjs-standards.md +20 -20
- package/framework/rules/nextjs-templates.md +40 -40
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +13 -8
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +13 -8
- package/framework/skills/core/morph-plan/SKILL.md +1 -1
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +10 -7
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +160 -160
- package/framework/standards/ai-agents/batching.md +193 -193
- package/framework/standards/ai-agents/context-providers.md +188 -188
- package/framework/standards/ai-agents/mcp-server.md +250 -250
- package/framework/standards/ai-agents/mcp-tools.md +197 -197
- package/framework/standards/ai-agents/middleware-patterns.md +207 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
- package/framework/standards/ai-agents/modalities-vision.md +201 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
- package/framework/standards/ai-agents/production.md +275 -275
- package/framework/standards/ai-agents/prompts-in-db.md +214 -214
- package/framework/standards/ai-agents/providers/model-registry.md +158 -158
- package/framework/standards/ai-agents/service-tier-flex.md +135 -135
- package/framework/standards/ai-agents/structured-output.md +249 -249
- package/framework/standards/ai-agents/sweet-spot.md +109 -109
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +113 -113
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +2 -1
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/index.js +13 -13
- package/src/commands/mcp/mcp-setup.js +519 -519
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +1 -1
- package/src/commands/project/update.js +4 -1
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/HookInspector.jsx +418 -418
- package/src/dashboard/app/pages/LiveState.jsx +239 -239
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/hooks-scanner.js +41 -41
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/scanner/standards-scanner.js +26 -26
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/metrics-aggregator.js +56 -56
- package/src/lib/agents/metrics-calculator.js +125 -125
- package/src/lib/agents/output-collector.js +73 -73
- package/src/lib/detectors/claude-config-detector.js +152 -152
- package/src/lib/detectors/design-system-detector.js +231 -231
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/generators/architecture-scanner.js +639 -639
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/index.js +13 -13
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/test-runner.js +81 -81
- package/src/lib/validators/architecture/architecture-validator.js +259 -259
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/packages/package-validator.js +347 -347
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/validation-runner.js +349 -349
- package/src/scripts/setup-infra.js +385 -385
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/skills-installer.js +174 -174
- package/src/utils/version-checker.js +2 -2
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook Executor — Agent Teams hook validation
|
|
3
|
-
*
|
|
4
|
-
* Executes validation hooks triggered by Claude Code agent-teams events:
|
|
5
|
-
* TeammateIdle, TaskCompleted, PhaseAdvanced
|
|
6
|
-
*
|
|
7
|
-
* Reads agents.json to find validators with matching hook_triggers,
|
|
8
|
-
* then runs each validator's checks against the current project state.
|
|
9
|
-
*
|
|
10
|
-
* @module hook-executor
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { readFileSync, existsSync } from 'fs';
|
|
14
|
-
import { join } from 'path';
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Loaders
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
function loadAgents(projectPath) {
|
|
21
|
-
const agentPaths = [
|
|
22
|
-
join(projectPath, '.morph/framework/agents.json'),
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const allAgents = {};
|
|
26
|
-
for (const agentPath of agentPaths) {
|
|
27
|
-
if (existsSync(agentPath)) {
|
|
28
|
-
try {
|
|
29
|
-
const data = JSON.parse(readFileSync(agentPath, 'utf8'));
|
|
30
|
-
Object.assign(allAgents, data.agents || {});
|
|
31
|
-
} catch (e) { /* skip malformed files */ }
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return allAgents;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function loadStateFeature(projectPath, featureName) {
|
|
38
|
-
const statePath = join(projectPath, '.morph/state.json');
|
|
39
|
-
if (!existsSync(statePath)) return null;
|
|
40
|
-
|
|
41
|
-
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
42
|
-
const features = state.features;
|
|
43
|
-
|
|
44
|
-
// Support both object format { featureName: {...} } and array format [{name, ...}]
|
|
45
|
-
if (Array.isArray(features)) {
|
|
46
|
-
return features.find(f => f.name === featureName) || null;
|
|
47
|
-
}
|
|
48
|
-
return features[featureName] || null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// Main Executor
|
|
53
|
-
// ============================================================================
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Execute agent-teams hook validation
|
|
57
|
-
* @param {string} projectPath - Absolute path to project root
|
|
58
|
-
* @param {string} featureName - Feature to validate
|
|
59
|
-
* @param {string} hookEvent - Event name (TeammateIdle, TaskCompleted, PhaseAdvanced)
|
|
60
|
-
* @param {Object} [opts]
|
|
61
|
-
* @param {boolean} [opts.dryRun=false] - Identify validators without running them
|
|
62
|
-
* @param {boolean} [opts.verbose=true] - Print results to console
|
|
63
|
-
* @returns {Promise<Object>} { passed, blocked, errors, warnings }
|
|
64
|
-
*/
|
|
65
|
-
export async function executeHook(projectPath, featureName, hookEvent, opts = {}) {
|
|
66
|
-
const { dryRun = false } = opts;
|
|
67
|
-
|
|
68
|
-
// Load feature state
|
|
69
|
-
const feature = loadStateFeature(projectPath, featureName);
|
|
70
|
-
if (!feature) {
|
|
71
|
-
return {
|
|
72
|
-
passed: false,
|
|
73
|
-
blocked: true,
|
|
74
|
-
errors: [`Feature not found: ${featureName}`],
|
|
75
|
-
warnings: []
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Load all available agents
|
|
80
|
-
const agents = loadAgents(projectPath);
|
|
81
|
-
|
|
82
|
-
// Filter: agents that run in hooks AND trigger on this event
|
|
83
|
-
const hookValidators = Object.entries(agents)
|
|
84
|
-
.filter(([, agent]) =>
|
|
85
|
-
agent.relationships?.runs_in === 'hooks' &&
|
|
86
|
-
Array.isArray(agent.relationships?.hook_triggers) &&
|
|
87
|
-
agent.relationships.hook_triggers.includes(hookEvent)
|
|
88
|
-
)
|
|
89
|
-
.map(([agentId, agent]) => ({ agentId, ...agent }));
|
|
90
|
-
|
|
91
|
-
// No validators configured for this event, or dry run — pass immediately
|
|
92
|
-
if (hookValidators.length === 0 || dryRun) {
|
|
93
|
-
return { passed: true, blocked: false, errors: [], warnings: [] };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Run validators
|
|
97
|
-
// In production, each validator would execute its actual checks.
|
|
98
|
-
// This implementation returns pass since real validation requires external tooling.
|
|
99
|
-
const errors = [];
|
|
100
|
-
const warnings = [];
|
|
101
|
-
|
|
102
|
-
const passed = errors.length === 0;
|
|
103
|
-
const blocked = errors.some(e => typeof e === 'object' && e.blocksOnFail !== false);
|
|
104
|
-
|
|
105
|
-
return { passed, blocked, errors, warnings };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ============================================================================
|
|
109
|
-
// Formatter
|
|
110
|
-
// ============================================================================
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Format hook execution results as a human-readable string
|
|
114
|
-
* @param {Object} results - Result from executeHook
|
|
115
|
-
* @param {string} hookEvent - Event name
|
|
116
|
-
* @returns {string} Formatted output
|
|
117
|
-
*/
|
|
118
|
-
export function formatHookResults(results, hookEvent) {
|
|
119
|
-
const { passed, blocked, errors = [], warnings = [] } = results;
|
|
120
|
-
const lines = [];
|
|
121
|
-
|
|
122
|
-
if (!passed && errors.length > 0) {
|
|
123
|
-
lines.push(`❌ Hook: ${hookEvent}`);
|
|
124
|
-
lines.push('FAILED');
|
|
125
|
-
lines.push('ERRORS (blocking):');
|
|
126
|
-
|
|
127
|
-
for (const err of errors) {
|
|
128
|
-
if (err.issues && Array.isArray(err.issues)) {
|
|
129
|
-
for (const issue of err.issues) {
|
|
130
|
-
lines.push(` ${issue.message}`);
|
|
131
|
-
if (issue.file && issue.line !== undefined) {
|
|
132
|
-
lines.push(` ${issue.file}:${issue.line}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
} else if (err.error) {
|
|
136
|
-
lines.push(` Error: ${err.error}`);
|
|
137
|
-
} else if (typeof err === 'string') {
|
|
138
|
-
lines.push(` ${err}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (blocked) {
|
|
143
|
-
lines.push('BLOCKED');
|
|
144
|
-
}
|
|
145
|
-
} else if (warnings.length === 0) {
|
|
146
|
-
lines.push(`✅ Hook: ${hookEvent}`);
|
|
147
|
-
lines.push('All validators passed');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (warnings.length > 0) {
|
|
151
|
-
lines.push(`⚠️ Hook: ${hookEvent}`);
|
|
152
|
-
lines.push('WARNINGS (non-blocking):');
|
|
153
|
-
|
|
154
|
-
for (const warn of warnings) {
|
|
155
|
-
if (warn.issues && Array.isArray(warn.issues)) {
|
|
156
|
-
for (const issue of warn.issues) {
|
|
157
|
-
lines.push(` ${issue.message}`);
|
|
158
|
-
if (issue.file && issue.line !== undefined) {
|
|
159
|
-
lines.push(` ${issue.file}:${issue.line}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return lines.join('\n');
|
|
167
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Hook Executor — Agent Teams hook validation
|
|
3
|
+
*
|
|
4
|
+
* Executes validation hooks triggered by Claude Code agent-teams events:
|
|
5
|
+
* TeammateIdle, TaskCompleted, PhaseAdvanced
|
|
6
|
+
*
|
|
7
|
+
* Reads agents.json to find validators with matching hook_triggers,
|
|
8
|
+
* then runs each validator's checks against the current project state.
|
|
9
|
+
*
|
|
10
|
+
* @module hook-executor
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Loaders
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
function loadAgents(projectPath) {
|
|
21
|
+
const agentPaths = [
|
|
22
|
+
join(projectPath, '.morph/framework/agents.json'),
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const allAgents = {};
|
|
26
|
+
for (const agentPath of agentPaths) {
|
|
27
|
+
if (existsSync(agentPath)) {
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(readFileSync(agentPath, 'utf8'));
|
|
30
|
+
Object.assign(allAgents, data.agents || {});
|
|
31
|
+
} catch (e) { /* skip malformed files */ }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return allAgents;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadStateFeature(projectPath, featureName) {
|
|
38
|
+
const statePath = join(projectPath, '.morph/state.json');
|
|
39
|
+
if (!existsSync(statePath)) return null;
|
|
40
|
+
|
|
41
|
+
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
42
|
+
const features = state.features;
|
|
43
|
+
|
|
44
|
+
// Support both object format { featureName: {...} } and array format [{name, ...}]
|
|
45
|
+
if (Array.isArray(features)) {
|
|
46
|
+
return features.find(f => f.name === featureName) || null;
|
|
47
|
+
}
|
|
48
|
+
return features[featureName] || null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Main Executor
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Execute agent-teams hook validation
|
|
57
|
+
* @param {string} projectPath - Absolute path to project root
|
|
58
|
+
* @param {string} featureName - Feature to validate
|
|
59
|
+
* @param {string} hookEvent - Event name (TeammateIdle, TaskCompleted, PhaseAdvanced)
|
|
60
|
+
* @param {Object} [opts]
|
|
61
|
+
* @param {boolean} [opts.dryRun=false] - Identify validators without running them
|
|
62
|
+
* @param {boolean} [opts.verbose=true] - Print results to console
|
|
63
|
+
* @returns {Promise<Object>} { passed, blocked, errors, warnings }
|
|
64
|
+
*/
|
|
65
|
+
export async function executeHook(projectPath, featureName, hookEvent, opts = {}) {
|
|
66
|
+
const { dryRun = false } = opts;
|
|
67
|
+
|
|
68
|
+
// Load feature state
|
|
69
|
+
const feature = loadStateFeature(projectPath, featureName);
|
|
70
|
+
if (!feature) {
|
|
71
|
+
return {
|
|
72
|
+
passed: false,
|
|
73
|
+
blocked: true,
|
|
74
|
+
errors: [`Feature not found: ${featureName}`],
|
|
75
|
+
warnings: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Load all available agents
|
|
80
|
+
const agents = loadAgents(projectPath);
|
|
81
|
+
|
|
82
|
+
// Filter: agents that run in hooks AND trigger on this event
|
|
83
|
+
const hookValidators = Object.entries(agents)
|
|
84
|
+
.filter(([, agent]) =>
|
|
85
|
+
agent.relationships?.runs_in === 'hooks' &&
|
|
86
|
+
Array.isArray(agent.relationships?.hook_triggers) &&
|
|
87
|
+
agent.relationships.hook_triggers.includes(hookEvent)
|
|
88
|
+
)
|
|
89
|
+
.map(([agentId, agent]) => ({ agentId, ...agent }));
|
|
90
|
+
|
|
91
|
+
// No validators configured for this event, or dry run — pass immediately
|
|
92
|
+
if (hookValidators.length === 0 || dryRun) {
|
|
93
|
+
return { passed: true, blocked: false, errors: [], warnings: [] };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Run validators
|
|
97
|
+
// In production, each validator would execute its actual checks.
|
|
98
|
+
// This implementation returns pass since real validation requires external tooling.
|
|
99
|
+
const errors = [];
|
|
100
|
+
const warnings = [];
|
|
101
|
+
|
|
102
|
+
const passed = errors.length === 0;
|
|
103
|
+
const blocked = errors.some(e => typeof e === 'object' && e.blocksOnFail !== false);
|
|
104
|
+
|
|
105
|
+
return { passed, blocked, errors, warnings };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Formatter
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format hook execution results as a human-readable string
|
|
114
|
+
* @param {Object} results - Result from executeHook
|
|
115
|
+
* @param {string} hookEvent - Event name
|
|
116
|
+
* @returns {string} Formatted output
|
|
117
|
+
*/
|
|
118
|
+
export function formatHookResults(results, hookEvent) {
|
|
119
|
+
const { passed, blocked, errors = [], warnings = [] } = results;
|
|
120
|
+
const lines = [];
|
|
121
|
+
|
|
122
|
+
if (!passed && errors.length > 0) {
|
|
123
|
+
lines.push(`❌ Hook: ${hookEvent}`);
|
|
124
|
+
lines.push('FAILED');
|
|
125
|
+
lines.push('ERRORS (blocking):');
|
|
126
|
+
|
|
127
|
+
for (const err of errors) {
|
|
128
|
+
if (err.issues && Array.isArray(err.issues)) {
|
|
129
|
+
for (const issue of err.issues) {
|
|
130
|
+
lines.push(` ${issue.message}`);
|
|
131
|
+
if (issue.file && issue.line !== undefined) {
|
|
132
|
+
lines.push(` ${issue.file}:${issue.line}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else if (err.error) {
|
|
136
|
+
lines.push(` Error: ${err.error}`);
|
|
137
|
+
} else if (typeof err === 'string') {
|
|
138
|
+
lines.push(` ${err}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (blocked) {
|
|
143
|
+
lines.push('BLOCKED');
|
|
144
|
+
}
|
|
145
|
+
} else if (warnings.length === 0) {
|
|
146
|
+
lines.push(`✅ Hook: ${hookEvent}`);
|
|
147
|
+
lines.push('All validators passed');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (warnings.length > 0) {
|
|
151
|
+
lines.push(`⚠️ Hook: ${hookEvent}`);
|
|
152
|
+
lines.push('WARNINGS (non-blocking):');
|
|
153
|
+
|
|
154
|
+
for (const warn of warnings) {
|
|
155
|
+
if (warn.issues && Array.isArray(warn.issues)) {
|
|
156
|
+
for (const issue of warn.issues) {
|
|
157
|
+
lines.push(` ${issue.message}`);
|
|
158
|
+
if (issue.file && issue.line !== undefined) {
|
|
159
|
+
lines.push(` ${issue.file}:${issue.line}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return lines.join('\n');
|
|
167
|
+
}
|