@mmmbuto/nexuscli 0.5.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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/bin/nexuscli.js +117 -0
  4. package/frontend/dist/apple-touch-icon.png +0 -0
  5. package/frontend/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  6. package/frontend/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  7. package/frontend/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  8. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  9. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  10. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  11. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  12. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  13. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  14. package/frontend/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  15. package/frontend/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  16. package/frontend/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  17. package/frontend/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  18. package/frontend/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  19. package/frontend/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  20. package/frontend/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  21. package/frontend/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  22. package/frontend/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  23. package/frontend/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  24. package/frontend/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  25. package/frontend/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  26. package/frontend/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  27. package/frontend/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  28. package/frontend/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  29. package/frontend/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  30. package/frontend/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  31. package/frontend/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  32. package/frontend/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  33. package/frontend/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  34. package/frontend/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  35. package/frontend/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  36. package/frontend/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  37. package/frontend/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  38. package/frontend/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  39. package/frontend/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  40. package/frontend/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  41. package/frontend/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  42. package/frontend/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  43. package/frontend/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  44. package/frontend/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  45. package/frontend/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  46. package/frontend/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  47. package/frontend/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  48. package/frontend/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  49. package/frontend/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  50. package/frontend/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  51. package/frontend/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  52. package/frontend/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  53. package/frontend/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  54. package/frontend/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  55. package/frontend/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  56. package/frontend/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  57. package/frontend/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  58. package/frontend/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  59. package/frontend/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  60. package/frontend/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  61. package/frontend/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  62. package/frontend/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  63. package/frontend/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  64. package/frontend/dist/assets/index-Bn_l1e6e.css +1 -0
  65. package/frontend/dist/assets/index-CikJbUR5.js +8617 -0
  66. package/frontend/dist/browserconfig.xml +12 -0
  67. package/frontend/dist/favicon-16x16.png +0 -0
  68. package/frontend/dist/favicon-32x32.png +0 -0
  69. package/frontend/dist/favicon-48x48.png +0 -0
  70. package/frontend/dist/favicon.ico +0 -0
  71. package/frontend/dist/icon-192.png +0 -0
  72. package/frontend/dist/icon-512.png +0 -0
  73. package/frontend/dist/icon-maskable-192.png +0 -0
  74. package/frontend/dist/icon-maskable-512.png +0 -0
  75. package/frontend/dist/index.html +79 -0
  76. package/frontend/dist/manifest.json +75 -0
  77. package/frontend/dist/sw.js +122 -0
  78. package/frontend/package.json +28 -0
  79. package/lib/cli/api.js +156 -0
  80. package/lib/cli/boot.js +172 -0
  81. package/lib/cli/config.js +185 -0
  82. package/lib/cli/engines.js +257 -0
  83. package/lib/cli/init.js +660 -0
  84. package/lib/cli/logs.js +72 -0
  85. package/lib/cli/start.js +220 -0
  86. package/lib/cli/status.js +187 -0
  87. package/lib/cli/stop.js +64 -0
  88. package/lib/cli/uninstall.js +194 -0
  89. package/lib/cli/users.js +295 -0
  90. package/lib/cli/workspaces.js +337 -0
  91. package/lib/config/manager.js +233 -0
  92. package/lib/server/.env.example +20 -0
  93. package/lib/server/db/adapter.js +314 -0
  94. package/lib/server/db/drivers/better-sqlite3.js +38 -0
  95. package/lib/server/db/drivers/sql-js.js +75 -0
  96. package/lib/server/db/migrate.js +174 -0
  97. package/lib/server/db/migrations/001_ultra_light_schema.sql +96 -0
  98. package/lib/server/db/migrations/002_session_conversation_mapping.sql +19 -0
  99. package/lib/server/db/migrations/003_message_engine_tracking.sql +18 -0
  100. package/lib/server/db/migrations/004_performance_indexes.sql +16 -0
  101. package/lib/server/db.js +2 -0
  102. package/lib/server/lib/cli-wrapper.js +164 -0
  103. package/lib/server/lib/output-parser.js +132 -0
  104. package/lib/server/lib/pty-adapter.js +57 -0
  105. package/lib/server/middleware/auth.js +103 -0
  106. package/lib/server/models/Conversation.js +259 -0
  107. package/lib/server/models/Message.js +228 -0
  108. package/lib/server/models/User.js +115 -0
  109. package/lib/server/package-lock.json +5895 -0
  110. package/lib/server/routes/auth.js +168 -0
  111. package/lib/server/routes/chat.js +206 -0
  112. package/lib/server/routes/codex.js +205 -0
  113. package/lib/server/routes/conversations.js +224 -0
  114. package/lib/server/routes/gemini.js +228 -0
  115. package/lib/server/routes/jobs.js +317 -0
  116. package/lib/server/routes/messages.js +60 -0
  117. package/lib/server/routes/models.js +198 -0
  118. package/lib/server/routes/sessions.js +285 -0
  119. package/lib/server/routes/upload.js +134 -0
  120. package/lib/server/routes/wake-lock.js +95 -0
  121. package/lib/server/routes/workspace.js +80 -0
  122. package/lib/server/routes/workspaces.js +142 -0
  123. package/lib/server/scripts/cleanup-ghost-sessions.js +71 -0
  124. package/lib/server/scripts/seed-users.js +37 -0
  125. package/lib/server/scripts/test-history-access.js +50 -0
  126. package/lib/server/server.js +227 -0
  127. package/lib/server/services/cache.js +85 -0
  128. package/lib/server/services/claude-wrapper.js +312 -0
  129. package/lib/server/services/cli-loader.js +384 -0
  130. package/lib/server/services/codex-output-parser.js +277 -0
  131. package/lib/server/services/codex-wrapper.js +224 -0
  132. package/lib/server/services/context-bridge.js +289 -0
  133. package/lib/server/services/gemini-output-parser.js +398 -0
  134. package/lib/server/services/gemini-wrapper.js +249 -0
  135. package/lib/server/services/history-sync.js +407 -0
  136. package/lib/server/services/output-parser.js +415 -0
  137. package/lib/server/services/session-manager.js +465 -0
  138. package/lib/server/services/summary-generator.js +259 -0
  139. package/lib/server/services/workspace-manager.js +516 -0
  140. package/lib/server/tests/history-sync.test.js +90 -0
  141. package/lib/server/tests/integration-session-sync.test.js +151 -0
  142. package/lib/server/tests/integration.test.js +76 -0
  143. package/lib/server/tests/performance.test.js +118 -0
  144. package/lib/server/tests/services.test.js +160 -0
  145. package/lib/setup/postinstall.js +216 -0
  146. package/lib/utils/paths.js +107 -0
  147. package/lib/utils/termux.js +145 -0
  148. package/package.json +82 -0
@@ -0,0 +1,185 @@
1
+ /**
2
+ * nexuscli config - Manage configuration
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const inquirer = require('inquirer');
7
+
8
+ const {
9
+ isInitialized,
10
+ getConfig,
11
+ getConfigValue,
12
+ setConfigValue,
13
+ getConfigKeys
14
+ } = require('../config/manager');
15
+ const { PATHS } = require('../utils/paths');
16
+
17
+ /**
18
+ * List all config values
19
+ */
20
+ function listConfig() {
21
+ const keys = getConfigKeys();
22
+ const maxKeyLen = Math.max(...keys.map(k => k.length));
23
+
24
+ console.log(chalk.cyan('Configuration:'));
25
+ console.log(chalk.gray(` File: ${PATHS.CONFIG_FILE}`));
26
+ console.log('');
27
+
28
+ for (const key of keys) {
29
+ const value = getConfigValue(key);
30
+ const displayValue = formatValue(value);
31
+ console.log(` ${chalk.white(key.padEnd(maxKeyLen))} ${displayValue}`);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Format a value for display
37
+ */
38
+ function formatValue(value) {
39
+ if (value === null || value === undefined) {
40
+ return chalk.gray('null');
41
+ }
42
+ if (typeof value === 'boolean') {
43
+ return value ? chalk.green('true') : chalk.red('false');
44
+ }
45
+ if (typeof value === 'number') {
46
+ return chalk.yellow(String(value));
47
+ }
48
+ if (Array.isArray(value)) {
49
+ return chalk.cyan(JSON.stringify(value));
50
+ }
51
+ if (typeof value === 'string') {
52
+ // Hide sensitive values
53
+ if (value.length > 20 && (value.includes('-') || value.includes('$'))) {
54
+ return chalk.gray('***hidden***');
55
+ }
56
+ return chalk.white(value);
57
+ }
58
+ return chalk.gray(String(value));
59
+ }
60
+
61
+ /**
62
+ * Main config command
63
+ */
64
+ async function config(action, key, value) {
65
+ console.log('');
66
+
67
+ if (!isInitialized()) {
68
+ console.log(chalk.yellow('NexusCLI is not configured.'));
69
+ console.log(`Run ${chalk.cyan('nexuscli init')} first.`);
70
+ console.log('');
71
+ return;
72
+ }
73
+
74
+ // No action = list all
75
+ if (!action || action === 'list') {
76
+ listConfig();
77
+ console.log('');
78
+ return;
79
+ }
80
+
81
+ // Get value
82
+ if (action === 'get') {
83
+ if (!key) {
84
+ console.log(chalk.red('Usage: nexuscli config get <key>'));
85
+ console.log('');
86
+ return;
87
+ }
88
+
89
+ const val = getConfigValue(key);
90
+ if (val === undefined) {
91
+ console.log(chalk.yellow(`Key not found: ${key}`));
92
+ } else {
93
+ console.log(formatValue(val));
94
+ }
95
+ console.log('');
96
+ return;
97
+ }
98
+
99
+ // Set value
100
+ if (action === 'set') {
101
+ if (!key || value === undefined) {
102
+ console.log(chalk.red('Usage: nexuscli config set <key> <value>'));
103
+ console.log('');
104
+ return;
105
+ }
106
+
107
+ // Confirm sensitive changes
108
+ const sensitiveKeys = ['auth.jwt_secret', 'auth.pass_hash'];
109
+ if (sensitiveKeys.includes(key)) {
110
+ const { confirm } = await inquirer.prompt([{
111
+ type: 'confirm',
112
+ name: 'confirm',
113
+ message: `Changing ${key} may break authentication. Continue?`,
114
+ default: false
115
+ }]);
116
+
117
+ if (!confirm) {
118
+ console.log(chalk.yellow('Cancelled.'));
119
+ console.log('');
120
+ return;
121
+ }
122
+ }
123
+
124
+ if (setConfigValue(key, value)) {
125
+ console.log(chalk.green(` ✓ ${key} = ${value}`));
126
+ } else {
127
+ console.log(chalk.red(` ✗ Failed to set ${key}`));
128
+ }
129
+ console.log('');
130
+ return;
131
+ }
132
+
133
+ // Interactive editor
134
+ if (action === 'edit') {
135
+ const currentConfig = getConfig();
136
+
137
+ const answers = await inquirer.prompt([
138
+ {
139
+ type: 'number',
140
+ name: 'port',
141
+ message: 'Server port:',
142
+ default: currentConfig.server.port
143
+ },
144
+ {
145
+ type: 'list',
146
+ name: 'claudeModel',
147
+ message: 'Claude default model:',
148
+ choices: ['haiku', 'sonnet', 'opus'],
149
+ default: currentConfig.engines?.claude?.model || 'sonnet'
150
+ },
151
+ {
152
+ type: 'confirm',
153
+ name: 'wakeLock',
154
+ message: 'Enable wake-lock?',
155
+ default: currentConfig.termux?.wake_lock ?? true
156
+ },
157
+ {
158
+ type: 'confirm',
159
+ name: 'notifications',
160
+ message: 'Enable notifications?',
161
+ default: currentConfig.termux?.notifications ?? true
162
+ }
163
+ ]);
164
+
165
+ setConfigValue('server.port', answers.port);
166
+ setConfigValue('engines.claude.model', answers.claudeModel);
167
+ setConfigValue('termux.wake_lock', answers.wakeLock);
168
+ setConfigValue('termux.notifications', answers.notifications);
169
+
170
+ console.log(chalk.green(' ✓ Configuration updated'));
171
+ console.log('');
172
+ return;
173
+ }
174
+
175
+ // Unknown action
176
+ console.log(chalk.red(`Unknown action: ${action}`));
177
+ console.log('Usage:');
178
+ console.log(' nexuscli config List all');
179
+ console.log(' nexuscli config get <k> Get value');
180
+ console.log(' nexuscli config set <k> <v> Set value');
181
+ console.log(' nexuscli config edit Interactive editor');
182
+ console.log('');
183
+ }
184
+
185
+ module.exports = config;
@@ -0,0 +1,257 @@
1
+ /**
2
+ * nexuscli engines - Manage AI engines
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const inquirer = require('inquirer');
7
+ const { execSync } = require('child_process');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const { isInitialized, getConfig, setConfigValue } = require('../config/manager');
12
+ const { HOME } = require('../utils/paths');
13
+
14
+ /**
15
+ * Detect available engines (TRI CLI v0.4.0)
16
+ */
17
+ function detectEngines() {
18
+ const engines = {
19
+ claude: { available: false, path: null, version: null },
20
+ codex: { available: false, path: null, version: null },
21
+ gemini: { available: false, path: null, version: null }
22
+ };
23
+
24
+ // Claude
25
+ try {
26
+ const claudePath = execSync('which claude 2>/dev/null', { encoding: 'utf8' }).trim();
27
+ const claudeVersion = execSync('claude --version 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
28
+ engines.claude = { available: true, path: claudePath, version: claudeVersion };
29
+ } catch {}
30
+
31
+ // Codex
32
+ try {
33
+ const codexPath = execSync('which codex 2>/dev/null', { encoding: 'utf8' }).trim();
34
+ const codexVersion = execSync('codex --version 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
35
+ engines.codex = { available: true, path: codexPath, version: codexVersion };
36
+ } catch {}
37
+
38
+ // Gemini
39
+ try {
40
+ const geminiPath = execSync('which gemini 2>/dev/null', { encoding: 'utf8' }).trim();
41
+ const geminiVersion = execSync('gemini --version 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
42
+ engines.gemini = { available: true, path: geminiPath, version: geminiVersion };
43
+ } catch {}
44
+
45
+ return engines;
46
+ }
47
+
48
+ /**
49
+ * List engines status (TRI CLI v0.4.0)
50
+ */
51
+ function listEngines() {
52
+ const config = getConfig();
53
+ const detected = detectEngines();
54
+
55
+ console.log(chalk.bold('╔═══════════════════════════════════════════╗'));
56
+ console.log(chalk.bold('║ AI Engines (TRI CLI) ║'));
57
+ console.log(chalk.bold('╠═══════════════════════════════════════════╣'));
58
+
59
+ // Claude
60
+ const claudeEnabled = config.engines?.claude?.enabled !== false;
61
+ const claudeModel = config.engines?.claude?.model || 'sonnet';
62
+ if (detected.claude.available) {
63
+ const status = claudeEnabled ? chalk.green('✓ enabled') : chalk.gray('○ disabled');
64
+ console.log(chalk.bold(`║ Claude: ${status} (${claudeModel})`));
65
+ console.log(chalk.gray(`║ ${detected.claude.version}`));
66
+ } else {
67
+ console.log(chalk.bold(`║ Claude: ${chalk.red('✗ not installed')}`));
68
+ }
69
+
70
+ // Codex
71
+ const codexEnabled = config.engines?.codex?.enabled === true;
72
+ if (detected.codex.available) {
73
+ const status = codexEnabled ? chalk.green('✓ enabled') : chalk.gray('○ disabled');
74
+ console.log(chalk.bold(`║ Codex: ${status}`));
75
+ console.log(chalk.gray(`║ ${detected.codex.version}`));
76
+ } else {
77
+ console.log(chalk.bold(`║ Codex: ${chalk.gray('○ not installed')}`));
78
+ }
79
+
80
+ // Gemini
81
+ const geminiEnabled = config.engines?.gemini?.enabled === true;
82
+ if (detected.gemini.available) {
83
+ const status = geminiEnabled ? chalk.green('✓ enabled') : chalk.gray('○ disabled');
84
+ console.log(chalk.bold(`║ Gemini: ${status}`));
85
+ console.log(chalk.gray(`║ ${detected.gemini.version}`));
86
+ } else {
87
+ console.log(chalk.bold(`║ Gemini: ${chalk.gray('○ not installed')}`));
88
+ }
89
+
90
+ console.log(chalk.bold('╚═══════════════════════════════════════════╝'));
91
+ }
92
+
93
+ /**
94
+ * Test an engine
95
+ */
96
+ async function testEngine(engine) {
97
+ console.log(chalk.cyan(`Testing ${engine}...`));
98
+
99
+ if (engine === 'claude') {
100
+ try {
101
+ execSync('claude --version', { stdio: 'inherit' });
102
+ console.log(chalk.green(` ✓ Claude CLI is working`));
103
+ return true;
104
+ } catch {
105
+ console.log(chalk.red(` ✗ Claude CLI failed`));
106
+ return false;
107
+ }
108
+ }
109
+
110
+ if (engine === 'codex') {
111
+ try {
112
+ execSync('codex --version', { stdio: 'inherit' });
113
+ console.log(chalk.green(` ✓ Codex CLI is working`));
114
+ return true;
115
+ } catch {
116
+ console.log(chalk.red(` ✗ Codex CLI not found`));
117
+ return false;
118
+ }
119
+ }
120
+
121
+ if (engine === 'gemini') {
122
+ try {
123
+ execSync('gemini --version', { stdio: 'inherit' });
124
+ console.log(chalk.green(` ✓ Gemini CLI is working`));
125
+ return true;
126
+ } catch {
127
+ console.log(chalk.red(` ✗ Gemini CLI not found`));
128
+ return false;
129
+ }
130
+ }
131
+
132
+ console.log(chalk.yellow(` Engine ${engine} not testable`));
133
+ return false;
134
+ }
135
+
136
+ /**
137
+ * Add/configure an engine (TRI CLI v0.4.0)
138
+ */
139
+ async function addEngine() {
140
+ const detected = detectEngines();
141
+
142
+ const choices = [];
143
+ if (detected.claude.available) {
144
+ choices.push({ name: `Claude (${detected.claude.version})`, value: 'claude' });
145
+ }
146
+ if (detected.codex.available) {
147
+ choices.push({ name: `Codex (${detected.codex.version})`, value: 'codex' });
148
+ }
149
+ if (detected.gemini.available) {
150
+ choices.push({ name: `Gemini (${detected.gemini.version})`, value: 'gemini' });
151
+ }
152
+
153
+ if (choices.length === 0) {
154
+ console.log(chalk.yellow(' No AI engines detected.'));
155
+ console.log(chalk.gray(' Install: claude, codex, or gemini CLI'));
156
+ return;
157
+ }
158
+
159
+ const { engine } = await inquirer.prompt([{
160
+ type: 'list',
161
+ name: 'engine',
162
+ message: 'Select engine to configure:',
163
+ choices
164
+ }]);
165
+
166
+ if (engine === 'claude') {
167
+ const { model } = await inquirer.prompt([{
168
+ type: 'list',
169
+ name: 'model',
170
+ message: 'Default Claude model:',
171
+ choices: [
172
+ { name: 'Haiku 4.5 (Fast)', value: 'haiku' },
173
+ { name: 'Sonnet 4.5 (Balanced)', value: 'sonnet' },
174
+ { name: 'Opus 4.5 (Most Intelligent)', value: 'opus' }
175
+ ],
176
+ default: 'sonnet'
177
+ }]);
178
+
179
+ setConfigValue('engines.claude.enabled', true);
180
+ setConfigValue('engines.claude.model', model);
181
+ console.log(chalk.green(` ✓ Claude configured (${model})`));
182
+ }
183
+
184
+ if (engine === 'codex') {
185
+ const { model } = await inquirer.prompt([{
186
+ type: 'list',
187
+ name: 'model',
188
+ message: 'Default Codex model:',
189
+ choices: [
190
+ { name: 'GPT-5.1 Codex Max (Best)', value: 'gpt-5.1-codex-max' },
191
+ { name: 'GPT-5.1 Codex', value: 'gpt-5.1-codex' },
192
+ { name: 'GPT-5.1 Codex Mini (Fast)', value: 'gpt-5.1-codex-mini' },
193
+ { name: 'GPT-5.1 (General)', value: 'gpt-5.1' }
194
+ ],
195
+ default: 'gpt-5.1-codex-max'
196
+ }]);
197
+
198
+ setConfigValue('engines.codex.enabled', true);
199
+ setConfigValue('engines.codex.model', model);
200
+ console.log(chalk.green(` ✓ Codex configured (${model})`));
201
+ }
202
+
203
+ if (engine === 'gemini') {
204
+ setConfigValue('engines.gemini.enabled', true);
205
+ setConfigValue('engines.gemini.model', 'gemini-3-pro-preview');
206
+ console.log(chalk.green(` ✓ Gemini configured (gemini-3-pro-preview)`));
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Main engines command
212
+ */
213
+ async function engines(action) {
214
+ console.log('');
215
+
216
+ if (!isInitialized()) {
217
+ console.log(chalk.yellow('NexusCLI is not configured.'));
218
+ console.log(`Run ${chalk.cyan('nexuscli init')} first.`);
219
+ console.log('');
220
+ return;
221
+ }
222
+
223
+ // No action = list
224
+ if (!action || action === 'list') {
225
+ listEngines();
226
+ console.log('');
227
+ return;
228
+ }
229
+
230
+ // Add engine
231
+ if (action === 'add') {
232
+ await addEngine();
233
+ console.log('');
234
+ return;
235
+ }
236
+
237
+ // Test engine
238
+ if (action === 'test') {
239
+ const config = getConfig();
240
+ const active = config.engines?.active || ['claude'];
241
+
242
+ for (const eng of active) {
243
+ await testEngine(eng);
244
+ }
245
+ console.log('');
246
+ return;
247
+ }
248
+
249
+ console.log(chalk.red(`Unknown action: ${action}`));
250
+ console.log('Usage:');
251
+ console.log(' nexuscli engines List engines');
252
+ console.log(' nexuscli engines add Add/configure engine');
253
+ console.log(' nexuscli engines test Test active engines');
254
+ console.log('');
255
+ }
256
+
257
+ module.exports = engines;