@datalabrotterdam/nova-ai-cli 1.2.0-alpha.5 → 1.2.0-alpha.7

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.7](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.6...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.7) (2026-10-24)
2
+
3
+
4
+ ### Features
5
+
6
+ * **agent:** Claude Code compatible hooks ([227fe8b](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/227fe8b273ec2e211e9d57674e76af5c42a473bd))
7
+ * **agent:** install the shared Nova skills ([64139dd](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/64139dd612933343f04f165de1a9ab78832d6864))
8
+ * **agent:** mark and protect bundled skills ([6ef52eb](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/6ef52eb6d048f8a3b4e75686244aabcfd0f62d6e))
9
+ * **agent:** thinking effort for reasoning models ([8551fea](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/8551feacff3e1675f369030e20fb2b5071f0db7f))
10
+ * **agent:** web_search tool ([12985eb](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/12985eb9ca06d6c814d4e0162465c651889d9370))
11
+ * **cli:** /effort slider for thinking models ([8ca67f2](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/8ca67f2f5dcc8f6bff8c36409a2baba5d43c4a02))
12
+ * **cli:** remember the thinking level ([883196c](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/883196c9fdb089f4f26ac82b557cc87dfc0b1c0b))
13
+ * OAuth sign-in for MCP servers ([ba57c79](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/ba57c79642f902ab2bc20f59e4627fb2b774bb4a))
14
+ * the user's agent files as session modes ([3386de8](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/3386de88d07b0351bd1e44960fcacfef26e3d45b))
15
+
16
+ # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.6](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.5...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.6) (2026-10-23)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **cli:** start the session before the first prompt ([0c26456](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/0c26456b8126e3b42a6de2e38212213ae5380e62))
22
+
23
+
24
+ ### Features
25
+
26
+ * **agent:** set workspace trust over ACP ([cec4a89](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/cec4a8949a4232f46448219e4d5363289651215d))
27
+
1
28
  # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.5](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.4...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.5) (2026-10-23)
2
29
 
3
30
 
package/README.md CHANGED
@@ -79,6 +79,8 @@ row. To select text, hold Shift (Option in some macOS terminals) while dragging.
79
79
 
80
80
  - **Modes** (Shift+Tab): `agent` does the work; `plan` reads the code, asks
81
81
  questions and builds a task list but changes nothing; `ask` only answers.
82
+ Your own agents (`~/.nova-ai/agents/<name>.agent.md`, the format the VS Code
83
+ extension uses) are extra modes after these; `/mode` lists them.
82
84
  - **Permissions**: before an edit or command you choose once, for this
83
85
  session, always (saved as an exact rule) or deny. Ctrl+K switches between
84
86
  asking, accepting edits, and bypass (never remembered).
@@ -95,12 +97,12 @@ row. To select text, hold Shift (Option in some macOS terminals) while dragging.
95
97
  | Enter / Shift+Enter | Send / new line (Shift+Enter where the terminal supports the kitty keyboard protocol: VS Code, kitty, WezTerm, Ghostty, foot, Alacritty, recent iTerm2; Alt+Enter, Ctrl+J or a trailing `\` work everywhere) |
96
98
  | Esc | Cancel the running turn |
97
99
  | Ctrl+C twice | Exit; prints `nova-ai --resume <id>` |
98
- | Shift+Tab | Cycle agent / ask / plan |
100
+ | Shift+Tab | Cycle agent / ask / plan and your own agents |
99
101
  | Ctrl+R / Ctrl+P / Ctrl+K | Sessions / models / permission mode |
100
102
  | Ctrl+O / Ctrl+T / Ctrl+B | Verbose tool output / tool inspector / background jobs |
101
103
  | Alt+V | Paste a clipboard image |
102
104
 
103
- Slash commands: `/help`, `/model`, `/mode`, `/permission`, `/session`,
105
+ Slash commands: `/help`, `/model`, `/effort` (thinking models), `/mode`, `/permission`, `/session`,
104
106
  `/resume`, `/clear`, `/compact`, `/rewind [n]`, `/queue`, `/steer`, `/usage`,
105
107
  `/tools`, `/mcp`, `/skills`, `/shell` and `/agent` (background jobs), `/trust`
106
108
  and `/exit`. Details:
@@ -169,7 +171,10 @@ need ACP. Everything is documented in
169
171
  and nested shells need an exact rule.
170
172
  - **Web pages are fetched only with approval.** `fetch_url` asks every time
171
173
  unless a rule allows that URL (a URL can carry data out), and a redirect to
172
- another site needs its own approval.
174
+ another site needs its own approval. `web_search` asks the same way, since
175
+ the query goes to the search providers (You.com, DuckDuckGo; your own
176
+ SearXNG server first when `~/.nova-ai/settings.json` has
177
+ `"webSearch": { "searxngUrl": "https://…" }`).
173
178
  - **File access stays in the workspace** (symlinks resolved); commands run in
174
179
  their own process group with a timeout, and the whole tree is stopped on
175
180
  cancel.
@@ -194,7 +199,22 @@ VS Code extension: settings, per-project trust, sessions and memory.
194
199
  skills folders are read too). `/skills` lists them; `/skills off <name>`
195
200
  switches one off in this project (`--global`: everywhere). Only names and
196
201
  short descriptions go into the prompt; Nova loads a skill when it fits.
202
+ Nova installs its own skills for Word, Excel, PowerPoint and PDF files
203
+ (`nova-docx`, `nova-xlsx`, `nova-pptx`, `nova-pdf`, from
204
+ [nova-ai-skills](https://github.com/Datalab-Rotterdam/nova-ai-skills)) in
205
+ `~/.nova-ai/skills` and keeps them up to date; they need Python 3.
206
+ - **Hooks**: Claude Code's `hooks` format in the same settings files (a
207
+ repository's own only once you trust the workspace): `PreToolUse`,
208
+ `PostToolUse`, `UserPromptSubmit`, `SessionStart` and `Stop`, with Claude
209
+ Code's tool names as matchers, so hooks from `.claude/settings.json` work
210
+ when copied over.
197
211
  - **MCP servers**: `.mcp.json` or `mcpServers` in `.nova-ai/settings.json`.
212
+ HTTP servers that use OAuth show "needs sign-in" in `/mcp`; `/mcp login
213
+ <server>` opens the sign-in page in your browser (tokens are kept in
214
+ `~/.nova-ai/mcp-auth`, refreshed automatically), `/mcp logout <server>`
215
+ forgets them. A server that wants a client registered by hand takes
216
+ `"oauth": { "clientId", "clientSecret", "scope", "callbackPort" }` in
217
+ `.mcp.json`.
198
218
 
199
219
  The complete layout and rule syntax: [docs/NOVA_HOME.md](https://github.com/Datalab-Rotterdam/nova-ai-cli/blob/main/docs/NOVA_HOME.md).
200
220
 
@@ -207,6 +227,12 @@ to a text protocol for models that don't support it. When the context window
207
227
  fills up, older messages are summarized automatically (`/compact` does it by
208
228
  hand).
209
229
 
230
+ Thinking models are marked ◆ in the model list. For those with levels the
231
+ status line shows the current one (`· thinking medium`) and `/effort` opens a
232
+ slider (←/→, Enter), or sets one directly (`/effort high`, `/effort off`). The
233
+ choice is remembered like the model; a model without that level uses its own
234
+ default.
235
+
210
236
  | Environment | |
211
237
  |---|---|
212
238
  | `NOVA_API_KEY`, `NOVA_MODEL` | Use this key / model instead of the stored ones |
@@ -1,149 +1,6 @@
1
- // src/tui/settings/workspace-mcp.ts
2
- import { readFileSync as readFileSync2 } from "fs";
3
- import { join as join2 } from "path";
4
- import { isWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
5
- import { readSettings } from "@datalabrotterdam/nova-ai-agent/core/policy/settings.js";
6
-
7
- // src/tui/settings/workspace-settings.ts
8
- import { readFileSync } from "fs";
9
- import { join } from "path";
10
- function settingsPath(cwd) {
11
- return join(cwd, ".nova-ai", "settings.json");
12
- }
13
- function readWorkspaceSettings(cwd) {
14
- try {
15
- const raw = readFileSync(settingsPath(cwd), "utf8");
16
- return JSON.parse(raw);
17
- } catch {
18
- return {};
19
- }
20
- }
21
-
22
- // src/tui/settings/workspace-mcp.ts
23
- var UNTRUSTED_MCP_MESSAGE = "Not started: MCP servers declared by this workspace only run once you trust it (/trust in the TUI, --trust-workspace headless).";
24
- function readWorkspaceMcpConfiguration(cwd, options = {}) {
25
- const configured = readWorkspaceSettings(cwd).mcpServers ?? [];
26
- const project = readProjectMcpConfiguration(cwd);
27
- const servers = new Map(configured.map((server) => [server.name, server]));
28
- for (const server of project.servers) servers.set(server.name, server);
29
- const declared = [...servers.values()];
30
- if (declared.length && !(options.trusted ?? isWorkspaceTrusted(cwd))) {
31
- return {
32
- servers: [],
33
- failures: [
34
- ...project.failures,
35
- ...declared.map((server) => ({ serverName: server.name, message: UNTRUSTED_MCP_MESSAGE }))
36
- ],
37
- untrusted: declared.map((server) => server.name)
38
- };
39
- }
40
- return { servers: declared, failures: project.failures };
41
- }
42
- function workspaceNeedsTrust(cwd) {
43
- if (isWorkspaceTrusted(cwd)) return false;
44
- if (readWorkspaceMcpConfiguration(cwd, { trusted: true }).servers.length) return true;
45
- return loadWorkspaceAllowRules(cwd).length > 0;
46
- }
47
- function loadWorkspaceAllowRules(cwd) {
48
- return ["settings.json", "settings.local.json"].flatMap((name) => {
49
- const allow = readSettings(join2(cwd, ".nova-ai", name))?.permissions?.allow;
50
- return Array.isArray(allow) ? allow : [];
51
- });
52
- }
53
- function readProjectMcpConfiguration(cwd) {
54
- const path = join2(cwd, ".mcp.json");
55
- let raw;
56
- try {
57
- raw = readFileSync2(path, "utf8");
58
- } catch (error) {
59
- if (error.code === "ENOENT") return { servers: [], failures: [] };
60
- return { servers: [], failures: [{ serverName: ".mcp.json", message: errorMessage(error, "Could not read file") }] };
61
- }
62
- let document;
63
- try {
64
- document = JSON.parse(raw);
65
- } catch (error) {
66
- return { servers: [], failures: [{ serverName: ".mcp.json", message: errorMessage(error, "Invalid JSON") }] };
67
- }
68
- const root = asRecord(document);
69
- const definitions = root ? asRecord(root.mcpServers) : null;
70
- if (!definitions) {
71
- return {
72
- servers: [],
73
- failures: [{ serverName: ".mcp.json", message: 'Expected an "mcpServers" object.' }]
74
- };
75
- }
76
- const servers = [];
77
- const failures = [];
78
- for (const [name, value] of Object.entries(definitions)) {
79
- const config = asRecord(value);
80
- if (config?.disabled === true || config?.enabled === false) continue;
81
- try {
82
- servers.push(normalizeProjectServer(name, config));
83
- } catch (error) {
84
- failures.push({ serverName: `.mcp.json:${name}`, message: errorMessage(error, "Invalid server configuration") });
85
- }
86
- }
87
- return { servers, failures };
88
- }
89
- function normalizeProjectServer(name, config) {
90
- if (!config) throw new Error("Server definition must be an object.");
91
- const type = typeof config.type === "string" ? config.type : config.url ? "http" : "stdio";
92
- if (type === "http" || type === "sse") {
93
- if (typeof config.url !== "string" || !config.url) throw new Error(`${type} server requires a URL.`);
94
- try {
95
- new URL(config.url);
96
- } catch {
97
- throw new Error(`${type} server requires a valid URL.`);
98
- }
99
- return {
100
- name,
101
- type,
102
- url: config.url,
103
- headers: normalizePairs(config.headers, "headers")
104
- };
105
- }
106
- if (type !== "stdio") throw new Error(`Unsupported transport type: ${type}.`);
107
- if (typeof config.command !== "string" || !config.command) throw new Error("stdio server requires a command.");
108
- if (config.args !== void 0 && (!Array.isArray(config.args) || !config.args.every((arg) => typeof arg === "string"))) {
109
- throw new Error("args must be an array of strings.");
110
- }
111
- return {
112
- name,
113
- command: config.command,
114
- args: config.args ?? [],
115
- env: normalizePairs(config.env, "env")
116
- };
117
- }
118
- function normalizePairs(value, label) {
119
- if (value === void 0) return [];
120
- if (Array.isArray(value)) {
121
- if (!value.every((entry) => {
122
- const pair = asRecord(entry);
123
- return typeof pair?.name === "string" && typeof pair.value === "string";
124
- })) {
125
- throw new Error(`${label} must contain name/value strings.`);
126
- }
127
- return value.map((entry) => {
128
- const pair = entry;
129
- return { name: pair.name, value: pair.value };
130
- });
131
- }
132
- const record = asRecord(value);
133
- if (!record || !Object.values(record).every((entry) => typeof entry === "string")) {
134
- throw new Error(`${label} must be an object of string values.`);
135
- }
136
- return Object.entries(record).map(([name, entry]) => ({ name, value: entry }));
137
- }
138
- function asRecord(value) {
139
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
140
- }
141
- function errorMessage(error, fallback) {
142
- return error instanceof Error && error.message ? `${fallback}: ${error.message}` : fallback;
143
- }
144
-
145
1
  // src/tui/session/tui-acp-client.ts
146
2
  import { NOVA_NOTIFICATIONS } from "@datalabrotterdam/nova-ai-agent/acp/extensions.js";
3
+ import { openBrowser } from "@datalabrotterdam/nova-ai-agent/acp/auth-server.js";
147
4
  import { readFile, writeFile, mkdir } from "fs/promises";
148
5
  import { dirname } from "path";
149
6
  import {
@@ -151,13 +8,11 @@ import {
151
8
  terminateProcessTree
152
9
  } from "@datalabrotterdam/nova-ai-agent/core/process-tree.js";
153
10
  import { resolveWorkspaceFile } from "@datalabrotterdam/nova-ai-agent/core/tools/workspace-paths.js";
154
- import {
155
- isInteractionMode
156
- } from "@datalabrotterdam/nova-ai-agent/core/interaction-modes.js";
157
11
  import {
158
12
  fromElicitationRequest,
159
13
  toElicitationResponse
160
14
  } from "@datalabrotterdam/nova-ai-agent/acp/user-questions.js";
15
+ import { INTERACTION_MODES } from "@datalabrotterdam/nova-ai-agent/core/interaction-modes.js";
161
16
  var DEFAULT_OUTPUT_LIMIT = 1e5;
162
17
  var nextMessageId = 0;
163
18
  function uid() {
@@ -175,6 +30,8 @@ var TuiAcpClient = class {
175
30
  cwd;
176
31
  onBackgroundLifecycle;
177
32
  onInteractionModeChange;
33
+ /** The modes the agent offers (built-in, then the user's agent files); set by the runner. */
34
+ modeIds = [...INTERACTION_MODES];
178
35
  capabilities = {
179
36
  fs: { readTextFile: true, writeTextFile: true },
180
37
  terminal: true,
@@ -235,6 +92,29 @@ var TuiAcpClient = class {
235
92
  appendUserMessage(text) {
236
93
  this.appendMessage({ id: uid(), role: "user", text });
237
94
  }
95
+ /** An MCP server's sign-in page: opened in the browser, and printed in case that fails. */
96
+ mcpAuthUrl(params) {
97
+ const { serverName, url } = params ?? {};
98
+ if (typeof serverName !== "string" || typeof url !== "string") return;
99
+ let page;
100
+ try {
101
+ page = new URL(url);
102
+ } catch {
103
+ return;
104
+ }
105
+ if (page.protocol !== "https:" && page.protocol !== "http:") {
106
+ this.appendError(`MCP server "${serverName}" sent a sign-in address that is not a web page; not opened.`);
107
+ return;
108
+ }
109
+ openBrowser(page.toString());
110
+ this.appendMessage({
111
+ id: uid(),
112
+ role: "assistant",
113
+ streaming: false,
114
+ text: `Sign in to MCP server "${serverName}" in your browser. If it did not open, visit:
115
+ ${page}`
116
+ });
117
+ }
238
118
  appendError(text) {
239
119
  this.appendMessage({ id: uid(), role: "error", text });
240
120
  }
@@ -439,7 +319,7 @@ ${err.message}`;
439
319
  }
440
320
  return;
441
321
  case "current_mode_update":
442
- if (isInteractionMode(update.currentModeId)) {
322
+ if (this.modeIds.includes(update.currentModeId)) {
443
323
  this.onInteractionModeChange?.(update.currentModeId);
444
324
  }
445
325
  return;
@@ -559,6 +439,8 @@ ${err.message}`;
559
439
  return this.backgroundUpdate(params);
560
440
  case NOVA_NOTIFICATIONS.queueChanged:
561
441
  return this.queueChanged(params);
442
+ case NOVA_NOTIFICATIONS.mcpAuthUrl:
443
+ return this.mcpAuthUrl(params);
562
444
  default:
563
445
  return;
564
446
  }
@@ -746,8 +628,6 @@ function createStore(initial) {
746
628
  }
747
629
 
748
630
  export {
749
- readWorkspaceMcpConfiguration,
750
- workspaceNeedsTrust,
751
631
  TuiAcpClient,
752
632
  choosePermissionOption,
753
633
  createStore
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  TuiAcpClient,
3
3
  choosePermissionOption,
4
- createStore,
5
- readWorkspaceMcpConfiguration
6
- } from "./chunk-NHGIUEIR.js";
4
+ createStore
5
+ } from "./chunk-EOPM4RRE.js";
7
6
 
8
7
  // src/headless/index.ts
9
8
  import { resolve } from "path";
@@ -12,7 +11,6 @@ import * as acp from "@agentclientprotocol/sdk";
12
11
  import { NovaAgent } from "@datalabrotterdam/nova-ai-agent/acp/agent.js";
13
12
  import { clientFromContext } from "@datalabrotterdam/nova-ai-agent/client/context-client.js";
14
13
  import { startInProcessAgent } from "@datalabrotterdam/nova-ai-agent/client/in-process.js";
15
- import { setWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
16
14
  import { listStoredSessions, loadStoredSession } from "@datalabrotterdam/nova-ai-agent/core/sessions.js";
17
15
 
18
16
  // src/headless/client.ts
@@ -191,15 +189,19 @@ ${HEADLESS_HELP}
191
189
  dependencies.agent ?? new NovaAgent()
192
190
  );
193
191
  const agent = connection.client;
194
- if (options.trustWorkspace) setWorkspaceTrusted(options.cwd, true);
195
- const mcpConfiguration = options.mcp ? readWorkspaceMcpConfiguration(options.cwd) : { servers: [], failures: [] };
196
- for (const failure of mcpConfiguration.failures) {
197
- output.write({
198
- type: "mcp.configuration_error",
199
- serverName: failure.serverName,
200
- message: failure.message
201
- });
202
- }
192
+ const sessionMeta = { "nova-ai-cli/workspaceMcp": options.mcp };
193
+ const reportMcpFailures = (meta) => {
194
+ const failures = toRecord(toRecord(meta)?.["nova-ai-cli/mcp"])?.failures;
195
+ for (const failure of Array.isArray(failures) ? failures : []) {
196
+ const item = toRecord(failure);
197
+ if (typeof item?.serverName !== "string" || typeof item.message !== "string") continue;
198
+ output.write({
199
+ type: "mcp.configuration_error",
200
+ serverName: item.serverName,
201
+ message: item.message
202
+ });
203
+ }
204
+ };
203
205
  let sessionId = null;
204
206
  let interrupted = false;
205
207
  const onInterrupt = () => {
@@ -215,18 +217,23 @@ ${HEADLESS_HELP}
215
217
  clientCapabilities: client.capabilities,
216
218
  clientInfo: { name: "nova-ai-cli-headless", version: "1" }
217
219
  });
220
+ if (options.trustWorkspace) await agent.workspaceTrust({ cwd: options.cwd, trusted: true });
218
221
  if (options.resume) {
219
- await agent.resumeSession({
222
+ const resumed = await agent.resumeSession({
220
223
  sessionId: options.resume,
221
224
  cwd: options.cwd,
222
- mcpServers: mcpConfiguration.servers
225
+ mcpServers: [],
226
+ _meta: sessionMeta
223
227
  });
228
+ reportMcpFailures(resumed._meta);
224
229
  sessionId = options.resume;
225
230
  } else {
226
231
  const created = await agent.newSession({
227
232
  cwd: options.cwd,
228
- mcpServers: mcpConfiguration.servers
233
+ mcpServers: [],
234
+ _meta: sessionMeta
229
235
  });
236
+ reportMcpFailures(created._meta);
230
237
  sessionId = created.sessionId;
231
238
  }
232
239
  await agent.setSessionConfigOption({
package/dist/index.js CHANGED
@@ -17,8 +17,7 @@ var MODE_FLAGS = {
17
17
  "--headless": "print",
18
18
  "--setup": "login",
19
19
  "--version": "version",
20
- "-v": "version",
21
- "--web": "web"
20
+ "-v": "version"
22
21
  };
23
22
  function parseCli(argv) {
24
23
  const first = argv[0];
@@ -34,7 +33,7 @@ function parseCli(argv) {
34
33
  const mode = [...modes][0];
35
34
  const wantsHelp = options.includes("--help") || options.includes("-h");
36
35
  if (!mode) return wantsHelp ? { mode: "help", args: [] } : { mode: "tui", args: argv };
37
- const drop = /* @__PURE__ */ new Set(["--acp", "--headless", "--setup", "--version", "-v", "--web"]);
36
+ const drop = /* @__PURE__ */ new Set(["--acp", "--headless", "--setup", "--version", "-v"]);
38
37
  return { mode, args: argv.filter((arg, index) => !(drop.has(arg) && (end < 0 || index < end))) };
39
38
  }
40
39
  function versionText() {
@@ -140,7 +139,7 @@ ${MAIN_HELP}
140
139
  }
141
140
  case "print": {
142
141
  const update = startUpdateCheck();
143
- const { default: runHeadless } = await import("./headless-OFJFVJ3O.js");
142
+ const { default: runHeadless } = await import("./headless-QLCJRT3X.js");
144
143
  const code = await runHeadless(command.args);
145
144
  if (update) await noticeUpdate(update, 500);
146
145
  return code;
@@ -153,11 +152,6 @@ ${MAIN_HELP}
153
152
  const { runLogout } = await import("@datalabrotterdam/nova-ai-agent/commands/login.js");
154
153
  return runLogout();
155
154
  }
156
- case "web": {
157
- const { default: runWebUi } = await import("./webui-L4GGMXJ3.js");
158
- await runWebUi();
159
- return 0;
160
- }
161
155
  case "tui": {
162
156
  let options;
163
157
  try {
@@ -175,7 +169,7 @@ ${MAIN_HELP}
175
169
  );
176
170
  return 2;
177
171
  }
178
- const { default: runChat } = await import("./tui-4XTC6VJP.js");
172
+ const { default: runChat } = await import("./tui-HJQOYGHL.js");
179
173
  return runChat(options);
180
174
  }
181
175
  }