myshell-tools 1.0.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +318 -0
  4. package/data/orchestrator.json +113 -0
  5. package/package.json +49 -0
  6. package/src/auth/recovery.mjs +328 -0
  7. package/src/auth/refresh.mjs +373 -0
  8. package/src/chef.mjs +348 -0
  9. package/src/cli/doctor.mjs +568 -0
  10. package/src/cli/reset.mjs +447 -0
  11. package/src/cli/status.mjs +379 -0
  12. package/src/cli.mjs +429 -0
  13. package/src/commands/doctor.mjs +375 -0
  14. package/src/commands/help.mjs +324 -0
  15. package/src/commands/status.mjs +331 -0
  16. package/src/monitor/health.mjs +486 -0
  17. package/src/monitor/performance.mjs +442 -0
  18. package/src/monitor/report.mjs +535 -0
  19. package/src/orchestrator/classify.mjs +391 -0
  20. package/src/orchestrator/confidence.mjs +151 -0
  21. package/src/orchestrator/handoffs.mjs +231 -0
  22. package/src/orchestrator/review.mjs +222 -0
  23. package/src/providers/balance.mjs +201 -0
  24. package/src/providers/claude.mjs +236 -0
  25. package/src/providers/codex.mjs +255 -0
  26. package/src/providers/detect.mjs +185 -0
  27. package/src/providers/errors.mjs +373 -0
  28. package/src/providers/select.mjs +162 -0
  29. package/src/repl-enhanced.mjs +417 -0
  30. package/src/repl.mjs +321 -0
  31. package/src/state/archive.mjs +366 -0
  32. package/src/state/atomic.mjs +116 -0
  33. package/src/state/cleanup.mjs +440 -0
  34. package/src/state/recovery.mjs +461 -0
  35. package/src/state/session.mjs +147 -0
  36. package/src/ui/errors.mjs +456 -0
  37. package/src/ui/formatter.mjs +327 -0
  38. package/src/ui/icons.mjs +318 -0
  39. package/src/ui/progress.mjs +468 -0
  40. package/templates/prompts/confidence-format.txt +14 -0
  41. package/templates/prompts/ic-with-feedback.txt +41 -0
  42. package/templates/prompts/ic.txt +13 -0
  43. package/templates/prompts/manager-review.txt +40 -0
  44. package/templates/prompts/manager.txt +14 -0
  45. package/templates/prompts/worker.txt +12 -0
@@ -0,0 +1,535 @@
1
+ /**
2
+ * report.mjs — Usage reporting and analytics
3
+ */
4
+
5
+ import { existsSync, readFileSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { generatePerformanceReport } from './performance.mjs';
8
+ import { runHealthCheck } from './health.mjs';
9
+ import { getSessionSummary } from '../state/session.mjs';
10
+ import { getStorageStats } from '../state/cleanup.mjs';
11
+
12
+ /**
13
+ * Colors for terminal output
14
+ */
15
+ const colors = {
16
+ red: '\x1b[31m',
17
+ green: '\x1b[32m',
18
+ yellow: '\x1b[33m',
19
+ blue: '\x1b[34m',
20
+ magenta: '\x1b[35m',
21
+ cyan: '\x1b[36m',
22
+ white: '\x1b[37m',
23
+ reset: '\x1b[0m',
24
+ bold: '\x1b[1m',
25
+ dim: '\x1b[2m'
26
+ };
27
+
28
+ /**
29
+ * Generate comprehensive session report
30
+ */
31
+ export async function generateSessionReport(workspace = process.cwd()) {
32
+ const timestamp = new Date().toISOString();
33
+
34
+ console.log(`${colors.bold}${colors.blue}📊 Cortex Session Report${colors.reset}\n`);
35
+ console.log(`${colors.dim}Generated: ${new Date().toLocaleString()}${colors.reset}\n`);
36
+
37
+ // Gather data from all monitoring sources
38
+ const performance = generatePerformanceReport(workspace);
39
+ const session = getSessionSummary(workspace);
40
+ const health = await runHealthCheck(workspace);
41
+ const storage = getStorageStats(workspace);
42
+
43
+ const report = {
44
+ timestamp,
45
+ workspace,
46
+ session,
47
+ performance,
48
+ health,
49
+ storage
50
+ };
51
+
52
+ // Display session overview
53
+ displaySessionOverview(session);
54
+
55
+ // Display performance metrics
56
+ displayPerformanceMetrics(performance);
57
+
58
+ // Display efficiency analysis
59
+ displayEfficiencyAnalysis(performance);
60
+
61
+ // Display system health
62
+ displayHealthStatus(health);
63
+
64
+ // Display recommendations
65
+ displayRecommendations(performance, health);
66
+
67
+ return report;
68
+ }
69
+
70
+ /**
71
+ * Display session overview
72
+ */
73
+ function displaySessionOverview(session) {
74
+ console.log(`${colors.bold}Session Overview${colors.reset}`);
75
+
76
+ if (session.messageCount === 0) {
77
+ console.log(` ${colors.dim}No active session${colors.reset}\n`);
78
+ return;
79
+ }
80
+
81
+ const duration = session.duration ? formatDuration(session.duration) : 'ongoing';
82
+ const avgResponseTime = session.duration && session.assistantMessageCount > 0 ?
83
+ formatDuration(session.duration / session.assistantMessageCount) : 'N/A';
84
+
85
+ console.log(` 📝 Messages: ${session.messageCount} (${session.userMessageCount} user, ${session.assistantMessageCount} assistant)`);
86
+ console.log(` ⏱️ Duration: ${duration}`);
87
+ console.log(` 📊 Avg Response: ${avgResponseTime}`);
88
+
89
+ if (session.lastMessage) {
90
+ const lastTime = new Date(session.lastMessage.timestamp).toLocaleString();
91
+ console.log(` 🕐 Last Activity: ${lastTime}`);
92
+ }
93
+
94
+ console.log();
95
+ }
96
+
97
+ /**
98
+ * Display performance metrics
99
+ */
100
+ function displayPerformanceMetrics(performance) {
101
+ console.log(`${colors.bold}Performance Metrics${colors.reset}`);
102
+
103
+ if (!performance.taskCount) {
104
+ console.log(` ${colors.dim}No tasks completed in this session${colors.reset}\n`);
105
+ return;
106
+ }
107
+
108
+ console.log(` 📋 Tasks Completed: ${performance.taskCount}`);
109
+ console.log(` 🔄 Total Handoffs: ${performance.totalHandoffs}`);
110
+ console.log(` 📈 Escalations: ${performance.escalationCount} (${performance.escalationRate})`);
111
+
112
+ if (performance.averageConfidence) {
113
+ const confidenceColor = parseFloat(performance.averageConfidence) >= 75 ? colors.green :
114
+ parseFloat(performance.averageConfidence) >= 60 ? colors.yellow : colors.red;
115
+ console.log(` 🎯 Avg Confidence: ${confidenceColor}${performance.averageConfidence}%${colors.reset}`);
116
+ }
117
+
118
+ // Cost metrics
119
+ if (performance.totalCostUSD) {
120
+ console.log(` 💰 Total Cost: $${performance.totalCostUSD}`);
121
+ if (performance.averageCostPerTask) {
122
+ console.log(` 💵 Avg Cost/Task: $${performance.averageCostPerTask}`);
123
+ }
124
+ }
125
+
126
+ // Tier distribution
127
+ if (performance.tierDistribution) {
128
+ console.log(` 🏗️ Tier Usage:`);
129
+ ['worker', 'ic', 'manager'].forEach(tier => {
130
+ const dist = performance.tierDistribution[tier];
131
+ if (dist && dist.count > 0) {
132
+ const tierColor = tier === 'worker' ? colors.blue :
133
+ tier === 'ic' ? colors.yellow : colors.red;
134
+ console.log(` ${tierColor}${tier.toUpperCase()}${colors.reset}: ${dist.count} (${dist.percentage}%)`);
135
+ }
136
+ });
137
+ }
138
+
139
+ console.log();
140
+ }
141
+
142
+ /**
143
+ * Display efficiency analysis
144
+ */
145
+ function displayEfficiencyAnalysis(performance) {
146
+ console.log(`${colors.bold}Efficiency Analysis${colors.reset}`);
147
+
148
+ if (!performance.efficiency) {
149
+ console.log(` ${colors.dim}Insufficient data for efficiency analysis${colors.reset}\n`);
150
+ return;
151
+ }
152
+
153
+ const efficiency = performance.efficiency;
154
+
155
+ // Token savings
156
+ const savingsPercentage = parseFloat(efficiency.tokenSavingsPercentage);
157
+ const savingsColor = savingsPercentage >= 50 ? colors.green :
158
+ savingsPercentage >= 25 ? colors.yellow : colors.red;
159
+ console.log(` 💾 Token Savings: ${savingsColor}${efficiency.tokenSavingsPercentage}% vs dual-verification${colors.reset}`);
160
+
161
+ if (efficiency.costSavingsUSD) {
162
+ console.log(` 💰 Cost Savings: ${savingsColor}$${efficiency.costSavingsUSD}${colors.reset}`);
163
+ }
164
+
165
+ // Escalation efficiency
166
+ const escalationRate = parseFloat(efficiency.escalationRate);
167
+ const escalationColor = escalationRate <= 25 ? colors.green :
168
+ escalationRate <= 40 ? colors.yellow : colors.red;
169
+ console.log(` 📈 Escalation Rate: ${escalationColor}${efficiency.escalationRate}%${colors.reset}`);
170
+
171
+ // Performance indicators
172
+ if (performance.indicators && performance.indicators.length > 0) {
173
+ console.log(`\n ${colors.bold}Key Insights:${colors.reset}`);
174
+ performance.indicators.forEach(indicator => {
175
+ const indicatorColor = indicator.type === 'excellent' ? colors.green :
176
+ indicator.type === 'good' ? colors.yellow :
177
+ indicator.type === 'warning' ? colors.red : colors.white;
178
+
179
+ const icon = indicator.type === 'excellent' ? '✅' :
180
+ indicator.type === 'good' ? '👍' :
181
+ indicator.type === 'warning' ? '⚠️' : 'ℹ️';
182
+
183
+ console.log(` ${icon} ${indicatorColor}${indicator.message}${colors.reset}`);
184
+ if (indicator.recommendation) {
185
+ console.log(` ${colors.dim}→ ${indicator.recommendation}${colors.reset}`);
186
+ }
187
+ });
188
+ }
189
+
190
+ console.log();
191
+ }
192
+
193
+ /**
194
+ * Display health status
195
+ */
196
+ function displayHealthStatus(health) {
197
+ console.log(`${colors.bold}System Health${colors.reset}`);
198
+
199
+ const overallColor = health.overall.status === 'healthy' ? colors.green :
200
+ health.overall.status === 'degraded' ? colors.yellow : colors.red;
201
+
202
+ console.log(` 🏥 Overall: ${overallColor}${health.overall.status.toUpperCase()} (${health.overall.score}/100)${colors.reset}`);
203
+
204
+ // Component health
205
+ const healthyComponents = Object.entries(health.components)
206
+ .filter(([_, comp]) => comp.healthScore?.status === 'healthy').length;
207
+ const totalComponents = Object.keys(health.components).length;
208
+
209
+ console.log(` 🧩 Components: ${healthyComponents}/${totalComponents} healthy`);
210
+
211
+ // Show critical issues
212
+ const criticalComponents = Object.entries(health.components)
213
+ .filter(([_, comp]) => comp.healthScore?.status === 'critical' || comp.healthScore?.status === 'unhealthy')
214
+ .map(([name, _]) => name);
215
+
216
+ if (criticalComponents.length > 0) {
217
+ console.log(` ${colors.red}⚠️ Issues: ${criticalComponents.join(', ')}${colors.reset}`);
218
+ }
219
+
220
+ console.log();
221
+ }
222
+
223
+ /**
224
+ * Display recommendations
225
+ */
226
+ function displayRecommendations(performance, health) {
227
+ console.log(`${colors.bold}Recommendations${colors.reset}`);
228
+
229
+ const recommendations = [];
230
+
231
+ // Performance recommendations
232
+ if (performance.efficiency) {
233
+ const savingsPercentage = parseFloat(performance.efficiency.tokenSavingsPercentage);
234
+ const escalationRate = parseFloat(performance.efficiency.escalationRate);
235
+
236
+ if (savingsPercentage < 25) {
237
+ recommendations.push({
238
+ priority: 'high',
239
+ type: 'efficiency',
240
+ message: 'Low token efficiency detected',
241
+ action: 'Review task routing and tier assignment logic'
242
+ });
243
+ }
244
+
245
+ if (escalationRate > 40) {
246
+ recommendations.push({
247
+ priority: 'medium',
248
+ type: 'escalation',
249
+ message: 'High escalation rate',
250
+ action: 'Consider starting complex tasks at higher tiers'
251
+ });
252
+ }
253
+
254
+ if (performance.averageConfidence && parseFloat(performance.averageConfidence) < 60) {
255
+ recommendations.push({
256
+ priority: 'medium',
257
+ type: 'confidence',
258
+ message: 'Low average confidence scores',
259
+ action: 'Review task complexity vs model capabilities'
260
+ });
261
+ }
262
+ }
263
+
264
+ // Health recommendations
265
+ const unhealthyComponents = Object.entries(health.components)
266
+ .filter(([_, comp]) => comp.healthScore?.status !== 'healthy')
267
+ .map(([name, comp]) => ({ name, status: comp.healthScore?.status }));
268
+
269
+ unhealthyComponents.forEach(({ name, status }) => {
270
+ recommendations.push({
271
+ priority: status === 'critical' ? 'critical' : 'medium',
272
+ type: 'health',
273
+ message: `${name} component is ${status}`,
274
+ action: 'Run --doctor for detailed diagnostics'
275
+ });
276
+ });
277
+
278
+ // Cost recommendations
279
+ if (performance.totalCostUSD && parseFloat(performance.totalCostUSD) > 1.0) {
280
+ recommendations.push({
281
+ priority: 'low',
282
+ type: 'cost',
283
+ message: 'High session cost detected',
284
+ action: 'Consider using lower tiers for simple tasks'
285
+ });
286
+ }
287
+
288
+ // Display recommendations
289
+ if (recommendations.length === 0) {
290
+ console.log(` ${colors.green}✅ No issues found - system is performing well${colors.reset}`);
291
+ } else {
292
+ recommendations
293
+ .sort((a, b) => {
294
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
295
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
296
+ })
297
+ .forEach((rec, index) => {
298
+ const priorityColor = rec.priority === 'critical' ? colors.red :
299
+ rec.priority === 'high' ? colors.red :
300
+ rec.priority === 'medium' ? colors.yellow : colors.cyan;
301
+
302
+ console.log(` ${index + 1}. ${priorityColor}[${rec.priority.toUpperCase()}]${colors.reset} ${rec.message}`);
303
+ console.log(` ${colors.dim}→ ${rec.action}${colors.reset}`);
304
+ });
305
+ }
306
+
307
+ console.log();
308
+ }
309
+
310
+ /**
311
+ * Generate historical trends report
312
+ */
313
+ export async function generateTrendsReport(workspace = process.cwd(), days = 7) {
314
+ console.log(`${colors.bold}${colors.blue}📈 Trends Report (${days} days)${colors.reset}\n`);
315
+
316
+ const metricsDir = join(workspace, '.cortex', 'metrics');
317
+ const reportsPath = join(metricsDir, 'session-reports.jsonl');
318
+
319
+ if (!existsSync(reportsPath)) {
320
+ console.log(`${colors.dim}No historical data available${colors.reset}\n`);
321
+ return null;
322
+ }
323
+
324
+ try {
325
+ const content = readFileSync(reportsPath, 'utf8');
326
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
327
+
328
+ const reports = content
329
+ .trim()
330
+ .split('\n')
331
+ .filter(line => line.trim())
332
+ .map(line => {
333
+ try {
334
+ return JSON.parse(line);
335
+ } catch {
336
+ return null;
337
+ }
338
+ })
339
+ .filter(Boolean)
340
+ .filter(report => new Date(report.timestamp).getTime() > cutoff)
341
+ .sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
342
+
343
+ if (reports.length === 0) {
344
+ console.log(`${colors.dim}No data available for the last ${days} days${colors.reset}\n`);
345
+ return null;
346
+ }
347
+
348
+ // Analyze trends
349
+ const trends = analyzeTrends(reports);
350
+
351
+ displayTrends(trends, days);
352
+
353
+ return trends;
354
+
355
+ } catch (error) {
356
+ console.log(`${colors.red}Error loading historical data: ${error.message}${colors.reset}\n`);
357
+ return null;
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Analyze trends from historical reports
363
+ */
364
+ function analyzeTrends(reports) {
365
+ const trends = {
366
+ sessions: reports.length,
367
+ totalTasks: reports.reduce((sum, r) => sum + (r.taskCount || 0), 0),
368
+ totalCost: reports.reduce((sum, r) => sum + parseFloat(r.totalCostUSD || 0), 0),
369
+
370
+ efficiency: {
371
+ average: calculateAverage(reports, 'efficiency.tokenSavingsPercentage'),
372
+ trend: calculateTrend(reports, 'efficiency.tokenSavingsPercentage')
373
+ },
374
+
375
+ escalationRate: {
376
+ average: calculateAverage(reports, 'escalationRate'),
377
+ trend: calculateTrend(reports, 'escalationRate')
378
+ },
379
+
380
+ confidence: {
381
+ average: calculateAverage(reports, 'averageConfidence'),
382
+ trend: calculateTrend(reports, 'averageConfidence')
383
+ },
384
+
385
+ costPerTask: {
386
+ average: calculateAverage(reports, 'averageCostPerTask'),
387
+ trend: calculateTrend(reports, 'averageCostPerTask')
388
+ }
389
+ };
390
+
391
+ return trends;
392
+ }
393
+
394
+ /**
395
+ * Calculate average of a metric across reports
396
+ */
397
+ function calculateAverage(reports, metricPath) {
398
+ const values = reports
399
+ .map(report => getNestedValue(report, metricPath))
400
+ .filter(val => val !== null && !isNaN(parseFloat(val)))
401
+ .map(val => parseFloat(val));
402
+
403
+ return values.length > 0 ? values.reduce((sum, val) => sum + val, 0) / values.length : null;
404
+ }
405
+
406
+ /**
407
+ * Calculate trend direction for a metric
408
+ */
409
+ function calculateTrend(reports, metricPath) {
410
+ const values = reports
411
+ .map(report => getNestedValue(report, metricPath))
412
+ .filter(val => val !== null && !isNaN(parseFloat(val)))
413
+ .map(val => parseFloat(val));
414
+
415
+ if (values.length < 2) return 'insufficient_data';
416
+
417
+ const firstHalf = values.slice(0, Math.floor(values.length / 2));
418
+ const secondHalf = values.slice(Math.floor(values.length / 2));
419
+
420
+ const firstAvg = firstHalf.reduce((sum, val) => sum + val, 0) / firstHalf.length;
421
+ const secondAvg = secondHalf.reduce((sum, val) => sum + val, 0) / secondHalf.length;
422
+
423
+ const change = ((secondAvg - firstAvg) / firstAvg) * 100;
424
+
425
+ return change > 5 ? 'improving' :
426
+ change < -5 ? 'declining' : 'stable';
427
+ }
428
+
429
+ /**
430
+ * Get nested object value by path
431
+ */
432
+ function getNestedValue(obj, path) {
433
+ return path.split('.').reduce((current, key) => current?.[key], obj);
434
+ }
435
+
436
+ /**
437
+ * Display trends
438
+ */
439
+ function displayTrends(trends, days) {
440
+ console.log(`${colors.bold}Summary${colors.reset}`);
441
+ console.log(` 📊 Sessions: ${trends.sessions}`);
442
+ console.log(` 📋 Total Tasks: ${trends.totalTasks}`);
443
+ console.log(` 💰 Total Cost: $${trends.totalCost.toFixed(4)}`);
444
+ console.log();
445
+
446
+ console.log(`${colors.bold}Performance Trends${colors.reset}`);
447
+
448
+ if (trends.efficiency.average !== null) {
449
+ const trendIcon = getTrendIcon(trends.efficiency.trend);
450
+ console.log(` 💾 Token Efficiency: ${trends.efficiency.average.toFixed(1)}% ${trendIcon}`);
451
+ }
452
+
453
+ if (trends.escalationRate.average !== null) {
454
+ const trendIcon = getTrendIcon(trends.escalationRate.trend);
455
+ console.log(` 📈 Escalation Rate: ${trends.escalationRate.average.toFixed(1)}% ${trendIcon}`);
456
+ }
457
+
458
+ if (trends.confidence.average !== null) {
459
+ const trendIcon = getTrendIcon(trends.confidence.trend);
460
+ console.log(` 🎯 Avg Confidence: ${trends.confidence.average.toFixed(1)}% ${trendIcon}`);
461
+ }
462
+
463
+ if (trends.costPerTask.average !== null) {
464
+ const trendIcon = getTrendIcon(trends.costPerTask.trend);
465
+ console.log(` 💵 Cost/Task: $${trends.costPerTask.average.toFixed(4)} ${trendIcon}`);
466
+ }
467
+
468
+ console.log();
469
+ }
470
+
471
+ /**
472
+ * Get trend icon
473
+ */
474
+ function getTrendIcon(trend) {
475
+ switch (trend) {
476
+ case 'improving':
477
+ return `${colors.green}📈 improving${colors.reset}`;
478
+ case 'declining':
479
+ return `${colors.red}📉 declining${colors.reset}`;
480
+ case 'stable':
481
+ return `${colors.yellow}➡️ stable${colors.reset}`;
482
+ default:
483
+ return `${colors.dim}❓ unknown${colors.reset}`;
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Format duration for display
489
+ */
490
+ function formatDuration(ms) {
491
+ const seconds = Math.floor(ms / 1000);
492
+ const minutes = Math.floor(seconds / 60);
493
+ const hours = Math.floor(minutes / 60);
494
+
495
+ if (hours > 0) {
496
+ return `${hours}h ${minutes % 60}m`;
497
+ } else if (minutes > 0) {
498
+ return `${minutes}m ${seconds % 60}s`;
499
+ } else {
500
+ return `${seconds}s`;
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Quick status summary
506
+ */
507
+ export function displayQuickSummary(workspace = process.cwd()) {
508
+ const performance = generatePerformanceReport(workspace);
509
+ const session = getSessionSummary(workspace);
510
+
511
+ if (session.messageCount === 0) {
512
+ console.log(`${colors.dim}No active session${colors.reset}`);
513
+ return;
514
+ }
515
+
516
+ let status = `📊 ${session.messageCount} msgs`;
517
+
518
+ if (performance.taskCount > 0) {
519
+ status += ` | 📋 ${performance.taskCount} tasks`;
520
+
521
+ if (performance.efficiency?.tokenSavingsPercentage) {
522
+ const savings = parseFloat(performance.efficiency.tokenSavingsPercentage);
523
+ const savingsColor = savings >= 50 ? colors.green : savings >= 25 ? colors.yellow : colors.red;
524
+ status += ` | ${savingsColor}💾 ${performance.efficiency.tokenSavingsPercentage}%${colors.reset}`;
525
+ }
526
+
527
+ if (performance.escalationRate) {
528
+ const escalations = parseFloat(performance.escalationRate);
529
+ const escalationColor = escalations <= 25 ? colors.green : escalations <= 40 ? colors.yellow : colors.red;
530
+ status += ` | ${escalationColor}📈 ${performance.escalationRate}${colors.reset}`;
531
+ }
532
+ }
533
+
534
+ console.log(status);
535
+ }