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,568 @@
1
+ /**
2
+ * doctor.mjs — Comprehensive system health check
3
+ */
4
+
5
+ import { spawnSync } from 'child_process';
6
+ import { existsSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { detectEnvironment, getAvailableModels } from '../providers/detect.mjs';
9
+ import { checkProviderHealth } from '../auth/recovery.mjs';
10
+ import { getCleanupStatus, getStorageStats } from '../state/cleanup.mjs';
11
+ import { getRecoveryStatus } from '../state/recovery.mjs';
12
+ import { loadRefreshState } from '../auth/refresh.mjs';
13
+
14
+ /**
15
+ * Colors for terminal output
16
+ */
17
+ const colors = {
18
+ red: '\x1b[31m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[34m',
22
+ magenta: '\x1b[35m',
23
+ cyan: '\x1b[36m',
24
+ white: '\x1b[37m',
25
+ reset: '\x1b[0m',
26
+ bold: '\x1b[1m',
27
+ dim: '\x1b[2m'
28
+ };
29
+
30
+ /**
31
+ * Status icons
32
+ */
33
+ const icons = {
34
+ pass: '✅',
35
+ warn: '⚠️ ',
36
+ fail: '❌',
37
+ info: 'ℹ️ ',
38
+ working: '🔍'
39
+ };
40
+
41
+ /**
42
+ * Comprehensive health check
43
+ */
44
+ export async function runDoctorCheck(workspace = process.cwd()) {
45
+ console.log(`${colors.bold}${colors.blue}🔍 Cortex System Health Check${colors.reset}\n`);
46
+
47
+ const results = {
48
+ timestamp: new Date().toISOString(),
49
+ workspace,
50
+ checks: [],
51
+ overall: { healthy: true, score: 0, maxScore: 0 },
52
+ recommendations: []
53
+ };
54
+
55
+ // System Information
56
+ await checkSystemInfo(results);
57
+
58
+ // CLI Installation Check
59
+ await checkCliInstallation(results);
60
+
61
+ // Authentication Check
62
+ await checkAuthentication(results);
63
+
64
+ // Provider Health Check
65
+ await checkProviders(results);
66
+
67
+ // Token Refresh Status
68
+ await checkTokenStatus(results);
69
+
70
+ // State Integrity Check
71
+ await checkStateIntegrity(results);
72
+
73
+ // Storage and Cleanup Check
74
+ await checkStorageStatus(results);
75
+
76
+ // Recovery Status Check
77
+ await checkRecoveryStatus(results);
78
+
79
+ // Network Connectivity Check
80
+ await checkNetworkConnectivity(results);
81
+
82
+ // Performance Check
83
+ await checkPerformance(results);
84
+
85
+ // Calculate overall health score
86
+ const passCount = results.checks.filter(c => c.status === 'pass').length;
87
+ const totalChecks = results.checks.length;
88
+ results.overall.score = passCount;
89
+ results.overall.maxScore = totalChecks;
90
+ results.overall.healthy = (passCount / totalChecks) >= 0.8;
91
+
92
+ // Display results
93
+ displayHealthResults(results);
94
+
95
+ return results;
96
+ }
97
+
98
+ /**
99
+ * Check system information
100
+ */
101
+ async function checkSystemInfo(results) {
102
+ const check = {
103
+ name: 'System Information',
104
+ status: 'info',
105
+ details: {}
106
+ };
107
+
108
+ try {
109
+ check.details.platform = process.platform;
110
+ check.details.arch = process.arch;
111
+ check.details.nodeVersion = process.version;
112
+ check.details.memory = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
113
+
114
+ console.log(`${icons.info}${colors.dim}Platform: ${check.details.platform}/${check.details.arch}${colors.reset}`);
115
+ console.log(`${colors.dim}Node.js: ${check.details.nodeVersion}${colors.reset}`);
116
+ console.log(`${colors.dim}Memory: ${check.details.memory}MB${colors.reset}\n`);
117
+
118
+ } catch (error) {
119
+ check.details.error = error.message;
120
+ }
121
+
122
+ results.checks.push(check);
123
+ }
124
+
125
+ /**
126
+ * Check CLI installation status
127
+ */
128
+ async function checkCliInstallation(results) {
129
+ console.log(`${colors.bold}CLI Installation${colors.reset}`);
130
+
131
+ // Check Claude CLI
132
+ const claudeCheck = checkSingleCLI('claude', ['--version']);
133
+ displaySingleCheck('Claude CLI', claudeCheck);
134
+ results.checks.push({
135
+ name: 'Claude CLI Installation',
136
+ status: claudeCheck.installed ? 'pass' : 'fail',
137
+ details: claudeCheck
138
+ });
139
+
140
+ // Check Codex CLI
141
+ const codexCheck = checkSingleCLI('codex', ['--version']);
142
+ displaySingleCheck('Codex CLI', codexCheck);
143
+ results.checks.push({
144
+ name: 'Codex CLI Installation',
145
+ status: codexCheck.installed ? 'pass' : 'fail',
146
+ details: codexCheck
147
+ });
148
+
149
+ console.log();
150
+ }
151
+
152
+ /**
153
+ * Check single CLI installation
154
+ */
155
+ function checkSingleCLI(command, args) {
156
+ try {
157
+ const result = spawnSync(command, args, {
158
+ encoding: 'utf8',
159
+ stdio: 'pipe',
160
+ timeout: 5000
161
+ });
162
+
163
+ return {
164
+ installed: result.status === 0,
165
+ version: result.status === 0 ? result.stdout.trim() : null,
166
+ error: result.status !== 0 ? result.stderr || 'Command failed' : null
167
+ };
168
+ } catch (error) {
169
+ return {
170
+ installed: false,
171
+ version: null,
172
+ error: error.code === 'ENOENT' ? 'Command not found' : error.message
173
+ };
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Check authentication status
179
+ */
180
+ async function checkAuthentication(results) {
181
+ console.log(`${colors.bold}Authentication${colors.reset}`);
182
+
183
+ const env = detectEnvironment();
184
+
185
+ // Claude authentication
186
+ const claudeStatus = env.claude.authed ? 'pass' : (env.claude.installed ? 'warn' : 'fail');
187
+ const claudeMessage = env.claude.authed ? 'Authenticated' :
188
+ env.claude.installed ? 'CLI installed but not authenticated' :
189
+ 'CLI not installed';
190
+
191
+ displaySingleCheck('Claude Auth', { status: claudeStatus, message: claudeMessage });
192
+ results.checks.push({
193
+ name: 'Claude Authentication',
194
+ status: claudeStatus,
195
+ details: { authed: env.claude.authed, installed: env.claude.installed }
196
+ });
197
+
198
+ // Codex authentication
199
+ const codexStatus = env.codex.authed ? 'pass' : (env.codex.installed ? 'warn' : 'fail');
200
+ const codexMessage = env.codex.authed ? 'Authenticated' :
201
+ env.codex.installed ? 'CLI installed but not authenticated' :
202
+ 'CLI not installed';
203
+
204
+ displaySingleCheck('Codex Auth', { status: codexStatus, message: codexMessage });
205
+ results.checks.push({
206
+ name: 'Codex Authentication',
207
+ status: codexStatus,
208
+ details: { authed: env.codex.authed, installed: env.codex.installed }
209
+ });
210
+
211
+ console.log();
212
+ }
213
+
214
+ /**
215
+ * Check provider health
216
+ */
217
+ async function checkProviders(results) {
218
+ console.log(`${colors.bold}Provider Health${colors.reset}`);
219
+
220
+ const env = detectEnvironment();
221
+ const models = getAvailableModels(env);
222
+ const health = await checkProviderHealth(env);
223
+
224
+ // Model tier availability
225
+ ['manager', 'ic', 'worker'].forEach(tier => {
226
+ const available = models[tier].length;
227
+ const status = available > 0 ? 'pass' : 'fail';
228
+ const message = available > 0 ? `${available} model(s) available` : 'No models available';
229
+
230
+ displaySingleCheck(`${tier.toUpperCase()} tier`, { status, message });
231
+ results.checks.push({
232
+ name: `${tier} Tier Availability`,
233
+ status,
234
+ details: { available, models: models[tier] }
235
+ });
236
+ });
237
+
238
+ // Overall provider health
239
+ const overallStatus = health.overall.healthy ? 'pass' : 'fail';
240
+ const overallMessage = `${health.overall.availableProviders} provider(s) healthy`;
241
+
242
+ displaySingleCheck('Overall Health', { status: overallStatus, message: overallMessage });
243
+ results.checks.push({
244
+ name: 'Provider Health',
245
+ status: overallStatus,
246
+ details: health
247
+ });
248
+
249
+ console.log();
250
+ }
251
+
252
+ /**
253
+ * Check token refresh status
254
+ */
255
+ async function checkTokenStatus(results) {
256
+ console.log(`${colors.bold}Token Management${colors.reset}`);
257
+
258
+ const refreshState = loadRefreshState();
259
+
260
+ if (refreshState) {
261
+ const lastRefresh = new Date(refreshState.lastRefresh);
262
+ const age = Math.round((Date.now() - lastRefresh.getTime()) / 1000 / 60 / 60); // hours
263
+
264
+ const status = age < 24 ? 'pass' : age < 48 ? 'warn' : 'fail';
265
+ const message = `Last refresh: ${age}h ago`;
266
+
267
+ displaySingleCheck('Token Refresh', { status, message });
268
+ results.checks.push({
269
+ name: 'Token Refresh Status',
270
+ status,
271
+ details: { lastRefresh: refreshState.lastRefresh, ageHours: age }
272
+ });
273
+ } else {
274
+ displaySingleCheck('Token Refresh', { status: 'warn', message: 'No refresh history' });
275
+ results.checks.push({
276
+ name: 'Token Refresh Status',
277
+ status: 'warn',
278
+ details: { lastRefresh: null }
279
+ });
280
+ }
281
+
282
+ console.log();
283
+ }
284
+
285
+ /**
286
+ * Check state integrity
287
+ */
288
+ async function checkStateIntegrity(results) {
289
+ console.log(`${colors.bold}State Integrity${colors.reset}`);
290
+
291
+ const recoveryStatus = getRecoveryStatus();
292
+
293
+ // Session integrity
294
+ const sessionStatus = recoveryStatus.sessionIntegrity ? 'pass' : 'fail';
295
+ const sessionMessage = recoveryStatus.sessionIntegrity ? 'Valid' : `${recoveryStatus.issues.length} issue(s)`;
296
+
297
+ displaySingleCheck('Session Files', { status: sessionStatus, message: sessionMessage });
298
+ results.checks.push({
299
+ name: 'Session Integrity',
300
+ status: sessionStatus,
301
+ details: { valid: recoveryStatus.sessionIntegrity, issues: recoveryStatus.issues }
302
+ });
303
+
304
+ // Interrupted work
305
+ const workStatus = recoveryStatus.hasInterruptedWork ? 'warn' : 'pass';
306
+ const workMessage = recoveryStatus.hasInterruptedWork ?
307
+ `${recoveryStatus.interruptedCount} interrupted session(s)` :
308
+ 'No interrupted work';
309
+
310
+ displaySingleCheck('Work State', { status: workStatus, message: workMessage });
311
+ results.checks.push({
312
+ name: 'Work State',
313
+ status: workStatus,
314
+ details: { hasInterrupted: recoveryStatus.hasInterruptedWork, count: recoveryStatus.interruptedCount }
315
+ });
316
+
317
+ console.log();
318
+ }
319
+
320
+ /**
321
+ * Check storage status
322
+ */
323
+ async function checkStorageStatus(results) {
324
+ console.log(`${colors.bold}Storage${colors.reset}`);
325
+
326
+ const storageStats = getStorageStats();
327
+ const cleanupStatus = getCleanupStatus();
328
+
329
+ // Total storage usage
330
+ const totalMB = Math.round(storageStats.totalSize / 1024 / 1024);
331
+ const storageStatus = totalMB < 100 ? 'pass' : totalMB < 500 ? 'warn' : 'fail';
332
+ const storageMessage = `${totalMB}MB used`;
333
+
334
+ displaySingleCheck('Storage Usage', { status: storageStatus, message: storageMessage });
335
+ results.checks.push({
336
+ name: 'Storage Usage',
337
+ status: storageStatus,
338
+ details: { totalSize: storageStats.totalSize, totalMB }
339
+ });
340
+
341
+ // Cleanup recommendations
342
+ const cleanupCount = cleanupStatus.recommendations.length;
343
+ const cleanupStatus2 = cleanupCount === 0 ? 'pass' : cleanupCount < 3 ? 'warn' : 'fail';
344
+ const cleanupMessage = cleanupCount === 0 ? 'No cleanup needed' : `${cleanupCount} recommendation(s)`;
345
+
346
+ displaySingleCheck('Cleanup Status', { status: cleanupStatus2, message: cleanupMessage });
347
+ results.checks.push({
348
+ name: 'Cleanup Status',
349
+ status: cleanupStatus2,
350
+ details: { recommendations: cleanupStatus.recommendations, issues: cleanupStatus.issues }
351
+ });
352
+
353
+ console.log();
354
+ }
355
+
356
+ /**
357
+ * Check recovery status
358
+ */
359
+ async function checkRecoveryStatus(results) {
360
+ console.log(`${colors.bold}Recovery${colors.reset}`);
361
+
362
+ const recoveryStatus = getRecoveryStatus();
363
+
364
+ const autoRecoveryStatus = recoveryStatus.canAutoRecover ? 'pass' : 'warn';
365
+ const autoRecoveryMessage = recoveryStatus.canAutoRecover ? 'Available' : 'Manual intervention may be needed';
366
+
367
+ displaySingleCheck('Auto Recovery', { status: autoRecoveryStatus, message: autoRecoveryMessage });
368
+ results.checks.push({
369
+ name: 'Auto Recovery',
370
+ status: autoRecoveryStatus,
371
+ details: recoveryStatus
372
+ });
373
+
374
+ console.log();
375
+ }
376
+
377
+ /**
378
+ * Check network connectivity
379
+ */
380
+ async function checkNetworkConnectivity(results) {
381
+ console.log(`${colors.bold}Network Connectivity${colors.reset}`);
382
+
383
+ const endpoints = [
384
+ { name: 'Claude API', url: 'https://console.anthropic.com' },
385
+ { name: 'OpenAI API', url: 'https://api.openai.com' }
386
+ ];
387
+
388
+ for (const endpoint of endpoints) {
389
+ const connectCheck = await checkConnection(endpoint.url);
390
+ const status = connectCheck.success ? 'pass' : 'fail';
391
+ const message = connectCheck.success ? `${connectCheck.responseTime}ms` : connectCheck.error;
392
+
393
+ displaySingleCheck(endpoint.name, { status, message });
394
+ results.checks.push({
395
+ name: `${endpoint.name} Connectivity`,
396
+ status,
397
+ details: connectCheck
398
+ });
399
+ }
400
+
401
+ console.log();
402
+ }
403
+
404
+ /**
405
+ * Simple connection check
406
+ */
407
+ async function checkConnection(url) {
408
+ return new Promise(resolve => {
409
+ const startTime = Date.now();
410
+
411
+ try {
412
+ const https = require('https');
413
+ const req = https.request(url, { timeout: 5000 }, (res) => {
414
+ resolve({
415
+ success: true,
416
+ responseTime: Date.now() - startTime,
417
+ statusCode: res.statusCode
418
+ });
419
+ req.destroy();
420
+ });
421
+
422
+ req.on('error', (error) => {
423
+ resolve({
424
+ success: false,
425
+ error: error.code || error.message,
426
+ responseTime: Date.now() - startTime
427
+ });
428
+ });
429
+
430
+ req.on('timeout', () => {
431
+ resolve({
432
+ success: false,
433
+ error: 'Timeout',
434
+ responseTime: Date.now() - startTime
435
+ });
436
+ req.destroy();
437
+ });
438
+
439
+ req.end();
440
+ } catch (error) {
441
+ resolve({
442
+ success: false,
443
+ error: error.message,
444
+ responseTime: Date.now() - startTime
445
+ });
446
+ }
447
+ });
448
+ }
449
+
450
+ /**
451
+ * Check performance metrics
452
+ */
453
+ async function checkPerformance(results) {
454
+ console.log(`${colors.bold}Performance${colors.reset}`);
455
+
456
+ // Memory usage
457
+ const memUsage = process.memoryUsage();
458
+ const heapMB = Math.round(memUsage.heapUsed / 1024 / 1024);
459
+ const memStatus = heapMB < 50 ? 'pass' : heapMB < 100 ? 'warn' : 'fail';
460
+ const memMessage = `${heapMB}MB heap`;
461
+
462
+ displaySingleCheck('Memory Usage', { status: memStatus, message: memMessage });
463
+ results.checks.push({
464
+ name: 'Memory Usage',
465
+ status: memStatus,
466
+ details: { heapMB, memUsage }
467
+ });
468
+
469
+ // Check for .cortex directory permissions
470
+ const cortexDir = join(process.cwd(), '.cortex');
471
+ const permStatus = await checkDirectoryPermissions(cortexDir);
472
+
473
+ displaySingleCheck('File Permissions', { status: permStatus.status, message: permStatus.message });
474
+ results.checks.push({
475
+ name: 'File Permissions',
476
+ status: permStatus.status,
477
+ details: permStatus
478
+ });
479
+
480
+ console.log();
481
+ }
482
+
483
+ /**
484
+ * Check directory permissions
485
+ */
486
+ async function checkDirectoryPermissions(dir) {
487
+ try {
488
+ if (!existsSync(dir)) {
489
+ return { status: 'pass', message: 'Directory will be created as needed' };
490
+ }
491
+
492
+ // Try to create a test file
493
+ const testFile = join(dir, '.test-permissions');
494
+ require('fs').writeFileSync(testFile, 'test');
495
+ require('fs').unlinkSync(testFile);
496
+
497
+ return { status: 'pass', message: 'Read/write access confirmed' };
498
+ } catch (error) {
499
+ return {
500
+ status: 'fail',
501
+ message: `Permission denied: ${error.message}`,
502
+ error: error.message
503
+ };
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Display single check result
509
+ */
510
+ function displaySingleCheck(name, check) {
511
+ const statusIcon = check.status === 'pass' ? icons.pass :
512
+ check.status === 'warn' ? icons.warn :
513
+ check.status === 'fail' ? icons.fail :
514
+ icons.info;
515
+
516
+ const statusColor = check.status === 'pass' ? colors.green :
517
+ check.status === 'warn' ? colors.yellow :
518
+ check.status === 'fail' ? colors.red :
519
+ colors.cyan;
520
+
521
+ console.log(` ${statusIcon} ${colors.bold}${name}:${colors.reset} ${statusColor}${check.message}${colors.reset}`);
522
+ }
523
+
524
+ /**
525
+ * Display comprehensive health results
526
+ */
527
+ function displayHealthResults(results) {
528
+ const score = results.overall.score;
529
+ const maxScore = results.overall.maxScore;
530
+ const percentage = Math.round((score / maxScore) * 100);
531
+
532
+ console.log(`${colors.bold}${colors.blue}Health Summary${colors.reset}`);
533
+ console.log(`${colors.bold}Score: ${score}/${maxScore} (${percentage}%)${colors.reset}`);
534
+
535
+ const overallStatus = results.overall.healthy ?
536
+ `${colors.green}${icons.pass} System is healthy${colors.reset}` :
537
+ `${colors.red}${icons.fail} System needs attention${colors.reset}`;
538
+
539
+ console.log(`${colors.bold}Status: ${overallStatus}${colors.reset}\n`);
540
+
541
+ // Show recommendations if any
542
+ const allRecommendations = results.checks
543
+ .flatMap(check => check.details?.recommendations || [])
544
+ .filter(Boolean);
545
+
546
+ if (allRecommendations.length > 0) {
547
+ console.log(`${colors.bold}${colors.yellow}Recommendations:${colors.reset}`);
548
+ allRecommendations.forEach((rec, index) => {
549
+ console.log(` ${index + 1}. ${rec.message}`);
550
+ if (rec.action) {
551
+ console.log(` ${colors.dim}→ ${rec.action}${colors.reset}`);
552
+ }
553
+ });
554
+ console.log();
555
+ }
556
+
557
+ // Show critical issues
558
+ const criticalIssues = results.checks.filter(check => check.status === 'fail');
559
+ if (criticalIssues.length > 0) {
560
+ console.log(`${colors.bold}${colors.red}Critical Issues:${colors.reset}`);
561
+ criticalIssues.forEach((issue, index) => {
562
+ console.log(` ${index + 1}. ${issue.name}: ${issue.details?.message || 'Failed'}`);
563
+ });
564
+ console.log();
565
+ }
566
+
567
+ console.log(`${colors.dim}Report generated: ${new Date().toLocaleString()}${colors.reset}`);
568
+ }