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
package/src/cli.mjs ADDED
@@ -0,0 +1,429 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * cli.mjs — NPX Cortex main entry point
5
+ */
6
+
7
+ import { detectEnvironment, getAvailableModels } from './providers/detect.mjs';
8
+ import { startREPL } from './repl.mjs';
9
+ import { startEnhancedREPL } from './repl-enhanced.mjs';
10
+ import { backgroundRefresh, displayRefreshStatus } from './auth/refresh.mjs';
11
+ import { autoRecovery } from './auth/recovery.mjs';
12
+ import { runDoctorCheck } from './commands/doctor.mjs';
13
+ import { displayStatus, getQuickStatus } from './commands/status.mjs';
14
+ import { displayHelp } from './commands/help.mjs';
15
+ import { performReset, previewReset, getResetLevels } from './cli/reset.mjs';
16
+ import { recoverInterruptedWork, resumePlan } from './state/recovery.mjs';
17
+ import { performStateCleanup } from './state/cleanup.mjs';
18
+ import { generateSessionReport, generateTrendsReport, displayQuickSummary } from './monitor/report.mjs';
19
+ import { endPerformanceSession } from './monitor/performance.mjs';
20
+ import { fmt, box, createTree, formatModel } from './ui/formatter.mjs';
21
+ import { status, tier, provider as providerIcons } from './ui/icons.mjs';
22
+ import { displayError, displayWarning, displaySuccess, handleError } from './ui/errors.mjs';
23
+ import { createSpinner, withSpinner } from './ui/progress.mjs';
24
+
25
+ const VERSION = '1.0.0';
26
+
27
+ /**
28
+ * Get provider usage balance for today (mockup for now)
29
+ */
30
+ export function getProviderBalance() {
31
+ // TODO: Replace with actual usage tracking
32
+ // This would read from usage logs and calculate today's balance
33
+ return {
34
+ claude: 45,
35
+ openai: 55,
36
+ total: 12,
37
+ label: 'Well balanced'
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Display enhanced banner and system status (inspired by dual-brain polish)
43
+ */
44
+ export function displayBanner(env, models) {
45
+ // Enhanced header with gradient-like effect
46
+ console.log(box(`🧠 Cortex v${VERSION}\nAI Org Chart in Your Shell`, {
47
+ borderColor: fmt.brightBlue(''),
48
+ textColor: fmt.bold(''),
49
+ padding: 1,
50
+ margin: 1
51
+ }));
52
+
53
+ // Provider status line (dual-brain style)
54
+ const claudeStatus = env.claude.authed ? status.success : env.claude.installed ? status.warning : status.error;
55
+ const codexStatus = env.codex.authed ? status.success : env.codex.installed ? status.warning : status.error;
56
+
57
+ console.log(` ${providerIcons.claude} Claude ${claudeStatus} ${providerIcons.codex} Codex ${codexStatus}`);
58
+
59
+ // Status summary line
60
+ if (env.claude.authed && env.codex.authed) {
61
+ console.log(` ${fmt.green('Dual-brain mode active — full AI orchestration available')}`);
62
+ } else if (env.claude.authed) {
63
+ console.log(` ${fmt.yellow('Claude ready')} ${fmt.dim('Ā· Add Codex for dual-brain features')}`);
64
+ } else if (env.codex.authed) {
65
+ console.log(` ${fmt.yellow('Codex ready')} ${fmt.dim('Ā· Add Claude for full features')}`);
66
+ } else {
67
+ console.log(` ${fmt.red('No providers authenticated')} ${fmt.dim('Ā· Run: cortex --doctor')}`);
68
+ }
69
+
70
+ // Provider balance bar (dual-brain inspired)
71
+ const balance = getProviderBalance();
72
+ if (balance.total > 0) {
73
+ console.log(` ${fmt.providerBalanceBar(balance.claude, balance.openai)}`);
74
+ }
75
+
76
+ console.log('');
77
+
78
+ // Compact org chart hierarchy (dual-brain style)
79
+ console.log(fmt.bold('Org Chart:'));
80
+ const tiers = [
81
+ { name: 'MANAGER', key: 'manager', icon: tier.manager, color: fmt.redBold },
82
+ { name: 'IC', key: 'ic', icon: tier.ic, color: fmt.yellowBold },
83
+ { name: 'WORKER', key: 'worker', icon: tier.worker, color: fmt.blueBold }
84
+ ];
85
+
86
+ for (const tierInfo of tiers) {
87
+ const tierModels = models[tierInfo.key] || [];
88
+ const count = tierModels.length;
89
+ const modelSummary = count > 0
90
+ ? fmt.dim(`${count} model${count !== 1 ? 's' : ''}`)
91
+ : fmt.red('none');
92
+
93
+ console.log(` ${tierInfo.icon} ${tierInfo.color(tierInfo.name.padEnd(7))} ${modelSummary}`);
94
+
95
+ if (count > 0 && tierModels.length <= 3) {
96
+ // Show models inline for small lists
97
+ const modelList = tierModels.map(m => `${m.provider}/${m.model}`).join(', ');
98
+ console.log(` ${fmt.dim('ā”œā”€ ' + modelList)}`);
99
+ } else if (count > 3) {
100
+ // Show summary for large lists
101
+ console.log(` ${fmt.dim(`ā”œā”€ ${tierModels[0].provider}/${tierModels[0].model} +${count-1} more`)}`);
102
+ }
103
+ }
104
+
105
+ console.log(`\n${fmt.dim('šŸ’” Type your request and press Enter — AI will route automatically')}`);
106
+ }
107
+
108
+ /**
109
+ * Display enhanced installation guidance for missing CLIs
110
+ */
111
+ function displayInstallationHelp(env) {
112
+ console.log('\n' + fmt.bold('šŸš€ Getting Started:'));
113
+ console.log('');
114
+
115
+ const steps = [];
116
+
117
+ if (!env.claude.installed) {
118
+ steps.push({
119
+ text: fmt.orange('Install Claude CLI:'),
120
+ children: [
121
+ { text: fmt.cyan('pip install anthropic-cli') },
122
+ { text: fmt.cyan('claude auth login') }
123
+ ]
124
+ });
125
+ } else if (!env.claude.authed) {
126
+ steps.push({
127
+ text: fmt.yellow('Authenticate Claude CLI:'),
128
+ children: [
129
+ { text: fmt.cyan('claude auth login') }
130
+ ]
131
+ });
132
+ }
133
+
134
+ if (!env.codex.installed) {
135
+ steps.push({
136
+ text: fmt.green('Install Codex CLI:'),
137
+ children: [
138
+ { text: fmt.cyan('npm install -g @openai/codex') },
139
+ { text: fmt.cyan('codex login') }
140
+ ]
141
+ });
142
+ } else if (!env.codex.authed) {
143
+ steps.push({
144
+ text: fmt.yellow('Authenticate Codex CLI:'),
145
+ children: [
146
+ { text: fmt.cyan('codex login') }
147
+ ]
148
+ });
149
+ }
150
+
151
+ if (steps.length > 0) {
152
+ const helpTree = createTree(steps);
153
+ helpTree.forEach(line => console.log(line));
154
+ console.log('');
155
+ }
156
+
157
+ console.log(fmt.warning('You need at least one authenticated CLI to use Cortex.'));
158
+ console.log(fmt.dim('Both CLIs enable different model tiers and redundancy.'));
159
+ console.log('');
160
+ }
161
+
162
+ /**
163
+ * Handle command line arguments
164
+ */
165
+ function parseArgs(argv) {
166
+ const args = {
167
+ help: false,
168
+ version: false,
169
+ doctor: false,
170
+ status: false,
171
+ reset: null,
172
+ resetForce: false,
173
+ resetPreview: false,
174
+ cleanup: false,
175
+ resume: null,
176
+ recovery: false,
177
+ verbose: false,
178
+ report: null,
179
+ trends: null
180
+ };
181
+
182
+ for (let i = 0; i < argv.length; i++) {
183
+ const arg = argv[i];
184
+
185
+ switch (arg) {
186
+ case '--help':
187
+ case '-h':
188
+ args.help = true;
189
+ break;
190
+ case '--version':
191
+ case '-v':
192
+ args.version = true;
193
+ break;
194
+ case '--doctor':
195
+ args.doctor = true;
196
+ break;
197
+ case '--status':
198
+ args.status = true;
199
+ break;
200
+ case '--reset':
201
+ args.reset = argv[i + 1] || 'sessions';
202
+ i++; // Skip next arg since it's the reset level
203
+ break;
204
+ case '--force':
205
+ args.resetForce = true;
206
+ break;
207
+ case '--preview':
208
+ args.resetPreview = true;
209
+ break;
210
+ case '--cleanup':
211
+ args.cleanup = true;
212
+ break;
213
+ case '--resume':
214
+ args.resume = argv[i + 1] || true;
215
+ if (argv[i + 1] && !argv[i + 1].startsWith('--')) i++;
216
+ break;
217
+ case '--recovery':
218
+ args.recovery = true;
219
+ break;
220
+ case '--verbose':
221
+ args.verbose = true;
222
+ break;
223
+ case '--report':
224
+ args.report = true;
225
+ break;
226
+ case '--trends':
227
+ args.trends = argv[i + 1] || '7';
228
+ if (argv[i + 1] && !argv[i + 1].startsWith('--')) i++;
229
+ break;
230
+ }
231
+ }
232
+
233
+ return args;
234
+ }
235
+
236
+ // Help display is now handled by the enhanced help command
237
+
238
+ // Doctor check is now handled by the enhanced doctor command
239
+
240
+ /**
241
+ * Main entry point
242
+ */
243
+ async function main() {
244
+ const args = parseArgs(process.argv.slice(2));
245
+
246
+ if (args.version) {
247
+ console.log(`Cortex v${VERSION}`);
248
+ return;
249
+ }
250
+
251
+ if (args.help) {
252
+ displayHelp();
253
+ return;
254
+ }
255
+
256
+ // Detect environment and available models
257
+ const env = detectEnvironment();
258
+ const models = getAvailableModels(env);
259
+
260
+ if (args.doctor) {
261
+ await runDoctorCheck();
262
+ return;
263
+ }
264
+
265
+ if (args.status) {
266
+ displayStatus(process.cwd(), { verbose: args.verbose });
267
+ return;
268
+ }
269
+
270
+ if (args.reset) {
271
+ if (args.resetPreview) {
272
+ previewReset(args.reset);
273
+ } else {
274
+ await performReset(args.reset, { force: args.resetForce, verbose: args.verbose });
275
+ }
276
+ return;
277
+ }
278
+
279
+ if (args.cleanup) {
280
+ console.log('🧹 Running state cleanup...\n');
281
+ const cleanupResults = performStateCleanup({ verbose: args.verbose });
282
+ console.log(`\nāœ… Cleanup completed. Saved ${Math.round(cleanupResults.spaceSaved / 1024)}KB`);
283
+ return;
284
+ }
285
+
286
+ if (args.recovery) {
287
+ const recovery = await recoverInterruptedWork();
288
+ if (!recovery.hasInterrupted) {
289
+ console.log('āœ… No interrupted work found');
290
+ }
291
+ return;
292
+ }
293
+
294
+ if (args.resume) {
295
+ if (args.resume === true) {
296
+ // Show available sessions to resume
297
+ const recovery = await recoverInterruptedWork();
298
+ return;
299
+ } else {
300
+ // Resume specific plan
301
+ try {
302
+ const plan = resumePlan(args.resume);
303
+ console.log(`āœ… Resumed plan: ${plan.description}`);
304
+ } catch (error) {
305
+ console.log(`āŒ Failed to resume: ${error.message}`);
306
+ return;
307
+ }
308
+ }
309
+ }
310
+
311
+ if (args.report) {
312
+ await generateSessionReport();
313
+ return;
314
+ }
315
+
316
+ if (args.trends) {
317
+ const days = parseInt(args.trends) || 7;
318
+ await generateTrendsReport(process.cwd(), days);
319
+ return;
320
+ }
321
+
322
+ // Background token refresh (non-blocking) with visual feedback
323
+ if (env.hasProviders) {
324
+ const refreshSpinner = createSpinner('Refreshing credentials...', 'dots');
325
+ refreshSpinner.start();
326
+
327
+ backgroundRefresh()
328
+ .then((result) => {
329
+ refreshSpinner.success('Credentials refreshed');
330
+ displayRefreshStatus(result);
331
+ })
332
+ .catch(() => {
333
+ refreshSpinner.stop(); // Silent failure for background refresh
334
+ });
335
+ }
336
+
337
+ // Display banner and status
338
+ displayBanner(env, models);
339
+
340
+ // Check if we can run
341
+ if (!env.hasProviders) {
342
+ displayInstallationHelp(env);
343
+
344
+ // Try auto-recovery with enhanced feedback
345
+ console.log('');
346
+ const recoveryResult = await withSpinner(
347
+ autoRecovery(env),
348
+ 'šŸ”§ Attempting auto-recovery...',
349
+ {
350
+ successText: 'Auto-recovery completed',
351
+ errorText: 'Auto-recovery failed'
352
+ }
353
+ );
354
+
355
+ if (!recoveryResult.recovered) {
356
+ displayError(new Error('No authenticated providers found'), {
357
+ operation: 'system startup',
358
+ suggestions: ['Run: cortex --doctor', 'Authenticate at least one provider']
359
+ });
360
+ process.exit(1);
361
+ } else {
362
+ displaySuccess('Auto-recovery successful', { nextStep: 'Continuing startup...' });
363
+ // Re-detect after recovery
364
+ const newEnv = detectEnvironment();
365
+ const newModels = getAvailableModels(newEnv);
366
+ if (!newEnv.hasProviders) {
367
+ console.log('āŒ Recovery failed: Still no authenticated providers');
368
+ process.exit(1);
369
+ }
370
+ // Update context with recovered environment
371
+ env.claude = newEnv.claude;
372
+ env.codex = newEnv.codex;
373
+ env.hasProviders = newEnv.hasProviders;
374
+ models.worker = newModels.worker;
375
+ models.ic = newModels.ic;
376
+ models.manager = newModels.manager;
377
+ }
378
+ }
379
+
380
+ if (models.manager.length === 0 && models.ic.length === 0 && models.worker.length === 0) {
381
+ handleError(new Error('No models available in any tier'), {
382
+ operation: 'model detection',
383
+ tier: 'all'
384
+ });
385
+ process.exit(1);
386
+ }
387
+
388
+ // Start the REPL with context
389
+ const context = {
390
+ environment: env,
391
+ availableModels: models,
392
+ options: {
393
+ timeoutMs: 120000 // 2 minutes
394
+ }
395
+ };
396
+
397
+ await startEnhancedREPL(context);
398
+ }
399
+
400
+ // Handle uncaught errors gracefully with rich formatting
401
+ process.on('unhandledRejection', (reason, promise) => {
402
+ console.log(''); // Add spacing
403
+ handleError(reason instanceof Error ? reason : new Error(String(reason)), {
404
+ operation: 'async operation',
405
+ context: 'unhandled promise rejection'
406
+ });
407
+ process.exit(1);
408
+ });
409
+
410
+ process.on('uncaughtException', (error) => {
411
+ console.log(''); // Add spacing
412
+ handleError(error, {
413
+ operation: 'system operation',
414
+ context: 'uncaught exception'
415
+ });
416
+ process.exit(1);
417
+ });
418
+
419
+ // Run the CLI with enhanced error handling
420
+ if (import.meta.url === `file://${process.argv[1]}`) {
421
+ main().catch(error => {
422
+ console.log(''); // Add spacing
423
+ handleError(error, {
424
+ operation: 'CLI startup',
425
+ context: 'main execution'
426
+ });
427
+ process.exit(1);
428
+ });
429
+ }