aiwf 0.3.21 → 0.3.23

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.
Files changed (140) hide show
  1. package/package.json +3 -2
  2. package/src/cli/index.js +39 -336
  3. package/src/lib/installer.js +310 -90
  4. package/src/utils/messages.js +24 -0
  5. package/src/DEPENDENCY_MAP.md +0 -90
  6. package/src/cli/cache-cli.js +0 -459
  7. package/src/cli/checkpoint-cli.js +0 -417
  8. package/src/cli/language-cli.js +0 -286
  9. package/src/cli/sprint-cli.js +0 -287
  10. package/src/commands/ai-tool.js +0 -383
  11. package/src/commands/compress.js +0 -60
  12. package/src/commands/create-project.js +0 -160
  13. package/src/commands/evaluate.js +0 -459
  14. package/src/commands/persona.js +0 -309
  15. package/src/commands/sprint-task.js +0 -262
  16. package/src/commands/token.js +0 -312
  17. package/src/lib/ai-persona-manager.js +0 -711
  18. package/src/lib/cache-system.js +0 -332
  19. package/src/lib/context-engine.js +0 -568
  20. package/src/lib/github-integration.js +0 -402
  21. package/src/lib/memory-profiler.js +0 -471
  22. package/src/lib/metrics-collector.js +0 -885
  23. package/src/lib/offline-detector.js +0 -386
  24. package/src/lib/resource-loader-enhanced.js +0 -399
  25. package/src/lib/resource-loader.js +0 -250
  26. package/src/lib/resources/commands/ai-persona.js +0 -602
  27. package/src/lib/resources/commands/compress-context.js +0 -389
  28. package/src/lib/resources/commands/evaluate.js +0 -246
  29. package/src/lib/resources/commands/persona-context-apply.js +0 -562
  30. package/src/lib/resources/commands/token-tracking.js +0 -443
  31. package/src/lib/resources/config/commit-patterns.js +0 -103
  32. package/src/lib/resources/config/language.json +0 -6
  33. package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
  34. package/src/lib/resources/personas/analyst.json +0 -44
  35. package/src/lib/resources/personas/architect/best_practices.md +0 -136
  36. package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
  37. package/src/lib/resources/personas/architect.json +0 -44
  38. package/src/lib/resources/personas/backend/best_practices.md +0 -766
  39. package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
  40. package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
  41. package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
  42. package/src/lib/resources/personas/developer.json +0 -44
  43. package/src/lib/resources/personas/developer.md +0 -37
  44. package/src/lib/resources/personas/evaluation_criteria.json +0 -137
  45. package/src/lib/resources/personas/frontend/best_practices.md +0 -445
  46. package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
  47. package/src/lib/resources/personas/persona-index.json +0 -31
  48. package/src/lib/resources/personas/reviewer.json +0 -44
  49. package/src/lib/resources/personas/security/best_practices.md +0 -307
  50. package/src/lib/resources/personas/security/knowledge_base.md +0 -498
  51. package/src/lib/resources/personas/tester.json +0 -44
  52. package/src/lib/resources/templates/README.md +0 -78
  53. package/src/lib/resources/templates/api-server/config.json +0 -64
  54. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
  55. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
  56. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
  57. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
  58. package/src/lib/resources/templates/api-server/template/.env.example +0 -29
  59. package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
  60. package/src/lib/resources/templates/api-server/template/README.md +0 -171
  61. package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
  62. package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
  63. package/src/lib/resources/templates/api-server/template/package.json +0 -61
  64. package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
  65. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
  66. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
  67. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
  68. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
  69. package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
  70. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
  71. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
  72. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
  73. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
  74. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
  75. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
  76. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
  77. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
  78. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
  79. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
  80. package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
  81. package/src/lib/resources/templates/npm-library/config.json +0 -61
  82. package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
  83. package/src/lib/resources/templates/npm-library/template/README.md +0 -201
  84. package/src/lib/resources/templates/npm-library/template/package.json +0 -78
  85. package/src/lib/resources/templates/web-app/config.json +0 -53
  86. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
  87. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
  88. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
  89. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
  90. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
  91. package/src/lib/resources/templates/web-app/template/README.md +0 -151
  92. package/src/lib/resources/templates/web-app/template/index.html +0 -14
  93. package/src/lib/resources/templates/web-app/template/package.json +0 -44
  94. package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
  95. package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
  96. package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
  97. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
  98. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
  99. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
  100. package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
  101. package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
  102. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
  103. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
  104. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
  105. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
  106. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
  107. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
  108. package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
  109. package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
  110. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
  111. package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
  112. package/src/lib/resources/utils/background-monitor.js +0 -223
  113. package/src/lib/resources/utils/compression-metrics.js +0 -1031
  114. package/src/lib/resources/utils/compression-strategies.js +0 -557
  115. package/src/lib/resources/utils/content-normalizer.js +0 -455
  116. package/src/lib/resources/utils/context-compressor.js +0 -583
  117. package/src/lib/resources/utils/context-rule-parser.js +0 -180
  118. package/src/lib/resources/utils/context-token-monitor.js +0 -463
  119. package/src/lib/resources/utils/context-update-manager.js +0 -502
  120. package/src/lib/resources/utils/git-utils.js +0 -220
  121. package/src/lib/resources/utils/importance-classifier.js +0 -645
  122. package/src/lib/resources/utils/information-filter.js +0 -710
  123. package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
  124. package/src/lib/resources/utils/prompt-injector.js +0 -242
  125. package/src/lib/resources/utils/simplified-evaluator.js +0 -119
  126. package/src/lib/resources/utils/text-summarizer.js +0 -492
  127. package/src/lib/resources/utils/token-counter.js +0 -177
  128. package/src/lib/resources/utils/token-monitor.js +0 -484
  129. package/src/lib/resources/utils/token-reporter.js +0 -452
  130. package/src/lib/resources/utils/token-storage.js +0 -433
  131. package/src/lib/resources/utils/token-tracker.js +0 -283
  132. package/src/lib/state/priority-calculator.js +0 -217
  133. package/src/lib/state/state-index.js +0 -155
  134. package/src/lib/state/task-scanner.js +0 -328
  135. package/src/lib/task-analyzer.js +0 -599
  136. package/src/lib/template-cache-system.js +0 -559
  137. package/src/lib/template-downloader.js +0 -467
  138. package/src/lib/template-version-manager.js +0 -491
  139. package/src/lib/token-optimizer.js +0 -517
  140. package/src/utils/engineering-guard.js +0 -399
@@ -1,885 +0,0 @@
1
- /**
2
- * Metrics Collector
3
- * Collects and analyzes metrics for AI persona performance
4
- */
5
-
6
- import fs from 'fs/promises';
7
- import path from 'path';
8
- import { EventEmitter } from 'events';
9
-
10
- class MetricsCollector extends EventEmitter {
11
- constructor(options = {}) {
12
- super();
13
-
14
- this.options = {
15
- metricsPath: options.metricsPath ||
16
- path.join(process.cwd(), '.aiwf', 'metrics'),
17
- maxHistorySize: options.maxHistorySize || 1000,
18
- aggregationInterval: options.aggregationInterval || 3600000, // 1 hour
19
- persistInterval: options.persistInterval || 300000, // 5 minutes
20
- ...options
21
- };
22
-
23
- this.metrics = {
24
- sessions: [],
25
- personaPerformance: {},
26
- taskOutcomes: [],
27
- aggregated: {}
28
- };
29
-
30
- this.currentSession = null;
31
- this.startTime = Date.now();
32
- this.persistTimer = null;
33
- }
34
-
35
- /**
36
- * Initialize metrics collector
37
- */
38
- async init() {
39
- try {
40
- // Ensure metrics directory exists
41
- await fs.mkdir(this.options.metricsPath, { recursive: true });
42
-
43
- // Load existing metrics
44
- await this.loadMetrics();
45
-
46
- // Start persist timer
47
- this.startPersistTimer();
48
-
49
- this.emit('initialized', {
50
- sessionsLoaded: this.metrics.sessions.length,
51
- startTime: this.startTime
52
- });
53
-
54
- return true;
55
- } catch (error) {
56
- this.emit('error', { error, phase: 'initialization' });
57
- throw error;
58
- }
59
- }
60
-
61
- /**
62
- * Start a new metrics session
63
- */
64
- startSession(persona, task) {
65
- // End current session if exists
66
- if (this.currentSession) {
67
- this.endSession();
68
- }
69
-
70
- this.currentSession = {
71
- id: this.generateSessionId(),
72
- persona: persona.name || persona,
73
- task: {
74
- type: task.type || 'general',
75
- description: task.description || task.command || '',
76
- complexity: this.estimateTaskComplexity(task)
77
- },
78
- startTime: Date.now(),
79
- events: [],
80
- metrics: {
81
- tokenUsage: {
82
- start: this.getCurrentTokenCount(),
83
- end: null,
84
- contextSwitches: 0
85
- },
86
- interactions: 0,
87
- errors: 0,
88
- completionStatus: null
89
- }
90
- };
91
-
92
- this.emit('sessionStarted', {
93
- sessionId: this.currentSession.id,
94
- persona: this.currentSession.persona
95
- });
96
-
97
- return this.currentSession.id;
98
- }
99
-
100
- /**
101
- * End current session
102
- */
103
- endSession(status = 'completed') {
104
- if (!this.currentSession) return;
105
-
106
- this.currentSession.endTime = Date.now();
107
- this.currentSession.duration = this.currentSession.endTime - this.currentSession.startTime;
108
- this.currentSession.metrics.completionStatus = status;
109
- this.currentSession.metrics.tokenUsage.end = this.getCurrentTokenCount();
110
-
111
- // Calculate quality metrics
112
- this.currentSession.qualityMetrics = this.calculateQualityMetrics(this.currentSession);
113
-
114
- // Store session
115
- this.metrics.sessions.push(this.currentSession);
116
- this.updatePersonaPerformance(this.currentSession);
117
-
118
- // Limit history size
119
- if (this.metrics.sessions.length > this.options.maxHistorySize) {
120
- this.metrics.sessions = this.metrics.sessions.slice(-this.options.maxHistorySize);
121
- }
122
-
123
- this.emit('sessionEnded', {
124
- sessionId: this.currentSession.id,
125
- duration: this.currentSession.duration,
126
- status
127
- });
128
-
129
- const session = this.currentSession;
130
- this.currentSession = null;
131
-
132
- return session;
133
- }
134
-
135
- /**
136
- * Track an event
137
- */
138
- trackEvent(eventType, data = {}) {
139
- const event = {
140
- type: eventType,
141
- timestamp: Date.now(),
142
- data
143
- };
144
-
145
- // Add to current session if active
146
- if (this.currentSession) {
147
- this.currentSession.events.push(event);
148
-
149
- // Update session metrics based on event
150
- this.updateSessionMetrics(eventType, data);
151
- }
152
-
153
- // Track global events
154
- this.trackGlobalEvent(eventType, data);
155
-
156
- this.emit('eventTracked', event);
157
- }
158
-
159
- /**
160
- * Update session metrics based on event
161
- */
162
- updateSessionMetrics(eventType, data) {
163
- if (!this.currentSession) return;
164
-
165
- switch (eventType) {
166
- case 'interaction':
167
- this.currentSession.metrics.interactions++;
168
- break;
169
-
170
- case 'error':
171
- this.currentSession.metrics.errors++;
172
- break;
173
-
174
- case 'context_switch':
175
- this.currentSession.metrics.tokenUsage.contextSwitches++;
176
- break;
177
-
178
- case 'task_completed':
179
- this.currentSession.metrics.completionStatus = 'completed';
180
- break;
181
-
182
- case 'task_failed':
183
- this.currentSession.metrics.completionStatus = 'failed';
184
- break;
185
- }
186
- }
187
-
188
- /**
189
- * Track global events (not session-specific)
190
- */
191
- trackGlobalEvent(eventType, data) {
192
- // Aggregate event counts
193
- if (!this.metrics.aggregated.eventCounts) {
194
- this.metrics.aggregated.eventCounts = {};
195
- }
196
-
197
- this.metrics.aggregated.eventCounts[eventType] =
198
- (this.metrics.aggregated.eventCounts[eventType] || 0) + 1;
199
-
200
- // Track persona switches
201
- if (eventType === 'persona_switch') {
202
- if (!this.metrics.aggregated.personaSwitches) {
203
- this.metrics.aggregated.personaSwitches = [];
204
- }
205
-
206
- this.metrics.aggregated.personaSwitches.push({
207
- from: data.from,
208
- to: data.to,
209
- trigger: data.trigger,
210
- timestamp: Date.now()
211
- });
212
- }
213
- }
214
-
215
- /**
216
- * Calculate quality metrics for a session
217
- */
218
- calculateQualityMetrics(session) {
219
- const metrics = {
220
- completionTime: session.duration,
221
- tokenEfficiency: this.calculateTokenEfficiency(session),
222
- errorRate: this.calculateErrorRate(session),
223
- interactionEfficiency: this.calculateInteractionEfficiency(session),
224
- overallScore: 0
225
- };
226
-
227
- // Calculate overall score (0-1)
228
- const weights = {
229
- completion: 0.3,
230
- tokenEfficiency: 0.3,
231
- errorRate: 0.2,
232
- interactionEfficiency: 0.2
233
- };
234
-
235
- let score = 0;
236
-
237
- // Completion score
238
- if (session.metrics.completionStatus === 'completed') {
239
- score += weights.completion;
240
- }
241
-
242
- // Token efficiency score
243
- score += metrics.tokenEfficiency * weights.tokenEfficiency;
244
-
245
- // Error rate score (inverse - lower is better)
246
- score += (1 - metrics.errorRate) * weights.errorRate;
247
-
248
- // Interaction efficiency score
249
- score += metrics.interactionEfficiency * weights.interactionEfficiency;
250
-
251
- metrics.overallScore = score;
252
-
253
- return metrics;
254
- }
255
-
256
- /**
257
- * Calculate token efficiency
258
- */
259
- calculateTokenEfficiency(session) {
260
- const tokensUsed = (session.metrics.tokenUsage.end || 0) -
261
- (session.metrics.tokenUsage.start || 0);
262
-
263
- if (tokensUsed === 0) return 1;
264
-
265
- // Estimate expected tokens based on task complexity
266
- const expectedTokens = session.task.complexity * 1000;
267
-
268
- // Efficiency is inverse of usage ratio (lower usage = higher efficiency)
269
- const efficiency = Math.min(expectedTokens / tokensUsed, 1);
270
-
271
- return efficiency;
272
- }
273
-
274
- /**
275
- * Calculate error rate
276
- */
277
- calculateErrorRate(session) {
278
- if (session.metrics.interactions === 0) return 0;
279
-
280
- return Math.min(session.metrics.errors / session.metrics.interactions, 1);
281
- }
282
-
283
- /**
284
- * Calculate interaction efficiency
285
- */
286
- calculateInteractionEfficiency(session) {
287
- if (session.metrics.interactions === 0) return 0;
288
-
289
- // Estimate expected interactions based on task complexity
290
- const expectedInteractions = Math.max(session.task.complexity * 2, 1);
291
-
292
- // Efficiency is inverse of interaction ratio
293
- const efficiency = Math.min(expectedInteractions / session.metrics.interactions, 1);
294
-
295
- return efficiency;
296
- }
297
-
298
- /**
299
- * Estimate task complexity
300
- */
301
- estimateTaskComplexity(task) {
302
- let complexity = 1; // Base complexity
303
-
304
- // Increase based on task type
305
- const complexTypes = ['debug', 'optimize', 'architect', 'refactor'];
306
- if (complexTypes.includes(task.type)) {
307
- complexity += 1;
308
- }
309
-
310
- // Increase based on keywords
311
- const complexKeywords = ['complex', 'difficult', 'large', 'entire', 'all'];
312
- const description = (task.description || '').toLowerCase();
313
- complexKeywords.forEach(keyword => {
314
- if (description.includes(keyword)) {
315
- complexity += 0.5;
316
- }
317
- });
318
-
319
- // Cap at 5
320
- return Math.min(complexity, 5);
321
- }
322
-
323
- /**
324
- * Update persona performance metrics
325
- */
326
- updatePersonaPerformance(session) {
327
- const personaName = session.persona;
328
-
329
- if (!this.metrics.personaPerformance[personaName]) {
330
- this.metrics.personaPerformance[personaName] = {
331
- totalSessions: 0,
332
- completedSessions: 0,
333
- totalDuration: 0,
334
- totalTokens: 0,
335
- totalErrors: 0,
336
- qualityScores: [],
337
- taskTypes: {}
338
- };
339
- }
340
-
341
- const perf = this.metrics.personaPerformance[personaName];
342
-
343
- perf.totalSessions++;
344
- if (session.metrics.completionStatus === 'completed') {
345
- perf.completedSessions++;
346
- }
347
-
348
- perf.totalDuration += session.duration;
349
-
350
- const tokensUsed = (session.metrics.tokenUsage.end || 0) -
351
- (session.metrics.tokenUsage.start || 0);
352
- perf.totalTokens += tokensUsed;
353
-
354
- perf.totalErrors += session.metrics.errors;
355
-
356
- if (session.qualityMetrics) {
357
- perf.qualityScores.push(session.qualityMetrics.overallScore);
358
- }
359
-
360
- // Track task types
361
- const taskType = session.task.type;
362
- perf.taskTypes[taskType] = (perf.taskTypes[taskType] || 0) + 1;
363
- }
364
-
365
- /**
366
- * Get current token count (simulated)
367
- */
368
- getCurrentTokenCount() {
369
- // In a real implementation, this would interface with the token tracking system
370
- // For now, return a simulated increasing value
371
- return Math.floor((Date.now() - this.startTime) / 100);
372
- }
373
-
374
- /**
375
- * Collect current metrics
376
- */
377
- async collectCurrentMetrics() {
378
- if (!this.currentSession) return null;
379
-
380
- return {
381
- duration: Date.now() - this.currentSession.startTime,
382
- interactions: this.currentSession.metrics.interactions,
383
- errors: this.currentSession.metrics.errors,
384
- tokenUsage: {
385
- current: this.getCurrentTokenCount() - this.currentSession.metrics.tokenUsage.start,
386
- contextSwitches: this.currentSession.metrics.tokenUsage.contextSwitches
387
- }
388
- };
389
- }
390
-
391
- /**
392
- * Get persona history
393
- */
394
- async getPersonaHistory(personaName) {
395
- return this.metrics.sessions.filter(
396
- session => session.persona === personaName
397
- );
398
- }
399
-
400
- /**
401
- * Get persona statistics
402
- */
403
- async getPersonaStats(personaName) {
404
- const perf = this.metrics.personaPerformance[personaName];
405
-
406
- if (!perf || perf.totalSessions === 0) {
407
- return {
408
- message: `No data available for persona: ${personaName}`
409
- };
410
- }
411
-
412
- const avgQualityScore = perf.qualityScores.length > 0 ?
413
- perf.qualityScores.reduce((a, b) => a + b, 0) / perf.qualityScores.length : 0;
414
-
415
- return {
416
- totalSessions: perf.totalSessions,
417
- completionRate: perf.completedSessions / perf.totalSessions,
418
- averageDuration: perf.totalDuration / perf.totalSessions,
419
- averageTokens: perf.totalTokens / perf.totalSessions,
420
- errorRate: perf.totalErrors / perf.totalSessions,
421
- averageQualityScore: avgQualityScore,
422
- taskDistribution: perf.taskTypes
423
- };
424
- }
425
-
426
- /**
427
- * Generate comprehensive report
428
- */
429
- async generateReport(options = {}) {
430
- const timeRange = options.timeRange || 'all';
431
- const startTime = this.getStartTime(timeRange);
432
-
433
- // Filter sessions by time range
434
- const sessions = this.metrics.sessions.filter(
435
- session => session.startTime >= startTime
436
- );
437
-
438
- const report = {
439
- generatedAt: new Date().toISOString(),
440
- timeRange,
441
- summary: {
442
- totalSessions: sessions.length,
443
- averageCompletionTime: this.calculateAverageTime(sessions),
444
- personaUsage: this.getPersonaUsageStats(sessions),
445
- qualityTrend: this.calculateQualityTrend(sessions)
446
- },
447
- personaAnalysis: this.analyzePersonaEffectiveness(sessions),
448
- taskAnalysis: this.analyzeTaskTypes(sessions),
449
- recommendations: this.generateRecommendations(sessions)
450
- };
451
-
452
- return report;
453
- }
454
-
455
- /**
456
- * Get start time for time range
457
- */
458
- getStartTime(timeRange) {
459
- const now = Date.now();
460
-
461
- switch (timeRange) {
462
- case 'hour':
463
- return now - 3600000;
464
- case 'day':
465
- return now - 86400000;
466
- case 'week':
467
- return now - 604800000;
468
- case 'month':
469
- return now - 2592000000;
470
- default:
471
- return 0;
472
- }
473
- }
474
-
475
- /**
476
- * Calculate average completion time
477
- */
478
- calculateAverageTime(sessions) {
479
- if (sessions.length === 0) return 0;
480
-
481
- const totalTime = sessions.reduce((sum, session) => sum + session.duration, 0);
482
- return totalTime / sessions.length;
483
- }
484
-
485
- /**
486
- * Get persona usage statistics
487
- */
488
- getPersonaUsageStats(sessions) {
489
- const usage = {};
490
-
491
- sessions.forEach(session => {
492
- usage[session.persona] = (usage[session.persona] || 0) + 1;
493
- });
494
-
495
- // Convert to percentages
496
- const total = sessions.length;
497
- const stats = {};
498
-
499
- for (const [persona, count] of Object.entries(usage)) {
500
- stats[persona] = {
501
- count,
502
- percentage: (count / total) * 100
503
- };
504
- }
505
-
506
- return stats;
507
- }
508
-
509
- /**
510
- * Calculate quality trend
511
- */
512
- calculateQualityTrend(sessions) {
513
- if (sessions.length < 2) {
514
- return { trend: 'insufficient_data' };
515
- }
516
-
517
- // Get quality scores over time
518
- const scores = sessions
519
- .filter(s => s.qualityMetrics)
520
- .map(s => ({
521
- time: s.startTime,
522
- score: s.qualityMetrics.overallScore
523
- }))
524
- .sort((a, b) => a.time - b.time);
525
-
526
- if (scores.length < 2) {
527
- return { trend: 'insufficient_data' };
528
- }
529
-
530
- // Calculate trend (simple linear regression)
531
- const n = scores.length;
532
- const sumX = scores.reduce((sum, s, i) => sum + i, 0);
533
- const sumY = scores.reduce((sum, s) => sum + s.score, 0);
534
- const sumXY = scores.reduce((sum, s, i) => sum + i * s.score, 0);
535
- const sumX2 = scores.reduce((sum, s, i) => sum + i * i, 0);
536
-
537
- const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
538
-
539
- return {
540
- trend: slope > 0.01 ? 'improving' : slope < -0.01 ? 'declining' : 'stable',
541
- slope,
542
- latestScore: scores[scores.length - 1].score,
543
- averageScore: sumY / n
544
- };
545
- }
546
-
547
- /**
548
- * Analyze persona effectiveness
549
- */
550
- analyzePersonaEffectiveness(sessions) {
551
- const analysis = {};
552
-
553
- // Group sessions by persona
554
- const personaSessions = {};
555
- sessions.forEach(session => {
556
- if (!personaSessions[session.persona]) {
557
- personaSessions[session.persona] = [];
558
- }
559
- personaSessions[session.persona].push(session);
560
- });
561
-
562
- // Analyze each persona
563
- for (const [persona, personaSessions] of Object.entries(personaSessions)) {
564
- const completed = personaSessions.filter(s => s.metrics.completionStatus === 'completed');
565
- const qualityScores = personaSessions
566
- .filter(s => s.qualityMetrics)
567
- .map(s => s.qualityMetrics.overallScore);
568
-
569
- analysis[persona] = {
570
- sessionCount: personaSessions.length,
571
- completionRate: completed.length / personaSessions.length,
572
- averageCompletionTime: this.calculateAverageTime(personaSessions),
573
- averageQualityScore: qualityScores.length > 0 ?
574
- qualityScores.reduce((a, b) => a + b, 0) / qualityScores.length : 0,
575
- tokenEfficiency: this.calculateAverageTokenEfficiency(personaSessions),
576
- bestUseCases: this.identifyBestUseCases(persona, personaSessions),
577
- improvementAreas: this.identifyImprovementAreas(persona, personaSessions)
578
- };
579
- }
580
-
581
- return analysis;
582
- }
583
-
584
- /**
585
- * Calculate average token efficiency
586
- */
587
- calculateAverageTokenEfficiency(sessions) {
588
- const efficiencies = sessions
589
- .filter(s => s.qualityMetrics)
590
- .map(s => s.qualityMetrics.tokenEfficiency);
591
-
592
- if (efficiencies.length === 0) return 0;
593
-
594
- return efficiencies.reduce((a, b) => a + b, 0) / efficiencies.length;
595
- }
596
-
597
- /**
598
- * Identify best use cases for persona
599
- */
600
- identifyBestUseCases(persona, sessions) {
601
- // Group by task type and calculate success rate
602
- const taskStats = {};
603
-
604
- sessions.forEach(session => {
605
- const taskType = session.task.type;
606
- if (!taskStats[taskType]) {
607
- taskStats[taskType] = {
608
- total: 0,
609
- successful: 0,
610
- avgQuality: 0,
611
- qualityCount: 0
612
- };
613
- }
614
-
615
- taskStats[taskType].total++;
616
-
617
- if (session.metrics.completionStatus === 'completed') {
618
- taskStats[taskType].successful++;
619
- }
620
-
621
- if (session.qualityMetrics) {
622
- taskStats[taskType].avgQuality += session.qualityMetrics.overallScore;
623
- taskStats[taskType].qualityCount++;
624
- }
625
- });
626
-
627
- // Calculate success rates and find best
628
- const taskScores = Object.entries(taskStats).map(([type, stats]) => ({
629
- type,
630
- successRate: stats.successful / stats.total,
631
- avgQuality: stats.qualityCount > 0 ? stats.avgQuality / stats.qualityCount : 0,
632
- count: stats.total
633
- }));
634
-
635
- // Return top 3 task types
636
- return taskScores
637
- .filter(t => t.count >= 2) // Minimum sample size
638
- .sort((a, b) => (b.successRate + b.avgQuality) - (a.successRate + a.avgQuality))
639
- .slice(0, 3)
640
- .map(t => t.type);
641
- }
642
-
643
- /**
644
- * Identify improvement areas
645
- */
646
- identifyImprovementAreas(persona, sessions) {
647
- const areas = [];
648
-
649
- // Check completion rate
650
- const completionRate = sessions.filter(s =>
651
- s.metrics.completionStatus === 'completed'
652
- ).length / sessions.length;
653
-
654
- if (completionRate < 0.8) {
655
- areas.push('completion_rate');
656
- }
657
-
658
- // Check error rate
659
- const avgErrors = sessions.reduce((sum, s) =>
660
- sum + s.metrics.errors, 0
661
- ) / sessions.length;
662
-
663
- if (avgErrors > 1) {
664
- areas.push('error_rate');
665
- }
666
-
667
- // Check token efficiency
668
- const avgTokenEfficiency = this.calculateAverageTokenEfficiency(sessions);
669
- if (avgTokenEfficiency < 0.5) {
670
- areas.push('token_efficiency');
671
- }
672
-
673
- return areas;
674
- }
675
-
676
- /**
677
- * Analyze task types
678
- */
679
- analyzeTaskTypes(sessions) {
680
- const taskAnalysis = {};
681
-
682
- sessions.forEach(session => {
683
- const taskType = session.task.type;
684
-
685
- if (!taskAnalysis[taskType]) {
686
- taskAnalysis[taskType] = {
687
- count: 0,
688
- totalDuration: 0,
689
- completedCount: 0,
690
- totalQuality: 0,
691
- qualityCount: 0,
692
- personas: {}
693
- };
694
- }
695
-
696
- const analysis = taskAnalysis[taskType];
697
- analysis.count++;
698
- analysis.totalDuration += session.duration;
699
-
700
- if (session.metrics.completionStatus === 'completed') {
701
- analysis.completedCount++;
702
- }
703
-
704
- if (session.qualityMetrics) {
705
- analysis.totalQuality += session.qualityMetrics.overallScore;
706
- analysis.qualityCount++;
707
- }
708
-
709
- // Track which personas handle this task type
710
- analysis.personas[session.persona] = (analysis.personas[session.persona] || 0) + 1;
711
- });
712
-
713
- // Calculate averages
714
- for (const [taskType, analysis] of Object.entries(taskAnalysis)) {
715
- analysis.averageDuration = analysis.totalDuration / analysis.count;
716
- analysis.completionRate = analysis.completedCount / analysis.count;
717
- analysis.averageQuality = analysis.qualityCount > 0 ?
718
- analysis.totalQuality / analysis.qualityCount : 0;
719
-
720
- // Find best persona for this task type
721
- analysis.bestPersona = Object.entries(analysis.personas)
722
- .sort(([,a], [,b]) => b - a)[0]?.[0];
723
- }
724
-
725
- return taskAnalysis;
726
- }
727
-
728
- /**
729
- * Generate recommendations
730
- */
731
- generateRecommendations(sessions) {
732
- const recommendations = [];
733
-
734
- // Analyze overall patterns
735
- const personaAnalysis = this.analyzePersonaEffectiveness(sessions);
736
- const taskAnalysis = this.analyzeTaskTypes(sessions);
737
-
738
- // Recommendation 1: Underutilized personas
739
- for (const [persona, stats] of Object.entries(personaAnalysis)) {
740
- if (stats.sessionCount < sessions.length * 0.1) {
741
- recommendations.push({
742
- type: 'persona_usage',
743
- priority: 'medium',
744
- message: `Consider using ${persona} persona more often for ${stats.bestUseCases.join(', ')} tasks`,
745
- data: { persona, usage: stats.sessionCount }
746
- });
747
- }
748
- }
749
-
750
- // Recommendation 2: Task-persona mismatches
751
- for (const [taskType, analysis] of Object.entries(taskAnalysis)) {
752
- if (analysis.averageQuality < 0.6 && analysis.count > 3) {
753
- recommendations.push({
754
- type: 'task_persona_match',
755
- priority: 'high',
756
- message: `${taskType} tasks have low quality scores. Try using ${analysis.bestPersona} persona`,
757
- data: { taskType, currentQuality: analysis.averageQuality }
758
- });
759
- }
760
- }
761
-
762
- // Recommendation 3: High error rates
763
- for (const [persona, stats] of Object.entries(personaAnalysis)) {
764
- if (stats.improvementAreas.includes('error_rate')) {
765
- recommendations.push({
766
- type: 'error_rate',
767
- priority: 'high',
768
- message: `${persona} persona has high error rate. Review context rules and tool recommendations`,
769
- data: { persona, areas: stats.improvementAreas }
770
- });
771
- }
772
- }
773
-
774
- return recommendations;
775
- }
776
-
777
- /**
778
- * Load metrics from disk
779
- */
780
- async loadMetrics() {
781
- try {
782
- const metricsFile = path.join(this.options.metricsPath, 'metrics.json');
783
- const data = await fs.readFile(metricsFile, 'utf-8');
784
- const loaded = JSON.parse(data);
785
-
786
- // Merge with current metrics
787
- this.metrics = {
788
- ...this.metrics,
789
- ...loaded,
790
- sessions: [...(loaded.sessions || []), ...this.metrics.sessions]
791
- };
792
-
793
- // Limit to max history size
794
- if (this.metrics.sessions.length > this.options.maxHistorySize) {
795
- this.metrics.sessions = this.metrics.sessions.slice(-this.options.maxHistorySize);
796
- }
797
- } catch (error) {
798
- // No existing metrics or error loading - start fresh
799
- }
800
- }
801
-
802
- /**
803
- * Persist metrics to disk
804
- */
805
- async persistMetrics() {
806
- try {
807
- const metricsFile = path.join(this.options.metricsPath, 'metrics.json');
808
- await fs.writeFile(
809
- metricsFile,
810
- JSON.stringify(this.metrics, null, 2),
811
- 'utf-8'
812
- );
813
-
814
- this.emit('metricsPersisted', {
815
- sessionCount: this.metrics.sessions.length
816
- });
817
- } catch (error) {
818
- this.emit('error', { error, phase: 'persist' });
819
- }
820
- }
821
-
822
- /**
823
- * Start persist timer
824
- */
825
- startPersistTimer() {
826
- this.persistTimer = setInterval(
827
- () => this.persistMetrics(),
828
- this.options.persistInterval
829
- );
830
- }
831
-
832
- /**
833
- * Generate session ID
834
- */
835
- generateSessionId() {
836
- return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
837
- }
838
-
839
- /**
840
- * Get current session metrics
841
- */
842
- async getCurrentSessionMetrics() {
843
- if (!this.currentSession) {
844
- return { message: 'No active session' };
845
- }
846
-
847
- return await this.collectCurrentMetrics();
848
- }
849
-
850
- /**
851
- * Reset metrics
852
- */
853
- async reset() {
854
- this.metrics = {
855
- sessions: [],
856
- personaPerformance: {},
857
- taskOutcomes: [],
858
- aggregated: {}
859
- };
860
-
861
- this.currentSession = null;
862
-
863
- await this.persistMetrics();
864
-
865
- this.emit('reset');
866
- }
867
-
868
- /**
869
- * Cleanup resources
870
- */
871
- async cleanup() {
872
- if (this.persistTimer) {
873
- clearInterval(this.persistTimer);
874
- this.persistTimer = null;
875
- }
876
-
877
- // Persist final metrics
878
- await this.persistMetrics();
879
-
880
- this.removeAllListeners();
881
- }
882
- }
883
-
884
- export default MetricsCollector;
885
- export { MetricsCollector };