@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,276 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Framework Detector
6
+ *
7
+ * Detects frameworks and libraries used in the project based on:
8
+ * - package.json dependencies
9
+ * - Import statements
10
+ * - Configuration files
11
+ * - File structure patterns
12
+ */
13
+
14
+ /**
15
+ * Framework detection rules
16
+ * Maps package names to frameworks
17
+ */
18
+ const FRAMEWORK_PACKAGES = {
19
+ // JavaScript/TypeScript frameworks
20
+ 'next': { name: 'Next.js', type: 'framework', category: 'web' },
21
+ 'react': { name: 'React', type: 'library', category: 'ui' },
22
+ 'vue': { name: 'Vue', type: 'framework', category: 'web' },
23
+ 'nuxt': { name: 'Nuxt', type: 'framework', category: 'web' },
24
+ 'angular': { name: 'Angular', type: 'framework', category: 'web' },
25
+ 'svelte': { name: 'Svelte', type: 'framework', category: 'ui' },
26
+ 'express': { name: 'Express', type: 'framework', category: 'backend' },
27
+ 'fastify': { name: 'Fastify', type: 'framework', category: 'backend' },
28
+ 'nestjs': { name: 'NestJS', type: 'framework', category: 'backend' },
29
+ 'koa': { name: 'Koa', type: 'framework', category: 'backend' },
30
+ 'hapi': { name: 'Hapi', type: 'framework', category: 'backend' },
31
+
32
+ // Testing frameworks
33
+ 'vitest': { name: 'Vitest', type: 'framework', category: 'testing' },
34
+ 'jest': { name: 'Jest', type: 'framework', category: 'testing' },
35
+ 'mocha': { name: 'Mocha', type: 'framework', category: 'testing' },
36
+ 'jasmine': { name: 'Jasmine', type: 'framework', category: 'testing' },
37
+ 'cypress': { name: 'Cypress', type: 'framework', category: 'testing' },
38
+ 'playwright': { name: 'Playwright', type: 'framework', category: 'testing' },
39
+
40
+ // Build tools
41
+ 'vite': { name: 'Vite', type: 'tool', category: 'build' },
42
+ 'webpack': { name: 'Webpack', type: 'tool', category: 'build' },
43
+ 'rollup': { name: 'Rollup', type: 'tool', category: 'build' },
44
+ 'esbuild': { name: 'esbuild', type: 'tool', category: 'build' },
45
+ 'parcel': { name: 'Parcel', type: 'tool', category: 'build' },
46
+
47
+ // Database/ORM
48
+ 'prisma': { name: 'Prisma', type: 'library', category: 'database' },
49
+ 'typeorm': { name: 'TypeORM', type: 'library', category: 'database' },
50
+ 'sequelize': { name: 'Sequelize', type: 'library', category: 'database' },
51
+ 'mongoose': { name: 'Mongoose', type: 'library', category: 'database' },
52
+ 'drizzle-orm': { name: 'Drizzle', type: 'library', category: 'database' },
53
+
54
+ // Styling
55
+ 'tailwindcss': { name: 'Tailwind CSS', type: 'library', category: 'styling' },
56
+ 'styled-components': { name: 'Styled Components', type: 'library', category: 'styling' },
57
+ '@emotion/react': { name: 'Emotion', type: 'library', category: 'styling' },
58
+ 'sass': { name: 'Sass', type: 'tool', category: 'styling' }
59
+ };
60
+
61
+ /**
62
+ * Configuration files that indicate specific frameworks
63
+ */
64
+ const CONFIG_FILES = {
65
+ 'next.config.js': 'Next.js',
66
+ 'next.config.mjs': 'Next.js',
67
+ 'next.config.ts': 'Next.js',
68
+ 'nuxt.config.js': 'Nuxt',
69
+ 'nuxt.config.ts': 'Nuxt',
70
+ 'vue.config.js': 'Vue',
71
+ 'angular.json': 'Angular',
72
+ 'svelte.config.js': 'Svelte',
73
+ 'vite.config.js': 'Vite',
74
+ 'vite.config.ts': 'Vite',
75
+ 'vitest.config.js': 'Vitest',
76
+ 'vitest.config.ts': 'Vitest',
77
+ 'jest.config.js': 'Jest',
78
+ 'jest.config.ts': 'Jest',
79
+ 'playwright.config.js': 'Playwright',
80
+ 'playwright.config.ts': 'Playwright',
81
+ 'tailwind.config.js': 'Tailwind CSS',
82
+ 'tailwind.config.ts': 'Tailwind CSS'
83
+ };
84
+
85
+ /**
86
+ * File structure patterns that indicate frameworks
87
+ */
88
+ const STRUCTURE_PATTERNS = {
89
+ 'Next.js': ['pages/', 'app/', 'public/'],
90
+ 'Nuxt': ['pages/', 'layouts/', 'nuxt.config'],
91
+ 'Angular': ['src/app/', 'angular.json'],
92
+ 'Vue': ['src/components/', 'src/views/'],
93
+ 'Express': ['routes/', 'middleware/'],
94
+ 'NestJS': ['src/modules/', 'src/controllers/']
95
+ };
96
+
97
+ /**
98
+ * Detect frameworks from package.json
99
+ *
100
+ * @param {string} projectRoot - Absolute path to project root
101
+ * @returns {Promise<Object[]>} Array of detected frameworks
102
+ */
103
+ export async function detectFromPackageJson(projectRoot) {
104
+ const packageJsonPath = path.join(projectRoot, 'package.json');
105
+ const frameworks = [];
106
+
107
+ try {
108
+ const content = await fs.readFile(packageJsonPath, 'utf-8');
109
+ const packageJson = JSON.parse(content);
110
+
111
+ const allDeps = {
112
+ ...packageJson.dependencies,
113
+ ...packageJson.devDependencies
114
+ };
115
+
116
+ for (const [pkg, version] of Object.entries(allDeps)) {
117
+ const framework = FRAMEWORK_PACKAGES[pkg];
118
+ if (framework) {
119
+ frameworks.push({
120
+ name: framework.name,
121
+ version: version.replace(/[\^~]/g, ''),
122
+ type: framework.type,
123
+ category: framework.category,
124
+ confidence: 95,
125
+ source: 'package.json'
126
+ });
127
+ }
128
+ }
129
+ } catch (error) {
130
+ // package.json not found or invalid - not an error, just no data
131
+ }
132
+
133
+ return frameworks;
134
+ }
135
+
136
+ /**
137
+ * Detect frameworks from config files
138
+ *
139
+ * @param {string[]} files - Array of file paths
140
+ * @returns {Object[]} Array of detected frameworks
141
+ */
142
+ export function detectFromConfigFiles(files) {
143
+ const frameworks = [];
144
+ const seen = new Set();
145
+
146
+ for (const file of files) {
147
+ const basename = path.basename(file);
148
+ const frameworkName = CONFIG_FILES[basename];
149
+
150
+ if (frameworkName && !seen.has(frameworkName)) {
151
+ seen.add(frameworkName);
152
+ frameworks.push({
153
+ name: frameworkName,
154
+ confidence: 90,
155
+ source: 'config file',
156
+ configFile: basename
157
+ });
158
+ }
159
+ }
160
+
161
+ return frameworks;
162
+ }
163
+
164
+ /**
165
+ * Detect frameworks from file structure
166
+ *
167
+ * @param {string[]} files - Array of file paths
168
+ * @returns {Object[]} Array of detected frameworks
169
+ */
170
+ export function detectFromStructure(files) {
171
+ const frameworks = [];
172
+
173
+ for (const [frameworkName, patterns] of Object.entries(STRUCTURE_PATTERNS)) {
174
+ let matchCount = 0;
175
+
176
+ for (const pattern of patterns) {
177
+ const hasPattern = files.some(file =>
178
+ file.startsWith(pattern) || file.includes(`/${pattern}`)
179
+ );
180
+ if (hasPattern) matchCount++;
181
+ }
182
+
183
+ if (matchCount > 0) {
184
+ const confidence = Math.min(85, 50 + (matchCount / patterns.length) * 35);
185
+ frameworks.push({
186
+ name: frameworkName,
187
+ confidence: Math.round(confidence),
188
+ source: 'file structure',
189
+ patternsMatched: matchCount,
190
+ patternsTotal: patterns.length
191
+ });
192
+ }
193
+ }
194
+
195
+ return frameworks;
196
+ }
197
+
198
+ /**
199
+ * Detect frameworks from import statements
200
+ *
201
+ * @param {string} content - File content to analyze
202
+ * @returns {string[]} Array of imported framework names
203
+ */
204
+ export function detectFromImports(content) {
205
+ const imports = new Set();
206
+ const importRegex = /(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g;
207
+
208
+ let match;
209
+ while ((match = importRegex.exec(content)) !== null) {
210
+ const importPath = match[1];
211
+ const packageName = importPath.startsWith('@')
212
+ ? importPath.split('/').slice(0, 2).join('/')
213
+ : importPath.split('/')[0];
214
+
215
+ if (FRAMEWORK_PACKAGES[packageName]) {
216
+ imports.add(FRAMEWORK_PACKAGES[packageName].name);
217
+ }
218
+ }
219
+
220
+ return Array.from(imports);
221
+ }
222
+
223
+ /**
224
+ * Merge and deduplicate framework detections
225
+ *
226
+ * @param {Object[][]} detectionResults - Multiple arrays of detected frameworks
227
+ * @returns {Object[]} Merged and deduplicated frameworks with highest confidence
228
+ */
229
+ export function mergeFrameworks(...detectionResults) {
230
+ const frameworkMap = new Map();
231
+
232
+ for (const results of detectionResults) {
233
+ for (const framework of results) {
234
+ const existing = frameworkMap.get(framework.name);
235
+
236
+ if (!existing || framework.confidence > existing.confidence) {
237
+ frameworkMap.set(framework.name, framework);
238
+ } else if (existing) {
239
+ // Merge sources
240
+ existing.sources = existing.sources || [existing.source];
241
+ if (!existing.sources.includes(framework.source)) {
242
+ existing.sources.push(framework.source);
243
+ }
244
+ // Boost confidence if detected from multiple sources
245
+ existing.confidence = Math.min(98, existing.confidence + 5);
246
+ }
247
+ }
248
+ }
249
+
250
+ return Array.from(frameworkMap.values())
251
+ .sort((a, b) => b.confidence - a.confidence);
252
+ }
253
+
254
+ /**
255
+ * Detect all frameworks in project
256
+ *
257
+ * @param {string[]} files - Array of file paths
258
+ * @param {string} projectRoot - Absolute path to project root
259
+ * @returns {Promise<Object[]>} Array of detected frameworks
260
+ */
261
+ export async function detectFrameworks(files, projectRoot) {
262
+ const fromPackage = await detectFromPackageJson(projectRoot);
263
+ const fromConfig = detectFromConfigFiles(files);
264
+ const fromStructure = detectFromStructure(files);
265
+
266
+ return mergeFrameworks(fromPackage, fromConfig, fromStructure);
267
+ }
268
+
269
+ export default {
270
+ detectFrameworks,
271
+ detectFromPackageJson,
272
+ detectFromConfigFiles,
273
+ detectFromStructure,
274
+ detectFromImports,
275
+ mergeFrameworks
276
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Detector Module Exports
3
+ *
4
+ * Centralized exports for all detector modules
5
+ */
6
+
7
+ export * from './language.js';
8
+ export * from './framework.js';
9
+ export * from './patterns.js';
10
+ export * from './conventions.js';
11
+
12
+ export { default as languageDetector } from './language.js';
13
+ export { default as frameworkDetector } from './framework.js';
14
+ export { default as patternDetector } from './patterns.js';
15
+ export { default as conventionDetector } from './conventions.js';
@@ -0,0 +1,199 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Language Detector
6
+ *
7
+ * Detects programming languages used in the project based on:
8
+ * - File extensions
9
+ * - File content analysis
10
+ * - Configuration files
11
+ */
12
+
13
+ /**
14
+ * Language detection rules
15
+ * Maps file extensions to languages
16
+ */
17
+ const LANGUAGE_EXTENSIONS = {
18
+ '.js': 'JavaScript',
19
+ '.mjs': 'JavaScript',
20
+ '.cjs': 'JavaScript',
21
+ '.jsx': 'JavaScript',
22
+ '.ts': 'TypeScript',
23
+ '.tsx': 'TypeScript',
24
+ '.py': 'Python',
25
+ '.java': 'Java',
26
+ '.go': 'Go',
27
+ '.rs': 'Rust',
28
+ '.rb': 'Ruby',
29
+ '.php': 'PHP',
30
+ '.cs': 'C#',
31
+ '.cpp': 'C++',
32
+ '.c': 'C',
33
+ '.h': 'C',
34
+ '.hpp': 'C++',
35
+ '.swift': 'Swift',
36
+ '.kt': 'Kotlin',
37
+ '.scala': 'Scala',
38
+ '.r': 'R',
39
+ '.jl': 'Julia',
40
+ '.lua': 'Lua',
41
+ '.sh': 'Shell',
42
+ '.bash': 'Shell',
43
+ '.zsh': 'Shell'
44
+ };
45
+
46
+ /**
47
+ * Language detection from config files
48
+ */
49
+ const CONFIG_FILES = {
50
+ 'tsconfig.json': 'TypeScript',
51
+ 'jsconfig.json': 'JavaScript',
52
+ 'package.json': 'JavaScript',
53
+ 'requirements.txt': 'Python',
54
+ 'setup.py': 'Python',
55
+ 'Pipfile': 'Python',
56
+ 'pyproject.toml': 'Python',
57
+ 'go.mod': 'Go',
58
+ 'Cargo.toml': 'Rust',
59
+ 'pom.xml': 'Java',
60
+ 'build.gradle': 'Java',
61
+ 'Gemfile': 'Ruby',
62
+ 'composer.json': 'PHP'
63
+ };
64
+
65
+ /**
66
+ * Detect languages from file list
67
+ *
68
+ * @param {string[]} files - Array of file paths relative to project root
69
+ * @param {string} projectRoot - Absolute path to project root
70
+ * @returns {Promise<Object[]>} Array of detected languages with confidence scores
71
+ */
72
+ export async function detectLanguages(files, projectRoot) {
73
+ const languageCounts = {};
74
+ const configEvidence = {};
75
+
76
+ // Count files by extension
77
+ for (const file of files) {
78
+ const ext = path.extname(file).toLowerCase();
79
+ const language = LANGUAGE_EXTENSIONS[ext];
80
+
81
+ if (language) {
82
+ if (!languageCounts[language]) {
83
+ languageCounts[language] = { files: 0, extensions: new Set() };
84
+ }
85
+ languageCounts[language].files++;
86
+ languageCounts[language].extensions.add(ext);
87
+ }
88
+
89
+ // Check for config files
90
+ const basename = path.basename(file);
91
+ const configLang = CONFIG_FILES[basename];
92
+ if (configLang) {
93
+ configEvidence[configLang] = (configEvidence[configLang] || 0) + 1;
94
+ }
95
+ }
96
+
97
+ // Calculate confidence scores
98
+ const totalFiles = files.length;
99
+ const languages = [];
100
+
101
+ for (const [language, data] of Object.entries(languageCounts)) {
102
+ const filePercentage = (data.files / totalFiles) * 100;
103
+ const configBonus = configEvidence[language] ? 10 : 0;
104
+ const extensionDiversity = data.extensions.size > 1 ? 5 : 0;
105
+
106
+ let confidence = Math.min(95, Math.round(filePercentage * 0.8 + configBonus + extensionDiversity));
107
+
108
+ // TypeScript bonus: if tsconfig.json exists
109
+ if (language === 'TypeScript' && configEvidence['TypeScript']) {
110
+ confidence = Math.min(95, confidence + 10);
111
+ }
112
+
113
+ languages.push({
114
+ name: language,
115
+ confidence,
116
+ files: data.files,
117
+ percentage: Math.round(filePercentage * 10) / 10,
118
+ extensions: Array.from(data.extensions)
119
+ });
120
+ }
121
+
122
+ // Sort by file count descending
123
+ languages.sort((a, b) => b.files - a.files);
124
+
125
+ return languages;
126
+ }
127
+
128
+ /**
129
+ * Detect primary language (highest confidence)
130
+ *
131
+ * @param {Object[]} languages - Array of detected languages
132
+ * @returns {Object|null} Primary language or null if none detected
133
+ */
134
+ export function getPrimaryLanguage(languages) {
135
+ if (languages.length === 0) return null;
136
+
137
+ // Return language with highest confidence
138
+ return languages.reduce((prev, current) =>
139
+ current.confidence > prev.confidence ? current : prev
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Analyze language-specific patterns in content
145
+ *
146
+ * @param {string} content - File content to analyze
147
+ * @param {string} language - Language to check for
148
+ * @returns {Object} Pattern detection result
149
+ */
150
+ export function analyzeLanguagePatterns(content, language) {
151
+ const patterns = {
152
+ TypeScript: {
153
+ features: [
154
+ { name: 'interfaces', regex: /interface\s+\w+/g },
155
+ { name: 'types', regex: /type\s+\w+\s*=/g },
156
+ { name: 'generics', regex: /<[A-Z]\w*>/g },
157
+ { name: 'decorators', regex: /@\w+/g }
158
+ ]
159
+ },
160
+ JavaScript: {
161
+ features: [
162
+ { name: 'async/await', regex: /async\s+function|await\s+/g },
163
+ { name: 'arrow functions', regex: /=>\s*{|=>\s*\w+/g },
164
+ { name: 'destructuring', regex: /const\s*{[^}]+}\s*=/g },
165
+ { name: 'template literals', regex: /`[^`]*\${[^}]*}[^`]*`/g }
166
+ ]
167
+ },
168
+ Python: {
169
+ features: [
170
+ { name: 'classes', regex: /class\s+\w+:/g },
171
+ { name: 'decorators', regex: /@\w+/g },
172
+ { name: 'list comprehensions', regex: /\[[^\]]+for\s+\w+\s+in\s+[^\]]+\]/g },
173
+ { name: 'type hints', regex: /:\s*\w+\s*(?:=|\))/g }
174
+ ]
175
+ }
176
+ };
177
+
178
+ const langPatterns = patterns[language];
179
+ if (!langPatterns) {
180
+ return { detected: false };
181
+ }
182
+
183
+ const detected = {};
184
+ for (const { name, regex } of langPatterns.features) {
185
+ const matches = content.match(regex);
186
+ detected[name] = matches ? matches.length : 0;
187
+ }
188
+
189
+ return {
190
+ detected: true,
191
+ features: detected
192
+ };
193
+ }
194
+
195
+ export default {
196
+ detectLanguages,
197
+ getPrimaryLanguage,
198
+ analyzeLanguagePatterns
199
+ };