@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.
Files changed (93) hide show
  1. package/README.md +131 -16
  2. package/bin/cli.js +1031 -47
  3. package/bin/hooks/README.md +338 -82
  4. package/bin/hooks/analyze-trigger.js +69 -0
  5. package/bin/hooks/block-random-docs.js +77 -0
  6. package/bin/hooks/health-check.js +153 -0
  7. package/bin/hooks/post-agent.js +101 -0
  8. package/bin/hooks/post-feature.js +227 -0
  9. package/bin/hooks/pre-agent.js +118 -0
  10. package/bin/hooks/pre-feature.js +138 -0
  11. package/lib/VALIDATION_README.md +455 -0
  12. package/lib/atomic.js +350 -0
  13. package/lib/ci/claude-action-integration.js +641 -0
  14. package/lib/ci/index.js +10 -0
  15. package/lib/core/CLAUDE_EXECUTOR.md +371 -0
  16. package/lib/core/README.md +321 -0
  17. package/lib/core/analyzer.js +497 -0
  18. package/lib/core/claude-executor.example.js +210 -0
  19. package/lib/core/claude-executor.js +1046 -0
  20. package/lib/core/cli.js +141 -0
  21. package/lib/core/detectors/conventions.js +342 -0
  22. package/lib/core/detectors/framework.js +276 -0
  23. package/lib/core/detectors/index.js +15 -0
  24. package/lib/core/detectors/language.js +199 -0
  25. package/lib/core/detectors/patterns.js +356 -0
  26. package/lib/core/generator.js +626 -0
  27. package/lib/core/index.js +9 -0
  28. package/lib/core/output-parser.example.js +250 -0
  29. package/lib/core/output-parser.js +458 -0
  30. package/lib/core/storage.js +515 -0
  31. package/lib/core/templates.js +556 -0
  32. package/lib/index.js +32 -0
  33. package/lib/init.js +252 -21
  34. package/lib/pipeline/cli.js +423 -0
  35. package/lib/pipeline/engine.js +928 -0
  36. package/lib/pipeline/executor.js +440 -0
  37. package/lib/pipeline/index.js +33 -0
  38. package/lib/pipeline/integrations.js +559 -0
  39. package/lib/pipeline/schemas.js +288 -0
  40. package/lib/presets.js +207 -0
  41. package/lib/remote/client.js +361 -0
  42. package/lib/server/auth.js +286 -0
  43. package/lib/server/client-example.js +190 -0
  44. package/lib/server/executor.js +426 -0
  45. package/lib/server/index.js +469 -0
  46. package/lib/update-helpers.js +505 -0
  47. package/lib/validation.js +460 -0
  48. package/package.json +19 -2
  49. package/template/.claude/agents/architect.md +260 -0
  50. package/template/.claude/agents/backend.md +203 -0
  51. package/template/.claude/agents/fixer.md +244 -0
  52. package/template/.claude/agents/frontend.md +232 -0
  53. package/template/.claude/agents/orchestrator.md +528 -0
  54. package/template/.claude/agents/product-analyzer.md +1130 -0
  55. package/template/.claude/agents/reviewer.md +229 -0
  56. package/template/.claude/agents/tester.md +242 -0
  57. package/{.claude → template/.claude}/commands/agentful-analyze.md +151 -43
  58. package/template/.claude/commands/agentful-decide.md +470 -0
  59. package/{.claude → template/.claude}/commands/agentful-product.md +89 -5
  60. package/template/.claude/commands/agentful-start.md +432 -0
  61. package/{.claude → template/.claude}/commands/agentful-status.md +88 -3
  62. package/template/.claude/commands/agentful-update.md +402 -0
  63. package/template/.claude/commands/agentful-validate.md +369 -0
  64. package/{.claude → template/.claude}/commands/agentful.md +110 -183
  65. package/template/.claude/product/EXAMPLES.md +167 -0
  66. package/{.claude → template/.claude}/settings.json +9 -13
  67. package/{.claude → template/.claude}/skills/conversation/SKILL.md +13 -7
  68. package/template/.claude/skills/deployment/SKILL.md +116 -0
  69. package/template/.claude/skills/product-planning/SKILL.md +463 -0
  70. package/template/.claude/skills/testing/SKILL.md +228 -0
  71. package/template/.claude/skills/validation/SKILL.md +650 -0
  72. package/template/CLAUDE.md +73 -5
  73. package/template/bin/hooks/block-random-docs.js +121 -0
  74. package/version.json +1 -1
  75. package/.claude/agents/architect.md +0 -524
  76. package/.claude/agents/backend.md +0 -315
  77. package/.claude/agents/fixer.md +0 -263
  78. package/.claude/agents/frontend.md +0 -274
  79. package/.claude/agents/orchestrator.md +0 -283
  80. package/.claude/agents/product-analyzer.md +0 -792
  81. package/.claude/agents/reviewer.md +0 -332
  82. package/.claude/agents/tester.md +0 -410
  83. package/.claude/commands/agentful-decide.md +0 -214
  84. package/.claude/commands/agentful-start.md +0 -182
  85. package/.claude/commands/agentful-validate.md +0 -127
  86. package/.claude/product/EXAMPLES.md +0 -610
  87. package/.claude/product/README.md +0 -326
  88. package/.claude/skills/validation/SKILL.md +0 -271
  89. package/bin/hooks/analyze-trigger.sh +0 -57
  90. package/bin/hooks/health-check.sh +0 -36
  91. /package/{.claude → template/.claude}/commands/agentful-generate.md +0 -0
  92. /package/{.claude → template/.claude}/product/index.md +0 -0
  93. /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
+ }