@itz4blitz/agentful 1.7.0 → 1.8.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.
package/README.md CHANGED
@@ -49,6 +49,16 @@ claude
49
49
  /agentful-start
50
50
  ```
51
51
 
52
+ ## Pattern Learning
53
+
54
+ Enable agents to learn from every session and reuse successful patterns:
55
+
56
+ ```bash
57
+ claude mcp add agentful -- npx -y @itz4blitz/agentful-mcp-server
58
+ ```
59
+
60
+ The reviewer stores error patterns, the fixer looks up known fixes, and the orchestrator stores successful implementation patterns - compounding knowledge across sessions.
61
+
52
62
  ## Installation Options
53
63
 
54
64
  ```bash
package/bin/cli.js CHANGED
@@ -411,6 +411,13 @@ async function init(args) {
411
411
  }
412
412
  log(colors.dim, 'Optional: Edit CLAUDE.md and .claude/product/index.md first to customize.');
413
413
  console.log('');
414
+ log(colors.bright, 'Recommended: Enable Pattern Learning');
415
+ console.log('');
416
+ log(colors.dim, ' Agents get smarter when they can store and reuse patterns.');
417
+ log(colors.dim, ' Run this once to enable:');
418
+ console.log('');
419
+ log(colors.cyan, ' claude mcp add agentful -- npx -y @itz4blitz/agentful-mcp-server');
420
+ console.log('');
414
421
  }
415
422
 
416
423
  function showStatus() {
@@ -176,7 +176,10 @@ if (isInAllowedDir) {
176
176
  '.agentful/conversation-state.json',
177
177
  '.agentful/conversation-history.json',
178
178
  '.agentful/agent-metrics.json',
179
- '.agentful/metadata.json'
179
+ '.agentful/metadata.json',
180
+ '.agentful/learnings.json',
181
+ '.agentful/last-validation.json',
182
+ '.agentful/product-analysis.json'
180
183
  ];
181
184
 
182
185
  if (agentfulFiles.includes(normalizedPath)) {
@@ -198,6 +201,9 @@ Allowed .agentful/ files:
198
201
  - conversation-history.json (message history)
199
202
  - agent-metrics.json (agent lifecycle hooks)
200
203
  - metadata.json (version tracking)
204
+ - learnings.json (compound engineering retrospectives)
205
+ - last-validation.json (latest validation report)
206
+ - product-analysis.json (product spec analysis)
201
207
 
202
208
  Do NOT create random state snapshots or debug files in .agentful/.
203
209
  `);
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Ensure Worktree Hook
5
+ *
6
+ * PreToolUse hook that enforces git worktree usage for file modifications.
7
+ *
8
+ * Modes:
9
+ * off - Allow all edits (backward compatible)
10
+ * block - Require existing worktree, reject if not in one
11
+ * auto - Create worktree automatically if not in one
12
+ *
13
+ * Environment Variables:
14
+ * AGENTFUL_WORKTREE_MODE - off|block|auto (default: auto)
15
+ * AGENTFUL_WORKTREE_DIR - Where to create worktrees (default: ../)
16
+ * AGENTFUL_WORKTREE_AUTO_CLEANUP - Auto-remove after completion (default: true)
17
+ * AGENTFUL_WORKTREE_RETENTION_DAYS - Days before cleanup (default: 7)
18
+ * AGENTFUL_WORKTREE_MAX_ACTIVE - Max active worktrees (default: 5)
19
+ *
20
+ * To disable this hook:
21
+ * Temporary: export AGENTFUL_WORKTREE_MODE=off
22
+ * Permanent: Remove from .claude/settings.json PreToolUse hooks
23
+ * Customize: Edit bin/hooks/ensure-worktree.js
24
+ */
25
+
26
+ import fs from 'fs';
27
+ import path from 'path';
28
+ import { execSync } from 'child_process';
29
+ import { fileURLToPath } from 'url';
30
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
31
+
32
+ // Get configuration
33
+ const MODE = process.env.AGENTFUL_WORKTREE_MODE || 'auto';
34
+ const WORKTREE_DIR = process.env.AGENTFUL_WORKTREE_DIR || '../';
35
+ const AUTO_CLEANUP = process.env.AGENTFUL_WORKTREE_AUTO_CLEANUP !== 'false';
36
+ const RETENTION_DAYS = parseInt(process.env.AGENTFUL_WORKTREE_RETENTION_DAYS || '7', 10);
37
+ const MAX_ACTIVE = parseInt(process.env.AGENTFUL_WORKTREE_MAX_ACTIVE || '5', 10);
38
+ const AGENT_TYPE = process.env.AGENTFUL_AGENT_TYPE || 'general';
39
+ const TASK_TYPE = process.env.AGENTFUL_TASK_TYPE || 'general';
40
+
41
+ /**
42
+ * Detect if currently in a git worktree
43
+ */
44
+ function isInWorktree() {
45
+ try {
46
+ const cwd = process.cwd();
47
+ const gitFile = path.join(cwd, '.git');
48
+
49
+ // If .git is a file (not a directory), we're in a worktree
50
+ if (existsSync(gitFile) && statSync(gitFile).isFile()) {
51
+ return true;
52
+ }
53
+ } catch (error) {
54
+ return false;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Determine worktree purpose from agent/task context
60
+ */
61
+ function determinePurpose() {
62
+ // Agent-based purposes
63
+ const agentPurposes = {
64
+ 'fixer': 'fix',
65
+ 'reviewer': 'review',
66
+ 'tester': 'test',
67
+ 'backend': 'backend',
68
+ 'frontend': 'frontend',
69
+ 'architect': 'architect',
70
+ 'orchestrator': 'orchestrator',
71
+ };
72
+
73
+ // Task-based purposes
74
+ const taskPurposes = {
75
+ 'feature': 'feature',
76
+ 'hotfix': 'hotfix',
77
+ 'bugfix': 'bugfix',
78
+ 'experiment': 'experiment',
79
+ };
80
+
81
+ // Determine purpose based on agent type, then task type
82
+ if (agentPurposes[AGENT_TYPE]) {
83
+ return agentPurposes[AGENT_TYPE];
84
+ }
85
+
86
+ if (taskPurposes[TASK_TYPE]) {
87
+ return taskPurposes[TASK_TYPE];
88
+ }
89
+
90
+ // Default to general
91
+ return 'general';
92
+ }
93
+
94
+ /**
95
+ * Show block message with helpful error
96
+ */
97
+ function showBlockMessage() {
98
+ const branch = getCurrentBranch();
99
+
100
+ console.error(`
101
+ :::error
102
+ ═════════════════════════════════════════════════
103
+ 🚫 Blocked: Direct Repository Edits
104
+ ═════════════════════════════════════════════════
105
+
106
+ You are attempting to edit files in the root repository, but AGENTFUL_WORKTREE_MODE
107
+ is set to "block" which requires working in a git worktree.
108
+
109
+ Current branch: ${branch}
110
+ Current directory: ${process.cwd()}
111
+
112
+ To proceed, choose one option:
113
+
114
+ 1. 🌳 Create a worktree (recommended):
115
+ git worktree add ../my-worktree -b ${branch}
116
+ cd ../my-worktree
117
+
118
+ 2. ⚙️ Change mode to auto (creates worktrees automatically):
119
+ export AGENTFUL_WORKTREE_MODE=auto
120
+
121
+ 3. 🚫 Disable worktree protection (not recommended):
122
+ export AGENTFUL_WORKTREE_MODE=off
123
+
124
+ For more information, see: /agentful-worktree or docs/pages/concepts/git-worktrees.mdx
125
+ :::`);
126
+
127
+ process.exit(1);
128
+ }
129
+
130
+ /**
131
+ * Create worktree automatically
132
+ */
133
+ function createWorktree() {
134
+ const repoRoot = findRepoRoot();
135
+ if (!repoRoot) {
136
+ console.error(':::error Not in a git repository:::');
137
+ process.exit(1);
138
+ }
139
+
140
+ const purpose = determinePurpose();
141
+ const branch = getCurrentBranch();
142
+ const timestamp = Date.now();
143
+
144
+ // Generate worktree name
145
+ const worktreeName = `agentful-${purpose}-${sanitizeBranchName(branch)}-${timestamp}`;
146
+
147
+ // Create worktree
148
+ const worktreePath = path.join(repoRoot, WORKTREE_DIR, worktreeName);
149
+
150
+ console.log(`🌳 Creating worktree: ${worktreeName}`);
151
+ console.log(` Branch: ${branch}`);
152
+ console.log(` Path: ${worktreePath}`);
153
+
154
+ try {
155
+ execSync(
156
+ `git worktree add "${worktreePath}" -b "${branch}"`,
157
+ { cwd: repoRoot, stdio: 'inherit' }
158
+ );
159
+ } catch (error) {
160
+ console.error(`:::error Failed to create worktree: ${error.message}:::`);
161
+ process.exit(1);
162
+ }
163
+
164
+ // Track in state.json (would happen in orchestrator, but this is standalone)
165
+ try {
166
+ const stateFile = path.join(repoRoot, '.agentful', 'state.json');
167
+ if (existsSync(stateFile)) {
168
+ const state = JSON.parse(readFileSync(stateFile, 'utf8'));
169
+ state.current_worktree = {
170
+ name: worktreeName,
171
+ path: worktreePath,
172
+ branch: branch,
173
+ purpose: purpose,
174
+ created_at: new Date().toISOString(),
175
+ };
176
+ writeFileSync(stateFile, JSON.stringify(state, null, 2));
177
+ }
178
+ } catch (error) {
179
+ // State file might not exist yet, that's okay
180
+ }
181
+
182
+ // Export for caller to capture
183
+ console.log(`export AGENTFUL_WORKTREE_DIR="${worktreePath}"`);
184
+
185
+ process.exit(0);
186
+ }
187
+
188
+ /**
189
+ * Main execution
190
+ */
191
+ (() => {
192
+ // Get tool and file from environment
193
+ const tool = process.env.TOOL || '';
194
+ const file = process.env.FILE || '';
195
+
196
+ // Only check Write and Edit tools
197
+ if (tool !== 'Write' && tool !== 'Edit') {
198
+ process.exit(0);
199
+ }
200
+
201
+ // Auto-disable in CI environments
202
+ if (isCIEnvironment()) {
203
+ process.exit(0);
204
+ }
205
+
206
+ // Check if already in worktree (explicitly set or detected)
207
+ if (WORKTREE_DIR) {
208
+ // In worktree - allow operation
209
+ process.exit(0);
210
+ }
211
+
212
+ // Not in worktree - handle based on mode
213
+ switch (MODE) {
214
+ case 'off':
215
+ // Allow all edits silently
216
+ process.exit(0);
217
+ break;
218
+
219
+ case 'block':
220
+ // Require existing worktree
221
+ showBlockMessage();
222
+ break;
223
+
224
+ case 'auto':
225
+ // Create worktree automatically
226
+ createWorktree();
227
+ break;
228
+
229
+ default:
230
+ console.error(`:::warning Invalid AGENTFUL_WORKTREE_MODE: ${MODE}:::`);
231
+ console.error(`:::warning Valid options: off, block, auto:::`);
232
+ process.exit(1);
233
+ }
234
+ })();