myshell-tools 1.0.0 → 2.0.0-alpha.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 (150) hide show
  1. package/CHANGELOG.md +44 -69
  2. package/LICENSE +21 -21
  3. package/README.md +178 -318
  4. package/dist/cli.d.ts +8 -0
  5. package/dist/cli.js +106 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/cost.d.ts +36 -0
  8. package/dist/commands/cost.js +103 -0
  9. package/dist/commands/cost.js.map +1 -0
  10. package/dist/commands/doctor.d.ts +36 -0
  11. package/dist/commands/doctor.js +115 -0
  12. package/dist/commands/doctor.js.map +1 -0
  13. package/dist/core/assess.d.ts +25 -0
  14. package/dist/core/assess.js +142 -0
  15. package/dist/core/assess.js.map +1 -0
  16. package/dist/core/classify.d.ts +19 -0
  17. package/dist/core/classify.js +80 -0
  18. package/dist/core/classify.js.map +1 -0
  19. package/dist/core/escalate.d.ts +32 -0
  20. package/dist/core/escalate.js +57 -0
  21. package/dist/core/escalate.js.map +1 -0
  22. package/dist/core/index.d.ts +13 -0
  23. package/dist/core/index.js +12 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/core/orchestrate.d.ts +42 -0
  26. package/dist/core/orchestrate.js +439 -0
  27. package/dist/core/orchestrate.js.map +1 -0
  28. package/dist/core/policy.d.ts +9 -0
  29. package/dist/core/policy.js +27 -0
  30. package/dist/core/policy.js.map +1 -0
  31. package/dist/core/prompt.d.ts +26 -0
  32. package/dist/core/prompt.js +125 -0
  33. package/dist/core/prompt.js.map +1 -0
  34. package/dist/core/review.d.ts +46 -0
  35. package/dist/core/review.js +148 -0
  36. package/dist/core/review.js.map +1 -0
  37. package/dist/core/route.d.ts +28 -0
  38. package/dist/core/route.js +52 -0
  39. package/dist/core/route.js.map +1 -0
  40. package/dist/core/types.d.ts +141 -0
  41. package/dist/core/types.js +14 -0
  42. package/dist/core/types.js.map +1 -0
  43. package/dist/infra/atomic.d.ts +53 -0
  44. package/dist/infra/atomic.js +171 -0
  45. package/dist/infra/atomic.js.map +1 -0
  46. package/dist/infra/clock.d.ts +9 -0
  47. package/dist/infra/clock.js +15 -0
  48. package/dist/infra/clock.js.map +1 -0
  49. package/dist/infra/index.d.ts +9 -0
  50. package/dist/infra/index.js +7 -0
  51. package/dist/infra/index.js.map +1 -0
  52. package/dist/infra/ledger.d.ts +49 -0
  53. package/dist/infra/ledger.js +90 -0
  54. package/dist/infra/ledger.js.map +1 -0
  55. package/dist/infra/paths.d.ts +28 -0
  56. package/dist/infra/paths.js +38 -0
  57. package/dist/infra/paths.js.map +1 -0
  58. package/dist/infra/pricing.d.ts +47 -0
  59. package/dist/infra/pricing.js +151 -0
  60. package/dist/infra/pricing.js.map +1 -0
  61. package/dist/infra/session.d.ts +28 -0
  62. package/dist/infra/session.js +61 -0
  63. package/dist/infra/session.js.map +1 -0
  64. package/dist/interface/render.d.ts +27 -0
  65. package/dist/interface/render.js +134 -0
  66. package/dist/interface/render.js.map +1 -0
  67. package/dist/interface/repl.d.ts +23 -0
  68. package/dist/interface/repl.js +90 -0
  69. package/dist/interface/repl.js.map +1 -0
  70. package/dist/interface/run.d.ts +20 -0
  71. package/dist/interface/run.js +31 -0
  72. package/dist/interface/run.js.map +1 -0
  73. package/dist/providers/claude-parse.d.ts +24 -0
  74. package/dist/providers/claude-parse.js +113 -0
  75. package/dist/providers/claude-parse.js.map +1 -0
  76. package/dist/providers/claude.d.ts +45 -0
  77. package/dist/providers/claude.js +122 -0
  78. package/dist/providers/claude.js.map +1 -0
  79. package/dist/providers/codex-parse.d.ts +32 -0
  80. package/dist/providers/codex-parse.js +145 -0
  81. package/dist/providers/codex-parse.js.map +1 -0
  82. package/dist/providers/codex.d.ts +44 -0
  83. package/dist/providers/codex.js +124 -0
  84. package/dist/providers/codex.js.map +1 -0
  85. package/dist/providers/detect.d.ts +49 -0
  86. package/dist/providers/detect.js +125 -0
  87. package/dist/providers/detect.js.map +1 -0
  88. package/dist/providers/errors.d.ts +49 -0
  89. package/dist/providers/errors.js +189 -0
  90. package/dist/providers/errors.js.map +1 -0
  91. package/dist/providers/index.d.ts +9 -0
  92. package/dist/providers/index.js +7 -0
  93. package/dist/providers/index.js.map +1 -0
  94. package/dist/providers/port.d.ts +74 -0
  95. package/dist/providers/port.js +16 -0
  96. package/dist/providers/port.js.map +1 -0
  97. package/dist/providers/registry.d.ts +21 -0
  98. package/dist/providers/registry.js +34 -0
  99. package/dist/providers/registry.js.map +1 -0
  100. package/dist/ui/banner.d.ts +19 -0
  101. package/dist/ui/banner.js +32 -0
  102. package/dist/ui/banner.js.map +1 -0
  103. package/dist/ui/spinner.d.ts +27 -0
  104. package/dist/ui/spinner.js +67 -0
  105. package/dist/ui/spinner.js.map +1 -0
  106. package/dist/ui/theme.d.ts +32 -0
  107. package/dist/ui/theme.js +56 -0
  108. package/dist/ui/theme.js.map +1 -0
  109. package/package.json +55 -49
  110. package/data/orchestrator.json +0 -113
  111. package/src/auth/recovery.mjs +0 -328
  112. package/src/auth/refresh.mjs +0 -373
  113. package/src/chef.mjs +0 -348
  114. package/src/cli/doctor.mjs +0 -568
  115. package/src/cli/reset.mjs +0 -447
  116. package/src/cli/status.mjs +0 -379
  117. package/src/cli.mjs +0 -429
  118. package/src/commands/doctor.mjs +0 -375
  119. package/src/commands/help.mjs +0 -324
  120. package/src/commands/status.mjs +0 -331
  121. package/src/monitor/health.mjs +0 -486
  122. package/src/monitor/performance.mjs +0 -442
  123. package/src/monitor/report.mjs +0 -535
  124. package/src/orchestrator/classify.mjs +0 -391
  125. package/src/orchestrator/confidence.mjs +0 -151
  126. package/src/orchestrator/handoffs.mjs +0 -231
  127. package/src/orchestrator/review.mjs +0 -222
  128. package/src/providers/balance.mjs +0 -201
  129. package/src/providers/claude.mjs +0 -236
  130. package/src/providers/codex.mjs +0 -255
  131. package/src/providers/detect.mjs +0 -185
  132. package/src/providers/errors.mjs +0 -373
  133. package/src/providers/select.mjs +0 -162
  134. package/src/repl-enhanced.mjs +0 -417
  135. package/src/repl.mjs +0 -321
  136. package/src/state/archive.mjs +0 -366
  137. package/src/state/atomic.mjs +0 -116
  138. package/src/state/cleanup.mjs +0 -440
  139. package/src/state/recovery.mjs +0 -461
  140. package/src/state/session.mjs +0 -147
  141. package/src/ui/errors.mjs +0 -456
  142. package/src/ui/formatter.mjs +0 -327
  143. package/src/ui/icons.mjs +0 -318
  144. package/src/ui/progress.mjs +0 -468
  145. package/templates/prompts/confidence-format.txt +0 -14
  146. package/templates/prompts/ic-with-feedback.txt +0 -41
  147. package/templates/prompts/ic.txt +0 -13
  148. package/templates/prompts/manager-review.txt +0 -40
  149. package/templates/prompts/manager.txt +0 -14
  150. package/templates/prompts/worker.txt +0 -12
@@ -0,0 +1,56 @@
1
+ /**
2
+ * src/ui/theme.ts — Centralised ANSI colour / format helpers.
3
+ *
4
+ * Every helper accepts a `color` boolean flag. When false the text is returned
5
+ * unchanged, which honours NO_COLOR and non-TTY contexts. No other module in
6
+ * the project should hardcode ANSI escape codes.
7
+ *
8
+ * Honesty contract: this file contains no hardcoded percentages, no fabricated
9
+ * figures, and no mock phrases.
10
+ */
11
+ // ---------------------------------------------------------------------------
12
+ // Primitive colour helpers
13
+ // ---------------------------------------------------------------------------
14
+ /** Dim / faint text. */
15
+ export function dim(text, color) {
16
+ return color ? `\x1b[2m${text}\x1b[0m` : text;
17
+ }
18
+ /** Bold text. */
19
+ export function bold(text, color) {
20
+ return color ? `\x1b[1m${text}\x1b[0m` : text;
21
+ }
22
+ /** Green text (success / ok). */
23
+ export function green(text, color) {
24
+ return color ? `\x1b[32m${text}\x1b[0m` : text;
25
+ }
26
+ /** Red text (failure / error). */
27
+ export function red(text, color) {
28
+ return color ? `\x1b[31m${text}\x1b[0m` : text;
29
+ }
30
+ /** Yellow text (warning / caution). */
31
+ export function yellow(text, color) {
32
+ return color ? `\x1b[33m${text}\x1b[0m` : text;
33
+ }
34
+ /** Cyan text (informational accent). */
35
+ export function cyan(text, color) {
36
+ return color ? `\x1b[36m${text}\x1b[0m` : text;
37
+ }
38
+ // ---------------------------------------------------------------------------
39
+ // Composite helpers
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Render a labelled key in a key/value pair.
43
+ * e.g. `label('Status', true)` → bold cyan "Status"
44
+ */
45
+ export function label(text, color) {
46
+ return bold(cyan(text, color), color);
47
+ }
48
+ /**
49
+ * Return a horizontal rule of the given width (default 40).
50
+ * The divider is dim when colour is enabled.
51
+ */
52
+ export function divider(color, width = 40) {
53
+ const line = '─'.repeat(width);
54
+ return dim(line, color);
55
+ }
56
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/ui/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,wBAAwB;AACxB,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,KAAc;IAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,KAAc;IAC/C,OAAO,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,KAAc;IAChD,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,KAAc;IAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,KAAc;IACjD,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,KAAc;IAC/C,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,KAAc;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,KAAK,GAAG,EAAE;IAChD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC"}
package/package.json CHANGED
@@ -1,49 +1,55 @@
1
- {
2
- "name": "myshell-tools",
3
- "version": "1.0.0",
4
- "description": "Hierarchical AI orchestration for your shell - Claude + GPT working together like an org chart",
5
- "type": "module",
6
- "bin": {
7
- "cortex": "src/cli.mjs"
8
- },
9
- "engines": {
10
- "node": ">=20.0.0"
11
- },
12
- "keywords": [
13
- "ai",
14
- "claude",
15
- "gpt",
16
- "orchestration",
17
- "cli",
18
- "shell",
19
- "coding",
20
- "assistant",
21
- "hierarchical",
22
- "automation",
23
- "developer-tools",
24
- "artificial-intelligence"
25
- ],
26
- "author": "HeyVera <hello@heyvera.org>",
27
- "license": "MIT",
28
- "repository": {
29
- "type": "git",
30
- "url": "git+https://github.com/heyvera/cortex-ai.git"
31
- },
32
- "bugs": {
33
- "url": "https://github.com/heyvera/cortex-ai/issues"
34
- },
35
- "homepage": "https://github.com/heyvera/cortex-ai#readme",
36
- "files": [
37
- "src/",
38
- "data/",
39
- "templates/",
40
- "README.md",
41
- "LICENSE",
42
- "CHANGELOG.md"
43
- ],
44
- "dependencies": {},
45
- "scripts": {
46
- "start": "node src/cli.mjs",
47
- "test": "node src/cli.mjs --doctor"
48
- }
49
- }
1
+ {
2
+ "name": "myshell-tools",
3
+ "version": "2.0.0-alpha.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "myshell-tools": "dist/cli.js",
7
+ "myshell": "dist/cli.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=20.0.0"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "test": "node --import ./test/register.mjs --experimental-strip-types --test \"test/unit/**/*.test.ts\" \"test/arch/**/*.test.ts\"",
15
+ "test:contract": "node --import ./test/register.mjs --experimental-strip-types --test \"test/contract/**/*.test.ts\"",
16
+ "test:e2e": "CORTEX_E2E=1 node --import ./test/register.mjs --experimental-strip-types --test \"test/integration/**/*.test.ts\"",
17
+ "lint": "eslint src test",
18
+ "format": "prettier --check .",
19
+ "typecheck": "tsc --noEmit",
20
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
21
+ "prepublishOnly": "npm run clean && npm run build && npm run test"
22
+ },
23
+ "files": [
24
+ "dist/",
25
+ "README.md",
26
+ "LICENSE",
27
+ "CHANGELOG.md"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/hey-vera/myshell-tools.git"
32
+ },
33
+ "author": "HeyVera <hello@heyvera.org>",
34
+ "license": "MIT",
35
+ "keywords": [
36
+ "ai",
37
+ "orchestration",
38
+ "cli",
39
+ "claude",
40
+ "codex",
41
+ "multi-model"
42
+ ],
43
+ "devDependencies": {
44
+ "@eslint/js": "^9.0.0",
45
+ "@types/node": "^20.0.0",
46
+ "c8": "^10.0.0",
47
+ "eslint": "^9.0.0",
48
+ "prettier": "^3.0.0",
49
+ "typescript": "^5.0.0",
50
+ "typescript-eslint": "^8.0.0"
51
+ },
52
+ "dependencies": {
53
+ "execa": "^9.6.1"
54
+ }
55
+ }
@@ -1,113 +0,0 @@
1
- {
2
- "subscriptions": {
3
- "claude": {
4
- "models": {
5
- "opus": {
6
- "tier": "manager",
7
- "input_per_mtok": 5.0,
8
- "output_per_mtok": 25.0,
9
- "context_window": 1000000,
10
- "max_output": 128000,
11
- "model_id": "opus",
12
- "strengths": ["complex reasoning", "architecture decisions", "code review", "error recovery"],
13
- "best_for": "high-stakes decisions, complex debugging, escalations"
14
- },
15
- "sonnet": {
16
- "tier": "ic",
17
- "input_per_mtok": 3.0,
18
- "output_per_mtok": 15.0,
19
- "context_window": 1000000,
20
- "max_output": 64000,
21
- "model_id": "sonnet",
22
- "strengths": ["best speed/intelligence ratio", "precise edits", "implementation"],
23
- "best_for": "most coding tasks, refactoring, implementing features"
24
- },
25
- "haiku": {
26
- "tier": "worker",
27
- "input_per_mtok": 1.0,
28
- "output_per_mtok": 5.0,
29
- "context_window": 200000,
30
- "max_output": 64000,
31
- "model_id": "haiku",
32
- "strengths": ["fastest latency", "cheapest", "good for simple tasks"],
33
- "best_for": "file lookups, grep, simple tasks, gathering information"
34
- }
35
- }
36
- },
37
- "openai": {
38
- "models": {
39
- "gpt-5.5": {
40
- "tier": "manager",
41
- "input_per_mtok": 5.0,
42
- "output_per_mtok": 30.0,
43
- "context_window": 1000000,
44
- "max_output": 128000,
45
- "model_id": "gpt-5.5",
46
- "strengths": ["complex reasoning", "independent perspective", "code review"],
47
- "best_for": "architectural decisions, second opinions, escalations"
48
- },
49
- "gpt-5.4": {
50
- "tier": "ic",
51
- "input_per_mtok": 2.5,
52
- "output_per_mtok": 15.0,
53
- "context_window": 1000000,
54
- "max_output": 128000,
55
- "model_id": "gpt-5.4",
56
- "strengths": ["good speed/cost ratio", "strong implementation"],
57
- "best_for": "implementation and execution tasks"
58
- },
59
- "gpt-4.1-mini": {
60
- "tier": "worker",
61
- "input_per_mtok": 0.40,
62
- "output_per_mtok": 1.60,
63
- "context_window": 1047576,
64
- "max_output": 32768,
65
- "model_id": "gpt-4.1-mini",
66
- "strengths": ["extremely cheap", "good for simple tasks"],
67
- "best_for": "simple lookups, grep, file operations"
68
- }
69
- }
70
- }
71
- },
72
-
73
- "tiers": {
74
- "worker": {
75
- "description": "Simple tasks: file lookups, grep, basic operations",
76
- "escalation_threshold": 0.6,
77
- "max_attempts": 2
78
- },
79
- "ic": {
80
- "description": "Individual contributor: coding, implementation, most work",
81
- "escalation_threshold": 0.5,
82
- "max_attempts": 2
83
- },
84
- "manager": {
85
- "description": "Manager: architecture, review, complex decisions",
86
- "escalation_threshold": 0.3,
87
- "max_attempts": 1
88
- }
89
- },
90
-
91
- "escalation_triggers": {
92
- "confidence_threshold": 0.5,
93
- "keywords": {
94
- "auth": "critical",
95
- "credential": "critical",
96
- "secret": "critical",
97
- "token": "critical",
98
- "password": "critical",
99
- "login": "high",
100
- "billing": "high",
101
- "payment": "high",
102
- "deploy": "high",
103
- "migration": "high"
104
- },
105
- "file_patterns": {
106
- "**/.env*": "critical",
107
- "**/auth/**": "high",
108
- "**/security/**": "high",
109
- "**/payment/**": "high",
110
- "**/billing/**": "high"
111
- }
112
- }
113
- }
@@ -1,328 +0,0 @@
1
- /**
2
- * recovery.mjs — Authentication failure recovery with helpful guidance
3
- */
4
-
5
- import { spawnSync } from 'child_process';
6
- import { backgroundRefresh, displayRefreshStatus } from './refresh.mjs';
7
-
8
- /**
9
- * Check if a provider CLI is installed
10
- */
11
- function isCliInstalled(command) {
12
- try {
13
- const result = spawnSync('which', [command], {
14
- encoding: 'utf8',
15
- stdio: 'pipe'
16
- });
17
- return result.status === 0;
18
- } catch {
19
- return false;
20
- }
21
- }
22
-
23
- /**
24
- * Detect OS for platform-specific installation guidance
25
- */
26
- function detectOS() {
27
- const platform = process.platform;
28
- const arch = process.arch;
29
-
30
- if (platform === 'darwin') return 'macOS';
31
- if (platform === 'win32') return 'Windows';
32
- if (platform === 'linux') {
33
- // Try to detect specific Linux distribution
34
- try {
35
- const result = spawnSync('cat', ['/etc/os-release'], {
36
- encoding: 'utf8',
37
- stdio: 'pipe'
38
- });
39
- if (result.status === 0) {
40
- if (result.stdout.includes('Ubuntu')) return 'Ubuntu';
41
- if (result.stdout.includes('Debian')) return 'Debian';
42
- if (result.stdout.includes('CentOS') || result.stdout.includes('Red Hat')) return 'RHEL/CentOS';
43
- }
44
- } catch {}
45
- return 'Linux';
46
- }
47
-
48
- return platform;
49
- }
50
-
51
- /**
52
- * Provide installation guidance for missing CLIs
53
- */
54
- export function provideCLIInstallationGuidance(provider) {
55
- const os = detectOS();
56
-
57
- console.log(`\nāŒ ${provider.toUpperCase()} CLI not found`);
58
- console.log(`šŸ“¦ Installation Instructions for ${os}:`);
59
-
60
- if (provider === 'claude') {
61
- switch (os) {
62
- case 'macOS':
63
- console.log(' # Option 1: Using pip');
64
- console.log(' pip install anthropic-cli');
65
- console.log('\n # Option 2: Using Homebrew (if available)');
66
- console.log(' brew install anthropic-cli');
67
- break;
68
-
69
- case 'Ubuntu':
70
- case 'Debian':
71
- console.log(' # Install pip if not available');
72
- console.log(' sudo apt update && sudo apt install python3-pip');
73
- console.log('\n # Install Claude CLI');
74
- console.log(' pip3 install anthropic-cli');
75
- break;
76
-
77
- case 'RHEL/CentOS':
78
- console.log(' # Install pip if not available');
79
- console.log(' sudo yum install python3-pip');
80
- console.log('\n # Install Claude CLI');
81
- console.log(' pip3 install anthropic-cli');
82
- break;
83
-
84
- case 'Windows':
85
- console.log(' # Install via pip (requires Python)');
86
- console.log(' pip install anthropic-cli');
87
- console.log('\n # Or download from: https://claude.ai/cli');
88
- break;
89
-
90
- default:
91
- console.log(' pip install anthropic-cli');
92
- break;
93
- }
94
-
95
- console.log('\nšŸ” After installation, authenticate:');
96
- console.log(' claude auth login');
97
-
98
- } else if (provider === 'codex') {
99
- switch (os) {
100
- case 'macOS':
101
- console.log(' # Using npm (recommended)');
102
- console.log(' npm install -g @openai/codex');
103
- console.log('\n # Or using Homebrew');
104
- console.log(' brew install openai-codex');
105
- break;
106
-
107
- case 'Ubuntu':
108
- case 'Debian':
109
- console.log(' # Install Node.js if not available');
110
- console.log(' curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -');
111
- console.log(' sudo apt-get install -y nodejs');
112
- console.log('\n # Install Codex CLI');
113
- console.log(' sudo npm install -g @openai/codex');
114
- break;
115
-
116
- case 'RHEL/CentOS':
117
- console.log(' # Install Node.js if not available');
118
- console.log(' curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -');
119
- console.log(' sudo yum install nodejs npm');
120
- console.log('\n # Install Codex CLI');
121
- console.log(' sudo npm install -g @openai/codex');
122
- break;
123
-
124
- case 'Windows':
125
- console.log(' # Install via npm (requires Node.js)');
126
- console.log(' npm install -g @openai/codex');
127
- console.log('\n # Or download from: https://platform.openai.com/docs/tools/codex-cli');
128
- break;
129
-
130
- default:
131
- console.log(' npm install -g @openai/codex');
132
- break;
133
- }
134
-
135
- console.log('\nšŸ” After installation, authenticate:');
136
- console.log(' codex login');
137
- }
138
-
139
- console.log('\nšŸ’” You need at least one authenticated CLI to use Cortex.');
140
- console.log(' Both CLIs enable different model tiers and redundancy.');
141
- }
142
-
143
- /**
144
- * Provide authentication guidance for installed but unauth'd CLIs
145
- */
146
- export function provideAuthGuidance(provider) {
147
- console.log(`\nāš ļø ${provider.toUpperCase()} CLI found but not authenticated`);
148
-
149
- if (provider === 'claude') {
150
- console.log('šŸ” Authenticate Claude CLI:');
151
- console.log(' claude auth login');
152
- console.log('\nšŸ’” This will open a browser to sign in to your Claude account.');
153
- console.log(' If running on a server, use: claude auth login --no-browser');
154
-
155
- } else if (provider === 'codex') {
156
- console.log('šŸ” Authenticate Codex CLI:');
157
- console.log(' codex login');
158
- console.log('\nšŸ’” This will prompt for your OpenAI API key.');
159
- console.log(' Get your key from: https://platform.openai.com/api-keys');
160
- }
161
-
162
- console.log('\nšŸ”„ After authentication, restart Cortex to refresh provider status.');
163
- }
164
-
165
- /**
166
- * Handle authentication failures during operation
167
- */
168
- export async function handleAuthFailure(provider, error) {
169
- console.log(`\nšŸ”“ Authentication failed for ${provider.toUpperCase()}`);
170
- console.log(` Error: ${error}`);
171
-
172
- // Try to refresh tokens first
173
- console.log('\nšŸ”„ Attempting token refresh...');
174
- const refreshResult = await backgroundRefresh();
175
-
176
- if (refreshResult.success && !refreshResult.needReauth.includes(provider)) {
177
- console.log('āœ… Token refresh successful! Please retry your request.');
178
- return { recovered: true, action: 'retry' };
179
- }
180
-
181
- // If refresh failed or this provider needs reauth
182
- console.log('āŒ Token refresh failed or expired. Re-authentication required.');
183
-
184
- if (provider === 'claude') {
185
- console.log('\nšŸ” Re-authenticate Claude CLI:');
186
- console.log(' claude auth login');
187
-
188
- // Check if it's a subscription issue
189
- if (error.includes('subscription') || error.includes('billing')) {
190
- console.log('\nšŸ’³ Possible subscription issue detected.');
191
- console.log(' Check your Claude Pro subscription at: https://claude.ai/settings');
192
- }
193
-
194
- } else if (provider === 'codex' || provider === 'openai') {
195
- console.log('\nšŸ” Re-authenticate Codex CLI:');
196
- console.log(' codex login');
197
-
198
- // Check if it's an API quota/billing issue
199
- if (error.includes('quota') || error.includes('billing') || error.includes('insufficient')) {
200
- console.log('\nšŸ’³ Possible API quota/billing issue detected.');
201
- console.log(' Check your OpenAI account at: https://platform.openai.com/usage');
202
- }
203
- }
204
-
205
- return { recovered: false, action: 'reauth_required' };
206
- }
207
-
208
- /**
209
- * Comprehensive provider health check
210
- */
211
- export async function checkProviderHealth(environment) {
212
- const health = {
213
- claude: { healthy: false, issues: [] },
214
- codex: { healthy: false, issues: [] },
215
- overall: { healthy: false, availableProviders: 0 }
216
- };
217
-
218
- // Check Claude
219
- if (!environment.claude.installed) {
220
- health.claude.issues.push('CLI not installed');
221
- } else if (!environment.claude.authed) {
222
- health.claude.issues.push('Not authenticated');
223
- } else {
224
- health.claude.healthy = true;
225
- health.overall.availableProviders++;
226
- }
227
-
228
- // Check Codex
229
- if (!environment.codex.installed) {
230
- health.codex.issues.push('CLI not installed');
231
- } else if (!environment.codex.authed) {
232
- health.codex.issues.push('Not authenticated');
233
- } else {
234
- health.codex.healthy = true;
235
- health.overall.availableProviders++;
236
- }
237
-
238
- health.overall.healthy = health.overall.availableProviders > 0;
239
-
240
- return health;
241
- }
242
-
243
- /**
244
- * Auto-recovery sequence with user guidance
245
- */
246
- export async function autoRecovery(environment) {
247
- console.log('šŸ”§ Running auto-recovery sequence...\n');
248
-
249
- const health = await checkProviderHealth(environment);
250
-
251
- // Try token refresh first if we have any auth'd providers
252
- if (health.overall.availableProviders > 0) {
253
- console.log('šŸ”„ Refreshing authentication tokens...');
254
- const refreshResult = await backgroundRefresh();
255
- displayRefreshStatus(refreshResult);
256
-
257
- if (refreshResult.success && refreshResult.refreshed.length > 0) {
258
- console.log('āœ… Some tokens refreshed successfully.');
259
- return { recovered: true, action: 'tokens_refreshed' };
260
- }
261
- }
262
-
263
- // Provide guidance for missing/unauth'd providers
264
- if (!health.claude.healthy) {
265
- if (health.claude.issues.includes('CLI not installed')) {
266
- provideCLIInstallationGuidance('claude');
267
- } else if (health.claude.issues.includes('Not authenticated')) {
268
- provideAuthGuidance('claude');
269
- }
270
- }
271
-
272
- if (!health.codex.healthy) {
273
- if (health.codex.issues.includes('CLI not installed')) {
274
- provideCLIInstallationGuidance('codex');
275
- } else if (health.codex.issues.includes('Not authenticated')) {
276
- provideAuthGuidance('codex');
277
- }
278
- }
279
-
280
- // Final status
281
- if (health.overall.healthy) {
282
- console.log('\nāœ… At least one provider is healthy. Cortex can continue.');
283
- return { recovered: true, action: 'partial_recovery' };
284
- } else {
285
- console.log('\nāŒ No healthy providers available. Please follow the guidance above.');
286
- return { recovered: false, action: 'manual_intervention_required' };
287
- }
288
- }
289
-
290
- /**
291
- * Recovery suggestions based on error type
292
- */
293
- export function getRecoverySuggestions(error) {
294
- const errorString = error.toString().toLowerCase();
295
- const suggestions = [];
296
-
297
- if (errorString.includes('enoent') || errorString.includes('command not found')) {
298
- suggestions.push('CLI tool not installed or not in PATH');
299
- suggestions.push('Check installation instructions above');
300
- }
301
-
302
- if (errorString.includes('authentication') || errorString.includes('unauthorized') || errorString.includes('401')) {
303
- suggestions.push('Authentication failed or expired');
304
- suggestions.push('Try re-authenticating with the CLI');
305
- }
306
-
307
- if (errorString.includes('timeout') || errorString.includes('network') || errorString.includes('enotfound')) {
308
- suggestions.push('Network connectivity issue');
309
- suggestions.push('Check internet connection and try again');
310
- }
311
-
312
- if (errorString.includes('rate limit') || errorString.includes('429')) {
313
- suggestions.push('API rate limit exceeded');
314
- suggestions.push('Wait a few minutes before retrying');
315
- }
316
-
317
- if (errorString.includes('quota') || errorString.includes('billing') || errorString.includes('subscription')) {
318
- suggestions.push('Account/billing issue detected');
319
- suggestions.push('Check your account status and subscription');
320
- }
321
-
322
- if (suggestions.length === 0) {
323
- suggestions.push('Unknown error occurred');
324
- suggestions.push('Try running with --doctor for more details');
325
- }
326
-
327
- return suggestions;
328
- }