@itz4blitz/agentful 1.3.0 → 1.4.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.
@@ -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
+ }
package/lib/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
- * agentful - Lightweight project initialization
2
+ * agentful - Autonomous product development framework
3
3
  *
4
- * The heavy analysis is done by Claude via /agentful-agents and /agentful-skills commands.
5
- * This module just handles template copying and state management.
4
+ * Coordinates specialized AI agents for product development with human-in-the-loop checkpoints.
5
+ * Includes centralized state validation, intelligent context awareness, and quality gates.
6
6
  *
7
7
  * @module agentful
8
+ * @version 1.3.0
8
9
  */
9
10
 
10
11
  export { initProject, copyDirectory, isInitialized, getState } from './init.js';
@@ -29,3 +30,16 @@ export * from './ci/index.js';
29
30
  export * from './server/index.js';
30
31
  export * from './server/auth.js';
31
32
  export * from './server/executor.js';
33
+
34
+ // Export state validation utilities
35
+ export {
36
+ STATE_SCHEMAS,
37
+ validateStateFile,
38
+ recoverStateFile,
39
+ validateAllState,
40
+ getDefaultState,
41
+ isStateFileValid,
42
+ getStateFile,
43
+ updateStateFile,
44
+ formatValidationResults
45
+ } from './state-validator.js';
package/lib/init.js CHANGED
@@ -134,10 +134,104 @@ export async function initProject(targetDir, config = null) {
134
134
  );
135
135
  createdFiles.push('.agentful/conversation-state.json');
136
136
 
137
- // Conversation history
137
+ // Conversation history - Full schema matching TypeScript types
138
+ const now = new Date().toISOString();
138
139
  const conversationHistory = {
139
- messages: [],
140
- created_at: new Date().toISOString()
140
+ _comment: 'Agentful Conversation History State - Tracks all interactions, context, and user preferences',
141
+ _doc: 'This file maintains the complete conversation history with the Agentful CLI.',
142
+ _schema_version: '1.0',
143
+
144
+ version: '1.0',
145
+ schema: 'conversation-history',
146
+
147
+ session: {
148
+ id: null,
149
+ started_at: null,
150
+ last_updated: null,
151
+ message_count: 0,
152
+ active: false,
153
+ mode: 'interactive'
154
+ },
155
+
156
+ conversation: {
157
+ messages: [],
158
+ summary: null,
159
+ key_topics: [],
160
+ user_goals: []
161
+ },
162
+
163
+ context: {
164
+ current_feature: null,
165
+ current_phase: null,
166
+ current_agent: null,
167
+ last_action: null,
168
+ last_action_time: null,
169
+ active_files: [],
170
+ active_branch: null
171
+ },
172
+
173
+ state_integration: {
174
+ _comment: 'References to external state files - read these files for current state',
175
+ state_file: '.agentful/state.json',
176
+ completion_file: '.agentful/completion.json',
177
+ decisions_file: '.agentful/decisions.json'
178
+ },
179
+
180
+ product_context: {
181
+ structure: 'flat',
182
+ current_feature_path: null,
183
+ domain: null,
184
+ all_features: [],
185
+ feature_dependencies: {}
186
+ },
187
+
188
+ user: {
189
+ preferences: {
190
+ verbosity: 'normal',
191
+ auto_approve: false,
192
+ show_thinking: false,
193
+ save_intermediate: false,
194
+ confirmation_style: 'explicit',
195
+ error_handling: 'interactive',
196
+ output_format: 'markdown'
197
+ },
198
+ goals: [],
199
+ constraints: [],
200
+ avoidances: [],
201
+ tech_preferences: [],
202
+ architecture_notes: []
203
+ },
204
+
205
+ agents: {
206
+ active: null,
207
+ history: []
208
+ },
209
+
210
+ skills_invoked: {
211
+ conversation: { count: 0, last_invoked: null },
212
+ product: { count: 0, last_invoked: null },
213
+ architecture: { count: 0, last_invoked: null },
214
+ development: { count: 0, last_invoked: null },
215
+ testing: { count: 0, last_invoked: null },
216
+ documentation: { count: 0, last_invoked: null },
217
+ review: { count: 0, last_invoked: null }
218
+ },
219
+
220
+ metadata: {
221
+ created_at: now,
222
+ created_by: 'agentful-cli',
223
+ environment: {
224
+ platform: process.platform,
225
+ node_version: process.version,
226
+ agentful_version: version
227
+ },
228
+ git_info: {
229
+ branch: null,
230
+ commit: null,
231
+ remote: null
232
+ },
233
+ project_root: targetDir
234
+ }
141
235
  };
142
236
 
143
237
  await fs.writeFile(
package/lib/presets.js CHANGED
@@ -19,7 +19,7 @@ export const presets = {
19
19
  description: 'Complete agentful installation (recommended)',
20
20
  agents: ['orchestrator', 'architect', 'backend', 'frontend', 'tester', 'reviewer', 'fixer', 'product-analyzer'],
21
21
  skills: ['product-tracking', 'validation', 'testing', 'conversation', 'product-planning', 'deployment', 'research'],
22
- hooks: ['health-check', 'block-random-docs', 'block-file-creation', 'product-spec-watcher', 'architect-drift-detector'],
22
+ hooks: ['session-start', 'health-check', 'block-random-docs', 'block-file-creation', 'product-spec-watcher', 'architect-drift-detector', 'analyze-trigger'],
23
23
  gates: ['types', 'tests', 'coverage', 'lint', 'security', 'dead-code']
24
24
  },
25
25
 
@@ -97,6 +97,60 @@ export const hookConfigurations = {
97
97
  command: 'npx prettier --write "$FILE" 2>/dev/null || true',
98
98
  description: 'Auto-format files on save'
99
99
  }
100
+ },
101
+
102
+ 'session-start': {
103
+ event: 'SessionStart',
104
+ config: {
105
+ type: 'command',
106
+ command: 'node .claude/hooks/session-start.js',
107
+ timeout: 3,
108
+ description: 'Intelligent context awareness - shows project status and suggests next steps'
109
+ }
110
+ },
111
+
112
+ 'post-action-suggestions': {
113
+ event: 'PostToolUse',
114
+ matcher: 'SlashCommand',
115
+ config: {
116
+ type: 'command',
117
+ command: 'node .claude/hooks/post-action-suggestions.js',
118
+ timeout: 3,
119
+ description: 'Smart suggestions for what to do next after slash commands'
120
+ }
121
+ },
122
+
123
+ 'analyze-trigger': {
124
+ event: 'PostToolUse',
125
+ matcher: 'Write|Edit',
126
+ config: {
127
+ type: 'command',
128
+ command: 'node bin/hooks/analyze-trigger.js',
129
+ timeout: 3,
130
+ description: 'Check if file changes warrant analysis'
131
+ }
132
+ },
133
+
134
+ 'architect-drift-detector': {
135
+ event: 'PostToolUse',
136
+ matcher: 'Write|Edit',
137
+ config: {
138
+ type: 'command',
139
+ command: 'node bin/hooks/architect-drift-detector.js',
140
+ timeout: 3,
141
+ description: 'Detect when architect needs to re-analyze project'
142
+ }
143
+ },
144
+
145
+ 'product-spec-watcher': {
146
+ event: 'PostToolUse',
147
+ matcher: 'Write|Edit',
148
+ config: {
149
+ type: 'command',
150
+ command: 'node bin/hooks/product-spec-watcher.js',
151
+ timeout: 3,
152
+ description: 'Watch for product spec changes and auto-trigger generation'
153
+ }
100
154
  }
101
155
  };
102
156