@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
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Architect Drift Detector Hook
5
+ *
6
+ * Detects when project changes require architect to re-analyze and update skills/agents.
7
+ *
8
+ * Triggers re-analysis when:
9
+ * - New dependencies added (package.json, requirements.txt, go.mod, etc.)
10
+ * - Tech stack changes (switched frameworks)
11
+ * - New patterns emerge (5+ files violating existing conventions)
12
+ * - Skills are stale (last analysis > 7 days old)
13
+ *
14
+ * Run: After any Write/Edit operation by agents
15
+ * Action: Updates .agentful/architecture.json with needs_reanalysis: true
16
+ */
17
+
18
+ import fs from 'fs';
19
+ import path from 'path';
20
+ import crypto from 'crypto';
21
+
22
+ const ARCHITECTURE_FILE = '.agentful/architecture.json';
23
+ const STALE_THRESHOLD_DAYS = 7;
24
+
25
+ /**
26
+ * Detect if architect needs to re-run
27
+ */
28
+ function detectArchitectDrift() {
29
+ try {
30
+ // Load current architecture analysis
31
+ const arch = loadArchitecture();
32
+
33
+ // Check various drift indicators
34
+ const driftReasons = [];
35
+
36
+ // 1. Check if dependency files changed
37
+ if (dependenciesChanged(arch)) {
38
+ driftReasons.push('dependencies_changed');
39
+ }
40
+
41
+ // 2. Check if tech stack files modified
42
+ if (techStackFilesModified(arch)) {
43
+ driftReasons.push('tech_stack_modified');
44
+ }
45
+
46
+ // 3. Check if analysis is stale
47
+ if (analysisIsStale(arch)) {
48
+ driftReasons.push('analysis_stale');
49
+ }
50
+
51
+ // 4. Check if new patterns emerged (heuristic: lots of new files)
52
+ if (newPatternsDetected(arch)) {
53
+ driftReasons.push('new_patterns_detected');
54
+ }
55
+
56
+ // If drift detected, mark for re-analysis
57
+ if (driftReasons.length > 0) {
58
+ markForReanalysis(arch, driftReasons);
59
+ console.log(`⚠️ Architect drift detected: ${driftReasons.join(', ')}`);
60
+ console.log(' Run /agentful-generate to update skills and agents');
61
+ return true;
62
+ }
63
+
64
+ return false;
65
+ } catch (error) {
66
+ // Fail silently - don't block operations if architecture.json missing
67
+ return false;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Load architecture.json
73
+ */
74
+ function loadArchitecture() {
75
+ if (!fs.existsSync(ARCHITECTURE_FILE)) {
76
+ // First run - architect hasn't analyzed yet
77
+ return {
78
+ last_analysis: null,
79
+ dependency_hashes: {},
80
+ file_count: 0,
81
+ needs_reanalysis: true
82
+ };
83
+ }
84
+
85
+ return JSON.parse(fs.readFileSync(ARCHITECTURE_FILE, 'utf8'));
86
+ }
87
+
88
+ /**
89
+ * Check if dependency files changed
90
+ */
91
+ function dependenciesChanged(arch) {
92
+ const depFiles = [
93
+ 'package.json',
94
+ 'package-lock.json',
95
+ 'requirements.txt',
96
+ 'pyproject.toml',
97
+ 'Pipfile',
98
+ 'go.mod',
99
+ 'go.sum',
100
+ 'Gemfile',
101
+ 'Gemfile.lock',
102
+ 'composer.json',
103
+ 'pom.xml',
104
+ 'build.gradle',
105
+ 'Cargo.toml'
106
+ ];
107
+
108
+ for (const file of depFiles) {
109
+ if (!fs.existsSync(file)) continue;
110
+
111
+ const currentHash = hashFile(file);
112
+ const previousHash = arch.dependency_hashes?.[file];
113
+
114
+ if (previousHash && currentHash !== previousHash) {
115
+ return true;
116
+ }
117
+ }
118
+
119
+ return false;
120
+ }
121
+
122
+ /**
123
+ * Check if tech stack config files modified
124
+ */
125
+ function techStackFilesModified(arch) {
126
+ const configFiles = [
127
+ 'tsconfig.json',
128
+ 'next.config.js',
129
+ 'vite.config.js',
130
+ 'webpack.config.js',
131
+ 'django/settings.py',
132
+ 'config/application.rb',
133
+ 'nest-cli.json'
134
+ ];
135
+
136
+ for (const file of configFiles) {
137
+ if (!fs.existsSync(file)) continue;
138
+
139
+ const currentHash = hashFile(file);
140
+ const previousHash = arch.dependency_hashes?.[file];
141
+
142
+ if (previousHash && currentHash !== previousHash) {
143
+ return true;
144
+ }
145
+ }
146
+
147
+ return false;
148
+ }
149
+
150
+ /**
151
+ * Check if analysis is stale (>7 days old)
152
+ */
153
+ function analysisIsStale(arch) {
154
+ if (!arch.last_analysis) return true;
155
+
156
+ const lastAnalysis = new Date(arch.last_analysis);
157
+ const daysSinceAnalysis = (Date.now() - lastAnalysis.getTime()) / (1000 * 60 * 60 * 24);
158
+
159
+ return daysSinceAnalysis > STALE_THRESHOLD_DAYS;
160
+ }
161
+
162
+ /**
163
+ * Heuristic: Check if significant new code added (potential new patterns)
164
+ */
165
+ function newPatternsDetected(arch) {
166
+ // Count current files in src/
167
+ const currentFileCount = countSourceFiles();
168
+ const previousFileCount = arch.file_count || 0;
169
+
170
+ // If 20% more files added, might have new patterns
171
+ const growthRatio = (currentFileCount - previousFileCount) / Math.max(previousFileCount, 1);
172
+
173
+ return growthRatio > 0.2;
174
+ }
175
+
176
+ /**
177
+ * Count source files
178
+ */
179
+ function countSourceFiles() {
180
+ const srcDirs = ['src', 'app', 'lib', 'pages', 'components'];
181
+ let count = 0;
182
+
183
+ for (const dir of srcDirs) {
184
+ if (fs.existsSync(dir)) {
185
+ count += countFilesRecursive(dir);
186
+ }
187
+ }
188
+
189
+ return count;
190
+ }
191
+
192
+ /**
193
+ * Count files recursively
194
+ */
195
+ function countFilesRecursive(dir) {
196
+ let count = 0;
197
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
198
+
199
+ for (const entry of entries) {
200
+ if (entry.name.startsWith('.')) continue;
201
+ if (entry.name === 'node_modules') continue;
202
+
203
+ const fullPath = path.join(dir, entry.name);
204
+
205
+ if (entry.isDirectory()) {
206
+ count += countFilesRecursive(fullPath);
207
+ } else if (entry.isFile()) {
208
+ count++;
209
+ }
210
+ }
211
+
212
+ return count;
213
+ }
214
+
215
+ /**
216
+ * Hash a file
217
+ */
218
+ function hashFile(filePath) {
219
+ const content = fs.readFileSync(filePath, 'utf8');
220
+ return crypto.createHash('md5').update(content).digest('hex');
221
+ }
222
+
223
+ /**
224
+ * Mark architecture.json for re-analysis
225
+ */
226
+ function markForReanalysis(arch, reasons) {
227
+ arch.needs_reanalysis = true;
228
+ arch.drift_reasons = reasons;
229
+ arch.drift_detected_at = new Date().toISOString();
230
+
231
+ // Ensure directory exists
232
+ const dir = path.dirname(ARCHITECTURE_FILE);
233
+ if (!fs.existsSync(dir)) {
234
+ fs.mkdirSync(dir, { recursive: true });
235
+ }
236
+
237
+ fs.writeFileSync(ARCHITECTURE_FILE, JSON.stringify(arch, null, 2));
238
+ }
239
+
240
+ // Run detection
241
+ const driftDetected = detectArchitectDrift();
242
+ process.exit(driftDetected ? 1 : 0);
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Product Spec Watcher Hook
5
+ *
6
+ * Triggered on Write/Edit of .claude/product/**.md files
7
+ * Auto-triggers /agentful-generate when product spec is updated
8
+ *
9
+ * Use case:
10
+ * 1. User runs /agentful-init → creates .claude/product/index.md
11
+ * 2. This hook detects the file creation
12
+ * 3. Checks if from /agentful-init flow
13
+ * 4. Auto-triggers agent generation with BOTH tech stack + requirements
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ // Get tool use metadata from environment
20
+ const toolUseEnv = process.env.CLAUDE_TOOL_USE || '{}';
21
+ let toolUse;
22
+
23
+ try {
24
+ toolUse = JSON.parse(toolUseEnv);
25
+ } catch (error) {
26
+ // Invalid JSON - exit silently
27
+ process.exit(0);
28
+ }
29
+
30
+ // Get the file path that was written/edited
31
+ const filePath = toolUse.parameters?.file_path || '';
32
+
33
+ // Only trigger on product spec files
34
+ if (!filePath.includes('.claude/product/')) {
35
+ process.exit(0);
36
+ }
37
+
38
+ // Get the root directory
39
+ const getRepoRoot = () => {
40
+ try {
41
+ const { execSync } = require('child_process');
42
+ return execSync('git rev-parse --show-toplevel', {
43
+ encoding: 'utf8'
44
+ }).trim();
45
+ } catch {
46
+ // Not a git repo - use cwd
47
+ return process.cwd();
48
+ }
49
+ };
50
+
51
+ const repoRoot = getRepoRoot();
52
+ const setupProgressPath = path.join(repoRoot, '.agentful', 'setup-progress.json');
53
+ const architecturePath = path.join(repoRoot, '.agentful', 'architecture.json');
54
+
55
+ // Check if this is from /agentful-init flow
56
+ let isFromInit = false;
57
+ let setupProgress = null;
58
+
59
+ if (fs.existsSync(setupProgressPath)) {
60
+ try {
61
+ setupProgress = JSON.parse(fs.readFileSync(setupProgressPath, 'utf8'));
62
+
63
+ // Check if setup just completed but agents not yet generated
64
+ isFromInit = setupProgress.completed &&
65
+ !setupProgress.agents_generated &&
66
+ // Must be recent (within last 5 minutes)
67
+ (Date.now() - new Date(setupProgress.timestamp).getTime()) < 5 * 60 * 1000;
68
+ } catch (error) {
69
+ // Ignore JSON parse errors
70
+ }
71
+ }
72
+
73
+ // Check if agents already exist
74
+ const hasExistingAgents = fs.existsSync(architecturePath);
75
+
76
+ if (isFromInit && !hasExistingAgents) {
77
+ console.log(`
78
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
79
+ Product Specification Updated
80
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
81
+
82
+ File: ${path.relative(repoRoot, filePath)}
83
+
84
+ Auto-triggering agent generation with:
85
+ ✓ Tech stack (detected)
86
+ ✓ Product requirements (from /agentful-init)
87
+
88
+ This ensures specialized agents are tailored to your product.
89
+
90
+ Starting analysis...
91
+ `);
92
+
93
+ // Mark that we're about to generate agents
94
+ if (setupProgress) {
95
+ setupProgress.agents_generation_triggered = true;
96
+ setupProgress.agents_generation_timestamp = new Date().toISOString();
97
+
98
+ try {
99
+ fs.writeFileSync(setupProgressPath, JSON.stringify(setupProgress, null, 2));
100
+ } catch (error) {
101
+ // Non-fatal - continue anyway
102
+ }
103
+ }
104
+
105
+ // NOTE: The actual triggering of /agentful-generate would happen here
106
+ // This depends on Claude Code's slash command triggering system
107
+ // For now, we just notify the user
108
+
109
+ console.log(`
110
+ ⏭️ Next: Agent generation will continue automatically.
111
+
112
+ If it doesn't start automatically, run:
113
+ /agentful-generate
114
+ `);
115
+
116
+ } else if (!isFromInit && !hasExistingAgents) {
117
+ // Manual edit of product spec, but no agents yet
118
+ console.log(`
119
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
120
+ Product Specification Updated
121
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
122
+
123
+ File: ${path.relative(repoRoot, filePath)}
124
+
125
+ To generate specialized agents based on your product spec:
126
+ /agentful-generate
127
+
128
+ Or to use the guided setup:
129
+ /agentful-init
130
+ `);
131
+
132
+ } else if (hasExistingAgents) {
133
+ // Agents already exist - notify about regeneration
134
+ console.log(`
135
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
136
+ Product Specification Updated
137
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
138
+
139
+ File: ${path.relative(repoRoot, filePath)}
140
+
141
+ Your product spec changed, but agents already exist.
142
+
143
+ To regenerate agents with updated requirements:
144
+ /agentful-generate
145
+
146
+ Or continue with existing agents:
147
+ /agentful-start
148
+ `);
149
+ }
150
+
151
+ process.exit(0);
package/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.1.0"
2
+ "version": "1.2.2"
3
3
  }
@@ -1,101 +0,0 @@
1
- #!/usr/bin/env node
2
- // post-agent.js
3
- // Tracks agent execution metrics
4
-
5
- import fs from 'fs';
6
-
7
- const METRICS_FILE = '.agentful/agent-metrics.json';
8
-
9
- /**
10
- * Track agent execution metrics
11
- * @param {Object} options - Tracking options
12
- * @param {string} options.agentName - Name of the agent
13
- * @param {string} [options.feature] - Feature being worked on
14
- * @param {string} [options.domain] - Domain context
15
- * @param {string} [options.timestamp] - ISO timestamp (defaults to current time)
16
- * @returns {Object} - Result with success flag and optional error
17
- */
18
- export function trackAgentMetrics({ agentName, feature = '', domain = '', timestamp = new Date().toISOString() }) {
19
- // Exit successfully if no agent specified
20
- if (!agentName) {
21
- return { success: true };
22
- }
23
-
24
- // Ensure .agentful directory exists
25
- const metricsDir = METRICS_FILE.substring(0, METRICS_FILE.lastIndexOf('/'));
26
- if (!fs.existsSync(metricsDir)) {
27
- fs.mkdirSync(metricsDir, { recursive: true });
28
- }
29
-
30
- // Create metrics file if it doesn't exist
31
- if (!fs.existsSync(METRICS_FILE)) {
32
- const initialMetrics = {
33
- invocations: {},
34
- last_invocation: null,
35
- feature_hooks: []
36
- };
37
- fs.writeFileSync(METRICS_FILE, JSON.stringify(initialMetrics, null, 2));
38
- }
39
-
40
- // Validate and read existing metrics file
41
- let metrics;
42
- if (!fs.existsSync(METRICS_FILE)) {
43
- // File doesn't exist, create new structure
44
- metrics = {
45
- invocations: {},
46
- last_invocation: null,
47
- feature_hooks: []
48
- };
49
- } else {
50
- try {
51
- const metricsContent = fs.readFileSync(METRICS_FILE, 'utf8');
52
- metrics = JSON.parse(metricsContent);
53
- } catch (err) {
54
- console.log('WARNING: agent-metrics.json is corrupted, recreating');
55
- metrics = {
56
- invocations: {},
57
- last_invocation: null,
58
- feature_hooks: []
59
- };
60
- }
61
- }
62
-
63
- // Update invocation count for this agent
64
- const currentCount = metrics.invocations[agentName] || 0;
65
- const newCount = currentCount + 1;
66
-
67
- // Update metrics
68
- metrics.invocations[agentName] = newCount;
69
- metrics.last_invocation = {
70
- agent: agentName,
71
- timestamp: timestamp,
72
- feature: feature,
73
- domain: domain
74
- };
75
-
76
- // Write updated metrics
77
- try {
78
- fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2));
79
- return { success: true };
80
- } catch (err) {
81
- console.error('WARNING: Failed to write agent-metrics.json');
82
- return { success: true, error: err.message };
83
- }
84
- }
85
-
86
- // CLI entrypoint
87
- if (import.meta.url === `file://${process.argv[1]}`) {
88
- const AGENT_NAME = process.env.AGENTFUL_AGENT || '';
89
- const FEATURE = process.env.AGENTFUL_FEATURE || '';
90
- const DOMAIN = process.env.AGENTFUL_DOMAIN || '';
91
- const TIMESTAMP = new Date().toISOString();
92
-
93
- trackAgentMetrics({
94
- agentName: AGENT_NAME,
95
- feature: FEATURE,
96
- domain: DOMAIN,
97
- timestamp: TIMESTAMP
98
- });
99
-
100
- process.exit(0);
101
- }