@itz4blitz/agentful 0.3.0 → 0.5.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.
Files changed (94) hide show
  1. package/README.md +139 -10
  2. package/bin/cli.js +1032 -48
  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 +497 -25
  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 +92 -8
  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 +111 -195
  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/{.claude → template/.claude}/skills/product-tracking/SKILL.md +10 -21
  71. package/template/.claude/skills/testing/SKILL.md +228 -0
  72. package/template/.claude/skills/validation/SKILL.md +650 -0
  73. package/template/CLAUDE.md +84 -16
  74. package/template/bin/hooks/block-random-docs.js +121 -0
  75. package/version.json +1 -1
  76. package/.claude/agents/architect.md +0 -524
  77. package/.claude/agents/backend.md +0 -315
  78. package/.claude/agents/fixer.md +0 -263
  79. package/.claude/agents/frontend.md +0 -274
  80. package/.claude/agents/orchestrator.md +0 -283
  81. package/.claude/agents/product-analyzer.md +0 -799
  82. package/.claude/agents/reviewer.md +0 -332
  83. package/.claude/agents/tester.md +0 -410
  84. package/.claude/commands/agentful-decide.md +0 -214
  85. package/.claude/commands/agentful-start.md +0 -182
  86. package/.claude/commands/agentful-validate.md +0 -127
  87. package/.claude/product/EXAMPLES.md +0 -610
  88. package/.claude/product/README.md +0 -344
  89. package/.claude/skills/validation/SKILL.md +0 -271
  90. package/bin/hooks/analyze-trigger.sh +0 -57
  91. package/bin/hooks/health-check.sh +0 -36
  92. package/template/PRODUCT.md +0 -584
  93. /package/{.claude → template/.claude}/commands/agentful-generate.md +0 -0
  94. /package/{.claude → template/.claude}/product/index.md +0 -0
@@ -0,0 +1,460 @@
1
+ import fs from 'fs';
2
+ import Ajv from 'ajv';
3
+ import addFormats from 'ajv-formats';
4
+
5
+ /**
6
+ * Centralized Validation Library
7
+ *
8
+ * Provides unified validation functions for state files with:
9
+ * - File existence checks
10
+ * - JSON parsing validation
11
+ * - Schema validation using AJV
12
+ * - Required field validation
13
+ *
14
+ * Standardized action codes:
15
+ * - "missing": File does not exist
16
+ * - "corrupted": File exists but is not valid JSON
17
+ * - "invalid": JSON is valid but doesn't match schema
18
+ * - "incomplete": Schema is valid but required fields are missing
19
+ */
20
+
21
+ // Initialize AJV with formats support
22
+ const ajv = new Ajv({ allErrors: true });
23
+ addFormats(ajv);
24
+
25
+ /**
26
+ * JSON Schemas for state files
27
+ */
28
+ export const schemas = {
29
+ state: {
30
+ type: 'object',
31
+ required: ['initialized', 'version', 'agents', 'skills'],
32
+ properties: {
33
+ initialized: {
34
+ type: 'string',
35
+ format: 'date-time'
36
+ },
37
+ version: {
38
+ type: 'string',
39
+ pattern: '^\\d+\\.\\d+\\.\\d+$'
40
+ },
41
+ agents: {
42
+ type: 'array',
43
+ items: {
44
+ type: 'string'
45
+ }
46
+ },
47
+ skills: {
48
+ type: 'array',
49
+ items: {
50
+ type: 'string'
51
+ }
52
+ }
53
+ },
54
+ additionalProperties: true
55
+ },
56
+
57
+ completion: {
58
+ type: 'object',
59
+ required: ['agents', 'skills', 'lastUpdated'],
60
+ properties: {
61
+ agents: {
62
+ type: 'object',
63
+ additionalProperties: {
64
+ type: 'object',
65
+ properties: {
66
+ completed: { type: 'boolean' },
67
+ progress: { type: 'number', minimum: 0, maximum: 100 }
68
+ }
69
+ }
70
+ },
71
+ skills: {
72
+ type: 'object',
73
+ additionalProperties: {
74
+ type: 'object',
75
+ properties: {
76
+ completed: { type: 'boolean' },
77
+ progress: { type: 'number', minimum: 0, maximum: 100 }
78
+ }
79
+ }
80
+ },
81
+ lastUpdated: {
82
+ type: 'string',
83
+ format: 'date-time'
84
+ }
85
+ },
86
+ additionalProperties: true
87
+ },
88
+
89
+ decisions: {
90
+ type: 'object',
91
+ required: ['decisions', 'lastUpdated'],
92
+ properties: {
93
+ decisions: {
94
+ type: 'array',
95
+ items: {
96
+ type: 'object',
97
+ required: ['id', 'question', 'status'],
98
+ properties: {
99
+ id: { type: 'string' },
100
+ question: { type: 'string' },
101
+ status: {
102
+ type: 'string',
103
+ enum: ['pending', 'answered', 'cancelled']
104
+ },
105
+ answer: { type: 'string' },
106
+ context: { type: 'string' },
107
+ created: { type: 'string', format: 'date-time' },
108
+ updated: { type: 'string', format: 'date-time' }
109
+ }
110
+ }
111
+ },
112
+ lastUpdated: {
113
+ type: 'string',
114
+ format: 'date-time'
115
+ }
116
+ },
117
+ additionalProperties: false
118
+ },
119
+
120
+ architecture: {
121
+ type: 'object',
122
+ required: ['version', 'analyzedAt', 'projectRoot', 'fileCount', 'confidence', 'languages', 'frameworks', 'patterns', 'conventions', 'recommendations'],
123
+ properties: {
124
+ version: { type: 'string' },
125
+ analyzedAt: {
126
+ type: 'string',
127
+ format: 'date-time'
128
+ },
129
+ duration: { type: 'number' },
130
+ projectRoot: { type: 'string' },
131
+ fileCount: { type: 'number', minimum: 0 },
132
+ isNewProject: { type: 'boolean' },
133
+ confidence: { type: 'number', minimum: 0, maximum: 100 },
134
+ languages: {
135
+ type: 'array',
136
+ items: {
137
+ type: 'object',
138
+ required: ['name', 'confidence', 'files'],
139
+ properties: {
140
+ name: { type: 'string' },
141
+ confidence: { type: 'number' },
142
+ files: { type: 'number' },
143
+ percentage: { type: 'number' },
144
+ extensions: { type: 'array', items: { type: 'string' } }
145
+ }
146
+ }
147
+ },
148
+ primaryLanguage: { type: ['string', 'null'] },
149
+ frameworks: {
150
+ type: 'array',
151
+ items: {
152
+ type: 'object',
153
+ required: ['name', 'confidence'],
154
+ properties: {
155
+ name: { type: 'string' },
156
+ version: { type: 'string' },
157
+ type: { type: 'string' },
158
+ category: { type: 'string' },
159
+ confidence: { type: 'number' },
160
+ source: { type: 'string' }
161
+ }
162
+ }
163
+ },
164
+ patterns: {
165
+ type: 'object',
166
+ properties: {
167
+ components: { type: 'object' },
168
+ api: { type: 'object' },
169
+ database: { type: 'object' },
170
+ tests: { type: 'object' },
171
+ auth: { type: 'object' }
172
+ }
173
+ },
174
+ conventions: {
175
+ type: 'object'
176
+ },
177
+ recommendations: {
178
+ type: 'array',
179
+ items: {
180
+ type: 'object',
181
+ required: ['type', 'priority', 'message', 'action'],
182
+ properties: {
183
+ type: { type: 'string' },
184
+ priority: { type: 'string' },
185
+ message: { type: 'string' },
186
+ action: { type: 'string' }
187
+ }
188
+ }
189
+ }
190
+ },
191
+ additionalProperties: true
192
+ },
193
+
194
+ product: {
195
+ type: 'object',
196
+ required: ['name', 'description', 'features', 'lastUpdated'],
197
+ properties: {
198
+ name: { type: 'string' },
199
+ description: { type: 'string' },
200
+ features: {
201
+ type: 'array',
202
+ items: {
203
+ type: 'object',
204
+ required: ['id', 'name'],
205
+ properties: {
206
+ id: { type: 'string' },
207
+ name: { type: 'string' },
208
+ description: { type: 'string' },
209
+ priority: {
210
+ type: 'string',
211
+ enum: ['high', 'medium', 'low']
212
+ }
213
+ }
214
+ }
215
+ },
216
+ lastUpdated: {
217
+ type: 'string',
218
+ format: 'date-time'
219
+ }
220
+ },
221
+ additionalProperties: true
222
+ }
223
+ };
224
+
225
+ // Compile all schemas
226
+ const compiledSchemas = {
227
+ state: ajv.compile(schemas.state),
228
+ completion: ajv.compile(schemas.completion),
229
+ decisions: ajv.compile(schemas.decisions),
230
+ architecture: ajv.compile(schemas.architecture),
231
+ product: ajv.compile(schemas.product)
232
+ };
233
+
234
+ /**
235
+ * Unified validation function for state files
236
+ *
237
+ * @param {string} filePath - Absolute path to the file to validate
238
+ * @param {string} schemaName - Name of schema to use ('state', 'completion', 'decisions', 'architecture', 'product')
239
+ * @param {string[]} requiredFields - Additional required fields to check beyond schema
240
+ * @returns {Object} Validation result with standardized structure
241
+ *
242
+ * Return format for invalid:
243
+ * { valid: false, action: "missing" | "corrupted" | "invalid" | "incomplete", errors?: array, missing_field?: string }
244
+ *
245
+ * Return format for valid:
246
+ * { valid: true, content: object }
247
+ */
248
+ export function validateStateFile(filePath, schemaName, requiredFields = []) {
249
+ // 1. Check file exists
250
+ if (!fs.existsSync(filePath)) {
251
+ return {
252
+ valid: false,
253
+ action: 'missing',
254
+ error: `File not found: ${filePath}`
255
+ };
256
+ }
257
+
258
+ // 2. Parse JSON
259
+ let content;
260
+ try {
261
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
262
+ content = JSON.parse(fileContent);
263
+ } catch (error) {
264
+ return {
265
+ valid: false,
266
+ action: 'corrupted',
267
+ error: `Invalid JSON in ${filePath}: ${error.message}`
268
+ };
269
+ }
270
+
271
+ // 3. Validate against schema using AJV
272
+ const validator = compiledSchemas[schemaName];
273
+ if (!validator) {
274
+ throw new Error(`Unknown schema name: ${schemaName}. Valid schemas: ${Object.keys(compiledSchemas).join(', ')}`);
275
+ }
276
+
277
+ const schemaValid = validator(content);
278
+ if (!schemaValid) {
279
+ return {
280
+ valid: false,
281
+ action: 'invalid',
282
+ errors: validator.errors,
283
+ error: `Schema validation failed for ${filePath}`
284
+ };
285
+ }
286
+
287
+ // 4. Check additional required fields
288
+ for (const field of requiredFields) {
289
+ if (!(field in content)) {
290
+ return {
291
+ valid: false,
292
+ action: 'incomplete',
293
+ missing_field: field,
294
+ error: `Missing required field '${field}' in ${filePath}`
295
+ };
296
+ }
297
+ }
298
+
299
+ // 5. Success
300
+ return {
301
+ valid: true,
302
+ content
303
+ };
304
+ }
305
+
306
+ /**
307
+ * Validate state.json file
308
+ *
309
+ * @param {string} filePath - Path to state.json (defaults to .agentful/state.json)
310
+ * @param {string[]} additionalFields - Additional required fields beyond schema
311
+ * @returns {Object} Validation result
312
+ */
313
+ export function validateState(filePath = '.agentful/state.json', additionalFields = []) {
314
+ return validateStateFile(filePath, 'state', additionalFields);
315
+ }
316
+
317
+ /**
318
+ * Validate completion.json file
319
+ *
320
+ * @param {string} filePath - Path to completion.json (defaults to .agentful/completion.json)
321
+ * @param {string[]} additionalFields - Additional required fields beyond schema
322
+ * @returns {Object} Validation result
323
+ */
324
+ export function validateCompletion(filePath = '.agentful/completion.json', additionalFields = []) {
325
+ return validateStateFile(filePath, 'completion', additionalFields);
326
+ }
327
+
328
+ /**
329
+ * Validate decisions.json file
330
+ *
331
+ * @param {string} filePath - Path to decisions.json (defaults to .agentful/decisions.json)
332
+ * @param {string[]} additionalFields - Additional required fields beyond schema
333
+ * @returns {Object} Validation result
334
+ */
335
+ export function validateDecisions(filePath = '.agentful/decisions.json', additionalFields = []) {
336
+ return validateStateFile(filePath, 'decisions', additionalFields);
337
+ }
338
+
339
+ /**
340
+ * Validate architecture.json file
341
+ *
342
+ * @param {string} filePath - Path to architecture.json (defaults to .agentful/architecture.json)
343
+ * @param {string[]} additionalFields - Additional required fields beyond schema
344
+ * @returns {Object} Validation result
345
+ */
346
+ export function validateArchitecture(filePath = '.agentful/architecture.json', additionalFields = []) {
347
+ return validateStateFile(filePath, 'architecture', additionalFields);
348
+ }
349
+
350
+ /**
351
+ * Validate product.json file
352
+ *
353
+ * @param {string} filePath - Path to product.json (defaults to .agentful/product.json)
354
+ * @param {string[]} additionalFields - Additional required fields beyond schema
355
+ * @returns {Object} Validation result
356
+ */
357
+ export function validateProduct(filePath = '.agentful/product.json', additionalFields = []) {
358
+ return validateStateFile(filePath, 'product', additionalFields);
359
+ }
360
+
361
+ /**
362
+ * Batch validate multiple state files
363
+ *
364
+ * @param {Object[]} files - Array of {filePath, schemaName, requiredFields?}
365
+ * @returns {Object} Map of filePath to validation result
366
+ *
367
+ * Example:
368
+ * const results = validateBatch([
369
+ * { filePath: '.agentful/state.json', schemaName: 'state' },
370
+ * { filePath: '.agentful/completion.json', schemaName: 'completion', requiredFields: ['gates'] }
371
+ * ]);
372
+ */
373
+ export function validateBatch(files) {
374
+ const results = {};
375
+
376
+ for (const { filePath, schemaName, requiredFields = [] } of files) {
377
+ results[filePath] = validateStateFile(filePath, schemaName, requiredFields);
378
+ }
379
+
380
+ return results;
381
+ }
382
+
383
+ /**
384
+ * Get human-readable error message for validation result
385
+ *
386
+ * @param {Object} validationResult - Result from validateStateFile
387
+ * @returns {string} Human-readable error message
388
+ */
389
+ export function getErrorMessage(validationResult) {
390
+ if (validationResult.valid) {
391
+ return 'Validation passed';
392
+ }
393
+
394
+ switch (validationResult.action) {
395
+ case 'missing':
396
+ return `File not found. Initialize it first.`;
397
+
398
+ case 'corrupted':
399
+ return `File is corrupted (invalid JSON). Backup and reset recommended.`;
400
+
401
+ case 'invalid':
402
+ if (validationResult.errors && validationResult.errors.length > 0) {
403
+ const errorDetails = validationResult.errors
404
+ .map(err => ` - ${err.instancePath || 'root'}: ${err.message}`)
405
+ .join('\n');
406
+ return `Schema validation failed:\n${errorDetails}`;
407
+ }
408
+ return `Schema validation failed.`;
409
+
410
+ case 'incomplete':
411
+ return `Missing required field: '${validationResult.missing_field}'`;
412
+
413
+ default:
414
+ return validationResult.error || 'Unknown validation error';
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Suggested action for validation result
420
+ *
421
+ * @param {Object} validationResult - Result from validateStateFile
422
+ * @returns {string} Suggested action to fix the issue
423
+ */
424
+ export function getSuggestedAction(validationResult) {
425
+ if (validationResult.valid) {
426
+ return 'No action needed';
427
+ }
428
+
429
+ switch (validationResult.action) {
430
+ case 'missing':
431
+ return 'Run initialization command to create the file';
432
+
433
+ case 'corrupted':
434
+ return 'Backup the file and create a new one with valid JSON';
435
+
436
+ case 'invalid':
437
+ return 'Fix schema validation errors in the file';
438
+
439
+ case 'incomplete':
440
+ return `Add the missing field: '${validationResult.missing_field}'`;
441
+
442
+ default:
443
+ return 'Check the file and fix any issues';
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Export AJV instance for custom schemas
449
+ */
450
+ export { ajv };
451
+
452
+ /**
453
+ * Export action constants for easy reference
454
+ */
455
+ export const ValidationActions = {
456
+ MISSING: 'missing',
457
+ CORRUPTED: 'corrupted',
458
+ INVALID: 'invalid',
459
+ INCOMPLETE: 'incomplete'
460
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itz4blitz/agentful",
3
- "version": "0.3.0",
3
+ "version": "0.5.1",
4
4
  "description": "Human-in-the-loop development kit for Claude Code with smart product analysis and natural conversation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,12 @@
8
8
  },
9
9
  "scripts": {
10
10
  "init": "node bin/cli.js init",
11
+ "test": "vitest run",
12
+ "test:unit": "vitest run test/unit",
13
+ "test:validation": "vitest run test/validation",
14
+ "test:watch": "vitest",
15
+ "test:coverage": "vitest run --coverage",
16
+ "lint": "eslint .",
11
17
  "release": "semantic-release",
12
18
  "release:dry-run": "semantic-release --dry-run",
13
19
  "docs:dev": "vocs dev",
@@ -23,7 +29,6 @@
23
29
  "bin/",
24
30
  "lib/",
25
31
  "template/",
26
- ".claude/",
27
32
  "version.json",
28
33
  "LICENSE"
29
34
  ],
@@ -49,6 +54,7 @@
49
54
  "node": ">=22.0.0"
50
55
  },
51
56
  "devDependencies": {
57
+ "@eslint/js": "^9.39.2",
52
58
  "@semantic-release/changelog": "^6.0.3",
53
59
  "@semantic-release/commit-analyzer": "^13.0.0",
54
60
  "@semantic-release/exec": "^6.0.3",
@@ -56,7 +62,18 @@
56
62
  "@semantic-release/github": "^11.0.1",
57
63
  "@semantic-release/npm": "^13.1.3",
58
64
  "@semantic-release/release-notes-generator": "^14.0.1",
65
+ "@vitest/coverage-v8": "^1.2.0",
66
+ "ajv": "^8.12.0",
67
+ "ajv-formats": "^3.0.1",
68
+ "eslint": "^9.39.2",
69
+ "qrcode.react": "^4.2.0",
59
70
  "semantic-release": "^25.0.2",
71
+ "vitest": "^1.2.0",
60
72
  "vocs": "^1.4.1"
73
+ },
74
+ "dependencies": {
75
+ "@xyflow/react": "^12.10.0",
76
+ "handlebars": "^4.7.8",
77
+ "js-yaml": "^4.1.0"
61
78
  }
62
79
  }