@appiq/flutter-workflow 1.4.4 → 2.1.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,526 @@
1
+ /**
2
+ * AppIQ Flutter Workflow - State Management System
3
+ * Provides crash recovery, state preservation, and coordination utilities
4
+ */
5
+
6
+ const fs = require('fs').promises;
7
+ const path = require('path');
8
+
9
+ class FeatureStateManager {
10
+ constructor(basePath = 'docs/features') {
11
+ this.basePath = basePath;
12
+ this.stateCache = new Map();
13
+ this.backupInterval = 300000; // 5 minutes
14
+ this.maxBackups = 10;
15
+ }
16
+
17
+ /**
18
+ * Initialize state management for a feature
19
+ */
20
+ async initializeFeatureState(featureName, initialData = {}) {
21
+ const stateData = {
22
+ featureName,
23
+ status: 'initialized',
24
+ currentPhase: 'planning',
25
+ currentAgent: 'feature-manager',
26
+ createdDate: new Date().toISOString(),
27
+ lastUpdated: new Date().toISOString(),
28
+ version: '1.0.0',
29
+ agents: {
30
+ 'po-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
31
+ 'ui-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
32
+ 'cubit-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
33
+ 'domain-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
34
+ 'data-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
35
+ 'security-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
36
+ 'test-agent': { status: 'pending', progress: 0, tasks: [], lastActivity: null },
37
+ 'integration-validator': { status: 'pending', progress: 0, tasks: [], lastActivity: null }
38
+ },
39
+ qualityGates: {
40
+ requirements: false,
41
+ ui: false,
42
+ state: false,
43
+ domain: false,
44
+ data: false,
45
+ security: false,
46
+ testing: false,
47
+ integration: false
48
+ },
49
+ parallelExecution: {
50
+ active: false,
51
+ groups: [],
52
+ coordination: {}
53
+ },
54
+ blockers: [],
55
+ deploymentReadiness: false,
56
+ crashRecovery: {
57
+ enabled: true,
58
+ lastBackup: new Date().toISOString(),
59
+ recoveryPoints: []
60
+ },
61
+ performance: {
62
+ startTime: new Date().toISOString(),
63
+ agentDurations: {},
64
+ totalDuration: null,
65
+ efficiency: {}
66
+ },
67
+ ...initialData
68
+ };
69
+
70
+ await this.saveFeatureState(featureName, stateData);
71
+ this.stateCache.set(featureName, stateData);
72
+
73
+ // Start automatic backup
74
+ this.scheduleBackup(featureName);
75
+
76
+ return stateData;
77
+ }
78
+
79
+ /**
80
+ * Load feature state from file
81
+ */
82
+ async loadFeatureState(featureName) {
83
+ try {
84
+ // Check cache first
85
+ if (this.stateCache.has(featureName)) {
86
+ return this.stateCache.get(featureName);
87
+ }
88
+
89
+ const stateFile = path.join(this.basePath, `${featureName}_state.json`);
90
+ const stateData = JSON.parse(await fs.readFile(stateFile, 'utf8'));
91
+
92
+ // Validate state integrity
93
+ this.validateStateIntegrity(stateData);
94
+
95
+ this.stateCache.set(featureName, stateData);
96
+ return stateData;
97
+ } catch (error) {
98
+ console.warn(`Could not load state for ${featureName}:`, error.message);
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Save feature state to file with backup
105
+ */
106
+ async saveFeatureState(featureName, stateData) {
107
+ try {
108
+ stateData.lastUpdated = new Date().toISOString();
109
+ stateData.version = this.incrementVersion(stateData.version || '1.0.0');
110
+
111
+ const stateFile = path.join(this.basePath, `${featureName}_state.json`);
112
+
113
+ // Create backup of current state
114
+ await this.createBackup(featureName, stateData);
115
+
116
+ // Save new state
117
+ await fs.writeFile(stateFile, JSON.stringify(stateData, null, 2));
118
+
119
+ // Update cache
120
+ this.stateCache.set(featureName, stateData);
121
+
122
+ console.log(`✅ State saved for ${featureName} - Version ${stateData.version}`);
123
+ return true;
124
+ } catch (error) {
125
+ console.error(`❌ Failed to save state for ${featureName}:`, error.message);
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Update agent status and progress
132
+ */
133
+ async updateAgentProgress(featureName, agentName, updates) {
134
+ const state = await this.loadFeatureState(featureName);
135
+ if (!state) {
136
+ throw new Error(`Feature state not found: ${featureName}`);
137
+ }
138
+
139
+ if (!state.agents[agentName]) {
140
+ throw new Error(`Agent not found: ${agentName}`);
141
+ }
142
+
143
+ // Update agent data
144
+ Object.assign(state.agents[agentName], {
145
+ ...updates,
146
+ lastActivity: new Date().toISOString()
147
+ });
148
+
149
+ // Update overall progress
150
+ const totalAgents = Object.keys(state.agents).length;
151
+ const completedAgents = Object.values(state.agents).filter(agent => agent.status === 'completed').length;
152
+ state.overallProgress = Math.round((completedAgents / totalAgents) * 100);
153
+
154
+ // Update current agent if status changed
155
+ if (updates.status === 'active') {
156
+ state.currentAgent = agentName;
157
+ } else if (updates.status === 'completed') {
158
+ state.currentAgent = this.getNextAgent(state, agentName);
159
+ }
160
+
161
+ await this.saveFeatureState(featureName, state);
162
+ return state;
163
+ }
164
+
165
+ /**
166
+ * Create recovery checkpoint
167
+ */
168
+ async createRecoveryCheckpoint(featureName, description = 'Manual checkpoint') {
169
+ const state = await this.loadFeatureState(featureName);
170
+ if (!state) {
171
+ throw new Error(`Feature state not found: ${featureName}`);
172
+ }
173
+
174
+ const checkpoint = {
175
+ id: `checkpoint_${Date.now()}`,
176
+ timestamp: new Date().toISOString(),
177
+ description,
178
+ state: JSON.parse(JSON.stringify(state)), // Deep copy
179
+ gitCommit: await this.getCurrentGitCommit(),
180
+ fileSystem: await this.captureFileSystemState(featureName)
181
+ };
182
+
183
+ state.crashRecovery.recoveryPoints.push(checkpoint);
184
+
185
+ // Keep only the latest 10 checkpoints
186
+ if (state.crashRecovery.recoveryPoints.length > this.maxBackups) {
187
+ state.crashRecovery.recoveryPoints = state.crashRecovery.recoveryPoints.slice(-this.maxBackups);
188
+ }
189
+
190
+ await this.saveFeatureState(featureName, state);
191
+ console.log(`📋 Recovery checkpoint created: ${checkpoint.id}`);
192
+ return checkpoint;
193
+ }
194
+
195
+ /**
196
+ * Restore from recovery checkpoint
197
+ */
198
+ async restoreFromCheckpoint(featureName, checkpointId = null) {
199
+ const state = await this.loadFeatureState(featureName);
200
+ if (!state) {
201
+ throw new Error(`Feature state not found: ${featureName}`);
202
+ }
203
+
204
+ const checkpoint = checkpointId
205
+ ? state.crashRecovery.recoveryPoints.find(cp => cp.id === checkpointId)
206
+ : state.crashRecovery.recoveryPoints[state.crashRecovery.recoveryPoints.length - 1];
207
+
208
+ if (!checkpoint) {
209
+ throw new Error(`Recovery checkpoint not found: ${checkpointId || 'latest'}`);
210
+ }
211
+
212
+ // Restore state
213
+ const restoredState = checkpoint.state;
214
+ restoredState.lastUpdated = new Date().toISOString();
215
+ restoredState.status = 'recovered';
216
+
217
+ await this.saveFeatureState(featureName, restoredState);
218
+
219
+ console.log(`🔄 Restored from checkpoint: ${checkpoint.id} (${checkpoint.description})`);
220
+ return restoredState;
221
+ }
222
+
223
+ /**
224
+ * Handle parallel agent coordination
225
+ */
226
+ async startParallelExecution(featureName, groupName, agents) {
227
+ const state = await this.loadFeatureState(featureName);
228
+ if (!state) {
229
+ throw new Error(`Feature state not found: ${featureName}`);
230
+ }
231
+
232
+ state.parallelExecution.active = true;
233
+ state.parallelExecution.groups.push({
234
+ name: groupName,
235
+ agents,
236
+ startTime: new Date().toISOString(),
237
+ status: 'active',
238
+ coordination: {
239
+ sharedContext: {},
240
+ conflictResolution: 'priority-based',
241
+ progressTracking: {}
242
+ }
243
+ });
244
+
245
+ // Update agent statuses
246
+ agents.forEach(agentName => {
247
+ if (state.agents[agentName]) {
248
+ state.agents[agentName].status = 'active';
249
+ state.agents[agentName].parallelGroup = groupName;
250
+ }
251
+ });
252
+
253
+ await this.saveFeatureState(featureName, state);
254
+ console.log(`⚡ Started parallel execution: ${groupName} with agents [${agents.join(', ')}]`);
255
+ return state;
256
+ }
257
+
258
+ /**
259
+ * Complete parallel execution group
260
+ */
261
+ async completeParallelExecution(featureName, groupName) {
262
+ const state = await this.loadFeatureState(featureName);
263
+ if (!state) {
264
+ throw new Error(`Feature state not found: ${featureName}`);
265
+ }
266
+
267
+ const group = state.parallelExecution.groups.find(g => g.name === groupName);
268
+ if (!group) {
269
+ throw new Error(`Parallel group not found: ${groupName}`);
270
+ }
271
+
272
+ group.status = 'completed';
273
+ group.endTime = new Date().toISOString();
274
+ group.duration = new Date(group.endTime) - new Date(group.startTime);
275
+
276
+ // Check if all parallel groups are completed
277
+ const activeGroups = state.parallelExecution.groups.filter(g => g.status === 'active');
278
+ if (activeGroups.length === 0) {
279
+ state.parallelExecution.active = false;
280
+ }
281
+
282
+ await this.saveFeatureState(featureName, state);
283
+ console.log(`✅ Completed parallel execution: ${groupName}`);
284
+ return state;
285
+ }
286
+
287
+ /**
288
+ * Validate quality gate
289
+ */
290
+ async validateQualityGate(featureName, gateName, criteria = {}) {
291
+ const state = await this.loadFeatureState(featureName);
292
+ if (!state) {
293
+ throw new Error(`Feature state not found: ${featureName}`);
294
+ }
295
+
296
+ const validation = {
297
+ gate: gateName,
298
+ timestamp: new Date().toISOString(),
299
+ criteria,
300
+ result: 'pending',
301
+ issues: []
302
+ };
303
+
304
+ // Perform validation logic here
305
+ // This would typically call external validation systems
306
+ const passed = await this.performQualityValidation(gateName, criteria);
307
+
308
+ validation.result = passed ? 'passed' : 'failed';
309
+ state.qualityGates[gateName] = passed;
310
+
311
+ if (!passed) {
312
+ state.blockers.push({
313
+ type: 'quality-gate-failure',
314
+ gate: gateName,
315
+ timestamp: validation.timestamp,
316
+ description: `Quality gate ${gateName} failed validation`,
317
+ resolution: 'pending'
318
+ });
319
+ }
320
+
321
+ await this.saveFeatureState(featureName, state);
322
+ console.log(`🚦 Quality gate ${gateName}: ${validation.result.toUpperCase()}`);
323
+ return validation;
324
+ }
325
+
326
+ /**
327
+ * Health check for feature state
328
+ */
329
+ async performHealthCheck(featureName = null) {
330
+ const healthReport = {
331
+ timestamp: new Date().toISOString(),
332
+ overall: 'healthy',
333
+ features: {},
334
+ system: {
335
+ stateManagerVersion: '1.0.0',
336
+ cacheSize: this.stateCache.size,
337
+ backupSystem: 'operational'
338
+ }
339
+ };
340
+
341
+ try {
342
+ if (featureName) {
343
+ // Check specific feature
344
+ const state = await this.loadFeatureState(featureName);
345
+ healthReport.features[featureName] = this.analyzeFeatureHealth(state);
346
+ } else {
347
+ // Check all features
348
+ const featureFiles = await fs.readdir(this.basePath);
349
+ const stateFiles = featureFiles.filter(file => file.endsWith('_state.json'));
350
+
351
+ for (const stateFile of stateFiles) {
352
+ const fName = stateFile.replace('_state.json', '');
353
+ const state = await this.loadFeatureState(fName);
354
+ healthReport.features[fName] = this.analyzeFeatureHealth(state);
355
+ }
356
+ }
357
+
358
+ // Determine overall health
359
+ const featureHealths = Object.values(healthReport.features);
360
+ const unhealthyFeatures = featureHealths.filter(health => health.status !== 'healthy');
361
+
362
+ if (unhealthyFeatures.length > 0) {
363
+ healthReport.overall = unhealthyFeatures.length > featureHealths.length / 2 ? 'critical' : 'warning';
364
+ }
365
+
366
+ } catch (error) {
367
+ healthReport.overall = 'error';
368
+ healthReport.error = error.message;
369
+ }
370
+
371
+ return healthReport;
372
+ }
373
+
374
+ // Helper methods
375
+
376
+ validateStateIntegrity(state) {
377
+ const required = ['featureName', 'status', 'agents', 'qualityGates'];
378
+ for (const field of required) {
379
+ if (!state[field]) {
380
+ throw new Error(`Invalid state: missing ${field}`);
381
+ }
382
+ }
383
+ }
384
+
385
+ incrementVersion(version) {
386
+ const [major, minor, patch] = version.split('.').map(Number);
387
+ return `${major}.${minor}.${patch + 1}`;
388
+ }
389
+
390
+ async createBackup(featureName, stateData) {
391
+ try {
392
+ const backupFile = path.join(this.basePath, `${featureName}_state_backup_${Date.now()}.json`);
393
+ await fs.writeFile(backupFile, JSON.stringify(stateData, null, 2));
394
+
395
+ // Clean old backups
396
+ await this.cleanOldBackups(featureName);
397
+ } catch (error) {
398
+ console.warn(`Backup creation failed for ${featureName}:`, error.message);
399
+ }
400
+ }
401
+
402
+ async cleanOldBackups(featureName) {
403
+ try {
404
+ const files = await fs.readdir(this.basePath);
405
+ const backupFiles = files
406
+ .filter(file => file.startsWith(`${featureName}_state_backup_`))
407
+ .map(file => ({
408
+ name: file,
409
+ path: path.join(this.basePath, file),
410
+ time: parseInt(file.split('_').pop().replace('.json', ''))
411
+ }))
412
+ .sort((a, b) => b.time - a.time);
413
+
414
+ // Keep only the latest backups
415
+ const filesToDelete = backupFiles.slice(this.maxBackups);
416
+ for (const file of filesToDelete) {
417
+ await fs.unlink(file.path);
418
+ }
419
+ } catch (error) {
420
+ console.warn(`Backup cleanup failed for ${featureName}:`, error.message);
421
+ }
422
+ }
423
+
424
+ getNextAgent(state, currentAgent) {
425
+ const agentOrder = [
426
+ 'po-agent', 'ui-agent', 'domain-agent', 'cubit-agent',
427
+ 'data-agent', 'security-agent', 'test-agent', 'integration-validator'
428
+ ];
429
+
430
+ const currentIndex = agentOrder.indexOf(currentAgent);
431
+ if (currentIndex === -1 || currentIndex === agentOrder.length - 1) {
432
+ return 'feature-manager'; // Last agent or unknown
433
+ }
434
+
435
+ return agentOrder[currentIndex + 1];
436
+ }
437
+
438
+ async getCurrentGitCommit() {
439
+ try {
440
+ const { exec } = require('child_process');
441
+ return new Promise((resolve) => {
442
+ exec('git rev-parse HEAD', (error, stdout) => {
443
+ resolve(error ? 'unknown' : stdout.trim());
444
+ });
445
+ });
446
+ } catch {
447
+ return 'unknown';
448
+ }
449
+ }
450
+
451
+ async captureFileSystemState(featureName) {
452
+ try {
453
+ const docsPath = path.join('docs', 'features');
454
+ const tasksPath = path.join('docs', 'tasks');
455
+
456
+ return {
457
+ featureFile: `${docsPath}/${featureName}.md`,
458
+ stateFile: `${docsPath}/${featureName}_state.json`,
459
+ tasksFile: `${tasksPath}/${featureName}_tasks.md`,
460
+ historyFile: `${tasksPath}/${featureName}_history.md`,
461
+ timestamp: new Date().toISOString()
462
+ };
463
+ } catch {
464
+ return {};
465
+ }
466
+ }
467
+
468
+ async performQualityValidation(gateName, criteria) {
469
+ // This would typically integrate with actual validation systems
470
+ // For now, return true as a placeholder
471
+ console.log(`🔍 Validating quality gate: ${gateName}`);
472
+ return true;
473
+ }
474
+
475
+ analyzeFeatureHealth(state) {
476
+ if (!state) {
477
+ return { status: 'error', message: 'State not found' };
478
+ }
479
+
480
+ const issues = [];
481
+
482
+ // Check for blockers
483
+ if (state.blockers && state.blockers.length > 0) {
484
+ issues.push(`${state.blockers.length} active blockers`);
485
+ }
486
+
487
+ // Check agent progress
488
+ const stuckAgents = Object.entries(state.agents)
489
+ .filter(([name, agent]) => {
490
+ const lastActivity = agent.lastActivity ? new Date(agent.lastActivity) : null;
491
+ const hoursSinceActivity = lastActivity ? (Date.now() - lastActivity.getTime()) / (1000 * 60 * 60) : Infinity;
492
+ return agent.status === 'active' && hoursSinceActivity > 24;
493
+ });
494
+
495
+ if (stuckAgents.length > 0) {
496
+ issues.push(`${stuckAgents.length} agents inactive for >24h`);
497
+ }
498
+
499
+ // Check quality gates
500
+ const failedGates = Object.entries(state.qualityGates)
501
+ .filter(([gate, passed]) => !passed).length;
502
+
503
+ if (failedGates > 0) {
504
+ issues.push(`${failedGates} quality gates not passed`);
505
+ }
506
+
507
+ return {
508
+ status: issues.length === 0 ? 'healthy' : issues.length < 3 ? 'warning' : 'critical',
509
+ issues,
510
+ lastUpdate: state.lastUpdated,
511
+ progress: state.overallProgress || 0
512
+ };
513
+ }
514
+
515
+ scheduleBackup(featureName) {
516
+ setInterval(async () => {
517
+ try {
518
+ await this.createRecoveryCheckpoint(featureName, 'Automatic backup');
519
+ } catch (error) {
520
+ console.warn(`Automatic backup failed for ${featureName}:`, error.message);
521
+ }
522
+ }, this.backupInterval);
523
+ }
524
+ }
525
+
526
+ module.exports = { FeatureStateManager };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@appiq/flutter-workflow",
3
- "version": "1.4.4",
4
- "description": "🚀 [BETA] Professional Flutter development with AI-powered agents following Clean Architecture principles - Automated agent-based feature development system by AppIQ Solutions",
3
+ "version": "2.1.0",
4
+ "description": "🚀 Professional Flutter development with AI-powered agent coordination & intelligent parallel execution - Complete workflow system with crash recovery and state management by AppIQ Solutions",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
7
7
  "appiq-workflow": "bin/cli.js"
@@ -56,6 +56,8 @@
56
56
  "bin/",
57
57
  "agents/",
58
58
  "templates/",
59
+ "config/",
60
+ "lib/",
59
61
  "README.md",
60
62
  "LICENSE",
61
63
  "CHANGELOG.md",