@appiq/flutter-workflow 1.4.3 → 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,565 @@
1
+ /**
2
+ * AppIQ Flutter Workflow - Independent Agent Tracking System
3
+ * Enables individual agents to work autonomously with full tracking and history
4
+ */
5
+
6
+ const fs = require('fs').promises;
7
+ const path = require('path');
8
+
9
+ class IndependentAgentTracker {
10
+ constructor(basePath = 'docs/independent-sessions') {
11
+ this.basePath = basePath;
12
+ this.sessionsPath = basePath;
13
+ this.activeSessions = new Map();
14
+ }
15
+
16
+ /**
17
+ * Initialize independent session for an agent
18
+ */
19
+ async startIndependentSession(agentName, taskDescription, relatedFeature = null) {
20
+ const sessionId = `${agentName}_${Date.now()}`;
21
+ const sessionData = {
22
+ sessionId,
23
+ agentName,
24
+ taskDescription,
25
+ relatedFeature,
26
+ startTime: new Date().toISOString(),
27
+ status: 'active',
28
+ mode: 'independent',
29
+ activities: [],
30
+ tracking: {
31
+ progress: 0,
32
+ currentActivity: 'initializing',
33
+ estimatedDuration: 'unknown',
34
+ actualDuration: null
35
+ },
36
+ quality: {
37
+ checks: [],
38
+ validations: [],
39
+ issues: []
40
+ },
41
+ collaboration: {
42
+ feedbackRequests: [],
43
+ agentCoordination: [],
44
+ escalations: []
45
+ },
46
+ deliverables: [],
47
+ nextSteps: []
48
+ };
49
+
50
+ // Ensure directory exists
51
+ await this.ensureDirectoryExists();
52
+
53
+ // Save session
54
+ await this.saveSession(sessionId, sessionData);
55
+ this.activeSessions.set(sessionId, sessionData);
56
+
57
+ // Log session start
58
+ await this.logActivity(sessionId, 'session_started', {
59
+ description: `Started independent session for ${agentName}`,
60
+ task: taskDescription,
61
+ relatedFeature
62
+ });
63
+
64
+ console.log(`🚀 Started independent session: ${sessionId}`);
65
+ return sessionData;
66
+ }
67
+
68
+ /**
69
+ * Find related feature for current task
70
+ */
71
+ async findRelatedFeature(taskDescription) {
72
+ try {
73
+ const featuresPath = 'docs/features';
74
+ const files = await fs.readdir(featuresPath);
75
+ const featureFiles = files.filter(file => file.endsWith('.md') && !file.includes('_state') && !file.includes('_history'));
76
+
77
+ const relatedFeatures = [];
78
+
79
+ for (const file of featureFiles) {
80
+ const featurePath = path.join(featuresPath, file);
81
+ const content = await fs.readFile(featurePath, 'utf8');
82
+
83
+ // Simple keyword matching - could be enhanced with better NLP
84
+ const keywords = this.extractKeywords(taskDescription.toLowerCase());
85
+ const featureContent = content.toLowerCase();
86
+
87
+ let relevanceScore = 0;
88
+ for (const keyword of keywords) {
89
+ if (featureContent.includes(keyword)) {
90
+ relevanceScore++;
91
+ }
92
+ }
93
+
94
+ if (relevanceScore > 0) {
95
+ relatedFeatures.push({
96
+ featureName: file.replace('.md', ''),
97
+ relevanceScore,
98
+ filePath: featurePath
99
+ });
100
+ }
101
+ }
102
+
103
+ // Sort by relevance
104
+ relatedFeatures.sort((a, b) => b.relevanceScore - a.relevanceScore);
105
+
106
+ return relatedFeatures.slice(0, 3); // Return top 3 matches
107
+ } catch (error) {
108
+ console.warn('Could not search for related features:', error.message);
109
+ return [];
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Update existing feature with independent agent work
115
+ */
116
+ async updateFeatureFromSession(sessionId, featureName) {
117
+ const session = await this.getSession(sessionId);
118
+ if (!session) {
119
+ throw new Error(`Session not found: ${sessionId}`);
120
+ }
121
+
122
+ // Update feature state if it exists
123
+ try {
124
+ const { FeatureStateManager } = require('./state-manager.js');
125
+ const stateManager = new FeatureStateManager();
126
+
127
+ await stateManager.updateAgentProgress(featureName, session.agentName, {
128
+ status: 'active',
129
+ progress: session.tracking.progress,
130
+ lastActivity: new Date().toISOString(),
131
+ independentSession: sessionId,
132
+ tasks: session.activities.map(activity => ({
133
+ id: activity.id,
134
+ description: activity.description,
135
+ status: activity.status || 'completed',
136
+ timestamp: activity.timestamp
137
+ }))
138
+ });
139
+
140
+ // Log coordination with feature workflow
141
+ await this.logActivity(sessionId, 'feature_coordination', {
142
+ description: `Coordinated with feature workflow: ${featureName}`,
143
+ featureName,
144
+ agentUpdated: session.agentName
145
+ });
146
+
147
+ console.log(`🔄 Updated feature ${featureName} with session ${sessionId} progress`);
148
+ return true;
149
+ } catch (error) {
150
+ console.warn(`Could not update feature ${featureName}:`, error.message);
151
+ return false;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Create standalone component outside feature workflow
157
+ */
158
+ async createStandaloneComponent(sessionId, componentName, componentType = 'widget') {
159
+ const session = await this.getSession(sessionId);
160
+ if (!session) {
161
+ throw new Error(`Session not found: ${sessionId}`);
162
+ }
163
+
164
+ const component = {
165
+ name: componentName,
166
+ type: componentType,
167
+ createdAt: new Date().toISOString(),
168
+ agentCreator: session.agentName,
169
+ sessionId: sessionId,
170
+ filePath: `lib/shared/widgets/${componentName.toLowerCase()}.dart`,
171
+ standalone: true,
172
+ relatedFeatures: [],
173
+ quality: {
174
+ responsive: false,
175
+ accessible: false,
176
+ performant: false,
177
+ tested: false
178
+ }
179
+ };
180
+
181
+ // Add to session deliverables
182
+ session.deliverables.push(component);
183
+ await this.saveSession(sessionId, session);
184
+
185
+ // Log component creation
186
+ await this.logActivity(sessionId, 'component_created', {
187
+ description: `Created standalone component: ${componentName}`,
188
+ componentName,
189
+ componentType,
190
+ filePath: component.filePath
191
+ });
192
+
193
+ console.log(`🎨 Created standalone component: ${componentName}`);
194
+ return component;
195
+ }
196
+
197
+ /**
198
+ * Log activity in independent session
199
+ */
200
+ async logActivity(sessionId, activityType, details = {}) {
201
+ const session = await this.getSession(sessionId);
202
+ if (!session) {
203
+ throw new Error(`Session not found: ${sessionId}`);
204
+ }
205
+
206
+ const activity = {
207
+ id: `activity_${Date.now()}`,
208
+ type: activityType,
209
+ timestamp: new Date().toISOString(),
210
+ agent: session.agentName,
211
+ description: details.description || activityType,
212
+ ...details
213
+ };
214
+
215
+ session.activities.push(activity);
216
+ session.tracking.currentActivity = activityType;
217
+ session.tracking.lastUpdate = activity.timestamp;
218
+
219
+ await this.saveSession(sessionId, session);
220
+
221
+ // Also log to central activity log
222
+ await this.logToCentralActivity(activity);
223
+
224
+ return activity;
225
+ }
226
+
227
+ /**
228
+ * Update session progress and status
229
+ */
230
+ async updateProgress(sessionId, progress, status = null, currentActivity = null) {
231
+ const session = await this.getSession(sessionId);
232
+ if (!session) {
233
+ throw new Error(`Session not found: ${sessionId}`);
234
+ }
235
+
236
+ session.tracking.progress = Math.max(0, Math.min(100, progress));
237
+ if (status) session.status = status;
238
+ if (currentActivity) session.tracking.currentActivity = currentActivity;
239
+
240
+ await this.saveSession(sessionId, session);
241
+
242
+ await this.logActivity(sessionId, 'progress_update', {
243
+ description: `Progress updated to ${progress}%`,
244
+ progress,
245
+ status,
246
+ currentActivity
247
+ });
248
+
249
+ return session;
250
+ }
251
+
252
+ /**
253
+ * Validate quality of independent work
254
+ */
255
+ async validateQuality(sessionId, checks = []) {
256
+ const session = await this.getSession(sessionId);
257
+ if (!session) {
258
+ throw new Error(`Session not found: ${sessionId}`);
259
+ }
260
+
261
+ const validation = {
262
+ timestamp: new Date().toISOString(),
263
+ checks: checks.length > 0 ? checks : ['responsive', 'accessible', 'performant', 'consistent'],
264
+ results: {},
265
+ overall: 'pending'
266
+ };
267
+
268
+ // Perform quality checks (placeholder - would integrate with actual validation)
269
+ for (const check of validation.checks) {
270
+ validation.results[check] = await this.performQualityCheck(check, session);
271
+ }
272
+
273
+ // Determine overall result
274
+ const passed = Object.values(validation.results).filter(result => result === true).length;
275
+ const total = validation.checks.length;
276
+ validation.overall = passed === total ? 'passed' : passed >= total * 0.8 ? 'warning' : 'failed';
277
+
278
+ session.quality.validations.push(validation);
279
+ await this.saveSession(sessionId, session);
280
+
281
+ await this.logActivity(sessionId, 'quality_validation', {
282
+ description: `Quality validation completed: ${validation.overall}`,
283
+ validation
284
+ });
285
+
286
+ return validation;
287
+ }
288
+
289
+ /**
290
+ * Request feedback from user
291
+ */
292
+ async requestFeedback(sessionId, feedbackType, context = {}) {
293
+ const session = await this.getSession(sessionId);
294
+ if (!session) {
295
+ throw new Error(`Session not found: ${sessionId}`);
296
+ }
297
+
298
+ const feedbackRequest = {
299
+ id: `feedback_${Date.now()}`,
300
+ type: feedbackType,
301
+ timestamp: new Date().toISOString(),
302
+ context,
303
+ status: 'pending',
304
+ response: null
305
+ };
306
+
307
+ session.collaboration.feedbackRequests.push(feedbackRequest);
308
+ await this.saveSession(sessionId, session);
309
+
310
+ await this.logActivity(sessionId, 'feedback_requested', {
311
+ description: `Requested ${feedbackType} feedback`,
312
+ feedbackType,
313
+ context
314
+ });
315
+
316
+ return feedbackRequest;
317
+ }
318
+
319
+ /**
320
+ * Coordinate with other agents
321
+ */
322
+ async coordinateWithAgents(sessionId, agents, purpose, context = {}) {
323
+ const session = await this.getSession(sessionId);
324
+ if (!session) {
325
+ throw new Error(`Session not found: ${sessionId}`);
326
+ }
327
+
328
+ const coordination = {
329
+ id: `coordination_${Date.now()}`,
330
+ timestamp: new Date().toISOString(),
331
+ initiatingAgent: session.agentName,
332
+ targetAgents: agents,
333
+ purpose,
334
+ context,
335
+ status: 'initiated',
336
+ responses: []
337
+ };
338
+
339
+ session.collaboration.agentCoordination.push(coordination);
340
+ await this.saveSession(sessionId, session);
341
+
342
+ await this.logActivity(sessionId, 'agent_coordination', {
343
+ description: `Coordinating with agents: ${agents.join(', ')}`,
344
+ purpose,
345
+ targetAgents: agents
346
+ });
347
+
348
+ return coordination;
349
+ }
350
+
351
+ /**
352
+ * Escalate to full workflow
353
+ */
354
+ async escalateToWorkflow(sessionId, reason, complexity = 'medium') {
355
+ const session = await this.getSession(sessionId);
356
+ if (!session) {
357
+ throw new Error(`Session not found: ${sessionId}`);
358
+ }
359
+
360
+ const escalation = {
361
+ id: `escalation_${Date.now()}`,
362
+ timestamp: new Date().toISOString(),
363
+ reason,
364
+ complexity,
365
+ sessionId,
366
+ agentName: session.agentName,
367
+ taskDescription: session.taskDescription,
368
+ currentProgress: session.tracking.progress,
369
+ workDone: session.activities,
370
+ status: 'pending'
371
+ };
372
+
373
+ session.collaboration.escalations.push(escalation);
374
+ session.status = 'escalated';
375
+ await this.saveSession(sessionId, session);
376
+
377
+ await this.logActivity(sessionId, 'workflow_escalation', {
378
+ description: `Escalated to full workflow: ${reason}`,
379
+ reason,
380
+ complexity
381
+ });
382
+
383
+ // Notify FeatureMaster about escalation
384
+ await this.notifyFeatureMaster(escalation);
385
+
386
+ return escalation;
387
+ }
388
+
389
+ /**
390
+ * Complete independent session
391
+ */
392
+ async completeSession(sessionId, summary = '') {
393
+ const session = await this.getSession(sessionId);
394
+ if (!session) {
395
+ throw new Error(`Session not found: ${sessionId}`);
396
+ }
397
+
398
+ session.status = 'completed';
399
+ session.endTime = new Date().toISOString();
400
+ session.tracking.actualDuration = new Date(session.endTime) - new Date(session.startTime);
401
+ session.tracking.progress = 100;
402
+ session.summary = summary;
403
+
404
+ await this.saveSession(sessionId, session);
405
+
406
+ await this.logActivity(sessionId, 'session_completed', {
407
+ description: `Session completed successfully`,
408
+ summary,
409
+ duration: session.tracking.actualDuration
410
+ });
411
+
412
+ this.activeSessions.delete(sessionId);
413
+ console.log(`✅ Completed independent session: ${sessionId}`);
414
+ return session;
415
+ }
416
+
417
+ /**
418
+ * Get session health status
419
+ */
420
+ async getSessionHealth(sessionId = null) {
421
+ if (sessionId) {
422
+ const session = await this.getSession(sessionId);
423
+ return this.analyzeSessionHealth(session);
424
+ }
425
+
426
+ // Analyze all active sessions
427
+ const healthReport = {
428
+ timestamp: new Date().toISOString(),
429
+ totalSessions: this.activeSessions.size,
430
+ healthySessions: 0,
431
+ warningSessions: 0,
432
+ criticalSessions: 0,
433
+ sessions: {}
434
+ };
435
+
436
+ for (const [id, session] of this.activeSessions) {
437
+ const health = this.analyzeSessionHealth(session);
438
+ healthReport.sessions[id] = health;
439
+
440
+ if (health.status === 'healthy') healthReport.healthySessions++;
441
+ else if (health.status === 'warning') healthReport.warningSessions++;
442
+ else healthReport.criticalSessions++;
443
+ }
444
+
445
+ return healthReport;
446
+ }
447
+
448
+ // Helper methods
449
+
450
+ async ensureDirectoryExists() {
451
+ try {
452
+ await fs.mkdir(this.basePath, { recursive: true });
453
+ } catch (error) {
454
+ if (error.code !== 'EEXIST') throw error;
455
+ }
456
+ }
457
+
458
+ async saveSession(sessionId, sessionData) {
459
+ const filePath = path.join(this.basePath, `${sessionId}.json`);
460
+ await fs.writeFile(filePath, JSON.stringify(sessionData, null, 2));
461
+ }
462
+
463
+ async getSession(sessionId) {
464
+ try {
465
+ if (this.activeSessions.has(sessionId)) {
466
+ return this.activeSessions.get(sessionId);
467
+ }
468
+
469
+ const filePath = path.join(this.basePath, `${sessionId}.json`);
470
+ const data = await fs.readFile(filePath, 'utf8');
471
+ const session = JSON.parse(data);
472
+
473
+ if (session.status === 'active') {
474
+ this.activeSessions.set(sessionId, session);
475
+ }
476
+
477
+ return session;
478
+ } catch (error) {
479
+ return null;
480
+ }
481
+ }
482
+
483
+ extractKeywords(text) {
484
+ const commonWords = ['the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'can', 'must', 'a', 'an'];
485
+ return text.split(/\s+/)
486
+ .filter(word => word.length > 2 && !commonWords.includes(word))
487
+ .slice(0, 10); // Top 10 keywords
488
+ }
489
+
490
+ async performQualityCheck(checkType, session) {
491
+ // Placeholder for actual quality checks
492
+ // In real implementation, this would integrate with testing frameworks
493
+ console.log(`🔍 Performing ${checkType} quality check for session ${session.sessionId}`);
494
+ return Math.random() > 0.2; // 80% pass rate for demo
495
+ }
496
+
497
+ async logToCentralActivity(activity) {
498
+ try {
499
+ const centralLogPath = 'docs/independent-sessions/activity-log.json';
500
+
501
+ let log = { activities: [] };
502
+ try {
503
+ const existingLog = await fs.readFile(centralLogPath, 'utf8');
504
+ log = JSON.parse(existingLog);
505
+ } catch {
506
+ // File doesn't exist, start fresh
507
+ }
508
+
509
+ log.activities.push(activity);
510
+
511
+ // Keep only last 1000 activities
512
+ if (log.activities.length > 1000) {
513
+ log.activities = log.activities.slice(-1000);
514
+ }
515
+
516
+ await fs.writeFile(centralLogPath, JSON.stringify(log, null, 2));
517
+ } catch (error) {
518
+ console.warn('Could not log to central activity log:', error.message);
519
+ }
520
+ }
521
+
522
+ async notifyFeatureMaster(escalation) {
523
+ // Placeholder for FeatureMaster notification
524
+ console.log(`📢 Notifying FeatureMaster about escalation: ${escalation.id}`);
525
+ // In real implementation, this would trigger FeatureMaster workflow
526
+ }
527
+
528
+ analyzeSessionHealth(session) {
529
+ if (!session) return { status: 'error', message: 'Session not found' };
530
+
531
+ const issues = [];
532
+ const now = new Date();
533
+ const sessionStart = new Date(session.startTime);
534
+ const hoursActive = (now - sessionStart) / (1000 * 60 * 60);
535
+
536
+ // Check session age
537
+ if (hoursActive > 24 && session.status === 'active') {
538
+ issues.push('Session active for over 24 hours');
539
+ }
540
+
541
+ // Check progress
542
+ if (session.tracking.progress < 10 && hoursActive > 2) {
543
+ issues.push('Low progress after significant time');
544
+ }
545
+
546
+ // Check activity
547
+ const lastActivity = session.activities[session.activities.length - 1];
548
+ if (lastActivity) {
549
+ const hoursSinceActivity = (now - new Date(lastActivity.timestamp)) / (1000 * 60 * 60);
550
+ if (hoursSinceActivity > 4 && session.status === 'active') {
551
+ issues.push('No activity for over 4 hours');
552
+ }
553
+ }
554
+
555
+ return {
556
+ status: issues.length === 0 ? 'healthy' : issues.length < 3 ? 'warning' : 'critical',
557
+ issues,
558
+ progress: session.tracking.progress,
559
+ duration: hoursActive,
560
+ lastActivity: lastActivity?.timestamp || session.startTime
561
+ };
562
+ }
563
+ }
564
+
565
+ module.exports = { IndependentAgentTracker };