@itz4blitz/agentful 1.2.0 → 1.3.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 (59) hide show
  1. package/README.md +28 -1
  2. package/bin/cli.js +11 -1055
  3. package/bin/hooks/block-file-creation.js +271 -0
  4. package/bin/hooks/product-spec-watcher.js +151 -0
  5. package/lib/index.js +0 -11
  6. package/lib/init.js +2 -21
  7. package/lib/parallel-execution.js +235 -0
  8. package/lib/presets.js +26 -4
  9. package/package.json +4 -7
  10. package/template/.claude/agents/architect.md +2 -2
  11. package/template/.claude/agents/backend.md +17 -30
  12. package/template/.claude/agents/frontend.md +17 -39
  13. package/template/.claude/agents/orchestrator.md +63 -4
  14. package/template/.claude/agents/product-analyzer.md +1 -1
  15. package/template/.claude/agents/tester.md +16 -29
  16. package/template/.claude/commands/agentful-generate.md +221 -14
  17. package/template/.claude/commands/agentful-init.md +621 -0
  18. package/template/.claude/commands/agentful-product.md +1 -1
  19. package/template/.claude/commands/agentful-start.md +99 -1
  20. package/template/.claude/product/EXAMPLES.md +2 -2
  21. package/template/.claude/product/index.md +1 -1
  22. package/template/.claude/settings.json +22 -0
  23. package/template/.claude/skills/research/SKILL.md +432 -0
  24. package/template/CLAUDE.md +5 -6
  25. package/template/bin/hooks/architect-drift-detector.js +242 -0
  26. package/template/bin/hooks/product-spec-watcher.js +151 -0
  27. package/version.json +1 -1
  28. package/bin/hooks/post-agent.js +0 -101
  29. package/bin/hooks/post-feature.js +0 -227
  30. package/bin/hooks/pre-agent.js +0 -118
  31. package/bin/hooks/pre-feature.js +0 -138
  32. package/lib/VALIDATION_README.md +0 -455
  33. package/lib/ci/claude-action-integration.js +0 -641
  34. package/lib/ci/index.js +0 -10
  35. package/lib/core/analyzer.js +0 -497
  36. package/lib/core/cli.js +0 -141
  37. package/lib/core/detectors/conventions.js +0 -342
  38. package/lib/core/detectors/framework.js +0 -276
  39. package/lib/core/detectors/index.js +0 -15
  40. package/lib/core/detectors/language.js +0 -199
  41. package/lib/core/detectors/patterns.js +0 -356
  42. package/lib/core/generator.js +0 -626
  43. package/lib/core/index.js +0 -9
  44. package/lib/core/output-parser.js +0 -458
  45. package/lib/core/storage.js +0 -515
  46. package/lib/core/templates.js +0 -556
  47. package/lib/pipeline/cli.js +0 -423
  48. package/lib/pipeline/engine.js +0 -928
  49. package/lib/pipeline/executor.js +0 -440
  50. package/lib/pipeline/index.js +0 -33
  51. package/lib/pipeline/integrations.js +0 -559
  52. package/lib/pipeline/schemas.js +0 -288
  53. package/lib/remote/client.js +0 -361
  54. package/lib/server/auth.js +0 -270
  55. package/lib/server/client-example.js +0 -190
  56. package/lib/server/executor.js +0 -477
  57. package/lib/server/index.js +0 -494
  58. package/lib/update-helpers.js +0 -505
  59. package/lib/validation.js +0 -460
@@ -1,559 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import yaml from 'js-yaml';
4
-
5
- /**
6
- * CI/CD Integration Adapters
7
- *
8
- * Provides adapters for various CI/CD platforms:
9
- * - GitHub Actions
10
- * - GitLab CI
11
- * - Jenkins
12
- * - Webhook triggers
13
- */
14
-
15
- /**
16
- * GitHub Actions Integration
17
- *
18
- * Converts agentful pipelines to GitHub Actions workflows
19
- */
20
- export class GitHubActionsAdapter {
21
- /**
22
- * Convert pipeline to GitHub Actions workflow
23
- *
24
- * @param {Object} pipeline - Agentful pipeline definition
25
- * @returns {Object} GitHub Actions workflow YAML
26
- */
27
- static convertToWorkflow(pipeline) {
28
- const workflow = {
29
- name: pipeline.name,
30
- on: this._convertTriggers(pipeline.triggers),
31
- env: pipeline.env || {},
32
- jobs: {}
33
- };
34
-
35
- // Convert each job
36
- for (const job of pipeline.jobs) {
37
- const ghJob = {
38
- 'runs-on': job.runsOn || 'ubuntu-latest',
39
- steps: this._convertJobSteps(job)
40
- };
41
-
42
- // Add dependencies
43
- if (job.dependsOn) {
44
- ghJob.needs = Array.isArray(job.dependsOn) ? job.dependsOn : [job.dependsOn];
45
- }
46
-
47
- // Add conditionals
48
- if (job.when) {
49
- ghJob.if = this._convertCondition(job.when);
50
- }
51
-
52
- // Add timeout
53
- if (job.timeout) {
54
- ghJob['timeout-minutes'] = Math.ceil(job.timeout / 60000);
55
- }
56
-
57
- workflow.jobs[job.id] = ghJob;
58
- }
59
-
60
- return workflow;
61
- }
62
-
63
- /**
64
- * Internal: Convert pipeline triggers to GitHub Actions triggers
65
- *
66
- * @private
67
- */
68
- static _convertTriggers(triggers) {
69
- if (!triggers) {
70
- return { workflow_dispatch: {} }; // Manual trigger only
71
- }
72
-
73
- const ghTriggers = {};
74
-
75
- for (const trigger of triggers) {
76
- if (trigger.type === 'push') {
77
- ghTriggers.push = {
78
- branches: trigger.branches || ['main']
79
- };
80
- } else if (trigger.type === 'pull_request') {
81
- ghTriggers.pull_request = {
82
- branches: trigger.branches || ['main']
83
- };
84
- } else if (trigger.type === 'schedule') {
85
- ghTriggers.schedule = [{ cron: trigger.cron }];
86
- } else if (trigger.type === 'manual') {
87
- ghTriggers.workflow_dispatch = {};
88
- }
89
- }
90
-
91
- return ghTriggers;
92
- }
93
-
94
- /**
95
- * Internal: Convert job steps
96
- *
97
- * @private
98
- */
99
- static _convertJobSteps(job) {
100
- const steps = [
101
- {
102
- name: 'Checkout code',
103
- uses: 'actions/checkout@v4'
104
- }
105
- ];
106
-
107
- // Add setup steps
108
- if (job.setup) {
109
- for (const setupStep of job.setup) {
110
- steps.push({
111
- name: setupStep.name,
112
- run: setupStep.command
113
- });
114
- }
115
- }
116
-
117
- // Add main agent execution step
118
- steps.push({
119
- name: `Execute ${job.name}`,
120
- run: this._buildAgentExecutionCommand(job),
121
- env: {
122
- AGENTFUL_AGENT: job.agent,
123
- AGENTFUL_CONTEXT: '${{ toJson(github) }}'
124
- }
125
- });
126
-
127
- return steps;
128
- }
129
-
130
- /**
131
- * Internal: Build agent execution command
132
- *
133
- * @private
134
- */
135
- static _buildAgentExecutionCommand(job) {
136
- return `
137
- npx agentful pipeline run \\
138
- --job ${job.id} \\
139
- --context-file context.json
140
- `.trim();
141
- }
142
-
143
- /**
144
- * Internal: Convert condition to GitHub Actions format
145
- *
146
- * @private
147
- */
148
- static _convertCondition(condition) {
149
- // Simple mapping - would need more sophisticated conversion
150
- return condition
151
- .replace(/status\s*==\s*'completed'/, "success()")
152
- .replace(/status\s*==\s*'failed'/, "failure()");
153
- }
154
-
155
- /**
156
- * Write GitHub Actions workflow file
157
- *
158
- * @param {Object} pipeline - Agentful pipeline definition
159
- * @param {string} outputPath - Path to write workflow file
160
- */
161
- static async writeWorkflowFile(pipeline, outputPath) {
162
- const workflow = this.convertToWorkflow(pipeline);
163
- const yamlContent = yaml.dump(workflow, { lineWidth: 120 });
164
-
165
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
166
- await fs.writeFile(outputPath, yamlContent);
167
-
168
- return outputPath;
169
- }
170
- }
171
-
172
- /**
173
- * GitLab CI Integration
174
- *
175
- * Converts agentful pipelines to GitLab CI configuration
176
- */
177
- export class GitLabCIAdapter {
178
- /**
179
- * Convert pipeline to GitLab CI configuration
180
- *
181
- * @param {Object} pipeline - Agentful pipeline definition
182
- * @returns {Object} GitLab CI YAML
183
- */
184
- static convertToConfig(pipeline) {
185
- const config = {
186
- stages: this._extractStages(pipeline.jobs),
187
- variables: pipeline.env || {}
188
- };
189
-
190
- // Convert each job
191
- for (const job of pipeline.jobs) {
192
- const glJob = {
193
- stage: job.stage || 'test',
194
- script: this._convertJobScript(job)
195
- };
196
-
197
- // Add dependencies
198
- if (job.dependsOn) {
199
- glJob.needs = Array.isArray(job.dependsOn)
200
- ? job.dependsOn.map(dep => ({ job: dep }))
201
- : [{ job: job.dependsOn }];
202
- }
203
-
204
- // Add rules (conditionals)
205
- if (job.when) {
206
- glJob.rules = [{ if: this._convertCondition(job.when) }];
207
- }
208
-
209
- // Add timeout
210
- if (job.timeout) {
211
- glJob.timeout = `${Math.ceil(job.timeout / 60)}m`;
212
- }
213
-
214
- // Add retry policy
215
- if (job.retry) {
216
- glJob.retry = {
217
- max: job.retry.maxAttempts || 2,
218
- when: ['runner_system_failure', 'stuck_or_timeout_failure']
219
- };
220
- }
221
-
222
- config[job.id] = glJob;
223
- }
224
-
225
- return config;
226
- }
227
-
228
- /**
229
- * Internal: Extract stages from jobs
230
- *
231
- * @private
232
- */
233
- static _extractStages(jobs) {
234
- const stages = new Set(['build', 'test', 'deploy']);
235
-
236
- for (const job of jobs) {
237
- if (job.stage) {
238
- stages.add(job.stage);
239
- }
240
- }
241
-
242
- return Array.from(stages);
243
- }
244
-
245
- /**
246
- * Internal: Convert job script
247
- *
248
- * @private
249
- */
250
- static _convertJobScript(job) {
251
- const script = [];
252
-
253
- // Setup commands
254
- if (job.setup) {
255
- for (const setupStep of job.setup) {
256
- script.push(setupStep.command);
257
- }
258
- }
259
-
260
- // Main agent execution
261
- script.push(
262
- `npx agentful pipeline run --job ${job.id} --agent ${job.agent}`
263
- );
264
-
265
- return script;
266
- }
267
-
268
- /**
269
- * Internal: Convert condition to GitLab CI format
270
- *
271
- * @private
272
- */
273
- static _convertCondition(condition) {
274
- // GitLab CI uses different syntax
275
- return condition
276
- .replace(/status\s*==\s*'completed'/, '$CI_JOB_STATUS == "success"')
277
- .replace(/status\s*==\s*'failed'/, '$CI_JOB_STATUS == "failed"');
278
- }
279
-
280
- /**
281
- * Write GitLab CI configuration file
282
- *
283
- * @param {Object} pipeline - Agentful pipeline definition
284
- * @param {string} outputPath - Path to write config file
285
- */
286
- static async writeConfigFile(pipeline, outputPath) {
287
- const config = this.convertToConfig(pipeline);
288
- const yamlContent = yaml.dump(config, { lineWidth: 120 });
289
-
290
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
291
- await fs.writeFile(outputPath, yamlContent);
292
-
293
- return outputPath;
294
- }
295
- }
296
-
297
- /**
298
- * Jenkins Integration
299
- *
300
- * Converts agentful pipelines to Jenkins pipeline syntax
301
- */
302
- export class JenkinsAdapter {
303
- /**
304
- * Convert pipeline to Jenkinsfile
305
- *
306
- * @param {Object} pipeline - Agentful pipeline definition
307
- * @returns {string} Jenkinsfile content
308
- */
309
- static convertToJenkinsfile(pipeline) {
310
- const stages = this._convertJobsToStages(pipeline.jobs);
311
-
312
- return `
313
- pipeline {
314
- agent any
315
-
316
- environment {
317
- ${this._formatEnvironment(pipeline.env || {})}
318
- }
319
-
320
- stages {
321
- ${stages}
322
- }
323
-
324
- post {
325
- always {
326
- echo 'Pipeline completed'
327
- }
328
- success {
329
- echo 'Pipeline succeeded'
330
- }
331
- failure {
332
- echo 'Pipeline failed'
333
- }
334
- }
335
- }
336
- `.trim();
337
- }
338
-
339
- /**
340
- * Internal: Convert jobs to Jenkins stages
341
- *
342
- * @private
343
- */
344
- static _convertJobsToStages(jobs) {
345
- const stages = [];
346
-
347
- for (const job of jobs) {
348
- const stage = `
349
- stage('${job.name || job.id}') {
350
- steps {
351
- ${this._formatSteps(job)}
352
- }
353
- }`;
354
- stages.push(stage);
355
- }
356
-
357
- return stages.join('\n');
358
- }
359
-
360
- /**
361
- * Internal: Format environment variables
362
- *
363
- * @private
364
- */
365
- static _formatEnvironment(env) {
366
- return Object.entries(env)
367
- .map(([key, value]) => ` ${key} = '${value}'`)
368
- .join('\n');
369
- }
370
-
371
- /**
372
- * Internal: Format job steps
373
- *
374
- * @private
375
- */
376
- static _formatSteps(job) {
377
- const steps = [];
378
-
379
- // Setup steps
380
- if (job.setup) {
381
- for (const setupStep of job.setup) {
382
- steps.push(` sh '${setupStep.command}'`);
383
- }
384
- }
385
-
386
- // Main agent execution
387
- steps.push(` sh 'npx agentful pipeline run --job ${job.id} --agent ${job.agent}'`);
388
-
389
- return steps.join('\n');
390
- }
391
-
392
- /**
393
- * Write Jenkinsfile
394
- *
395
- * @param {Object} pipeline - Agentful pipeline definition
396
- * @param {string} outputPath - Path to write Jenkinsfile
397
- */
398
- static async writeJenkinsfile(pipeline, outputPath) {
399
- const content = this.convertToJenkinsfile(pipeline);
400
-
401
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
402
- await fs.writeFile(outputPath, content);
403
-
404
- return outputPath;
405
- }
406
- }
407
-
408
- /**
409
- * Webhook Handler
410
- *
411
- * Handles webhook triggers for pipelines
412
- */
413
- export class WebhookHandler {
414
- constructor(pipelineEngine) {
415
- this.pipelineEngine = pipelineEngine;
416
- this.webhooks = new Map(); // webhookId -> WebhookConfig
417
- }
418
-
419
- /**
420
- * Register webhook trigger
421
- *
422
- * @param {Object} config - Webhook configuration
423
- * @returns {string} Webhook ID
424
- */
425
- registerWebhook(config) {
426
- const webhookId = this._generateWebhookId();
427
-
428
- this.webhooks.set(webhookId, {
429
- id: webhookId,
430
- pipeline: config.pipeline,
431
- secret: config.secret,
432
- filters: config.filters || {},
433
- transform: config.transform || null,
434
- createdAt: new Date().toISOString()
435
- });
436
-
437
- return webhookId;
438
- }
439
-
440
- /**
441
- * Handle webhook request
442
- *
443
- * @param {string} webhookId - Webhook ID
444
- * @param {Object} payload - Webhook payload
445
- * @param {Object} headers - Request headers
446
- * @returns {Promise<Object>} Result
447
- */
448
- async handleWebhook(webhookId, payload, headers = {}) {
449
- const webhook = this.webhooks.get(webhookId);
450
- if (!webhook) {
451
- throw new Error(`Webhook not found: ${webhookId}`);
452
- }
453
-
454
- // Verify signature if secret is set
455
- if (webhook.secret) {
456
- this._verifySignature(webhook.secret, payload, headers);
457
- }
458
-
459
- // Apply filters
460
- if (!this._matchesFilters(payload, webhook.filters)) {
461
- return {
462
- success: true,
463
- message: 'Webhook ignored due to filters',
464
- triggered: false
465
- };
466
- }
467
-
468
- // Transform payload to context
469
- const context = webhook.transform
470
- ? webhook.transform(payload)
471
- : { webhook: payload };
472
-
473
- // Trigger pipeline
474
- const runId = await this.pipelineEngine.startPipeline(webhook.pipeline, {
475
- ...context,
476
- triggeredBy: 'webhook',
477
- webhookId
478
- });
479
-
480
- return {
481
- success: true,
482
- message: 'Pipeline triggered',
483
- triggered: true,
484
- runId
485
- };
486
- }
487
-
488
- /**
489
- * Internal: Verify webhook signature
490
- *
491
- * @private
492
- */
493
- _verifySignature(secret, payload, headers) {
494
- // Implementation depends on the webhook provider
495
- // GitHub, GitLab, etc. have different signature schemes
496
-
497
- // For GitHub:
498
- // const signature = headers['x-hub-signature-256'];
499
- // const expectedSignature = crypto
500
- // .createHmac('sha256', secret)
501
- // .update(JSON.stringify(payload))
502
- // .digest('hex');
503
-
504
- // For now, just check if signature header exists
505
- const signature = headers['x-webhook-signature'] || headers['x-hub-signature-256'];
506
- if (!signature) {
507
- throw new Error('Missing webhook signature');
508
- }
509
- }
510
-
511
- /**
512
- * Internal: Check if payload matches filters
513
- *
514
- * @private
515
- */
516
- _matchesFilters(payload, filters) {
517
- for (const [key, value] of Object.entries(filters)) {
518
- const payloadValue = this._getNestedValue(payload, key);
519
-
520
- if (Array.isArray(value)) {
521
- if (!value.includes(payloadValue)) return false;
522
- } else if (payloadValue !== value) {
523
- return false;
524
- }
525
- }
526
-
527
- return true;
528
- }
529
-
530
- /**
531
- * Internal: Get nested value from object
532
- *
533
- * @private
534
- */
535
- _getNestedValue(obj, path) {
536
- return path.split('.').reduce((current, prop) => {
537
- return current && current[prop] !== undefined ? current[prop] : undefined;
538
- }, obj);
539
- }
540
-
541
- /**
542
- * Internal: Generate webhook ID
543
- *
544
- * @private
545
- */
546
- _generateWebhookId() {
547
- return `webhook-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
548
- }
549
- }
550
-
551
- /**
552
- * Export all adapters
553
- */
554
- export default {
555
- GitHubActionsAdapter,
556
- GitLabCIAdapter,
557
- JenkinsAdapter,
558
- WebhookHandler
559
- };