@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,271 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Block File Creation Hook
5
+ *
6
+ * Prevents creation of arbitrary files outside of permitted directories and types.
7
+ * Stops agents from littering the codebase with random JSON, TXT, LOG files.
8
+ *
9
+ * Allowed file creation:
10
+ * - Source code files (.js, .ts, .jsx, .tsx, .py, .go, .rs, .java, .c, .cpp, .h, .cs, .rb, .php, etc.)
11
+ * - Config files in project root (package.json, tsconfig.json, vite.config.js, etc.)
12
+ * - Test files in test directories
13
+ * - Documentation in approved locations (see block-random-docs hook)
14
+ *
15
+ * Blocked:
16
+ * - Random .json files outside of root config
17
+ * - Random .txt, .log, .tmp files anywhere
18
+ * - State snapshots outside .agentful/
19
+ * - Arbitrary data files in src/, lib/, test/ directories
20
+ *
21
+ * Permitted directories for non-code artifacts:
22
+ * - .agentful/ - Runtime state (with validation)
23
+ * - fixtures/ - Test fixtures
24
+ * - mocks/ - Test mocks
25
+ * - public/assets/ - Static assets
26
+ * - docs/ - Documentation (if directory exists)
27
+ */
28
+
29
+ import path from 'path';
30
+ import fs from 'fs';
31
+
32
+ // Get file path from environment (set by Claude Code hooks)
33
+ const filePath = process.env.FILE || '';
34
+ const toolName = process.env.TOOL_NAME || '';
35
+
36
+ // Only intercept Write tool calls
37
+ if (toolName !== 'Write') {
38
+ process.exit(0);
39
+ }
40
+
41
+ // Normalize path
42
+ const normalizedPath = path.normalize(filePath);
43
+
44
+ // Source code extensions (always allowed)
45
+ const SOURCE_CODE_EXTENSIONS = [
46
+ '.js', '.mjs', '.cjs',
47
+ '.ts', '.mts', '.cts',
48
+ '.jsx', '.tsx',
49
+ '.py', '.pyi',
50
+ '.go',
51
+ '.rs',
52
+ '.java',
53
+ '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp',
54
+ '.cs',
55
+ '.rb',
56
+ '.php',
57
+ '.swift',
58
+ '.kt', '.kts',
59
+ '.scala',
60
+ '.ex', '.exs',
61
+ '.clj', '.cljs',
62
+ '.sh', '.bash',
63
+ '.sql',
64
+ '.graphql', '.gql',
65
+ '.proto',
66
+ '.vue', '.svelte',
67
+ '.css', '.scss', '.sass', '.less',
68
+ '.html', '.htm',
69
+ '.xml', '.yaml', '.yml',
70
+ '.toml', '.ini', '.env.example',
71
+ '.gitignore', '.dockerignore', '.eslintignore'
72
+ ];
73
+
74
+ // Check if file is source code
75
+ const ext = path.extname(normalizedPath);
76
+ if (SOURCE_CODE_EXTENSIONS.includes(ext)) {
77
+ process.exit(0);
78
+ }
79
+
80
+ // Markdown files are handled by block-random-docs hook
81
+ if (ext === '.md' || ext === '.mdx') {
82
+ process.exit(0);
83
+ }
84
+
85
+ // Root-level config files (explicitly allowed)
86
+ const ROOT_CONFIG_FILES = [
87
+ 'package.json',
88
+ 'package-lock.json',
89
+ 'yarn.lock',
90
+ 'pnpm-lock.yaml',
91
+ 'bun.lockb',
92
+ 'tsconfig.json',
93
+ 'jsconfig.json',
94
+ 'vite.config.js',
95
+ 'vite.config.ts',
96
+ 'vitest.config.js',
97
+ 'vitest.config.ts',
98
+ 'vitest.setup.js',
99
+ 'vitest.setup.ts',
100
+ 'vitest.global-teardown.js',
101
+ 'jest.config.js',
102
+ 'jest.config.ts',
103
+ 'next.config.js',
104
+ 'next.config.mjs',
105
+ 'nuxt.config.js',
106
+ 'nuxt.config.ts',
107
+ 'svelte.config.js',
108
+ 'astro.config.mjs',
109
+ 'tailwind.config.js',
110
+ 'tailwind.config.ts',
111
+ 'postcss.config.js',
112
+ 'eslint.config.js',
113
+ '.eslintrc.json',
114
+ '.eslintrc.js',
115
+ '.prettierrc',
116
+ '.prettierrc.json',
117
+ 'prettier.config.js',
118
+ 'turbo.json',
119
+ 'nx.json',
120
+ 'lerna.json',
121
+ 'Dockerfile',
122
+ 'docker-compose.yml',
123
+ 'docker-compose.yaml',
124
+ '.dockerignore',
125
+ '.gitignore',
126
+ '.env.example',
127
+ 'vercel.json',
128
+ 'railway.json',
129
+ 'render.yaml',
130
+ 'fly.toml'
131
+ ];
132
+
133
+ // Check if file is a root config file
134
+ const fileName = path.basename(normalizedPath);
135
+ if (ROOT_CONFIG_FILES.includes(fileName) && !normalizedPath.includes('/')) {
136
+ process.exit(0);
137
+ }
138
+
139
+ // Allowed directory patterns for non-code files
140
+ const ALLOWED_DIRECTORY_PATTERNS = [
141
+ /^\.agentful\//, // Runtime state
142
+ /^\.claude\//, // Claude configuration
143
+ /^fixtures\//, // Test fixtures
144
+ /^test\/fixtures\//,
145
+ /^tests\/fixtures\//,
146
+ /^__fixtures__\//,
147
+ /^mocks\//, // Test mocks
148
+ /^test\/mocks\//,
149
+ /^tests\/mocks\//,
150
+ /^__mocks__\//,
151
+ /^public\/assets\//, // Static assets
152
+ /^public\/data\//,
153
+ /^static\/assets\//,
154
+ /^static\/data\//,
155
+ /^docs\/assets\//, // Documentation assets
156
+ /^\.github\//, // GitHub config
157
+ /^\.vscode\//, // VSCode config
158
+ /^config\//, // Config directory
159
+ /^\.config\//,
160
+ /^dist\//, // Build output (warn but allow)
161
+ /^build\//,
162
+ /^out\//
163
+ ];
164
+
165
+ // Check if file is in allowed directory
166
+ const isInAllowedDir = ALLOWED_DIRECTORY_PATTERNS.some(pattern => pattern.test(normalizedPath));
167
+
168
+ if (isInAllowedDir) {
169
+ // Additional validation for .agentful/ files
170
+ if (normalizedPath.startsWith('.agentful/')) {
171
+ const agentfulFiles = [
172
+ '.agentful/state.json',
173
+ '.agentful/completion.json',
174
+ '.agentful/decisions.json',
175
+ '.agentful/architecture.json',
176
+ '.agentful/conversation-state.json',
177
+ '.agentful/conversation-history.json',
178
+ '.agentful/agent-metrics.json',
179
+ '.agentful/metadata.json'
180
+ ];
181
+
182
+ if (agentfulFiles.includes(normalizedPath)) {
183
+ process.exit(0);
184
+ }
185
+
186
+ // Block random files in .agentful/
187
+ console.error(`
188
+ ❌ BLOCKED: Arbitrary file creation in .agentful/
189
+
190
+ File: ${filePath}
191
+
192
+ Allowed .agentful/ files:
193
+ - state.json (current work phase)
194
+ - completion.json (feature progress)
195
+ - decisions.json (pending decisions)
196
+ - architecture.json (tech stack)
197
+ - conversation-state.json (conversation context)
198
+ - conversation-history.json (message history)
199
+ - agent-metrics.json (agent lifecycle hooks)
200
+ - metadata.json (version tracking)
201
+
202
+ Do NOT create random state snapshots or debug files in .agentful/.
203
+ `);
204
+ process.exit(1);
205
+ }
206
+
207
+ // Allow other permitted directories
208
+ process.exit(0);
209
+ }
210
+
211
+ // Risky file extensions (almost never allowed outside approved directories)
212
+ const RISKY_EXTENSIONS = [
213
+ '.json',
214
+ '.txt',
215
+ '.log',
216
+ '.tmp',
217
+ '.temp',
218
+ '.bak',
219
+ '.backup',
220
+ '.old',
221
+ '.data',
222
+ '.dat',
223
+ '.bin',
224
+ '.dump',
225
+ '.out'
226
+ ];
227
+
228
+ if (RISKY_EXTENSIONS.includes(ext)) {
229
+ console.error(`
230
+ ❌ BLOCKED: Random file creation
231
+
232
+ File: ${filePath}
233
+ Type: ${ext} file
234
+
235
+ This file type is not allowed outside of approved directories.
236
+
237
+ Allowed locations for ${ext} files:
238
+ ${ext === '.json' ? '- Project root (config files like package.json, tsconfig.json)' : ''}
239
+ - .agentful/ (runtime state, validation required)
240
+ - fixtures/ (test fixtures)
241
+ - mocks/ (test mocks)
242
+ - public/assets/ (static assets)
243
+ ${ext === '.json' ? '- test/**/fixtures/ (test data)' : ''}
244
+
245
+ Source code files (.js, .ts, .py, etc.) can be created anywhere.
246
+
247
+ Instead of creating random ${ext} files:
248
+ 1. Use proper source code files
249
+ 2. Store test data in fixtures/
250
+ 3. Store config in root (package.json, tsconfig.json)
251
+ 4. Store runtime state in .agentful/ (validated files only)
252
+
253
+ Do NOT litter the codebase with arbitrary data files.
254
+ `);
255
+ process.exit(1);
256
+ }
257
+
258
+ // Unknown file type - warn but allow (might be a legitimate file)
259
+ console.warn(`
260
+ ⚠️ WARNING: Creating file with uncommon extension
261
+
262
+ File: ${filePath}
263
+ Extension: ${ext || '(no extension)'}
264
+
265
+ This file type is not explicitly allowed or blocked.
266
+ If this is a legitimate file, you can proceed.
267
+ If this is a random artifact, please reconsider.
268
+ `);
269
+
270
+ // Allow with warning
271
+ process.exit(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/ markdown 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
+ import fs from 'fs';
17
+ import path from 'path';
18
+ import { execSync } from 'child_process';
19
+
20
+ // Get tool use metadata from environment
21
+ const toolUseEnv = process.env.CLAUDE_TOOL_USE || '{}';
22
+ let toolUse;
23
+
24
+ try {
25
+ toolUse = JSON.parse(toolUseEnv);
26
+ } catch (error) {
27
+ // Invalid JSON - exit silently
28
+ process.exit(0);
29
+ }
30
+
31
+ // Get the file path that was written/edited
32
+ const filePath = toolUse.parameters?.file_path || '';
33
+
34
+ // Only trigger on product spec files
35
+ if (!filePath.includes('.claude/product/')) {
36
+ process.exit(0);
37
+ }
38
+
39
+ // Get the root directory
40
+ const getRepoRoot = () => {
41
+ try {
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/lib/index.js CHANGED
@@ -12,10 +12,6 @@ export { initProject, copyDirectory, isInitialized, getState } from './init.js';
12
12
  // Export pipeline orchestration system
13
13
  export * from './pipeline/index.js';
14
14
 
15
- // Export codebase analyzer
16
- export * from './core/analyzer.js';
17
- export * from './core/detectors/index.js';
18
-
19
15
  // Export Claude Code executor abstraction
20
16
  export {
21
17
  ClaudeExecutor,
@@ -26,13 +22,6 @@ export {
26
22
  executeAgent
27
23
  } from './core/claude-executor.js';
28
24
 
29
- // Export output parser for agent responses
30
- export * from './core/output-parser.js';
31
-
32
- // Export safe agent execution (prevents runaway agents)
33
- export { SafeAgentExecutor, executeAgentSafely } from './safe-agent-executor.js';
34
- export { ParallelAgentOrchestrator, spawnAgentsSafely } from './orchestrator-safe-spawn.js';
35
-
36
25
  // Export CI integration for claude-code-action
37
26
  export * from './ci/index.js';
38
27
 
package/lib/init.js CHANGED
@@ -1,11 +1,6 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import {
5
- initializeMetadata,
6
- recordFileMetadata,
7
- computeFileHash
8
- } from './update-helpers.js';
9
4
  import { generateHooksConfig } from './presets.js';
10
5
 
11
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -32,14 +27,11 @@ export async function initProject(targetDir, config = null) {
32
27
  // Ensure target directory exists
33
28
  await fs.access(targetDir);
34
29
 
35
- // Read package version for metadata tracking
30
+ // Read package version
36
31
  const packageJsonPath = path.join(PACKAGE_ROOT, 'package.json');
37
32
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
38
33
  const version = packageJson.version;
39
34
 
40
- // Initialize metadata tracking
41
- await initializeMetadata(targetDir, version);
42
-
43
35
  // 1. Copy .claude/ directory (agents, skills, commands) from template
44
36
  const claudeTargetDir = path.join(targetDir, '.claude');
45
37
 
@@ -77,9 +69,7 @@ export async function initProject(targetDir, config = null) {
77
69
  await fs.access(claudeMdSource);
78
70
  await fs.copyFile(claudeMdSource, claudeMdTarget);
79
71
 
80
- // Track CLAUDE.md
81
- const hash = await computeFileHash(claudeMdTarget);
82
- await recordFileMetadata(targetDir, 'CLAUDE.md', hash, version);
72
+ // CLAUDE.md copied
83
73
 
84
74
  createdFiles.push('CLAUDE.md');
85
75
  } catch {
@@ -473,8 +463,6 @@ async function copySelectiveComponents(src, dest, targetDir, config, version) {
473
463
 
474
464
  try {
475
465
  await fs.copyFile(sourcePath, targetPath);
476
- const hash = await computeFileHash(targetPath);
477
- await recordFileMetadata(targetDir, `.claude/agents/${agentFile}`, hash, version);
478
466
  } catch (error) {
479
467
  console.warn(`Warning: Agent ${agentName} not found, skipping`);
480
468
  }
@@ -539,8 +527,6 @@ async function copySelectiveComponents(src, dest, targetDir, config, version) {
539
527
  };
540
528
 
541
529
  await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
542
- const hash = await computeFileHash(settingsPath);
543
- await recordFileMetadata(targetDir, '.claude/settings.json', hash, version);
544
530
 
545
531
  // 5. Create product directory (always included)
546
532
  const productDir = path.join(dest, 'product');
@@ -599,11 +585,6 @@ export async function copyDirectoryWithTracking(src, dest, targetDir, baseRelati
599
585
  } else {
600
586
  // Copy file
601
587
  await fs.copyFile(srcPath, destPath);
602
-
603
- // Compute hash and track file
604
- const relativePath = path.join(baseRelativePath, entry.name);
605
- const hash = await computeFileHash(destPath);
606
- await recordFileMetadata(targetDir, relativePath, hash, version);
607
588
  }
608
589
  }
609
590
  }