@itz4blitz/agentful 0.4.0 → 1.0.0
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 +131 -16
- package/bin/cli.js +1031 -47
- package/bin/hooks/README.md +338 -82
- package/bin/hooks/analyze-trigger.js +69 -0
- package/bin/hooks/block-random-docs.js +77 -0
- package/bin/hooks/health-check.js +153 -0
- package/bin/hooks/post-agent.js +101 -0
- package/bin/hooks/post-feature.js +227 -0
- package/bin/hooks/pre-agent.js +118 -0
- package/bin/hooks/pre-feature.js +138 -0
- package/lib/VALIDATION_README.md +455 -0
- package/lib/atomic.js +350 -0
- package/lib/ci/claude-action-integration.js +641 -0
- package/lib/ci/index.js +10 -0
- package/lib/core/CLAUDE_EXECUTOR.md +371 -0
- package/lib/core/README.md +321 -0
- package/lib/core/analyzer.js +497 -0
- package/lib/core/claude-executor.example.js +210 -0
- package/lib/core/claude-executor.js +1046 -0
- package/lib/core/cli.js +141 -0
- package/lib/core/detectors/conventions.js +342 -0
- package/lib/core/detectors/framework.js +276 -0
- package/lib/core/detectors/index.js +15 -0
- package/lib/core/detectors/language.js +199 -0
- package/lib/core/detectors/patterns.js +356 -0
- package/lib/core/generator.js +626 -0
- package/lib/core/index.js +9 -0
- package/lib/core/output-parser.example.js +250 -0
- package/lib/core/output-parser.js +458 -0
- package/lib/core/storage.js +515 -0
- package/lib/core/templates.js +556 -0
- package/lib/index.js +32 -0
- package/lib/init.js +252 -21
- package/lib/pipeline/cli.js +423 -0
- package/lib/pipeline/engine.js +928 -0
- package/lib/pipeline/executor.js +440 -0
- package/lib/pipeline/index.js +33 -0
- package/lib/pipeline/integrations.js +559 -0
- package/lib/pipeline/schemas.js +288 -0
- package/lib/presets.js +207 -0
- package/lib/remote/client.js +361 -0
- package/lib/server/auth.js +286 -0
- package/lib/server/client-example.js +190 -0
- package/lib/server/executor.js +426 -0
- package/lib/server/index.js +469 -0
- package/lib/update-helpers.js +505 -0
- package/lib/validation.js +460 -0
- package/package.json +19 -2
- package/template/.claude/agents/architect.md +260 -0
- package/template/.claude/agents/backend.md +203 -0
- package/template/.claude/agents/fixer.md +244 -0
- package/template/.claude/agents/frontend.md +232 -0
- package/template/.claude/agents/orchestrator.md +528 -0
- package/template/.claude/agents/product-analyzer.md +1130 -0
- package/template/.claude/agents/reviewer.md +229 -0
- package/template/.claude/agents/tester.md +242 -0
- package/{.claude → template/.claude}/commands/agentful-analyze.md +151 -43
- package/template/.claude/commands/agentful-decide.md +470 -0
- package/{.claude → template/.claude}/commands/agentful-product.md +89 -5
- package/template/.claude/commands/agentful-start.md +432 -0
- package/{.claude → template/.claude}/commands/agentful-status.md +88 -3
- package/template/.claude/commands/agentful-update.md +402 -0
- package/template/.claude/commands/agentful-validate.md +369 -0
- package/{.claude → template/.claude}/commands/agentful.md +110 -183
- package/template/.claude/product/EXAMPLES.md +167 -0
- package/{.claude → template/.claude}/settings.json +9 -13
- package/{.claude → template/.claude}/skills/conversation/SKILL.md +13 -7
- package/template/.claude/skills/deployment/SKILL.md +116 -0
- package/template/.claude/skills/product-planning/SKILL.md +463 -0
- package/template/.claude/skills/testing/SKILL.md +228 -0
- package/template/.claude/skills/validation/SKILL.md +650 -0
- package/template/CLAUDE.md +73 -5
- package/template/bin/hooks/block-random-docs.js +121 -0
- package/version.json +1 -1
- package/.claude/agents/architect.md +0 -524
- package/.claude/agents/backend.md +0 -315
- package/.claude/agents/fixer.md +0 -263
- package/.claude/agents/frontend.md +0 -274
- package/.claude/agents/orchestrator.md +0 -283
- package/.claude/agents/product-analyzer.md +0 -792
- package/.claude/agents/reviewer.md +0 -332
- package/.claude/agents/tester.md +0 -410
- package/.claude/commands/agentful-decide.md +0 -214
- package/.claude/commands/agentful-start.md +0 -182
- package/.claude/commands/agentful-validate.md +0 -127
- package/.claude/product/EXAMPLES.md +0 -610
- package/.claude/product/README.md +0 -326
- package/.claude/skills/validation/SKILL.md +0 -271
- package/bin/hooks/analyze-trigger.sh +0 -57
- package/bin/hooks/health-check.sh +0 -36
- /package/{.claude → template/.claude}/commands/agentful-generate.md +0 -0
- /package/{.claude → template/.claude}/product/index.md +0 -0
- /package/{.claude → template/.claude}/skills/product-tracking/SKILL.md +0 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schemas for Pipeline Definitions
|
|
3
|
+
*
|
|
4
|
+
* Provides validation schemas for pipeline YAML files
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const pipelineSchema = {
|
|
8
|
+
type: 'object',
|
|
9
|
+
required: ['name', 'jobs'],
|
|
10
|
+
properties: {
|
|
11
|
+
name: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'Pipeline name',
|
|
14
|
+
pattern: '^[a-z0-9-]+$'
|
|
15
|
+
},
|
|
16
|
+
version: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Pipeline version',
|
|
19
|
+
pattern: '^\\d+\\.\\d+(\\.\\d+)?$',
|
|
20
|
+
default: '1.0'
|
|
21
|
+
},
|
|
22
|
+
description: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Pipeline description'
|
|
25
|
+
},
|
|
26
|
+
triggers: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
description: 'Pipeline triggers',
|
|
29
|
+
items: {
|
|
30
|
+
$ref: '#/definitions/trigger'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
env: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
description: 'Environment variables',
|
|
36
|
+
additionalProperties: { type: 'string' }
|
|
37
|
+
},
|
|
38
|
+
jobs: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
description: 'Pipeline jobs',
|
|
41
|
+
minItems: 1,
|
|
42
|
+
items: {
|
|
43
|
+
$ref: '#/definitions/job'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
concurrency: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
description: 'Concurrency settings',
|
|
49
|
+
properties: {
|
|
50
|
+
maxConcurrentJobs: {
|
|
51
|
+
type: 'integer',
|
|
52
|
+
minimum: 1,
|
|
53
|
+
maximum: 10,
|
|
54
|
+
default: 3
|
|
55
|
+
},
|
|
56
|
+
cancelInProgress: {
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
default: false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
timeout: {
|
|
63
|
+
type: 'integer',
|
|
64
|
+
description: 'Default job timeout in milliseconds',
|
|
65
|
+
minimum: 1000,
|
|
66
|
+
default: 1800000 // 30 minutes
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
definitions: {
|
|
70
|
+
trigger: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
required: ['type'],
|
|
73
|
+
properties: {
|
|
74
|
+
type: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
enum: ['push', 'pull_request', 'schedule', 'manual', 'webhook']
|
|
77
|
+
},
|
|
78
|
+
branches: {
|
|
79
|
+
type: 'array',
|
|
80
|
+
items: { type: 'string' }
|
|
81
|
+
},
|
|
82
|
+
cron: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'Cron expression for scheduled triggers'
|
|
85
|
+
},
|
|
86
|
+
webhook: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
secret: { type: 'string' },
|
|
90
|
+
filters: { type: 'object' }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
job: {
|
|
96
|
+
type: 'object',
|
|
97
|
+
required: ['id', 'agent'],
|
|
98
|
+
properties: {
|
|
99
|
+
id: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
description: 'Unique job identifier',
|
|
102
|
+
pattern: '^[a-z0-9-]+$'
|
|
103
|
+
},
|
|
104
|
+
name: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'Human-readable job name'
|
|
107
|
+
},
|
|
108
|
+
agent: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description: 'Agent to execute (name or path)'
|
|
111
|
+
},
|
|
112
|
+
task: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Task description for the agent'
|
|
115
|
+
},
|
|
116
|
+
prompt: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Additional prompt instructions'
|
|
119
|
+
},
|
|
120
|
+
dependsOn: {
|
|
121
|
+
oneOf: [
|
|
122
|
+
{ type: 'string' },
|
|
123
|
+
{ type: 'array', items: { type: 'string' } }
|
|
124
|
+
],
|
|
125
|
+
description: 'Job dependencies'
|
|
126
|
+
},
|
|
127
|
+
when: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
description: 'Conditional execution expression'
|
|
130
|
+
},
|
|
131
|
+
inputs: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
description: 'Job-specific inputs',
|
|
134
|
+
additionalProperties: true
|
|
135
|
+
},
|
|
136
|
+
timeout: {
|
|
137
|
+
type: 'integer',
|
|
138
|
+
description: 'Job timeout in milliseconds',
|
|
139
|
+
minimum: 1000
|
|
140
|
+
},
|
|
141
|
+
retry: {
|
|
142
|
+
$ref: '#/definitions/retry'
|
|
143
|
+
},
|
|
144
|
+
continueOnError: {
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
description: 'Continue pipeline even if this job fails',
|
|
147
|
+
default: false
|
|
148
|
+
},
|
|
149
|
+
execution: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
description: 'Execution configuration',
|
|
152
|
+
properties: {
|
|
153
|
+
method: {
|
|
154
|
+
type: 'string',
|
|
155
|
+
enum: ['subprocess', 'api'],
|
|
156
|
+
default: 'subprocess'
|
|
157
|
+
},
|
|
158
|
+
isolation: {
|
|
159
|
+
type: 'string',
|
|
160
|
+
enum: ['shared', 'isolated'],
|
|
161
|
+
default: 'shared'
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
runsOn: {
|
|
166
|
+
type: 'string',
|
|
167
|
+
description: 'Runner environment (for CI/CD integrations)',
|
|
168
|
+
default: 'ubuntu-latest'
|
|
169
|
+
},
|
|
170
|
+
stage: {
|
|
171
|
+
type: 'string',
|
|
172
|
+
description: 'Pipeline stage (for GitLab CI)',
|
|
173
|
+
default: 'test'
|
|
174
|
+
},
|
|
175
|
+
setup: {
|
|
176
|
+
type: 'array',
|
|
177
|
+
description: 'Setup steps before job execution',
|
|
178
|
+
items: {
|
|
179
|
+
type: 'object',
|
|
180
|
+
required: ['command'],
|
|
181
|
+
properties: {
|
|
182
|
+
name: { type: 'string' },
|
|
183
|
+
command: { type: 'string' }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
retry: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
description: 'Retry policy',
|
|
192
|
+
properties: {
|
|
193
|
+
maxAttempts: {
|
|
194
|
+
type: 'integer',
|
|
195
|
+
minimum: 0,
|
|
196
|
+
maximum: 5,
|
|
197
|
+
default: 0
|
|
198
|
+
},
|
|
199
|
+
backoff: {
|
|
200
|
+
type: 'string',
|
|
201
|
+
enum: ['exponential', 'linear', 'fixed'],
|
|
202
|
+
default: 'exponential'
|
|
203
|
+
},
|
|
204
|
+
delayMs: {
|
|
205
|
+
type: 'integer',
|
|
206
|
+
minimum: 0,
|
|
207
|
+
default: 2000
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Validate pipeline definition
|
|
216
|
+
*
|
|
217
|
+
* @param {Object} pipeline - Pipeline definition
|
|
218
|
+
* @returns {Object} Validation result { valid: boolean, errors: array }
|
|
219
|
+
*/
|
|
220
|
+
export function validatePipeline(pipeline) {
|
|
221
|
+
// Basic validation
|
|
222
|
+
const errors = [];
|
|
223
|
+
|
|
224
|
+
if (!pipeline || typeof pipeline !== 'object') {
|
|
225
|
+
errors.push('Pipeline must be a valid object');
|
|
226
|
+
return { valid: false, errors };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!pipeline.name) {
|
|
230
|
+
errors.push('Pipeline must have a name');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!pipeline.jobs || !Array.isArray(pipeline.jobs) || pipeline.jobs.length === 0) {
|
|
234
|
+
errors.push('Pipeline must have at least one job');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Collect all job IDs first
|
|
238
|
+
const jobIds = new Set();
|
|
239
|
+
for (const job of pipeline.jobs || []) {
|
|
240
|
+
if (job.id) {
|
|
241
|
+
if (jobIds.has(job.id)) {
|
|
242
|
+
errors.push(`Duplicate job id: ${job.id}`);
|
|
243
|
+
} else {
|
|
244
|
+
jobIds.add(job.id);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Validate jobs
|
|
250
|
+
for (const job of pipeline.jobs || []) {
|
|
251
|
+
// Check if job is a valid object
|
|
252
|
+
if (!job || typeof job !== 'object') {
|
|
253
|
+
errors.push('Each job must be a valid object');
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!job.id) {
|
|
258
|
+
errors.push('Each job must have an id');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!job.agent) {
|
|
262
|
+
errors.push(`Job ${job.id} must specify an agent`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Validate dependencies
|
|
266
|
+
if ('dependsOn' in job) {
|
|
267
|
+
const deps = Array.isArray(job.dependsOn) ? job.dependsOn : [job.dependsOn];
|
|
268
|
+
for (const depId of deps) {
|
|
269
|
+
// Check for empty string or invalid values
|
|
270
|
+
if (typeof depId !== 'string' || depId === '') {
|
|
271
|
+
errors.push(`Job ${job.id} depends on unknown job: ${depId}`);
|
|
272
|
+
} else if (!jobIds.has(depId)) {
|
|
273
|
+
errors.push(`Job ${job.id} depends on unknown job: ${depId}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
valid: errors.length === 0,
|
|
281
|
+
errors
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export default {
|
|
286
|
+
pipelineSchema,
|
|
287
|
+
validatePipeline
|
|
288
|
+
};
|
package/lib/presets.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset configurations for agentful initialization
|
|
3
|
+
*
|
|
4
|
+
* SIMPLIFIED: Only two concepts
|
|
5
|
+
* 1. "default" (or no preset) - Everything installed (recommended)
|
|
6
|
+
* 2. "minimal" - Minimal setup for simple scripts/CLIs
|
|
7
|
+
*
|
|
8
|
+
* Tech stack is auto-detected on first run, irrelevant to installation.
|
|
9
|
+
*
|
|
10
|
+
* Each preset defines:
|
|
11
|
+
* - agents: array of agent names to include
|
|
12
|
+
* - skills: array of skill names to include
|
|
13
|
+
* - hooks: array of hook identifiers to configure
|
|
14
|
+
* - gates: array of quality gate identifiers
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export const presets = {
|
|
18
|
+
default: {
|
|
19
|
+
description: 'Complete agentful installation (recommended)',
|
|
20
|
+
agents: ['orchestrator', 'architect', 'backend', 'frontend', 'tester', 'reviewer', 'fixer', 'product-analyzer'],
|
|
21
|
+
skills: ['product-tracking', 'validation', 'testing', 'conversation', 'product-planning', 'deployment'],
|
|
22
|
+
hooks: ['health-check'],
|
|
23
|
+
gates: ['types', 'tests', 'coverage', 'lint', 'security', 'dead-code']
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
minimal: {
|
|
27
|
+
description: 'Minimal setup for simple scripts/CLIs (orchestrator + backend only)',
|
|
28
|
+
agents: ['orchestrator', 'backend'],
|
|
29
|
+
skills: ['validation'],
|
|
30
|
+
hooks: [],
|
|
31
|
+
gates: ['types', 'tests']
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Hook configurations mapping
|
|
37
|
+
* Maps hook identifiers to actual hook configurations
|
|
38
|
+
*/
|
|
39
|
+
export const hookConfigurations = {
|
|
40
|
+
'health-check': {
|
|
41
|
+
event: 'SessionStart',
|
|
42
|
+
config: {
|
|
43
|
+
type: 'command',
|
|
44
|
+
command: 'node bin/hooks/health-check.js',
|
|
45
|
+
timeout: 5,
|
|
46
|
+
description: 'Quick agentful health check'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
'typescript-validation': {
|
|
51
|
+
event: 'PostToolUse',
|
|
52
|
+
matcher: 'Write|Edit',
|
|
53
|
+
config: {
|
|
54
|
+
type: 'command',
|
|
55
|
+
command: 'npx tsc --noEmit 2>&1 | head -5 || true',
|
|
56
|
+
description: 'TypeScript validation after file changes'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
'notifications': {
|
|
61
|
+
event: 'PostToolUse',
|
|
62
|
+
matcher: 'Write|Edit',
|
|
63
|
+
config: {
|
|
64
|
+
type: 'command',
|
|
65
|
+
command: 'osascript -e \'display notification "File updated: $FILE" with title "agentful"\' 2>/dev/null || true',
|
|
66
|
+
description: 'Desktop notifications for file changes'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
'format-on-save': {
|
|
71
|
+
event: 'PostToolUse',
|
|
72
|
+
matcher: 'Write|Edit',
|
|
73
|
+
config: {
|
|
74
|
+
type: 'command',
|
|
75
|
+
command: 'npx prettier --write "$FILE" 2>/dev/null || true',
|
|
76
|
+
description: 'Auto-format files on save'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse comma-separated CLI flag into array
|
|
83
|
+
* @param {string} value - Comma-separated values
|
|
84
|
+
* @returns {string[]}
|
|
85
|
+
*/
|
|
86
|
+
export function parseArrayFlag(value) {
|
|
87
|
+
if (!value) return [];
|
|
88
|
+
return value.split(',').map(v => v.trim()).filter(Boolean);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Merge preset with CLI flags (flags take precedence)
|
|
93
|
+
* @param {Object} preset - Preset configuration
|
|
94
|
+
* @param {Object} flags - CLI flags
|
|
95
|
+
* @returns {Object} Merged configuration
|
|
96
|
+
*/
|
|
97
|
+
export function mergePresetWithFlags(preset, flags) {
|
|
98
|
+
return {
|
|
99
|
+
agents: flags.agents || preset.agents,
|
|
100
|
+
skills: flags.skills || preset.skills,
|
|
101
|
+
hooks: flags.hooks || preset.hooks,
|
|
102
|
+
gates: flags.gates || preset.gates,
|
|
103
|
+
techStack: flags.techStack || preset.techStack
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get preset by name
|
|
109
|
+
* @param {string} presetName - Name of preset
|
|
110
|
+
* @returns {Object|null} Preset configuration or null if not found
|
|
111
|
+
*/
|
|
112
|
+
export function getPreset(presetName) {
|
|
113
|
+
return presets[presetName] || null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* List all available presets
|
|
118
|
+
* @returns {Object[]} Array of preset info
|
|
119
|
+
*/
|
|
120
|
+
export function listPresets() {
|
|
121
|
+
return Object.entries(presets).map(([name, config]) => ({
|
|
122
|
+
name,
|
|
123
|
+
description: config.description,
|
|
124
|
+
agents: config.agents.length,
|
|
125
|
+
skills: config.skills.length,
|
|
126
|
+
hooks: config.hooks.length,
|
|
127
|
+
gates: config.gates.length
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validate configuration
|
|
133
|
+
* @param {Object} config - Configuration to validate
|
|
134
|
+
* @returns {{valid: boolean, errors: string[]}}
|
|
135
|
+
*/
|
|
136
|
+
export function validateConfiguration(config) {
|
|
137
|
+
const errors = [];
|
|
138
|
+
const availableAgents = ['orchestrator', 'architect', 'backend', 'frontend', 'tester', 'reviewer', 'fixer', 'product-analyzer'];
|
|
139
|
+
const availableSkills = ['product-tracking', 'validation', 'testing', 'conversation', 'product-planning', 'deployment'];
|
|
140
|
+
const availableHooks = Object.keys(hookConfigurations);
|
|
141
|
+
|
|
142
|
+
// Always require orchestrator
|
|
143
|
+
if (!config.agents || !config.agents.includes('orchestrator')) {
|
|
144
|
+
errors.push('orchestrator agent is required and will be added automatically');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Validate agents
|
|
148
|
+
if (config.agents) {
|
|
149
|
+
const invalidAgents = config.agents.filter(a => !availableAgents.includes(a));
|
|
150
|
+
if (invalidAgents.length > 0) {
|
|
151
|
+
errors.push(`Invalid agents: ${invalidAgents.join(', ')}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Validate skills
|
|
156
|
+
if (config.skills) {
|
|
157
|
+
const invalidSkills = config.skills.filter(s => !availableSkills.includes(s));
|
|
158
|
+
if (invalidSkills.length > 0) {
|
|
159
|
+
errors.push(`Invalid skills: ${invalidSkills.join(', ')}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Validate hooks
|
|
164
|
+
if (config.hooks) {
|
|
165
|
+
const invalidHooks = config.hooks.filter(h => !availableHooks.includes(h));
|
|
166
|
+
if (invalidHooks.length > 0) {
|
|
167
|
+
errors.push(`Invalid hooks: ${invalidHooks.join(', ')}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
valid: errors.length === 0,
|
|
173
|
+
errors
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Generate settings.json hooks section from hook identifiers
|
|
179
|
+
* @param {string[]} hookIds - Array of hook identifiers
|
|
180
|
+
* @returns {Object} Settings.json hooks section
|
|
181
|
+
*/
|
|
182
|
+
export function generateHooksConfig(hookIds) {
|
|
183
|
+
const hooks = {};
|
|
184
|
+
|
|
185
|
+
for (const hookId of hookIds) {
|
|
186
|
+
const hookConfig = hookConfigurations[hookId];
|
|
187
|
+
if (!hookConfig) continue;
|
|
188
|
+
|
|
189
|
+
const event = hookConfig.event;
|
|
190
|
+
if (!hooks[event]) {
|
|
191
|
+
hooks[event] = [];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const hookEntry = {
|
|
195
|
+
hooks: [hookConfig.config]
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Add matcher if specified
|
|
199
|
+
if (hookConfig.matcher) {
|
|
200
|
+
hookEntry.matcher = hookConfig.matcher;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
hooks[event].push(hookEntry);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return hooks;
|
|
207
|
+
}
|