@itz4blitz/agentful 1.3.0 → 1.4.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.
@@ -0,0 +1,369 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Context Awareness Module
5
+ *
6
+ * Analyzes project state and provides intelligent suggestions for next actions.
7
+ * Used by session-start and post-action hooks.
8
+ */
9
+
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ /**
18
+ * Read JSON file safely
19
+ */
20
+ function readJSON(filePath) {
21
+ try {
22
+ if (!fs.existsSync(filePath)) return null;
23
+ const content = fs.readFileSync(filePath, 'utf8');
24
+ return JSON.parse(content);
25
+ } catch (error) {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Check if file exists
32
+ */
33
+ function fileExists(filePath) {
34
+ try {
35
+ return fs.existsSync(filePath);
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Analyze project state and return context
43
+ */
44
+ export function analyzeProjectState(projectRoot = process.cwd()) {
45
+ const state = {
46
+ hasProductSpec: false,
47
+ hasArchitecture: false,
48
+ hasState: false,
49
+ hasCompletion: false,
50
+ hasPendingDecisions: false,
51
+ architectureValid: true,
52
+ architectureIssues: [],
53
+ currentPhase: 'idle',
54
+ completionPercent: 0,
55
+ totalFeatures: 0,
56
+ completedFeatures: 0,
57
+ pendingDecisionCount: 0,
58
+ blockingIssues: [],
59
+ suggestedActions: []
60
+ };
61
+
62
+ // Check product spec
63
+ const productIndex = path.join(projectRoot, '.claude/product/index.md');
64
+ const productDomains = path.join(projectRoot, '.claude/product/domains');
65
+ state.hasProductSpec = fileExists(productIndex) || fileExists(productDomains);
66
+
67
+ // Check architecture
68
+ const architecturePath = path.join(projectRoot, '.agentful/architecture.json');
69
+ if (fileExists(architecturePath)) {
70
+ state.hasArchitecture = true;
71
+ const arch = readJSON(architecturePath);
72
+
73
+ if (arch) {
74
+ // Validate architecture structure
75
+ if (!arch.techStack || !arch.agents) {
76
+ state.architectureValid = false;
77
+ state.architectureIssues.push('Missing techStack or agents fields');
78
+ }
79
+
80
+ // Check if architecture is stale (older than package.json)
81
+ try {
82
+ const packagePath = path.join(projectRoot, 'package.json');
83
+ if (fileExists(packagePath)) {
84
+ const archStat = fs.statSync(architecturePath);
85
+ const pkgStat = fs.statSync(packagePath);
86
+
87
+ if (pkgStat.mtime > archStat.mtime) {
88
+ state.architectureValid = false;
89
+ state.architectureIssues.push('Architecture older than package.json - may need regeneration');
90
+ }
91
+ }
92
+ } catch (error) {
93
+ // Ignore stat errors
94
+ }
95
+ } else {
96
+ state.architectureValid = false;
97
+ state.architectureIssues.push('Invalid JSON format');
98
+ }
99
+ }
100
+
101
+ // Check state
102
+ const statePath = path.join(projectRoot, '.agentful/state.json');
103
+ if (fileExists(statePath)) {
104
+ state.hasState = true;
105
+ const stateData = readJSON(statePath);
106
+ if (stateData) {
107
+ state.currentPhase = stateData.current_phase || 'idle';
108
+ }
109
+ }
110
+
111
+ // Check completion
112
+ const completionPath = path.join(projectRoot, '.agentful/completion.json');
113
+ if (fileExists(completionPath)) {
114
+ state.hasCompletion = true;
115
+ const completion = readJSON(completionPath);
116
+
117
+ if (completion && completion.features) {
118
+ const features = Object.values(completion.features);
119
+ state.totalFeatures = features.length;
120
+ state.completedFeatures = features.filter(f => f.completion >= 100).length;
121
+
122
+ if (state.totalFeatures > 0) {
123
+ state.completionPercent = Math.round((state.completedFeatures / state.totalFeatures) * 100);
124
+ }
125
+ }
126
+ }
127
+
128
+ // Check decisions
129
+ const decisionsPath = path.join(projectRoot, '.agentful/decisions.json');
130
+ if (fileExists(decisionsPath)) {
131
+ const decisions = readJSON(decisionsPath);
132
+
133
+ if (decisions && decisions.pending) {
134
+ state.pendingDecisionCount = decisions.pending.length;
135
+ state.hasPendingDecisions = state.pendingDecisionCount > 0;
136
+ }
137
+ }
138
+
139
+ // Determine blocking issues
140
+ if (!state.hasProductSpec) {
141
+ state.blockingIssues.push('No product specification found');
142
+ }
143
+
144
+ if (state.hasArchitecture && !state.architectureValid) {
145
+ state.blockingIssues.push('Architecture needs attention');
146
+ }
147
+
148
+ if (state.hasPendingDecisions) {
149
+ state.blockingIssues.push(`${state.pendingDecisionCount} pending decision(s)`);
150
+ }
151
+
152
+ // Generate suggested actions
153
+ state.suggestedActions = generateSuggestions(state);
154
+
155
+ return state;
156
+ }
157
+
158
+ /**
159
+ * Generate smart suggestions based on project state
160
+ */
161
+ function generateSuggestions(state) {
162
+ const suggestions = [];
163
+
164
+ // First-time setup
165
+ if (!state.hasProductSpec) {
166
+ suggestions.push({
167
+ priority: 'critical',
168
+ action: 'Create product specification',
169
+ command: 'Edit .claude/product/index.md',
170
+ description: 'Define your product requirements'
171
+ });
172
+ return suggestions; // Block other suggestions until product spec exists
173
+ }
174
+
175
+ if (!state.hasArchitecture) {
176
+ suggestions.push({
177
+ priority: 'critical',
178
+ action: 'Generate architecture',
179
+ command: '/agentful-generate',
180
+ description: 'Analyze tech stack and create specialized agents'
181
+ });
182
+ return suggestions;
183
+ }
184
+
185
+ // Architecture issues
186
+ if (state.hasArchitecture && !state.architectureValid) {
187
+ suggestions.push({
188
+ priority: 'high',
189
+ action: 'Fix architecture',
190
+ command: '/agentful-generate',
191
+ description: state.architectureIssues.join('; ')
192
+ });
193
+ }
194
+
195
+ // Pending decisions block work
196
+ if (state.hasPendingDecisions) {
197
+ suggestions.push({
198
+ priority: 'high',
199
+ action: 'Answer pending decisions',
200
+ command: '/agentful-decide',
201
+ description: `${state.pendingDecisionCount} decision(s) blocking progress`
202
+ });
203
+ }
204
+
205
+ // Phase-specific suggestions
206
+ if (state.currentPhase === 'idle' || state.currentPhase === 'planning') {
207
+ suggestions.push({
208
+ priority: 'medium',
209
+ action: 'Start development',
210
+ command: '/agentful-start',
211
+ description: 'Begin or resume structured development'
212
+ });
213
+ }
214
+
215
+ if (state.currentPhase === 'implementation' || state.currentPhase === 'testing') {
216
+ suggestions.push({
217
+ priority: 'medium',
218
+ action: 'Check progress',
219
+ command: '/agentful-status',
220
+ description: `Current: ${state.completionPercent}% complete (${state.completedFeatures}/${state.totalFeatures} features)`
221
+ });
222
+ }
223
+
224
+ // Validation suggestion
225
+ if (state.hasCompletion && state.completionPercent > 0) {
226
+ suggestions.push({
227
+ priority: 'low',
228
+ action: 'Run quality checks',
229
+ command: '/agentful-validate',
230
+ description: 'Verify code quality and production readiness'
231
+ });
232
+ }
233
+
234
+ // Product analysis
235
+ suggestions.push({
236
+ priority: 'low',
237
+ action: 'Analyze product spec',
238
+ command: '/agentful-product',
239
+ description: 'Check for gaps and ambiguities'
240
+ });
241
+
242
+ return suggestions;
243
+ }
244
+
245
+ /**
246
+ * Format suggestions for display
247
+ */
248
+ export function formatSuggestions(suggestions, options = {}) {
249
+ const { maxSuggestions = 5, includeNumbers = true } = options;
250
+
251
+ if (suggestions.length === 0) {
252
+ return '💡 All set! Type a command or ask what you need.';
253
+ }
254
+
255
+ // Sort by priority
256
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
257
+ const sorted = suggestions.sort((a, b) => {
258
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
259
+ });
260
+
261
+ // Take top N
262
+ const topSuggestions = sorted.slice(0, maxSuggestions);
263
+
264
+ // Format output
265
+ let output = '💡 Suggested next steps:\n';
266
+
267
+ topSuggestions.forEach((suggestion, index) => {
268
+ const number = includeNumbers ? `${index + 1}. ` : ' • ';
269
+ const icon = suggestion.priority === 'critical' ? '🔴' :
270
+ suggestion.priority === 'high' ? '⚠️ ' : '';
271
+
272
+ output += ` ${number}${icon}${suggestion.action} → ${suggestion.command}\n`;
273
+ if (suggestion.description) {
274
+ output += ` ${suggestion.description}\n`;
275
+ }
276
+ });
277
+
278
+ return output.trim();
279
+ }
280
+
281
+ /**
282
+ * Generate session start message
283
+ */
284
+ export function generateSessionStartMessage(projectRoot = process.cwd()) {
285
+ const state = analyzeProjectState(projectRoot);
286
+ let message = '';
287
+
288
+ // Status line
289
+ if (state.hasCompletion && state.totalFeatures > 0) {
290
+ message += `📊 Project Status: ${state.completionPercent}% complete (${state.completedFeatures}/${state.totalFeatures} features)\n`;
291
+ } else if (state.hasArchitecture) {
292
+ message += '📊 Project Status: Architecture ready, no active development\n';
293
+ } else {
294
+ message += '📊 Project Status: Initial setup\n';
295
+ }
296
+
297
+ // Blocking issues
298
+ if (state.blockingIssues.length > 0) {
299
+ state.blockingIssues.forEach(issue => {
300
+ message += `⚠️ ${issue}\n`;
301
+ });
302
+ message += '\n';
303
+ }
304
+
305
+ // Current phase
306
+ if (state.currentPhase !== 'idle') {
307
+ message += `🔄 Current Phase: ${state.currentPhase}\n\n`;
308
+ }
309
+
310
+ // Suggestions
311
+ message += formatSuggestions(state.suggestedActions, { maxSuggestions: 3 });
312
+
313
+ return message;
314
+ }
315
+
316
+ /**
317
+ * Generate post-action suggestions
318
+ */
319
+ export function generatePostActionSuggestions(action, projectRoot = process.cwd()) {
320
+ const state = analyzeProjectState(projectRoot);
321
+
322
+ // Action-specific follow-ups
323
+ const actionMap = {
324
+ 'agentful-generate': () => {
325
+ if (state.completionPercent === 0) {
326
+ return [{
327
+ priority: 'high',
328
+ action: 'Start development',
329
+ command: '/agentful-start',
330
+ description: 'Begin building features'
331
+ }];
332
+ }
333
+ return [];
334
+ },
335
+
336
+ 'agentful-start': () => [{
337
+ priority: 'medium',
338
+ action: 'Monitor progress',
339
+ command: '/agentful-status',
340
+ description: 'Check completion and active work'
341
+ }],
342
+
343
+ 'agentful-decide': () => {
344
+ if (state.pendingDecisionCount === 0) {
345
+ return [{
346
+ priority: 'high',
347
+ action: 'Resume development',
348
+ command: '/agentful-start',
349
+ description: 'Continue with unblocked work'
350
+ }];
351
+ }
352
+ return [];
353
+ },
354
+
355
+ 'agentful-validate': () => [{
356
+ priority: 'medium',
357
+ action: 'Fix validation issues',
358
+ command: 'Review output and address failures',
359
+ description: 'Fixer agent can auto-resolve some issues'
360
+ }]
361
+ };
362
+
363
+ const specificSuggestions = actionMap[action]?.() || [];
364
+ const generalSuggestions = state.suggestedActions.filter(s =>
365
+ s.command !== `/${action}`
366
+ );
367
+
368
+ return [...specificSuggestions, ...generalSuggestions];
369
+ }
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Post-Action Suggestions Hook
5
+ *
6
+ * Runs after specific slash commands complete.
7
+ * Provides smart suggestions for what to do next.
8
+ */
9
+
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ // Try to import context-awareness module
18
+ let contextModule = null;
19
+ try {
20
+ const modulePath = path.join(__dirname, './context-awareness.js');
21
+ if (fs.existsSync(modulePath)) {
22
+ contextModule = await import(modulePath);
23
+ }
24
+ } catch (error) {
25
+ // Silently fail
26
+ }
27
+
28
+ // Detect which action was just completed
29
+ const lastAction = process.env.LAST_COMMAND || '';
30
+
31
+ // Map of slash commands to action names
32
+ const actionMap = {
33
+ 'agentful-generate': 'agentful-generate',
34
+ 'agentful-start': 'agentful-start',
35
+ 'agentful-decide': 'agentful-decide',
36
+ 'agentful-validate': 'agentful-validate',
37
+ 'agentful-status': 'agentful-status',
38
+ 'agentful-product': 'agentful-product'
39
+ };
40
+
41
+ // Find matching action
42
+ let detectedAction = null;
43
+ for (const [cmd, action] of Object.entries(actionMap)) {
44
+ if (lastAction.includes(cmd)) {
45
+ detectedAction = action;
46
+ break;
47
+ }
48
+ }
49
+
50
+ // Show suggestions if we have context module and detected action
51
+ if (contextModule && contextModule.generatePostActionSuggestions && detectedAction) {
52
+ try {
53
+ const suggestions = contextModule.generatePostActionSuggestions(
54
+ detectedAction,
55
+ process.cwd()
56
+ );
57
+
58
+ if (suggestions.length > 0) {
59
+ const formatted = contextModule.formatSuggestions(suggestions, {
60
+ maxSuggestions: 3,
61
+ includeNumbers: true
62
+ });
63
+
64
+ console.log('');
65
+ console.log('─'.repeat(60));
66
+ console.log(formatted);
67
+ console.log('─'.repeat(60));
68
+ }
69
+ } catch (error) {
70
+ // Silently fail - don't disrupt user experience
71
+ if (process.env.VERBOSE) {
72
+ console.log(`Post-action suggestion error: ${error.message}`);
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Session Start Hook
5
+ *
6
+ * Runs when Claude Code session starts.
7
+ * Provides intelligent context awareness and suggests next actions.
8
+ */
9
+
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { execSync } from 'child_process';
13
+ import { fileURLToPath } from 'url';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = path.dirname(__filename);
17
+
18
+ // Try to import context-awareness module from agentful package
19
+ let contextModule = null;
20
+ try {
21
+ // Import from same directory (bin/hooks/)
22
+ const modulePath = path.join(__dirname, './context-awareness.js');
23
+ if (fs.existsSync(modulePath)) {
24
+ contextModule = await import(modulePath);
25
+ }
26
+ } catch (error) {
27
+ // Silently fail - we'll show basic status instead
28
+ }
29
+
30
+ /**
31
+ * Detect if TeammateTool (parallel execution) is enabled
32
+ */
33
+ function detectParallelExecution() {
34
+ try {
35
+ // Find Claude Code binary
36
+ const npmRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
37
+ const cliPath = path.join(npmRoot, '@anthropic-ai', 'claude-code', 'cli.js');
38
+
39
+ if (!fs.existsSync(cliPath)) {
40
+ return { enabled: false, reason: 'Claude Code binary not found' };
41
+ }
42
+
43
+ // Check for TeammateTool pattern
44
+ const content = fs.readFileSync(cliPath, 'utf8');
45
+ const hasTeammateTool = /TeammateTool|teammate_mailbox|launchSwarm/.test(content);
46
+
47
+ if (!hasTeammateTool) {
48
+ return { enabled: false, reason: 'Claude Code version too old' };
49
+ }
50
+
51
+ // Check if gate is disabled (means it's patched/enabled)
52
+ const SWARM_GATE_MARKER = /tengu_brass_pebble/;
53
+ const gateExists = SWARM_GATE_MARKER.test(content);
54
+
55
+ if (!gateExists) {
56
+ return { enabled: true, method: 'patched' };
57
+ }
58
+
59
+ return { enabled: false, reason: 'TeammateTool not enabled' };
60
+ } catch (error) {
61
+ return { enabled: false, reason: error.message };
62
+ }
63
+ }
64
+
65
+ // Main execution
66
+ const detection = detectParallelExecution();
67
+
68
+ // Basic parallel execution status
69
+ const parallelStatus = detection.enabled
70
+ ? '✅ Agentful ready (parallel execution: ON)'
71
+ : '⚠️ Agentful ready (parallel execution: OFF - agents will run sequentially)';
72
+
73
+ // Try to show intelligent context awareness
74
+ if (contextModule && contextModule.generateSessionStartMessage) {
75
+ try {
76
+ const message = contextModule.generateSessionStartMessage(process.cwd());
77
+ console.log(parallelStatus);
78
+ console.log('');
79
+ console.log(message);
80
+ } catch (error) {
81
+ // Fall back to basic status
82
+ console.log(parallelStatus);
83
+ if (process.env.VERBOSE) {
84
+ console.log(` Context awareness error: ${error.message}`);
85
+ }
86
+ }
87
+ } else {
88
+ // No context module - show basic status
89
+ console.log(parallelStatus);
90
+ if (process.env.VERBOSE && !detection.enabled) {
91
+ console.log(` Reason: ${detection.reason}`);
92
+ }
93
+ }