@kmugalkhod/lightcode 0.1.2 → 0.2.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 (6) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -0
  3. package/cli.js +1350 -503
  4. package/lightcode.cjs +41 -26
  5. package/package.json +4 -2
  6. package/server.js +917 -181
package/cli.js CHANGED
@@ -45,6 +45,14 @@ function getErrorMessage(error, fallback = "Unknown error") {
45
45
  }
46
46
 
47
47
  // packages/shared/src/logger.ts
48
+ import {
49
+ appendFileSync,
50
+ existsSync,
51
+ mkdirSync,
52
+ readdirSync,
53
+ unlinkSync
54
+ } from "fs";
55
+ import path2 from "path";
48
56
  function resolveConfiguredLevel() {
49
57
  const raw = process.env.LIGHTCODE_LOG_LEVEL?.trim().toLowerCase();
50
58
  if (raw === "debug" || raw === "info" || raw === "warn" || raw === "error") {
@@ -52,6 +60,34 @@ function resolveConfiguredLevel() {
52
60
  }
53
61
  return "info";
54
62
  }
63
+ function currentDateStamp() {
64
+ return new Date().toISOString().slice(0, 10);
65
+ }
66
+ function pruneOldLogFiles(directory) {
67
+ try {
68
+ const logFiles = readdirSync(directory).filter((name) => logFilePattern.test(name)).sort();
69
+ for (const name of logFiles.slice(0, -maxLogFilesKept)) {
70
+ unlinkSync(path2.join(directory, name));
71
+ }
72
+ } catch {}
73
+ }
74
+ function writeToFileSink(line) {
75
+ if (!fileSinkPrefix) {
76
+ return;
77
+ }
78
+ try {
79
+ const date = currentDateStamp();
80
+ if (!fileSinkState || fileSinkState.date !== date) {
81
+ fileSinkState = {
82
+ date,
83
+ filePath: path2.join(fileSinkPrefix, `server-${date}.log`)
84
+ };
85
+ pruneOldLogFiles(fileSinkPrefix);
86
+ }
87
+ appendFileSync(fileSinkState.filePath, `${line}
88
+ `);
89
+ } catch {}
90
+ }
55
91
  function createLogger(name) {
56
92
  const write = (level, event, fields) => {
57
93
  if (levelOrder[level] < levelOrder[resolveConfiguredLevel()]) {
@@ -66,6 +102,7 @@ function createLogger(name) {
66
102
  });
67
103
  process.stderr.write(`${line}
68
104
  `);
105
+ writeToFileSink(line);
69
106
  };
70
107
  return {
71
108
  debug: (event, fields) => write("debug", event, fields),
@@ -74,18 +111,20 @@ function createLogger(name) {
74
111
  error: (event, fields) => write("error", event, fields)
75
112
  };
76
113
  }
77
- var levelOrder;
114
+ var levelOrder, maxLogFilesKept = 5, logFilePattern, fileSinkPrefix = null, fileSinkState = null;
78
115
  var init_logger = __esm(() => {
116
+ init_data_dir();
79
117
  levelOrder = {
80
118
  debug: 10,
81
119
  info: 20,
82
120
  warn: 30,
83
121
  error: 40
84
122
  };
123
+ logFilePattern = /^server-\d{4}-\d{2}-\d{2}\.log$/;
85
124
  });
86
125
 
87
126
  // packages/shared/src/version.ts
88
- var lightcodeVersion = "0.1.2";
127
+ var lightcodeVersion = "0.2.0";
89
128
 
90
129
  // packages/shared/src/index.ts
91
130
  var productName = "Lightcode";
@@ -97,7 +136,7 @@ var init_src = __esm(() => {
97
136
  // apps/cli/src/lib/api-base-url.ts
98
137
  var apiBaseUrl;
99
138
  var init_api_base_url = __esm(() => {
100
- apiBaseUrl = Bun.env.LIGHTCODE_API_URL ?? "http://localhost:3000";
139
+ apiBaseUrl = Bun.env.LIGHTCODE_API_URL ?? "http://127.0.0.1:4983";
101
140
  });
102
141
 
103
142
  // apps/cli/src/lib/server-launcher.ts
@@ -107,9 +146,9 @@ __export(exports_server_launcher, {
107
146
  getOwnedServerProcess: () => getOwnedServerProcess,
108
147
  ensureServerRunning: () => ensureServerRunning
109
148
  });
110
- import { existsSync, mkdirSync } from "fs";
149
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
111
150
  import { homedir as homedir2, platform as platform2 } from "os";
112
- import path2 from "path";
151
+ import path3 from "path";
113
152
  import { fileURLToPath } from "url";
114
153
  function getOwnedServerProcess() {
115
154
  return currentOwnedProcess;
@@ -129,22 +168,30 @@ async function restartOwnedServer() {
129
168
  function getLightcodeDataDir2() {
130
169
  const configuredHome = process.env.LIGHTCODE_HOME?.trim();
131
170
  if (configuredHome) {
132
- return path2.resolve(configuredHome);
171
+ return path3.resolve(configuredHome);
133
172
  }
134
173
  if (platform2() === "win32") {
135
174
  const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
136
- return appData ? path2.join(appData, "lightcode") : path2.join(homedir2(), "AppData", "Roaming", "lightcode");
175
+ return appData ? path3.join(appData, "lightcode") : path3.join(homedir2(), "AppData", "Roaming", "lightcode");
137
176
  }
138
- return path2.join(homedir2(), ".lightcode");
177
+ return path3.join(homedir2(), ".lightcode");
139
178
  }
140
- async function isServerHealthy(url) {
179
+ async function checkServer(url) {
141
180
  try {
142
181
  const response = await fetch(new URL("/config/status", url), {
143
182
  signal: AbortSignal.timeout(healthCheckTimeoutMs)
144
183
  });
145
- return response.ok;
184
+ if (!response.ok) {
185
+ return "foreign";
186
+ }
187
+ const contentType = response.headers.get("content-type") ?? "";
188
+ if (!contentType.includes("application/json")) {
189
+ return "foreign";
190
+ }
191
+ const payload = await response.json();
192
+ return payload && typeof payload === "object" && "selectedProvider" in payload ? "healthy" : "foreign";
146
193
  } catch {
147
- return false;
194
+ return "down";
148
195
  }
149
196
  }
150
197
  function resolveServerEntry() {
@@ -152,20 +199,28 @@ function resolveServerEntry() {
152
199
  fileURLToPath(new URL("./server.js", import.meta.url)),
153
200
  fileURLToPath(new URL("../../../server/src/index.ts", import.meta.url))
154
201
  ];
155
- return candidates.find((candidate) => existsSync(candidate)) ?? null;
202
+ return candidates.find((candidate) => existsSync2(candidate)) ?? null;
156
203
  }
157
204
  async function ensureServerRunning() {
158
- if (await isServerHealthy(apiBaseUrl)) {
205
+ const port = new URL(apiBaseUrl).port || "4983";
206
+ const initialHealth = await checkServer(apiBaseUrl);
207
+ if (initialHealth === "healthy") {
159
208
  return { url: apiBaseUrl, ownedProcess: null };
160
209
  }
210
+ if (initialHealth === "foreign") {
211
+ return {
212
+ url: apiBaseUrl,
213
+ ownedProcess: null,
214
+ error: `Port ${port} is in use by another application (not Lightcode). ` + "Free the port, or point Lightcode elsewhere with " + "LIGHTCODE_API_URL (CLI) and PORT (server)."
215
+ };
216
+ }
161
217
  const serverEntry = resolveServerEntry();
162
218
  if (!serverEntry) {
163
219
  return { url: apiBaseUrl, ownedProcess: null };
164
220
  }
165
- const logDirectory = path2.join(getLightcodeDataDir2(), "logs");
166
- mkdirSync(logDirectory, { recursive: true });
167
- const logFile = Bun.file(path2.join(logDirectory, "server.log"));
168
- const port = new URL(apiBaseUrl).port || "3000";
221
+ const logDirectory = path3.join(getLightcodeDataDir2(), "logs");
222
+ mkdirSync2(logDirectory, { recursive: true });
223
+ const logFile = Bun.file(path3.join(logDirectory, "server.log"));
169
224
  const ownedProcess = Bun.spawn([process.execPath, serverEntry], {
170
225
  stdout: logFile,
171
226
  stderr: logFile,
@@ -175,12 +230,16 @@ async function ensureServerRunning() {
175
230
  }
176
231
  });
177
232
  for (let attempt = 0;attempt < startupPollAttempts; attempt += 1) {
178
- if (await isServerHealthy(apiBaseUrl)) {
233
+ if (await checkServer(apiBaseUrl) === "healthy") {
179
234
  currentOwnedProcess = ownedProcess;
180
235
  return { url: apiBaseUrl, ownedProcess };
181
236
  }
182
237
  if (ownedProcess.exitCode !== null) {
183
- return { url: apiBaseUrl, ownedProcess: null };
238
+ return {
239
+ url: apiBaseUrl,
240
+ ownedProcess: null,
241
+ error: "The Lightcode server exited during startup. Check the log at " + `${path3.join(logDirectory, "server.log")} \u2014 a port conflict or ` + "invalid settings.json are the usual causes."
242
+ };
184
243
  }
185
244
  await Bun.sleep(startupPollIntervalMs);
186
245
  }
@@ -435,7 +494,8 @@ var init_help_overlay = __esm(() => {
435
494
  screenHints = [
436
495
  { context: "Chat", hint: "Tab/Ctrl+T switch mode | @ attach files | /compact, /undo actions" },
437
496
  { context: "Chat", hint: "Ctrl+O expand tool output | Enter send | Ctrl+Enter newline" },
438
- { context: "Sessions", hint: "Enter resume | r rename | f fork | / filter | e export | d delete" }
497
+ { context: "Copy", hint: "Ctrl+Y copy mode (\u2191/\u2193 pick, Enter copy, c code) | /copy [last|code|all]" },
498
+ { context: "Sessions", hint: "Enter resume | r rename | f fork | / filter | e export (.md) | d delete" }
439
499
  ];
440
500
  });
441
501
 
@@ -513,12 +573,19 @@ var init_route_registry = __esm(() => {
513
573
  shortcut: "/config"
514
574
  },
515
575
  {
516
- id: "model",
517
- label: "Model",
518
- description: "Show provider, model, base URL, and key hints",
576
+ id: "model-select",
577
+ label: "Switch Model",
578
+ description: "Pick any OpenRouter model; applies immediately and persists to settings.json",
519
579
  path: "/model",
520
580
  shortcut: "/model"
521
581
  },
582
+ {
583
+ id: "model",
584
+ label: "Model Info",
585
+ description: "Show provider, model, base URL, and key hints",
586
+ path: "/model-info",
587
+ shortcut: "/model-info"
588
+ },
522
589
  {
523
590
  id: "latest-session",
524
591
  label: "Resume Latest",
@@ -2403,6 +2470,63 @@ var init_agent_tools = __esm(() => {
2403
2470
  });
2404
2471
  });
2405
2472
 
2473
+ // packages/ai/src/chat-error.ts
2474
+ function formatChatStreamError(info) {
2475
+ const payload = {
2476
+ ...info,
2477
+ message: info.message.slice(0, maxTransportMessageLength)
2478
+ };
2479
+ return `${chatStreamErrorPrefix}${JSON.stringify(payload)}`;
2480
+ }
2481
+ function parseChatStreamError(message) {
2482
+ if (!message || !message.startsWith(chatStreamErrorPrefix)) {
2483
+ return null;
2484
+ }
2485
+ try {
2486
+ const parsed = JSON.parse(message.slice(chatStreamErrorPrefix.length));
2487
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.message !== "string" || typeof parsed.retryable !== "boolean" || typeof parsed.kind !== "string" || !chatErrorKinds.has(parsed.kind)) {
2488
+ return null;
2489
+ }
2490
+ return {
2491
+ kind: parsed.kind,
2492
+ statusCode: typeof parsed.statusCode === "number" ? parsed.statusCode : undefined,
2493
+ retryable: parsed.retryable,
2494
+ message: parsed.message
2495
+ };
2496
+ } catch {
2497
+ return null;
2498
+ }
2499
+ }
2500
+ function describeChatStreamError(info) {
2501
+ const status = info.statusCode ? ` (HTTP ${info.statusCode})` : "";
2502
+ return `${chatErrorKindLabels[info.kind]}${status}: ${info.message}`;
2503
+ }
2504
+ var chatStreamErrorPrefix = "LIGHTCODE_ERROR:", maxTransportMessageLength = 500, chatErrorKinds, chatErrorKindLabels;
2505
+ var init_chat_error = __esm(() => {
2506
+ chatErrorKinds = new Set([
2507
+ "auth",
2508
+ "billing",
2509
+ "rate_limit",
2510
+ "invalid_request",
2511
+ "context_overflow",
2512
+ "provider_unavailable",
2513
+ "network",
2514
+ "aborted",
2515
+ "unknown"
2516
+ ]);
2517
+ chatErrorKindLabels = {
2518
+ auth: "Authentication error",
2519
+ billing: "Billing or quota error",
2520
+ rate_limit: "Rate limited",
2521
+ invalid_request: "Provider rejected the request",
2522
+ context_overflow: "Context window exceeded",
2523
+ provider_unavailable: "Provider unavailable",
2524
+ network: "Connection error",
2525
+ aborted: "Request aborted",
2526
+ unknown: "Provider error"
2527
+ };
2528
+ });
2529
+
2406
2530
  // packages/ai/src/context/context-state.ts
2407
2531
  import { z as z22 } from "zod";
2408
2532
  var contextStateTierSchema, sessionContextStateSchema;
@@ -2735,7 +2859,9 @@ var init_coding_agent_prompt = __esm(() => {
2735
2859
  init_coding_agent_modes();
2736
2860
  defaultCodingAgentSystemPrompt = "You are a basic coding agent. Use tools for filesystem and codebase tasks instead of guessing. " + "Respect the user's intent, explain changes clearly, and prefer incremental, auditable actions. " + "You can only interact with files under this working directory: {cwd}. " + "Use glob_search for path discovery, grep for text search, structured git tools for repository inspection, and bash only when dedicated tools are not enough. " + "Use todo_write to keep multi-step implementation work visible and current. " + "For risky or uncertain operations, inspect context first and be explicit about assumptions. " + "While working, emit brief progress notes in natural language before major tool actions and after important findings. " + `Keep them short, human, and concrete. Vary wording naturally and avoid repetitive templates or rigid labels.
2737
2861
 
2738
- ` + "You are an agent \u2014 keep working until the user's request is fully resolved before ending your turn. " + "Never stop with a partial answer. If you say you will do something, do it in the same turn by calling a tool. " + "Use todo_write to track multi-step work and do not finish while any todo is pending or in_progress. " + "Only end your turn when the task is complete or you are blocked on input that only the user can provide.";
2862
+ ` + "You are an agent \u2014 keep working until the user's request is fully resolved before ending your turn. " + "Never stop with a partial answer. If you say you will do something, do it in the same turn by calling a tool. " + "Use todo_write to track multi-step work and do not finish while any todo is pending or in_progress. " + `Only end your turn when the task is complete or you are blocked on input that only the user can provide.
2863
+
2864
+ ` + "Long-running processes: never run dev servers, watchers, or other non-exiting commands as plain foreground bash \u2014 " + "the bash tool enforces a timeout and the command will be killed. Start them detached with output redirected to a " + 'log file (e.g. on Windows: start /B cmd /c "npm run dev > dev.log 2>&1"; on Unix: nohup npm run dev > dev.log 2>&1 &), ' + "wait briefly, then verify by reading the log file or with a bounded probe like curl --max-time 5. " + "Dev servers do not always get their default port \u2014 read the actual port from the startup log instead of assuming it. " + "Never kill processes or free ports you did not open yourself; other tools (including this agent's own backend) may be using them.";
2739
2865
  toolCallingDisciplineAddendum = "Invoke tools only through the function-calling interface. " + "Never write tool calls as XML or plain text such as <tool_call>, <function_call>, <invoke>, or bare JSON in your message. " + "Tool arguments must be valid JSON matching the tool's schema. " + "Call one tool at a time and wait for its result before deciding the next step.";
2740
2866
  });
2741
2867
 
@@ -3380,6 +3506,7 @@ function createCodingAgent({
3380
3506
  promptOverride,
3381
3507
  maxOutputTokens = 16384,
3382
3508
  maxSteps = 30,
3509
+ maxRetries = 5,
3383
3510
  providerOptions,
3384
3511
  includeToolDiscipline = false
3385
3512
  }) {
@@ -3389,6 +3516,7 @@ function createCodingAgent({
3389
3516
  tools,
3390
3517
  stopWhen: stepCountIs(maxSteps),
3391
3518
  maxOutputTokens,
3519
+ maxRetries,
3392
3520
  providerOptions,
3393
3521
  experimental_repairToolCall: repairCodingAgentToolCall,
3394
3522
  callOptionsSchema: codingAgentCallOptionsSchema,
@@ -3442,20 +3570,20 @@ var init_coding_agent = __esm(() => {
3442
3570
  });
3443
3571
 
3444
3572
  // packages/ai/src/config/credentials.ts
3445
- import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
3573
+ import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync, writeFileSync } from "fs";
3446
3574
  import os from "os";
3447
- import path3 from "path";
3575
+ import path4 from "path";
3448
3576
  import { z as z25 } from "zod";
3449
3577
  function getCredentialsPath(env = process.env) {
3450
3578
  const configuredPath = env.LIGHTCODE_CREDENTIALS?.trim();
3451
3579
  if (configuredPath) {
3452
- return path3.resolve(configuredPath);
3580
+ return path4.resolve(configuredPath);
3453
3581
  }
3454
- return path3.join(os.homedir(), ".lightcode", "credentials.json");
3582
+ return path4.join(os.homedir(), ".lightcode", "credentials.json");
3455
3583
  }
3456
3584
  function readStoredCredentials(env = process.env) {
3457
3585
  const credentialsPath = getCredentialsPath(env);
3458
- if (!existsSync2(credentialsPath)) {
3586
+ if (!existsSync3(credentialsPath)) {
3459
3587
  return {};
3460
3588
  }
3461
3589
  try {
@@ -3471,7 +3599,7 @@ function writeStoredCredentials(credentials, env = process.env) {
3471
3599
  ...readStoredCredentials(env),
3472
3600
  ...credentials
3473
3601
  });
3474
- mkdirSync2(path3.dirname(credentialsPath), { recursive: true });
3602
+ mkdirSync3(path4.dirname(credentialsPath), { recursive: true });
3475
3603
  writeFileSync(credentialsPath, `${JSON.stringify(merged, null, 2)}
3476
3604
  `, "utf8");
3477
3605
  try {
@@ -3850,11 +3978,11 @@ function normalizeReadPathKey(input) {
3850
3978
  if (!isRecord3(input)) {
3851
3979
  return null;
3852
3980
  }
3853
- const path4 = input.path;
3854
- if (typeof path4 !== "string" || !path4.trim()) {
3981
+ const path5 = input.path;
3982
+ if (typeof path5 !== "string" || !path5.trim()) {
3855
3983
  return null;
3856
3984
  }
3857
- return path4.trim().replaceAll("\\", "/").toLowerCase();
3985
+ return path5.trim().replaceAll("\\", "/").toLowerCase();
3858
3986
  }
3859
3987
  function getPartState(part) {
3860
3988
  const state = isRecord3(part) ? Reflect.get(part, "state") : undefined;
@@ -4057,18 +4185,18 @@ var init_context = __esm(() => {
4057
4185
  });
4058
4186
 
4059
4187
  // packages/ai/src/config/lightcode-config.ts
4060
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
4188
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
4061
4189
  import os2 from "os";
4062
- import path4 from "path";
4190
+ import path5 from "path";
4063
4191
  import { z as z28 } from "zod";
4064
4192
  function getDefaultUserConfigPath(env) {
4065
4193
  if (env.LIGHTCODE_USER_CONFIG) {
4066
4194
  return env.LIGHTCODE_USER_CONFIG;
4067
4195
  }
4068
- return path4.join(os2.homedir(), ".lightcode", "settings.json");
4196
+ return path5.join(os2.homedir(), ".lightcode", "settings.json");
4069
4197
  }
4070
4198
  function readConfigFile(scope, configPath) {
4071
- if (!existsSync3(configPath)) {
4199
+ if (!existsSync4(configPath)) {
4072
4200
  return {
4073
4201
  config: {},
4074
4202
  file: {
@@ -4159,6 +4287,7 @@ function readEnvConfig(env) {
4159
4287
  permissionMode: env.LIGHTCODE_PERMISSION_MODE,
4160
4288
  maxOutputTokens: parseNumberEnv(env.LIGHTCODE_MAX_OUTPUT_TOKENS, "LIGHTCODE_MAX_OUTPUT_TOKENS"),
4161
4289
  maxSteps: parseNumberEnv(env.LIGHTCODE_MAX_STEPS, "LIGHTCODE_MAX_STEPS"),
4290
+ maxRetries: parseNumberEnv(env.LIGHTCODE_MAX_RETRIES, "LIGHTCODE_MAX_RETRIES"),
4162
4291
  context: Object.keys(compactContextConfig).length > 0 ? compactContextConfig : undefined
4163
4292
  };
4164
4293
  const compactConfig = Object.fromEntries(Object.entries(rawConfig).filter(([, value]) => value !== undefined));
@@ -4186,10 +4315,30 @@ function mergeConfig(...configs) {
4186
4315
  allowedTools: config2.allowedTools ?? mergedConfig.allowedTools
4187
4316
  }), {});
4188
4317
  }
4318
+ function updateUserSettings(update, env = process.env) {
4319
+ const configPath = getDefaultUserConfigPath(env);
4320
+ const existing = readConfigFile("user", configPath).config;
4321
+ const merged = { ...existing };
4322
+ for (const [key, value] of Object.entries(update)) {
4323
+ if (value === undefined || value === null) {
4324
+ delete merged[key];
4325
+ } else {
4326
+ Reflect.set(merged, key, value);
4327
+ }
4328
+ }
4329
+ const parsed = lightcodeConfigSchema.safeParse(merged);
4330
+ if (!parsed.success) {
4331
+ throw new LightcodeConfigError(`Invalid settings update: ${parsed.error.message}`);
4332
+ }
4333
+ mkdirSync4(path5.dirname(configPath), { recursive: true });
4334
+ writeFileSync2(configPath, `${JSON.stringify(parsed.data, null, 2)}
4335
+ `, "utf8");
4336
+ return { path: configPath, config: parsed.data };
4337
+ }
4189
4338
  function loadLightcodeConfig({
4190
4339
  cwd = process.cwd(),
4191
4340
  env = process.env,
4192
- projectConfigPath = path4.join(cwd, ".lightcode", "settings.json"),
4341
+ projectConfigPath = path5.join(cwd, ".lightcode", "settings.json"),
4193
4342
  userConfigPath = getDefaultUserConfigPath(env)
4194
4343
  } = {}) {
4195
4344
  const userConfig = readConfigFile("user", userConfigPath);
@@ -4240,7 +4389,7 @@ var init_lightcode_config = __esm(() => {
4240
4389
  enabled: true,
4241
4390
  maxAutoContinues: 50,
4242
4391
  maxErrorRetries: 5,
4243
- stallTimeoutSeconds: 120
4392
+ stallTimeoutSeconds: 180
4244
4393
  };
4245
4394
  lightcodeConfigSchema = z28.object({
4246
4395
  provider: lightcodeProviderSchema.optional(),
@@ -4255,6 +4404,7 @@ var init_lightcode_config = __esm(() => {
4255
4404
  context: contextOptimizerConfigSchema.optional(),
4256
4405
  maxOutputTokens: z28.number().int().min(1).max(200000).optional(),
4257
4406
  maxSteps: z28.number().int().min(1).max(100).optional(),
4407
+ maxRetries: z28.number().int().min(0).max(10).optional(),
4258
4408
  autoContinue: autoContinueConfigSchema.optional()
4259
4409
  }).strict();
4260
4410
  lightcodeConfigDefaults = {
@@ -4263,6 +4413,7 @@ var init_lightcode_config = __esm(() => {
4263
4413
  context: defaultContextOptimizerConfig,
4264
4414
  maxOutputTokens: 32768,
4265
4415
  maxSteps: 30,
4416
+ maxRetries: 5,
4266
4417
  autoContinue: defaultAutoContinueConfig
4267
4418
  };
4268
4419
  loadedConfigFileSchema = z28.object({
@@ -4277,6 +4428,7 @@ var init_lightcode_config = __esm(() => {
4277
4428
  context: resolvedContextOptimizerConfigSchema,
4278
4429
  maxOutputTokens: z28.number().int().min(1).max(200000),
4279
4430
  maxSteps: z28.number().int().min(1).max(100),
4431
+ maxRetries: z28.number().int().min(0).max(10),
4280
4432
  autoContinue: resolvedAutoContinueConfigSchema
4281
4433
  });
4282
4434
  lightcodeConfigLoadResultSchema = z28.object({
@@ -5184,27 +5336,27 @@ var init_schema16 = __esm(() => {
5184
5336
  });
5185
5337
 
5186
5338
  // packages/ai/src/plugins/runtime.ts
5187
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync } from "fs";
5339
+ import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
5188
5340
  import os3 from "os";
5189
- import path5 from "path";
5341
+ import path6 from "path";
5190
5342
  function pluginRoots(cwd) {
5191
5343
  return [
5192
- path5.join(cwd, ".lightcode", "plugins"),
5193
- path5.join(os3.homedir(), ".lightcode", "plugins")
5344
+ path6.join(cwd, ".lightcode", "plugins"),
5345
+ path6.join(os3.homedir(), ".lightcode", "plugins")
5194
5346
  ];
5195
5347
  }
5196
5348
  function listPlugins({ cwd = process.cwd() } = {}) {
5197
5349
  const plugins = [];
5198
5350
  for (const root of pluginRoots(cwd)) {
5199
- if (!existsSync4(root)) {
5351
+ if (!existsSync5(root)) {
5200
5352
  continue;
5201
5353
  }
5202
- for (const entry of readdirSync(root, { withFileTypes: true })) {
5354
+ for (const entry of readdirSync2(root, { withFileTypes: true })) {
5203
5355
  if (!entry.isDirectory()) {
5204
5356
  continue;
5205
5357
  }
5206
- const manifestPath = path5.join(root, entry.name, "plugin.json");
5207
- if (!existsSync4(manifestPath)) {
5358
+ const manifestPath = path6.join(root, entry.name, "plugin.json");
5359
+ if (!existsSync5(manifestPath)) {
5208
5360
  continue;
5209
5361
  }
5210
5362
  const manifest = pluginManifestSchema.parse(JSON.parse(readFileSync3(manifestPath, "utf8")));
@@ -5243,7 +5395,7 @@ async function runPluginHooks({
5243
5395
  for (const hook of manifest.hooks.filter((candidate) => candidate.event === event)) {
5244
5396
  hooksRun += 1;
5245
5397
  const child = Bun.spawn([hook.command, ...hook.args], {
5246
- cwd: path5.dirname(plugin.path),
5398
+ cwd: path6.dirname(plugin.path),
5247
5399
  stdout: "pipe",
5248
5400
  stderr: "pipe"
5249
5401
  });
@@ -5283,14 +5435,14 @@ var init_runtime2 = __esm(() => {
5283
5435
  });
5284
5436
 
5285
5437
  // packages/ai/src/skills/runtime.ts
5286
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
5438
+ import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync } from "fs";
5287
5439
  import os4 from "os";
5288
- import path6 from "path";
5440
+ import path7 from "path";
5289
5441
  function defaultSkillRoots(cwd) {
5290
5442
  return [
5291
- { source: "project", path: path6.join(cwd, ".lightcode", "skills") },
5292
- { source: "project", path: path6.join(cwd, ".agents", "skills") },
5293
- { source: "user", path: path6.join(os4.homedir(), ".lightcode", "skills") }
5443
+ { source: "project", path: path7.join(cwd, ".lightcode", "skills") },
5444
+ { source: "project", path: path7.join(cwd, ".agents", "skills") },
5445
+ { source: "user", path: path7.join(os4.homedir(), ".lightcode", "skills") }
5294
5446
  ];
5295
5447
  }
5296
5448
  function normalizeSkillName(value) {
@@ -5312,7 +5464,7 @@ function parseBasicFrontmatter(content) {
5312
5464
  ]));
5313
5465
  }
5314
5466
  function findSkillFiles(root) {
5315
- if (!existsSync5(root.path)) {
5467
+ if (!existsSync6(root.path)) {
5316
5468
  return [];
5317
5469
  }
5318
5470
  const files = [];
@@ -5320,8 +5472,8 @@ function findSkillFiles(root) {
5320
5472
  if (depth > 4) {
5321
5473
  return;
5322
5474
  }
5323
- for (const entry of readdirSync2(directory)) {
5324
- const entryPath = path6.join(directory, entry);
5475
+ for (const entry of readdirSync3(directory)) {
5476
+ const entryPath = path7.join(directory, entry);
5325
5477
  const stat = statSync(entryPath);
5326
5478
  if (stat.isDirectory()) {
5327
5479
  visit(entryPath, depth + 1);
@@ -5342,7 +5494,7 @@ function listSkills({
5342
5494
  for (const skillPath of findSkillFiles(root)) {
5343
5495
  const content = readFileSync4(skillPath, "utf8");
5344
5496
  const frontmatter = parseBasicFrontmatter(content);
5345
- const fallbackName = path6.basename(path6.dirname(skillPath));
5497
+ const fallbackName = path7.basename(path7.dirname(skillPath));
5346
5498
  const name = normalizeSkillName(frontmatter.name ?? fallbackName);
5347
5499
  if (!name || seen.has(name)) {
5348
5500
  continue;
@@ -5417,6 +5569,7 @@ var init_subagent_schemas = __esm(() => {
5417
5569
  // packages/ai/src/index.ts
5418
5570
  var init_src2 = __esm(() => {
5419
5571
  init_agent_tools();
5572
+ init_chat_error();
5420
5573
  init_chat_interaction_schemas();
5421
5574
  init_plan_block();
5422
5575
  init_chat_schemas();
@@ -5453,8 +5606,27 @@ var init_client = __esm(() => {
5453
5606
  client = hc(apiBaseUrl);
5454
5607
  });
5455
5608
 
5609
+ // apps/cli/src/utils/markdown-code.ts
5610
+ function extractCodeBlocks(text) {
5611
+ const blocks = [];
5612
+ FENCE_RE.lastIndex = 0;
5613
+ let match;
5614
+ while ((match = FENCE_RE.exec(text)) !== null) {
5615
+ const lang = match[1]?.trim();
5616
+ const code = (match[2] ?? "").replace(/\r?\n$/, "");
5617
+ if (code.trim()) {
5618
+ blocks.push({ lang: lang ? lang : undefined, code });
5619
+ }
5620
+ }
5621
+ return blocks;
5622
+ }
5623
+ var FENCE_RE;
5624
+ var init_markdown_code = __esm(() => {
5625
+ FENCE_RE = /```([^\n`]*)\r?\n([\s\S]*?)```/g;
5626
+ });
5627
+
5456
5628
  // apps/cli/src/commands/export-session-markdown.ts
5457
- import path7 from "path";
5629
+ import path8 from "path";
5458
5630
  function escapeMarkdown(text) {
5459
5631
  return text.replace(/```/g, "\\`\\`\\`").replace(/`/g, "\\`").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/_([^_]+)_/g, "$1");
5460
5632
  }
@@ -5571,33 +5743,33 @@ function getExportDirectory() {
5571
5743
  return process.cwd();
5572
5744
  }
5573
5745
  function getExportPath(sessionTitle, sessionId) {
5574
- return path7.join(getExportDirectory(), generateExportFilename(sessionTitle, sessionId));
5746
+ return path8.join(getExportDirectory(), generateExportFilename(sessionTitle, sessionId));
5575
5747
  }
5576
5748
  var init_export_session_markdown = () => {};
5577
5749
 
5578
5750
  // packages/ai/src/common/resolve-within-workspace.ts
5579
- import path8 from "path";
5751
+ import path9 from "path";
5580
5752
  import {
5581
- existsSync as existsSync6,
5753
+ existsSync as existsSync7,
5582
5754
  lstatSync,
5583
5755
  realpathSync,
5584
5756
  statSync as statSync2
5585
5757
  } from "fs";
5586
5758
  function stripTrailingSeparator(value) {
5587
- const parsed = path8.parse(value);
5759
+ const parsed = path9.parse(value);
5588
5760
  if (value === parsed.root) {
5589
5761
  return value;
5590
5762
  }
5591
5763
  return value.replace(/[\\/]+$/, "");
5592
5764
  }
5593
5765
  function normalizeForComparison(value) {
5594
- const normalized = stripTrailingSeparator(path8.resolve(value));
5766
+ const normalized = stripTrailingSeparator(path9.resolve(value));
5595
5767
  return process.platform === "win32" ? normalized.toLowerCase() : normalized;
5596
5768
  }
5597
5769
  function isWithinWorkspace(absolutePath, context2) {
5598
5770
  const workspace = normalizeForComparison(context2.root);
5599
5771
  const target = normalizeForComparison(absolutePath);
5600
- return target === workspace || target.startsWith(`${workspace}${path8.sep}`);
5772
+ return target === workspace || target.startsWith(`${workspace}${path9.sep}`);
5601
5773
  }
5602
5774
  function assertWithinWorkspace(absolutePath, context2, input) {
5603
5775
  if (!isWithinWorkspace(absolutePath, context2)) {
@@ -5612,17 +5784,17 @@ function getExistingRealPath(absolutePath) {
5612
5784
  }
5613
5785
  }
5614
5786
  function findNearestExistingParent(absolutePath, context2, input) {
5615
- let currentPath = path8.dirname(absolutePath);
5787
+ let currentPath = path9.dirname(absolutePath);
5616
5788
  while (true) {
5617
5789
  assertWithinWorkspace(currentPath, context2, input);
5618
- if (existsSync6(currentPath)) {
5790
+ if (existsSync7(currentPath)) {
5619
5791
  const realParentPath = getExistingRealPath(currentPath);
5620
5792
  if (!realParentPath) {
5621
5793
  throw new Error(`Unable to resolve existing parent for path: ${input}`);
5622
5794
  }
5623
5795
  return realParentPath;
5624
5796
  }
5625
- const parentPath = path8.dirname(currentPath);
5797
+ const parentPath = path9.dirname(currentPath);
5626
5798
  if (parentPath === currentPath) {
5627
5799
  throw new Error(`Unable to find an existing workspace parent for path: ${input}`);
5628
5800
  }
@@ -5642,7 +5814,7 @@ function getDefaultWorkspaceContext() {
5642
5814
  }
5643
5815
  function resolveWithinWorkspace(input, options = {}) {
5644
5816
  const context2 = options.context ?? defaultWorkspaceContext;
5645
- const resolved = path8.resolve(context2.root, input);
5817
+ const resolved = path9.resolve(context2.root, input);
5646
5818
  assertWithinWorkspace(resolved, context2, input);
5647
5819
  const realPath = getExistingRealPath(resolved);
5648
5820
  if (realPath) {
@@ -5667,13 +5839,13 @@ function resolveWithinWorkspace(input, options = {}) {
5667
5839
  return resolved;
5668
5840
  }
5669
5841
  function toWorkspaceRelativePath(absolutePath, context2 = defaultWorkspaceContext) {
5670
- const resolved = path8.resolve(absolutePath);
5842
+ const resolved = path9.resolve(absolutePath);
5671
5843
  assertWithinWorkspace(resolved, context2, absolutePath);
5672
- const relativePath = path8.relative(context2.root, resolved);
5844
+ const relativePath = path9.relative(context2.root, resolved);
5673
5845
  if (relativePath === "") {
5674
5846
  return ".";
5675
5847
  }
5676
- return relativePath.split(path8.sep).join("/");
5848
+ return relativePath.split(path9.sep).join("/");
5677
5849
  }
5678
5850
  var nativeRealpathSync, defaultWorkspaceContext, WORKSPACE;
5679
5851
  var init_resolve_within_workspace = __esm(() => {
@@ -5781,16 +5953,16 @@ var init_runtime4 = __esm(() => {
5781
5953
  // packages/ai/src/checkpoints/runtime.ts
5782
5954
  import { createHash } from "crypto";
5783
5955
  import { promises as fs } from "fs";
5784
- import path9 from "path";
5956
+ import path10 from "path";
5785
5957
  import { z as z32 } from "zod";
5786
5958
  function safeSessionDirName(sessionId) {
5787
5959
  return sessionId.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120);
5788
5960
  }
5789
5961
  function getCheckpointsDir(sessionId) {
5790
- return path9.join(getLightcodeDataDir(), "checkpoints", safeSessionDirName(sessionId));
5962
+ return path10.join(getLightcodeDataDir(), "checkpoints", safeSessionDirName(sessionId));
5791
5963
  }
5792
5964
  function getManifestPath(sessionId) {
5793
- return path9.join(getCheckpointsDir(sessionId), "manifest.json");
5965
+ return path10.join(getCheckpointsDir(sessionId), "manifest.json");
5794
5966
  }
5795
5967
  async function loadManifest(sessionId) {
5796
5968
  try {
@@ -5803,7 +5975,7 @@ async function loadManifest(sessionId) {
5803
5975
  }
5804
5976
  async function saveManifest(sessionId, manifest) {
5805
5977
  const manifestPath = getManifestPath(sessionId);
5806
- await fs.mkdir(path9.dirname(manifestPath), { recursive: true });
5978
+ await fs.mkdir(path10.dirname(manifestPath), { recursive: true });
5807
5979
  await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
5808
5980
  `, "utf8");
5809
5981
  }
@@ -5828,8 +6000,8 @@ async function recordFileCheckpoint({
5828
6000
  if (previousContent !== null) {
5829
6001
  const pathHash = createHash("sha1").update(workspaceRelativePath).digest("hex").slice(0, 12);
5830
6002
  snapshotFile = `${turnKey}-${pathHash}.snap`;
5831
- const snapshotPath = path9.join(getCheckpointsDir(sessionId), snapshotFile);
5832
- await fs.mkdir(path9.dirname(snapshotPath), { recursive: true });
6003
+ const snapshotPath = path10.join(getCheckpointsDir(sessionId), snapshotFile);
6004
+ await fs.mkdir(path10.dirname(snapshotPath), { recursive: true });
5833
6005
  await fs.writeFile(snapshotPath, previousContent, "utf8");
5834
6006
  }
5835
6007
  turn.files.push({ workspaceRelativePath, absolutePath, snapshotFile });
@@ -5849,9 +6021,9 @@ async function undoLastTurn({
5849
6021
  if (file.snapshotFile === null) {
5850
6022
  await fs.rm(file.absolutePath, { force: true });
5851
6023
  } else {
5852
- const snapshotPath = path9.join(getCheckpointsDir(sessionId), file.snapshotFile);
6024
+ const snapshotPath = path10.join(getCheckpointsDir(sessionId), file.snapshotFile);
5853
6025
  const snapshotContent = await fs.readFile(snapshotPath, "utf8");
5854
- await fs.mkdir(path9.dirname(file.absolutePath), { recursive: true });
6026
+ await fs.mkdir(path10.dirname(file.absolutePath), { recursive: true });
5855
6027
  await fs.writeFile(file.absolutePath, snapshotContent, "utf8");
5856
6028
  await fs.rm(snapshotPath, { force: true });
5857
6029
  }
@@ -5963,7 +6135,7 @@ var init_runtime6 = __esm(() => {
5963
6135
  // packages/ai/src/grep/runtime.ts
5964
6136
  import { execFile } from "child_process";
5965
6137
  import { readdir, readFile, stat } from "fs/promises";
5966
- import path10 from "path";
6138
+ import path11 from "path";
5967
6139
  import { promisify as promisify2 } from "util";
5968
6140
  function isRecord6(value) {
5969
6141
  return typeof value === "object" && value !== null;
@@ -6020,7 +6192,7 @@ function parseRgMatches(stdout, maxResults, workspaceContext) {
6020
6192
  if (!event) {
6021
6193
  continue;
6022
6194
  }
6023
- const absolutePath = path10.isAbsolute(event.rawPath) ? event.rawPath : path10.resolve(workspaceContext.root, event.rawPath);
6195
+ const absolutePath = path11.isAbsolute(event.rawPath) ? event.rawPath : path11.resolve(workspaceContext.root, event.rawPath);
6024
6196
  let relativePath;
6025
6197
  try {
6026
6198
  relativePath = toWorkspaceRelativePath(absolutePath, workspaceContext);
@@ -6080,7 +6252,7 @@ async function collectSearchFiles(searchRootPath) {
6080
6252
  if (entry.isDirectory() && fallbackIgnoredDirectories.has(entry.name)) {
6081
6253
  continue;
6082
6254
  }
6083
- const entryPath = path10.join(searchRootPath, entry.name);
6255
+ const entryPath = path11.join(searchRootPath, entry.name);
6084
6256
  if (entry.isDirectory()) {
6085
6257
  files.push(...await collectSearchFiles(entryPath));
6086
6258
  continue;
@@ -6205,7 +6377,7 @@ var init_runtime7 = __esm(() => {
6205
6377
 
6206
6378
  // packages/ai/src/glob-search/runtime.ts
6207
6379
  import { lstat, readdir as readdir2, stat as stat2 } from "fs/promises";
6208
- import path11 from "path";
6380
+ import path12 from "path";
6209
6381
  function normalizeGlobPath(value) {
6210
6382
  return value.replaceAll("\\", "/").replace(/^\.\//, "");
6211
6383
  }
@@ -6263,7 +6435,7 @@ function entryTypeFromStats(stats) {
6263
6435
  return "other";
6264
6436
  }
6265
6437
  async function collectEntry(absolutePath, searchRoot, parsedInput, matcher, workspaceContext) {
6266
- const relativeToSearchRoot = normalizeGlobPath(path11.relative(searchRoot, absolutePath));
6438
+ const relativeToSearchRoot = normalizeGlobPath(path12.relative(searchRoot, absolutePath));
6267
6439
  const comparablePath = relativeToSearchRoot === "" ? "." : relativeToSearchRoot;
6268
6440
  if (!parsedInput.includeHidden && isHiddenPath(comparablePath)) {
6269
6441
  return null;
@@ -6293,7 +6465,7 @@ async function executeGlobSearch(input, workspaceContext = getDefaultWorkspaceCo
6293
6465
  const matches = [];
6294
6466
  let truncated = false;
6295
6467
  if (searchRootStats.isFile()) {
6296
- const match = await collectEntry(searchRoot, path11.dirname(searchRoot), parsedInput, matcher, workspaceContext);
6468
+ const match = await collectEntry(searchRoot, path12.dirname(searchRoot), parsedInput, matcher, workspaceContext);
6297
6469
  if (match) {
6298
6470
  matches.push(match);
6299
6471
  }
@@ -6314,7 +6486,7 @@ async function executeGlobSearch(input, workspaceContext = getDefaultWorkspaceCo
6314
6486
  if (directoryEntry.isDirectory() && shouldSkipDirectory(directoryEntry.name, parsedInput.includeHidden)) {
6315
6487
  continue;
6316
6488
  }
6317
- const absoluteEntryPath = path11.join(currentDirectory, directoryEntry.name);
6489
+ const absoluteEntryPath = path12.join(currentDirectory, directoryEntry.name);
6318
6490
  const match = await collectEntry(absoluteEntryPath, searchRoot, parsedInput, matcher, workspaceContext);
6319
6491
  if (match) {
6320
6492
  matches.push(match);
@@ -6600,7 +6772,7 @@ var init_runtime9 = __esm(() => {
6600
6772
 
6601
6773
  // packages/ai/src/list-files/runtime.ts
6602
6774
  import { promises as fs3 } from "fs";
6603
- import path12 from "path";
6775
+ import path13 from "path";
6604
6776
  function entryTypeFromStats2(stats) {
6605
6777
  if (stats.isFile()) {
6606
6778
  return "file";
@@ -6646,7 +6818,7 @@ async function executeListFiles(input, workspaceContext = getDefaultWorkspaceCon
6646
6818
  if (!parsedInput.includeHidden && isHidden) {
6647
6819
  continue;
6648
6820
  }
6649
- const absoluteEntryPath = path12.join(currentDirectoryPath, dirEntry.name);
6821
+ const absoluteEntryPath = path13.join(currentDirectoryPath, dirEntry.name);
6650
6822
  const relativeEntryPath = toWorkspaceRelativePath(absoluteEntryPath, workspaceContext);
6651
6823
  const entryStats = await fs3.lstat(absoluteEntryPath);
6652
6824
  const entryType = entryTypeFromStats2(entryStats);
@@ -6724,7 +6896,7 @@ var init_runtime11 = __esm(() => {
6724
6896
 
6725
6897
  // packages/ai/src/todo-write/runtime.ts
6726
6898
  import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
6727
- import path13 from "path";
6899
+ import path14 from "path";
6728
6900
  import { z as z33 } from "zod";
6729
6901
  function safeTodoSessionId(sessionId) {
6730
6902
  const trimmed = sessionId?.trim();
@@ -6734,7 +6906,7 @@ function safeTodoSessionId(sessionId) {
6734
6906
  return trimmed.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120);
6735
6907
  }
6736
6908
  function getTodoFilePath(sessionId, workspaceContext) {
6737
- return resolveWithinWorkspace(path13.join(".lightcode", "todos", `${safeTodoSessionId(sessionId)}.json`), {
6909
+ return resolveWithinWorkspace(path14.join(".lightcode", "todos", `${safeTodoSessionId(sessionId)}.json`), {
6738
6910
  context: workspaceContext,
6739
6911
  allowMissing: true
6740
6912
  });
@@ -6770,7 +6942,7 @@ async function executeTodoWrite(input, {
6770
6942
  ...todo,
6771
6943
  id: todo.id ?? `todo-${index + 1}`
6772
6944
  }));
6773
- await mkdir(path13.dirname(todoFilePath), { recursive: true });
6945
+ await mkdir(path14.dirname(todoFilePath), { recursive: true });
6774
6946
  await writeFile(todoFilePath, JSON.stringify(todos, null, 2), "utf8");
6775
6947
  return todoWriteOutputSchema.parse({
6776
6948
  sessionId: safeTodoSessionId(sessionId),
@@ -7135,7 +7307,7 @@ var init_runtime15 = __esm(() => {
7135
7307
 
7136
7308
  // packages/ai/src/write-file/runtime.ts
7137
7309
  import { promises as fs5 } from "fs";
7138
- import path14 from "path";
7310
+ import path15 from "path";
7139
7311
  async function executeWriteFile(input, workspaceContext = getDefaultWorkspaceContext(), editContext = {}) {
7140
7312
  const parsedInput = writeFileInputSchema.parse(input);
7141
7313
  const resolvedPath = resolveWithinWorkspace(parsedInput.path, {
@@ -7172,7 +7344,7 @@ async function executeWriteFile(input, workspaceContext = getDefaultWorkspaceCon
7172
7344
  previousContent
7173
7345
  });
7174
7346
  }
7175
- await fs5.mkdir(path14.dirname(resolvedPath), { recursive: true });
7347
+ await fs5.mkdir(path15.dirname(resolvedPath), { recursive: true });
7176
7348
  await fs5.writeFile(resolvedPath, parsedInput.content, "utf8");
7177
7349
  const bytesWritten = Buffer.byteLength(parsedInput.content, "utf8");
7178
7350
  return writeFileOutputSchema.parse({
@@ -7292,6 +7464,7 @@ __export(exports_runtime, {
7292
7464
  xmlToolCallMiddleware: () => xmlToolCallMiddleware,
7293
7465
  writeStoredCredentials: () => writeStoredCredentials,
7294
7466
  withXmlToolCallSupport: () => withXmlToolCallSupport,
7467
+ updateUserSettings: () => updateUserSettings,
7295
7468
  undoLastTurn: () => undoLastTurn,
7296
7469
  truncateText: () => truncateText,
7297
7470
  toolCallingDisciplineAddendum: () => toolCallingDisciplineAddendum,
@@ -7372,6 +7545,7 @@ __export(exports_runtime, {
7372
7545
  permissionModeMeets: () => permissionModeMeets,
7373
7546
  permissionDecisionSchema: () => permissionDecisionSchema,
7374
7547
  parseCodingToolInput: () => parseCodingToolInput,
7548
+ parseChatStreamError: () => parseChatStreamError,
7375
7549
  normalizeSandboxConfig: () => normalizeSandboxConfig,
7376
7550
  normalizeContextOptimizerConfig: () => normalizeContextOptimizerConfig,
7377
7551
  messageUsageMetadataSchema: () => messageUsageMetadataSchema,
@@ -7415,6 +7589,7 @@ __export(exports_runtime, {
7415
7589
  getCodingAgentModeDefinition: () => getCodingAgentModeDefinition,
7416
7590
  getActiveCodingToolsForPolicy: () => getActiveCodingToolsForPolicy,
7417
7591
  formatPermissionDecision: () => formatPermissionDecision,
7592
+ formatChatStreamError: () => formatChatStreamError,
7418
7593
  extractProposedPlanBlocks: () => extractProposedPlanBlocks,
7419
7594
  extractLatestProposedPlan: () => extractLatestProposedPlan,
7420
7595
  executeWriteFile: () => executeWriteFile,
@@ -7450,6 +7625,7 @@ __export(exports_runtime, {
7450
7625
  diagnosticsDatabaseSchema: () => diagnosticsDatabaseSchema,
7451
7626
  diagnosticStatusSchema: () => diagnosticStatusSchema,
7452
7627
  diagnosticCheckSchema: () => diagnosticCheckSchema,
7628
+ describeChatStreamError: () => describeChatStreamError,
7453
7629
  defaultSubagentTaskMode: () => defaultSubagentTaskMode,
7454
7630
  defaultPermissionModeForCodingMode: () => defaultPermissionModeForCodingMode,
7455
7631
  defaultContextOptimizerConfig: () => defaultContextOptimizerConfig,
@@ -7616,9 +7792,74 @@ async function runExportAction({
7616
7792
  notify(`Export failed: ${error instanceof Error ? error.message : "unknown error"}`, "error");
7617
7793
  }
7618
7794
  }
7795
+ function latestAssistantText(messages) {
7796
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
7797
+ const message = messages[index];
7798
+ if (message?.role === "assistant") {
7799
+ const text = collectMessageText(message);
7800
+ if (text.trim()) {
7801
+ return text;
7802
+ }
7803
+ }
7804
+ }
7805
+ return null;
7806
+ }
7807
+ function conversationTranscript(messages) {
7808
+ return messages.filter((message) => message.role === "user" || message.role === "assistant").map((message) => {
7809
+ const text = collectMessageText(message);
7810
+ if (!text.trim()) {
7811
+ return null;
7812
+ }
7813
+ const label = message.role === "user" ? "You" : "Assistant";
7814
+ return `## ${label}
7815
+
7816
+ ${text}`;
7817
+ }).filter((entry) => entry !== null).join(`
7818
+
7819
+ `);
7820
+ }
7821
+ async function runCopyAction({
7822
+ args,
7823
+ messages,
7824
+ notify,
7825
+ copyToClipboard
7826
+ }) {
7827
+ const scope = args.trim().toLowerCase() || "last";
7828
+ let payload = null;
7829
+ let label;
7830
+ if (scope === "all") {
7831
+ const transcript = conversationTranscript(messages);
7832
+ payload = transcript.trim() ? transcript : null;
7833
+ label = "conversation";
7834
+ } else if (scope === "code") {
7835
+ const lastText = latestAssistantText(messages);
7836
+ const blocks = lastText ? extractCodeBlocks(lastText) : [];
7837
+ payload = blocks.length > 0 ? blocks.map((block) => block.code).join(`
7838
+
7839
+ `) : null;
7840
+ label = blocks.length === 1 ? "code block" : "code blocks";
7841
+ } else if (scope === "last") {
7842
+ payload = latestAssistantText(messages);
7843
+ label = "last reply";
7844
+ } else {
7845
+ notify("Usage: /copy [last|code|all]", "error");
7846
+ return;
7847
+ }
7848
+ if (!payload) {
7849
+ notify(scope === "code" ? "No code blocks found in the last reply." : "Nothing to copy yet.", "error");
7850
+ return;
7851
+ }
7852
+ const copied = await copyToClipboard(payload);
7853
+ notify(copied ? `Copied ${label} to clipboard (${payload.length} chars).` : "Copy failed: clipboard unavailable in this terminal.", copied ? "info" : "error");
7854
+ }
7619
7855
  function findChatSlashAction(text) {
7620
- const normalized = text.trim().toLowerCase();
7621
- return chatSlashActions.find((action) => action.shortcut === normalized) ?? null;
7856
+ const command = text.trim().split(/\s+/)[0]?.toLowerCase() ?? "";
7857
+ return chatSlashActions.find((action) => action.shortcut === command) ?? null;
7858
+ }
7859
+ function parseChatSlashArgs(text) {
7860
+ const trimmed = text.trim();
7861
+ const firstSpace = trimmed.search(/\s/);
7862
+ return firstSpace === -1 ? "" : trimmed.slice(firstSpace + 1).trim();
7622
7863
  }
7623
7864
  function getChatSlashActionById(id) {
7624
7865
  return chatSlashActions.find((action) => action.id === id) ?? null;
@@ -7634,8 +7875,17 @@ var chatSlashActions;
7634
7875
  var init_chat_slash_actions = __esm(() => {
7635
7876
  init_src2();
7636
7877
  init_client();
7878
+ init_markdown_code();
7637
7879
  init_export_session_markdown();
7638
7880
  chatSlashActions = [
7881
+ {
7882
+ kind: "chat-action",
7883
+ id: "copy",
7884
+ label: "Copy",
7885
+ description: "Copy chat content to the clipboard: /copy [last|code|all]",
7886
+ shortcut: "/copy",
7887
+ run: runCopyAction
7888
+ },
7639
7889
  {
7640
7890
  kind: "chat-action",
7641
7891
  id: "export",
@@ -7841,12 +8091,20 @@ function isTodoWriteOutput(toolName, output) {
7841
8091
  return toolName === "todo_write" && typeof output === "object" && output !== null && Array.isArray(Reflect.get(output, "todos"));
7842
8092
  }
7843
8093
  function normalizeChatErrorMessage(message) {
8094
+ const structured = parseChatStreamError(message);
8095
+ if (structured) {
8096
+ return describeChatStreamError(structured);
8097
+ }
7844
8098
  if (disconnectErrorPattern.test(message)) {
7845
8099
  return recoverableDisconnectMessage;
7846
8100
  }
7847
8101
  return message;
7848
8102
  }
7849
8103
  function isRecoverableChatErrorMessage(message) {
8104
+ const structured = parseChatStreamError(message);
8105
+ if (structured) {
8106
+ return structured.retryable;
8107
+ }
7850
8108
  return disconnectErrorPattern.test(message) || transientProviderErrorPattern.test(message);
7851
8109
  }
7852
8110
  function isRetryCommand(input) {
@@ -7893,6 +8151,27 @@ function hasUnresolvedToolParts(messages) {
7893
8151
  ].includes(part.state);
7894
8152
  }));
7895
8153
  }
8154
+ function sanitizeMessagesForRetry(messages) {
8155
+ const trimmed = trimIncompleteTrailingAssistantMessages(messages);
8156
+ return trimmed.map((message) => {
8157
+ if (message.role !== "assistant") {
8158
+ return message;
8159
+ }
8160
+ let changed = false;
8161
+ const parts = message.parts.map((part) => {
8162
+ if (!isToolUIPart(part) || terminalToolPartStates.has(part.state)) {
8163
+ return part;
8164
+ }
8165
+ changed = true;
8166
+ return {
8167
+ ...part,
8168
+ state: "output-error",
8169
+ errorText: interruptedToolRetryErrorText
8170
+ };
8171
+ });
8172
+ return changed ? { ...message, parts } : message;
8173
+ });
8174
+ }
7896
8175
  function hasActiveClientToolWork({
7897
8176
  messages,
7898
8177
  pendingApprovals,
@@ -8024,7 +8303,7 @@ function useCodingSessionChat({
8024
8303
  maxAutoContinues: autoContinue?.maxAutoContinues ?? defaultAutoContinueLimits.maxAutoContinues
8025
8304
  };
8026
8305
  const maxErrorRetries = autoContinue?.maxErrorRetries ?? 5;
8027
- const stallTimeoutMs = (autoContinue?.stallTimeoutSeconds ?? 120) * 1000;
8306
+ const stallTimeoutMs = (autoContinue?.stallTimeoutSeconds ?? 180) * 1000;
8028
8307
  const autoContinueLimitsRef = useRef(autoContinueLimits);
8029
8308
  autoContinueLimitsRef.current = autoContinueLimits;
8030
8309
  const maxErrorRetriesRef = useRef(maxErrorRetries);
@@ -8058,8 +8337,27 @@ function useCodingSessionChat({
8058
8337
  loadPersistedMessagesRef.current = loadPersistedMessages;
8059
8338
  loadPersistedInteractionsRef.current = loadPersistedInteractions;
8060
8339
  const transport = useMemo(() => {
8340
+ const monitoredFetch = async (input, init) => {
8341
+ const response = await globalThis.fetch(input, init);
8342
+ lastStreamActivityRef.current = Date.now();
8343
+ if (!response.body) {
8344
+ return response;
8345
+ }
8346
+ const monitoredBody = response.body.pipeThrough(new TransformStream({
8347
+ transform(chunk, controller) {
8348
+ lastStreamActivityRef.current = Date.now();
8349
+ controller.enqueue(chunk);
8350
+ }
8351
+ }));
8352
+ return new Response(monitoredBody, {
8353
+ status: response.status,
8354
+ statusText: response.statusText,
8355
+ headers: response.headers
8356
+ });
8357
+ };
8061
8358
  return new DefaultChatTransport({
8062
8359
  api: chatApi,
8360
+ fetch: monitoredFetch,
8063
8361
  body: () => ({
8064
8362
  cwd: cwdRef.current,
8065
8363
  mode: modeRef.current,
@@ -8381,11 +8679,11 @@ function useCodingSessionChat({
8381
8679
  setToolExecutionError("No recoverable response is waiting to retry.");
8382
8680
  return;
8383
8681
  }
8384
- if (pendingApprovals.length > 0 || pendingUserPrompts.length > 0 || hasUnresolvedToolParts(messages)) {
8682
+ if (pendingApprovals.length > 0 || pendingUserPrompts.length > 0) {
8385
8683
  setToolExecutionError("Resolve pending tool approvals or user prompts before retrying.");
8386
8684
  return;
8387
8685
  }
8388
- const retryMessages = trimIncompleteTrailingAssistantMessages(messages);
8686
+ const retryMessages = sanitizeMessagesForRetry(messages);
8389
8687
  setMessages(retryMessages);
8390
8688
  clearError();
8391
8689
  setToolExecutionError(null);
@@ -8400,10 +8698,10 @@ function useCodingSessionChat({
8400
8698
  setMessages
8401
8699
  ]);
8402
8700
  useEffect(() => {
8403
- if (status === "streaming") {
8701
+ if (status === "ready" && !error) {
8404
8702
  errorRetriesRef.current = 0;
8405
8703
  }
8406
- }, [status]);
8704
+ }, [error, status]);
8407
8705
  useEffect(() => {
8408
8706
  if (status === "streaming" || status === "submitted") {
8409
8707
  lastStreamActivityRef.current = Date.now();
@@ -8433,7 +8731,7 @@ function useCodingSessionChat({
8433
8731
  (async () => {
8434
8732
  try {
8435
8733
  await stop();
8436
- setMessages((current) => trimIncompleteTrailingAssistantMessages(current));
8734
+ setMessages((current) => sanitizeMessagesForRetry(current));
8437
8735
  clearError();
8438
8736
  setToolExecutionError(null);
8439
8737
  await sendMessage();
@@ -8506,10 +8804,7 @@ function useCodingSessionChat({
8506
8804
  if (errorRetriesRef.current >= maxErrorRetriesRef.current) {
8507
8805
  return;
8508
8806
  }
8509
- const retryMessages = trimIncompleteTrailingAssistantMessages(messages);
8510
- if (hasUnresolvedToolParts(retryMessages)) {
8511
- return;
8512
- }
8807
+ const retryMessages = sanitizeMessagesForRetry(messages);
8513
8808
  const attempt = errorRetriesRef.current + 1;
8514
8809
  const delay = Math.min(errorRetryBaseDelayMs * 2 ** (attempt - 1), errorRetryMaxDelayMs);
8515
8810
  errorRetryTimerRef.current = setTimeout(() => {
@@ -8732,9 +9027,10 @@ function useCodingSessionChat({
8732
9027
  isSessionIdValid
8733
9028
  };
8734
9029
  }
8735
- var recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", buildModeAutoPromptResponse = "Proceed with implementation in Build mode. Continue without additional plan questions.", chatMessageUpdateThrottleMs = 50, retryCommandPattern, codingToolNameSet, errorRetryBaseDelayMs = 1000, errorRetryMaxDelayMs = 8000, disconnectErrorPattern, transientProviderErrorPattern;
9030
+ var recoverableDisconnectMessage = "Connection interrupted. Please retry or regenerate your last message.", buildModeAutoPromptResponse = "Proceed with implementation in Build mode. Continue without additional plan questions.", chatMessageUpdateThrottleMs = 50, retryCommandPattern, codingToolNameSet, errorRetryBaseDelayMs = 1000, errorRetryMaxDelayMs = 8000, disconnectErrorPattern, transientProviderErrorPattern, interruptedToolRetryErrorText = "Tool call was interrupted by a connection retry. Re-issue the tool call if it is still needed.", terminalToolPartStates;
8736
9031
  var init_use_coding_session_chat = __esm(() => {
8737
9032
  init_agent_tools();
9033
+ init_chat_error();
8738
9034
  init_chat_interaction_schemas();
8739
9035
  init_output_utils();
8740
9036
  init_resolve_within_workspace();
@@ -8748,6 +9044,11 @@ var init_use_coding_session_chat = __esm(() => {
8748
9044
  codingToolNameSet = new Set(Object.keys(codingToolInputSchemas));
8749
9045
  disconnectErrorPattern = /(failed to fetch|networkerror|socket|connection|aborted|terminated|timed?\s?out|timeout|econnreset)/i;
8750
9046
  transientProviderErrorPattern = /(rate.?limit|too many requests|\b429\b|overloaded|quota)/i;
9047
+ terminalToolPartStates = new Set([
9048
+ "output-available",
9049
+ "output-error",
9050
+ "output-denied"
9051
+ ]);
8751
9052
  });
8752
9053
 
8753
9054
  // packages/ai/src/react/index.ts
@@ -8755,6 +9056,59 @@ var init_react = __esm(() => {
8755
9056
  init_use_coding_session_chat();
8756
9057
  });
8757
9058
 
9059
+ // apps/cli/src/lib/clipboard.ts
9060
+ import { platform as platform3 } from "os";
9061
+ async function copyText(renderer, text) {
9062
+ if (!text) {
9063
+ return false;
9064
+ }
9065
+ let copied = false;
9066
+ try {
9067
+ if (renderer?.copyToClipboardOSC52?.(text)) {
9068
+ copied = true;
9069
+ }
9070
+ } catch {}
9071
+ if (await copyViaNativeTool(text)) {
9072
+ copied = true;
9073
+ }
9074
+ return copied;
9075
+ }
9076
+ function nativeClipboardCommand() {
9077
+ switch (platform3()) {
9078
+ case "win32":
9079
+ return ["clip"];
9080
+ case "darwin":
9081
+ return ["pbcopy"];
9082
+ default:
9083
+ return null;
9084
+ }
9085
+ }
9086
+ async function copyViaNativeTool(text) {
9087
+ const direct = nativeClipboardCommand();
9088
+ if (direct) {
9089
+ return spawnCopy(direct, text);
9090
+ }
9091
+ if (await spawnCopy(["wl-copy"], text)) {
9092
+ return true;
9093
+ }
9094
+ return spawnCopy(["xclip", "-selection", "clipboard"], text);
9095
+ }
9096
+ async function spawnCopy(command, text) {
9097
+ try {
9098
+ const proc = Bun.spawn(command, {
9099
+ stdin: "pipe",
9100
+ stdout: "ignore",
9101
+ stderr: "ignore"
9102
+ });
9103
+ proc.stdin.write(text);
9104
+ await proc.stdin.end();
9105
+ return await proc.exited === 0;
9106
+ } catch {
9107
+ return false;
9108
+ }
9109
+ }
9110
+ var init_clipboard = () => {};
9111
+
8758
9112
  // apps/cli/src/utils/text-utils.ts
8759
9113
  function truncateInline2(text, maxLength = 96) {
8760
9114
  const normalized = text.replace(/\s+/g, " ").trim();
@@ -9575,7 +9929,7 @@ function collapseDiff(diff) {
9575
9929
  hiddenLines: lines.length - cut
9576
9930
  };
9577
9931
  }
9578
- function ChatDiffCard({ path: path15, diff }) {
9932
+ function ChatDiffCard({ path: path16, diff }) {
9579
9933
  const wasPreTruncated = diff.trimEnd().endsWith(TRUNCATION_MARKER);
9580
9934
  const cleanDiff = wasPreTruncated ? diff.trimEnd().slice(0, -TRUNCATION_MARKER.length).trimEnd() : diff;
9581
9935
  const { text, hiddenLines } = collapseDiff(cleanDiff);
@@ -9594,14 +9948,14 @@ function ChatDiffCard({ path: path15, diff }) {
9594
9948
  children: /* @__PURE__ */ jsxDEV11("text", {
9595
9949
  fg: cliTheme.text.secondary,
9596
9950
  attributes: TextAttributes7.BOLD,
9597
- children: path15
9951
+ children: path16
9598
9952
  }, undefined, false, undefined, this)
9599
9953
  }, undefined, false, undefined, this),
9600
9954
  /* @__PURE__ */ jsxDEV11("diff", {
9601
9955
  width: "100%",
9602
9956
  diff: text,
9603
9957
  view: "unified",
9604
- filetype: inferFiletype(path15),
9958
+ filetype: inferFiletype(path16),
9605
9959
  syntaxStyle: codeSyntaxStyle,
9606
9960
  showLineNumbers: true,
9607
9961
  addedBg,
@@ -9674,11 +10028,11 @@ function getFileEditDiff(part) {
9674
10028
  return null;
9675
10029
  }
9676
10030
  const diff = Reflect.get(output, "diff");
9677
- const path15 = Reflect.get(output, "path");
9678
- if (typeof diff !== "string" || !diff.trim() || typeof path15 !== "string") {
10031
+ const path16 = Reflect.get(output, "path");
10032
+ if (typeof diff !== "string" || !diff.trim() || typeof path16 !== "string") {
9679
10033
  return null;
9680
10034
  }
9681
- return { path: path15, diff };
10035
+ return { path: path16, diff };
9682
10036
  }
9683
10037
  function getSecondaryParams(input, target) {
9684
10038
  if (!input || typeof input !== "object") {
@@ -10674,6 +11028,60 @@ var init_loading_timer = __esm(() => {
10674
11028
  init_cli_theme();
10675
11029
  });
10676
11030
 
11031
+ // apps/cli/src/components/chat/copy-mode-overlay.tsx
11032
+ import { TextAttributes as TextAttributes15 } from "@opentui/core";
11033
+ import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
11034
+ function previewOf(message) {
11035
+ const text = collectMessageText(message).replace(/\s+/g, " ").trim();
11036
+ return text.length > 0 ? text : "(no text)";
11037
+ }
11038
+ function CopyModeOverlay({ messages, selectedIndex }) {
11039
+ const total = messages.length;
11040
+ const half = Math.floor(WINDOW_SIZE / 2);
11041
+ const end = Math.min(total, Math.max(selectedIndex + half + 1, WINDOW_SIZE));
11042
+ const start = Math.max(0, end - WINDOW_SIZE);
11043
+ const visible = messages.slice(start, end);
11044
+ return /* @__PURE__ */ jsxDEV20("box", {
11045
+ flexDirection: "column",
11046
+ paddingX: 1,
11047
+ paddingY: 1,
11048
+ borderStyle: "single",
11049
+ borderColor: cliTheme.accent.primary,
11050
+ children: [
11051
+ /* @__PURE__ */ jsxDEV20("text", {
11052
+ fg: cliTheme.accent.primary,
11053
+ attributes: TextAttributes15.BOLD,
11054
+ children: "Copy mode \u2014 \u2191/\u2193 select \xB7 Enter copy \xB7 c code blocks \xB7 Esc exit"
11055
+ }, undefined, false, undefined, this),
11056
+ visible.map((message, offset) => {
11057
+ const index = start + offset;
11058
+ const isSelected = index === selectedIndex;
11059
+ const rowColors = getOverlayRowColors(isSelected);
11060
+ const role = message.role === "user" ? "You" : "Assistant";
11061
+ return /* @__PURE__ */ jsxDEV20("box", {
11062
+ paddingX: 1,
11063
+ backgroundColor: rowColors.backgroundColor,
11064
+ children: /* @__PURE__ */ jsxDEV20("text", {
11065
+ fg: rowColors.primaryTextColor,
11066
+ children: `${role}: ${truncateInline2(previewOf(message), 80)}`
11067
+ }, undefined, false, undefined, this)
11068
+ }, message.id, false, undefined, this);
11069
+ }),
11070
+ total > visible.length ? /* @__PURE__ */ jsxDEV20("text", {
11071
+ fg: cliTheme.text.muted,
11072
+ attributes: TextAttributes15.DIM,
11073
+ children: `${selectedIndex + 1}/${total}`
11074
+ }, undefined, false, undefined, this) : null
11075
+ ]
11076
+ }, undefined, true, undefined, this);
11077
+ }
11078
+ var WINDOW_SIZE = 7;
11079
+ var init_copy_mode_overlay = __esm(() => {
11080
+ init_src2();
11081
+ init_cli_theme();
11082
+ init_text_utils();
11083
+ });
11084
+
10677
11085
  // apps/cli/src/hooks/use-auto-continue-config.ts
10678
11086
  import { useEffect as useEffect5, useState as useState7 } from "react";
10679
11087
  function useAutoContinueConfig() {
@@ -10813,7 +11221,7 @@ var init_chat_context_utils = __esm(() => {
10813
11221
 
10814
11222
  // apps/cli/src/utils/file-mentions.ts
10815
11223
  import { promises as fs6 } from "fs";
10816
- import path15 from "path";
11224
+ import path16 from "path";
10817
11225
  function extractFileMentions(text) {
10818
11226
  const mentions = new Set;
10819
11227
  for (const match of text.matchAll(mentionTokenPattern)) {
@@ -10835,8 +11243,8 @@ async function appendMentionAttachments(text, cwd) {
10835
11243
  }
10836
11244
  const attachmentBlocks = [];
10837
11245
  for (const mentionPath of mentionPaths) {
10838
- const absolutePath = path15.resolve(cwd, mentionPath);
10839
- if (!absolutePath.startsWith(path15.resolve(cwd))) {
11246
+ const absolutePath = path16.resolve(cwd, mentionPath);
11247
+ if (!absolutePath.startsWith(path16.resolve(cwd))) {
10840
11248
  continue;
10841
11249
  }
10842
11250
  try {
@@ -10893,10 +11301,10 @@ var init_file_mentions = __esm(() => {
10893
11301
  });
10894
11302
 
10895
11303
  // apps/cli/src/screens/chat-screen.tsx
10896
- import { useKeyboard as useKeyboard4 } from "@opentui/react";
11304
+ import { useKeyboard as useKeyboard4, useRenderer } from "@opentui/react";
10897
11305
  import { Fragment, useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef4, useState as useState10 } from "react";
10898
11306
  import { useLocation, useNavigate, useParams } from "react-router";
10899
- import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
11307
+ import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
10900
11308
  function shouldImplementApprovedPlan(response) {
10901
11309
  if (response.selectedValue === "yes") {
10902
11310
  return true;
@@ -11139,14 +11547,44 @@ function ChatScreen() {
11139
11547
  const notifyChatAction = useCallback4((text, tone = "info") => {
11140
11548
  setActionNotice({ text, tone });
11141
11549
  }, []);
11142
- const runChatSlashAction = useCallback4((action) => {
11550
+ const renderer = useRenderer();
11551
+ const [copyModeOpen, setCopyModeOpen] = useState10(false);
11552
+ const [copyModeIndex, setCopyModeIndex] = useState10(0);
11553
+ const copyableMessages = useMemo4(() => messages.filter((message) => (message.role === "user" || message.role === "assistant") && collectMessageText(message).trim().length > 0), [messages]);
11554
+ const copySelectedMessage = useCallback4(async (kind) => {
11555
+ const message = copyableMessages[copyModeIndex];
11556
+ if (!message) {
11557
+ return;
11558
+ }
11559
+ const fullText = collectMessageText(message);
11560
+ let payload = fullText;
11561
+ let label = "message";
11562
+ if (kind === "code") {
11563
+ const blocks = extractCodeBlocks(fullText);
11564
+ if (blocks.length === 0) {
11565
+ notifyChatAction("No code blocks in this message.", "error");
11566
+ return;
11567
+ }
11568
+ payload = blocks.map((block) => block.code).join(`
11569
+
11570
+ `);
11571
+ label = blocks.length === 1 ? "code block" : "code blocks";
11572
+ }
11573
+ const copied = await copyText(renderer, payload);
11574
+ notifyChatAction(copied ? `Copied ${label} to clipboard (${payload.length} chars).` : "Copy failed: clipboard unavailable in this terminal.", copied ? "info" : "error");
11575
+ setCopyModeOpen(false);
11576
+ }, [copyModeIndex, copyableMessages, notifyChatAction, renderer]);
11577
+ const runChatSlashAction = useCallback4((action, args = "") => {
11143
11578
  action.run({
11144
11579
  sessionId,
11580
+ args,
11581
+ messages,
11145
11582
  setContextState,
11146
11583
  notify: notifyChatAction,
11147
- setPermissionMode: setPermissionModeToState
11584
+ setPermissionMode: setPermissionModeToState,
11585
+ copyToClipboard: (text) => copyText(renderer, text)
11148
11586
  });
11149
- }, [notifyChatAction, sessionId]);
11587
+ }, [messages, notifyChatAction, renderer, sessionId]);
11150
11588
  const updatePermissionMode = useCallback4(async (newMode) => {
11151
11589
  setPermissionMode(newMode);
11152
11590
  setPermissionSelectorOpen(false);
@@ -11221,7 +11659,11 @@ function ChatScreen() {
11221
11659
  const action = findChatSlashAction(text);
11222
11660
  if (action) {
11223
11661
  closeSlashMenu();
11224
- runChatSlashAction(action);
11662
+ if (action.id === "permission") {
11663
+ setPermissionSelectorOpen(true);
11664
+ return;
11665
+ }
11666
+ runChatSlashAction(action, parseChatSlashArgs(text));
11225
11667
  return;
11226
11668
  }
11227
11669
  if (navigateIfSlashRoute(text)) {
@@ -11279,6 +11721,50 @@ function ChatScreen() {
11279
11721
  }, [mode, queuedImplementationInstruction, sendDirectMessage]);
11280
11722
  useKeyboard4((keyEvent) => {
11281
11723
  const keyName = keyEvent.name.toLowerCase();
11724
+ if (copyModeOpen) {
11725
+ if (isEscapeKey(keyName)) {
11726
+ keyEvent.preventDefault();
11727
+ keyEvent.stopPropagation();
11728
+ setCopyModeOpen(false);
11729
+ return;
11730
+ }
11731
+ if (isUpKey(keyName, { vim: true })) {
11732
+ keyEvent.preventDefault();
11733
+ keyEvent.stopPropagation();
11734
+ setCopyModeIndex((index) => Math.max(0, index - 1));
11735
+ return;
11736
+ }
11737
+ if (isDownKey(keyName, { vim: true })) {
11738
+ keyEvent.preventDefault();
11739
+ keyEvent.stopPropagation();
11740
+ setCopyModeIndex((index) => Math.min(copyableMessages.length - 1, index + 1));
11741
+ return;
11742
+ }
11743
+ if (isEnterKey(keyName) || keyName === "y") {
11744
+ keyEvent.preventDefault();
11745
+ keyEvent.stopPropagation();
11746
+ copySelectedMessage("text");
11747
+ return;
11748
+ }
11749
+ if (keyName === "c") {
11750
+ keyEvent.preventDefault();
11751
+ keyEvent.stopPropagation();
11752
+ copySelectedMessage("code");
11753
+ return;
11754
+ }
11755
+ return;
11756
+ }
11757
+ const isCtrlY = keyName === "y" && keyEvent.ctrl && !keyEvent.meta && !keyEvent.super && !keyEvent.hyper && !keyEvent.shift;
11758
+ if (isCtrlY) {
11759
+ if (!isSessionIdValid || hasBlockingPopup || slashMenuOpen || copyableMessages.length === 0) {
11760
+ return;
11761
+ }
11762
+ keyEvent.preventDefault();
11763
+ keyEvent.stopPropagation();
11764
+ setCopyModeIndex(copyableMessages.length - 1);
11765
+ setCopyModeOpen(true);
11766
+ return;
11767
+ }
11282
11768
  const isPlainTab = (keyName === "tab" || keyEvent.sequence === "\t") && !keyEvent.ctrl && !keyEvent.meta && !keyEvent.super && !keyEvent.hyper && !keyEvent.shift;
11283
11769
  const isCtrlT = keyName === "t" && keyEvent.ctrl && !keyEvent.meta && !keyEvent.super && !keyEvent.hyper && !keyEvent.shift;
11284
11770
  if (!isPlainTab && !isCtrlT) {
@@ -11291,74 +11777,77 @@ function ChatScreen() {
11291
11777
  keyEvent.stopPropagation();
11292
11778
  setMode((currentMode) => cycleCodingAgentMode(currentMode));
11293
11779
  });
11294
- return /* @__PURE__ */ jsxDEV20("box", {
11780
+ return /* @__PURE__ */ jsxDEV21("box", {
11295
11781
  width: "100%",
11296
11782
  height: "100%",
11297
- children: /* @__PURE__ */ jsxDEV20(ChatShell, {
11783
+ children: /* @__PURE__ */ jsxDEV21(ChatShell, {
11298
11784
  hasMessages: messages.length > 0 || isStreaming || pendingApprovals.length > 0 || activeUserPrompt !== null || planConfirmationOpen,
11299
11785
  messageCount: messages.length,
11300
11786
  errorMessage,
11301
- inputArea: /* @__PURE__ */ jsxDEV20(ChatTextArea, {
11787
+ inputArea: /* @__PURE__ */ jsxDEV21(ChatTextArea, {
11302
11788
  placeholder: isLoading ? "Waiting for response..." : "Reply... (@ to attach files)",
11303
- focused: canTypeInChat,
11304
- disabled: !canTypeInChat,
11789
+ focused: canTypeInChat && !copyModeOpen,
11790
+ disabled: !canTypeInChat || copyModeOpen,
11305
11791
  slashMenuOpen,
11306
11792
  mentionCandidates,
11307
11793
  onTextChange: syncSlashMenuFromInput,
11308
- beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV20(SlashPageMenu, {
11794
+ beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV21(SlashPageMenu, {
11309
11795
  query: slashMenuQuery,
11310
11796
  selectedIndex: selectedSlashRouteIndex,
11311
11797
  routes: slashRoutes
11312
11798
  }, undefined, false, undefined, this) : null,
11313
- footer: /* @__PURE__ */ jsxDEV20("box", {
11799
+ footer: /* @__PURE__ */ jsxDEV21("box", {
11314
11800
  flexDirection: "row",
11315
11801
  justifyContent: "space-between",
11316
11802
  children: [
11317
- canRetryRecoverableResponse ? /* @__PURE__ */ jsxDEV20("text", {
11803
+ copyModeOpen ? /* @__PURE__ */ jsxDEV21("text", {
11804
+ fg: cliTheme.text.muted,
11805
+ children: "Copy mode: \u2191/\u2193 select \xB7 Enter copy \xB7 c code \xB7 Esc exit"
11806
+ }, undefined, false, undefined, this) : canRetryRecoverableResponse ? /* @__PURE__ */ jsxDEV21("text", {
11318
11807
  fg: cliTheme.text.muted,
11319
11808
  children: "Type retry or regenerate to try again"
11320
- }, undefined, false, undefined, this) : pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV20("text", {
11809
+ }, undefined, false, undefined, this) : pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV21("text", {
11321
11810
  fg: cliTheme.text.muted,
11322
11811
  children: "Use approval card or approve/deny commands"
11323
- }, undefined, false, undefined, this) : hasBlockingPopup ? /* @__PURE__ */ jsxDEV20("text", {
11812
+ }, undefined, false, undefined, this) : hasBlockingPopup ? /* @__PURE__ */ jsxDEV21("text", {
11324
11813
  fg: cliTheme.text.muted,
11325
11814
  children: "Complete the inline prompt to continue"
11326
- }, undefined, false, undefined, this) : slashMenuOpen ? /* @__PURE__ */ jsxDEV20("text", {
11815
+ }, undefined, false, undefined, this) : slashMenuOpen ? /* @__PURE__ */ jsxDEV21("text", {
11327
11816
  fg: cliTheme.text.muted,
11328
11817
  children: "Choose a page or press Esc"
11329
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("text", {
11818
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV21("text", {
11330
11819
  fg: cliTheme.text.muted,
11331
- children: "Tab/Ctrl+T switch mode | Ctrl+P commands"
11820
+ children: "Tab/Ctrl+T mode | Ctrl+Y copy | Ctrl+P commands"
11332
11821
  }, undefined, false, undefined, this),
11333
- /* @__PURE__ */ jsxDEV20("box", {
11822
+ /* @__PURE__ */ jsxDEV21("box", {
11334
11823
  flexDirection: "row",
11335
11824
  gap: 1,
11336
11825
  alignItems: "center",
11337
11826
  children: [
11338
- /* @__PURE__ */ jsxDEV20("text", {
11827
+ /* @__PURE__ */ jsxDEV21("text", {
11339
11828
  fg: contextMeterColor,
11340
11829
  children: contextEstimate.displayText
11341
11830
  }, undefined, false, undefined, this),
11342
- /* @__PURE__ */ jsxDEV20("text", {
11831
+ /* @__PURE__ */ jsxDEV21("text", {
11343
11832
  children: [
11344
- /* @__PURE__ */ jsxDEV20("span", {
11833
+ /* @__PURE__ */ jsxDEV21("span", {
11345
11834
  fg: cliTheme.accent.primary,
11346
11835
  children: modeDefinition.label
11347
11836
  }, undefined, false, undefined, this),
11348
- /* @__PURE__ */ jsxDEV20("span", {
11837
+ /* @__PURE__ */ jsxDEV21("span", {
11349
11838
  fg: cliTheme.text.muted,
11350
11839
  children: " mode"
11351
11840
  }, undefined, false, undefined, this)
11352
11841
  ]
11353
11842
  }, undefined, true, undefined, this),
11354
- /* @__PURE__ */ jsxDEV20("text", {
11355
- children: /* @__PURE__ */ jsxDEV20("span", {
11843
+ /* @__PURE__ */ jsxDEV21("text", {
11844
+ children: /* @__PURE__ */ jsxDEV21("span", {
11356
11845
  fg: cliTheme.semantic.warning,
11357
11846
  children: "|"
11358
11847
  }, undefined, false, undefined, this)
11359
11848
  }, undefined, false, undefined, this),
11360
- /* @__PURE__ */ jsxDEV20("text", {
11361
- children: /* @__PURE__ */ jsxDEV20("span", {
11849
+ /* @__PURE__ */ jsxDEV21("text", {
11850
+ children: /* @__PURE__ */ jsxDEV21("span", {
11362
11851
  fg: permissionMode === "danger-full-access" ? cliTheme.semantic.error : permissionMode === "read-only" ? cliTheme.text.muted : cliTheme.text.primary,
11363
11852
  children: permissionMode ?? "default"
11364
11853
  }, undefined, false, undefined, this)
@@ -11371,30 +11860,30 @@ function ChatScreen() {
11371
11860
  onSubmit: submitChatInput
11372
11861
  }, undefined, false, undefined, this),
11373
11862
  children: [
11374
- contextState && !anchorVisibleInMessages ? /* @__PURE__ */ jsxDEV20(ChatContextStateCard, {
11863
+ contextState && !anchorVisibleInMessages ? /* @__PURE__ */ jsxDEV21(ChatContextStateCard, {
11375
11864
  contextState
11376
11865
  }, undefined, false, undefined, this) : null,
11377
- messages.map((message) => /* @__PURE__ */ jsxDEV20(Fragment, {
11866
+ messages.map((message) => /* @__PURE__ */ jsxDEV21(Fragment, {
11378
11867
  children: [
11379
- isAutoContinueMessage(message) ? null : isContextSummaryMessage(message) ? /* @__PURE__ */ jsxDEV20(ChatContextSummaryCard, {
11868
+ isAutoContinueMessage(message) ? null : isContextSummaryMessage(message) ? /* @__PURE__ */ jsxDEV21(ChatContextSummaryCard, {
11380
11869
  message
11381
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20(ChatMessage, {
11870
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV21(ChatMessage, {
11382
11871
  message,
11383
11872
  pendingApprovalIds
11384
11873
  }, undefined, false, undefined, this),
11385
- contextState && contextState.anchorMessageId === message.id ? /* @__PURE__ */ jsxDEV20(ChatContextStateCard, {
11874
+ contextState && contextState.anchorMessageId === message.id ? /* @__PURE__ */ jsxDEV21(ChatContextStateCard, {
11386
11875
  contextState
11387
11876
  }, undefined, false, undefined, this) : null
11388
11877
  ]
11389
11878
  }, message.id, true, undefined, this)),
11390
- actionNotice ? /* @__PURE__ */ jsxDEV20("box", {
11879
+ actionNotice ? /* @__PURE__ */ jsxDEV21("box", {
11391
11880
  paddingX: 1,
11392
- children: /* @__PURE__ */ jsxDEV20("text", {
11881
+ children: /* @__PURE__ */ jsxDEV21("text", {
11393
11882
  fg: actionNotice.tone === "error" ? cliTheme.semantic.error : cliTheme.semantic.info,
11394
11883
  children: actionNotice.text
11395
11884
  }, undefined, false, undefined, this)
11396
11885
  }, undefined, false, undefined, this) : null,
11397
- modelHasToolCallXmlLeak ? /* @__PURE__ */ jsxDEV20("box", {
11886
+ modelHasToolCallXmlLeak ? /* @__PURE__ */ jsxDEV21("box", {
11398
11887
  paddingX: 1,
11399
11888
  paddingY: 1,
11400
11889
  borderStyle: "single",
@@ -11402,47 +11891,47 @@ function ChatScreen() {
11402
11891
  flexDirection: "column",
11403
11892
  gap: 1,
11404
11893
  children: [
11405
- /* @__PURE__ */ jsxDEV20("text", {
11894
+ /* @__PURE__ */ jsxDEV21("text", {
11406
11895
  fg: cliTheme.semantic.warning,
11407
11896
  children: "Model compatibility issue: this model output raw tool call XML instead of invoking tools."
11408
11897
  }, undefined, false, undefined, this),
11409
- /* @__PURE__ */ jsxDEV20("text", {
11898
+ /* @__PURE__ */ jsxDEV21("text", {
11410
11899
  fg: cliTheme.text.muted,
11411
11900
  children: "Switch to a model with native function calling (Claude, GPT-4, Llama 3) via /model."
11412
11901
  }, undefined, false, undefined, this)
11413
11902
  ]
11414
- }, undefined, true, undefined, this) : autoContinueState?.guardTripped ? /* @__PURE__ */ jsxDEV20("box", {
11903
+ }, undefined, true, undefined, this) : autoContinueState?.guardTripped ? /* @__PURE__ */ jsxDEV21("box", {
11415
11904
  paddingX: 1,
11416
- children: /* @__PURE__ */ jsxDEV20("text", {
11905
+ children: /* @__PURE__ */ jsxDEV21("text", {
11417
11906
  fg: cliTheme.semantic.warning,
11418
11907
  children: autoContinueState.guardTripped === "doom-loop" ? "Agent appears stuck repeating itself \u2014 automatic continuation stopped. Send a message to resume." : `Reached the automatic continuation limit (${autoContinueState.attempt}). Send a message to resume.`
11419
11908
  }, undefined, false, undefined, this)
11420
- }, undefined, false, undefined, this) : autoContinueState ? /* @__PURE__ */ jsxDEV20("box", {
11909
+ }, undefined, false, undefined, this) : autoContinueState ? /* @__PURE__ */ jsxDEV21("box", {
11421
11910
  paddingX: 1,
11422
- children: /* @__PURE__ */ jsxDEV20("text", {
11911
+ children: /* @__PURE__ */ jsxDEV21("text", {
11423
11912
  fg: cliTheme.semantic.info,
11424
11913
  children: autoContinueState.kind === "continue-length" ? `Output limit hit \u2014 continuing automatically (${autoContinueState.attempt})...` : autoContinueState.kind === "retry-error" ? `Connection dropped \u2014 retrying automatically (${autoContinueState.attempt})...` : `Agent stopped early \u2014 continuing automatically (${autoContinueState.attempt})...`
11425
11914
  }, undefined, false, undefined, this)
11426
11915
  }, undefined, false, undefined, this) : null,
11427
- isLoading || isStreaming ? /* @__PURE__ */ jsxDEV20(LoadingTimer, {
11916
+ isLoading || isStreaming ? /* @__PURE__ */ jsxDEV21(LoadingTimer, {
11428
11917
  elapsedSeconds
11429
11918
  }, undefined, false, undefined, this) : null,
11430
- isStreaming ? /* @__PURE__ */ jsxDEV20("box", {
11919
+ isStreaming ? /* @__PURE__ */ jsxDEV21("box", {
11431
11920
  paddingX: 1,
11432
- children: /* @__PURE__ */ jsxDEV20("text", {
11921
+ children: /* @__PURE__ */ jsxDEV21("text", {
11433
11922
  fg: cliTheme.semantic.info,
11434
11923
  children: "Assistant is thinking..."
11435
11924
  }, undefined, false, undefined, this)
11436
11925
  }, undefined, false, undefined, this) : null,
11437
- pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV20(ChatToolApprovalCard, {
11926
+ pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV21(ChatToolApprovalCard, {
11438
11927
  approvals: pendingApprovals,
11439
11928
  onResolve: resolveToolApproval,
11440
11929
  onResolveAll: resolveAllToolApprovals
11441
11930
  }, undefined, false, undefined, this) : null,
11442
- todos.length > 0 ? /* @__PURE__ */ jsxDEV20(ChatTodoStatusCard, {
11931
+ todos.length > 0 ? /* @__PURE__ */ jsxDEV21(ChatTodoStatusCard, {
11443
11932
  todos
11444
11933
  }, undefined, false, undefined, this) : null,
11445
- activeUserPrompt ? /* @__PURE__ */ jsxDEV20(ChatInteractionPopup, {
11934
+ activeUserPrompt ? /* @__PURE__ */ jsxDEV21(ChatInteractionPopup, {
11446
11935
  title: activeUserPrompt.header ?? "Question",
11447
11936
  question: activeUserPrompt.question,
11448
11937
  options: activeUserPrompt.options.map((option) => ({
@@ -11473,7 +11962,7 @@ function ChatScreen() {
11473
11962
  });
11474
11963
  }
11475
11964
  }, undefined, false, undefined, this) : null,
11476
- planConfirmationOpen ? /* @__PURE__ */ jsxDEV20(ChatInteractionPopup, {
11965
+ planConfirmationOpen ? /* @__PURE__ */ jsxDEV21(ChatInteractionPopup, {
11477
11966
  title: "Plan review",
11478
11967
  question: "Implement this plan now?",
11479
11968
  options: [
@@ -11521,10 +12010,14 @@ function ChatScreen() {
11521
12010
  sendDirectMessage(defaultPlanRevisionRequest);
11522
12011
  }
11523
12012
  }, undefined, false, undefined, this) : null,
11524
- permissionSelectorOpen ? /* @__PURE__ */ jsxDEV20(PermissionModeSelector, {
12013
+ permissionSelectorOpen ? /* @__PURE__ */ jsxDEV21(PermissionModeSelector, {
11525
12014
  currentMode: permissionMode,
11526
12015
  onSelect: updatePermissionMode,
11527
12016
  onClose: () => setPermissionSelectorOpen(false)
12017
+ }, undefined, false, undefined, this) : null,
12018
+ copyModeOpen ? /* @__PURE__ */ jsxDEV21(CopyModeOverlay, {
12019
+ messages: copyableMessages,
12020
+ selectedIndex: Math.min(copyModeIndex, Math.max(copyableMessages.length - 1, 0))
11528
12021
  }, undefined, false, undefined, this) : null
11529
12022
  ]
11530
12023
  }, undefined, true, undefined, this)
@@ -11535,6 +12028,7 @@ var init_chat_screen = __esm(() => {
11535
12028
  init_src2();
11536
12029
  init_react();
11537
12030
  init_chat_slash_actions();
12031
+ init_clipboard();
11538
12032
  init_slash_menu_items();
11539
12033
  init_slash_page_menu();
11540
12034
  init_chat_context_summary_card();
@@ -11547,6 +12041,7 @@ var init_chat_screen = __esm(() => {
11547
12041
  init_chat_todo_status_card();
11548
12042
  init_chat_tool_approval_card();
11549
12043
  init_loading_timer();
12044
+ init_copy_mode_overlay();
11550
12045
  init_use_auto_continue_config();
11551
12046
  init_use_context_window();
11552
12047
  init_use_loading_timer();
@@ -11557,6 +12052,7 @@ var init_chat_screen = __esm(() => {
11557
12052
  init_cli_theme();
11558
12053
  init_chat_context_utils();
11559
12054
  init_file_mentions();
12055
+ init_markdown_code();
11560
12056
  planConfirmationAcceptKeywords = [
11561
12057
  "yes",
11562
12058
  "y",
@@ -11584,12 +12080,12 @@ var init_chat_screen = __esm(() => {
11584
12080
  });
11585
12081
 
11586
12082
  // apps/cli/src/screens/diagnostics-screen.tsx
11587
- import { TextAttributes as TextAttributes15 } from "@opentui/core";
12083
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
11588
12084
  import { useKeyboard as useKeyboard5 } from "@opentui/react";
11589
12085
  import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo5, useState as useState11 } from "react";
11590
12086
  import { useLocation as useLocation2 } from "react-router";
11591
12087
  import { z as z35 } from "zod";
11592
- import { jsxDEV as jsxDEV21, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
12088
+ import { jsxDEV as jsxDEV22, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
11593
12089
  function getStatusColor2(status) {
11594
12090
  if (status === "ok") {
11595
12091
  return cliTheme.semantic.success;
@@ -11611,16 +12107,16 @@ function InfoRow({
11611
12107
  tone
11612
12108
  }) {
11613
12109
  const valueColor = tone === "muted" || tone === undefined ? cliTheme.text.secondary : getStatusColor2(tone);
11614
- return /* @__PURE__ */ jsxDEV21("box", {
12110
+ return /* @__PURE__ */ jsxDEV22("box", {
11615
12111
  flexDirection: "row",
11616
12112
  gap: 1,
11617
12113
  children: [
11618
- /* @__PURE__ */ jsxDEV21("text", {
12114
+ /* @__PURE__ */ jsxDEV22("text", {
11619
12115
  fg: cliTheme.text.muted,
11620
12116
  width: 24,
11621
12117
  children: label
11622
12118
  }, undefined, false, undefined, this),
11623
- /* @__PURE__ */ jsxDEV21("text", {
12119
+ /* @__PURE__ */ jsxDEV22("text", {
11624
12120
  fg: valueColor,
11625
12121
  children: formatNullable(value)
11626
12122
  }, undefined, false, undefined, this)
@@ -11631,7 +12127,7 @@ function Section({
11631
12127
  title,
11632
12128
  children
11633
12129
  }) {
11634
- return /* @__PURE__ */ jsxDEV21("box", {
12130
+ return /* @__PURE__ */ jsxDEV22("box", {
11635
12131
  flexDirection: "column",
11636
12132
  gap: 1,
11637
12133
  paddingX: 1,
@@ -11640,9 +12136,9 @@ function Section({
11640
12136
  borderColor: cliTheme.borders.subtle,
11641
12137
  backgroundColor: cliTheme.surfaces.panel,
11642
12138
  children: [
11643
- /* @__PURE__ */ jsxDEV21("text", {
12139
+ /* @__PURE__ */ jsxDEV22("text", {
11644
12140
  fg: cliTheme.accent.primary,
11645
- attributes: TextAttributes15.BOLD,
12141
+ attributes: TextAttributes16.BOLD,
11646
12142
  children: title
11647
12143
  }, undefined, false, undefined, this),
11648
12144
  children
@@ -11653,123 +12149,123 @@ function renderStatus(payload, routeState) {
11653
12149
  const parsedRouteState = diagnosticsRouteStateSchema.safeParse(routeState);
11654
12150
  const currentSession = parsedRouteState.success ? parsedRouteState.data : {};
11655
12151
  const latestSession = payload.sessions.latest;
11656
- return /* @__PURE__ */ jsxDEV21(Fragment2, {
12152
+ return /* @__PURE__ */ jsxDEV22(Fragment2, {
11657
12153
  children: [
11658
- /* @__PURE__ */ jsxDEV21(Section, {
12154
+ /* @__PURE__ */ jsxDEV22(Section, {
11659
12155
  title: "Current Chat",
11660
12156
  children: [
11661
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12157
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11662
12158
  label: "Session",
11663
12159
  value: currentSession.sessionId ?? "not in chat"
11664
12160
  }, undefined, false, undefined, this),
11665
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12161
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11666
12162
  label: "Mode",
11667
12163
  value: currentSession.mode ?? "not set"
11668
12164
  }, undefined, false, undefined, this),
11669
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12165
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11670
12166
  label: "Permission mode",
11671
12167
  value: currentSession.permissionMode ?? "not set"
11672
12168
  }, undefined, false, undefined, this),
11673
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12169
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11674
12170
  label: "CWD",
11675
12171
  value: currentSession.cwd ?? payload.server.cwd
11676
12172
  }, undefined, false, undefined, this),
11677
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12173
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11678
12174
  label: "Messages",
11679
12175
  value: currentSession.messageCount ?? null
11680
12176
  }, undefined, false, undefined, this),
11681
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12177
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11682
12178
  label: "Pending approvals",
11683
12179
  value: currentSession.pendingApprovalCount ?? 0,
11684
12180
  tone: (currentSession.pendingApprovalCount ?? 0) > 0 ? "warn" : "ok"
11685
12181
  }, undefined, false, undefined, this)
11686
12182
  ]
11687
12183
  }, undefined, true, undefined, this),
11688
- /* @__PURE__ */ jsxDEV21(Section, {
12184
+ /* @__PURE__ */ jsxDEV22(Section, {
11689
12185
  title: "Runtime",
11690
12186
  children: [
11691
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12187
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11692
12188
  label: "Server",
11693
12189
  value: payload.server.status,
11694
12190
  tone: payload.server.status
11695
12191
  }, undefined, false, undefined, this),
11696
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12192
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11697
12193
  label: "Provider",
11698
12194
  value: payload.provider.provider,
11699
12195
  tone: payload.provider.status
11700
12196
  }, undefined, false, undefined, this),
11701
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12197
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11702
12198
  label: "Model",
11703
12199
  value: payload.provider.model
11704
12200
  }, undefined, false, undefined, this),
11705
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12201
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11706
12202
  label: "Database",
11707
12203
  value: payload.database.status,
11708
12204
  tone: payload.database.status
11709
12205
  }, undefined, false, undefined, this),
11710
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12206
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11711
12207
  label: "Tool count",
11712
12208
  value: payload.tools.total
11713
12209
  }, undefined, false, undefined, this),
11714
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12210
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11715
12211
  label: "Schema status",
11716
12212
  value: payload.tools.providerSchemaStatus,
11717
12213
  tone: payload.tools.providerSchemaStatus
11718
12214
  }, undefined, false, undefined, this)
11719
12215
  ]
11720
12216
  }, undefined, true, undefined, this),
11721
- /* @__PURE__ */ jsxDEV21(Section, {
12217
+ /* @__PURE__ */ jsxDEV22(Section, {
11722
12218
  title: "Extensions",
11723
12219
  children: [
11724
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12220
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11725
12221
  label: "Skills",
11726
12222
  value: payload.extensions.skills.count
11727
12223
  }, undefined, false, undefined, this),
11728
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12224
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11729
12225
  label: "MCP servers",
11730
12226
  value: payload.extensions.mcp.configuredServers
11731
12227
  }, undefined, false, undefined, this),
11732
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12228
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11733
12229
  label: "MCP running",
11734
12230
  value: payload.extensions.mcp.runningServers
11735
12231
  }, undefined, false, undefined, this),
11736
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12232
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11737
12233
  label: "MCP degraded",
11738
12234
  value: payload.extensions.mcp.degradedServers,
11739
12235
  tone: payload.extensions.mcp.degradedServers > 0 ? "warn" : "ok"
11740
12236
  }, undefined, false, undefined, this),
11741
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12237
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11742
12238
  label: "Plugins",
11743
12239
  value: payload.extensions.plugins.count
11744
12240
  }, undefined, false, undefined, this),
11745
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12241
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11746
12242
  label: "Plugin hooks",
11747
12243
  value: payload.extensions.plugins.hooks
11748
12244
  }, undefined, false, undefined, this)
11749
12245
  ]
11750
12246
  }, undefined, true, undefined, this),
11751
- /* @__PURE__ */ jsxDEV21(Section, {
12247
+ /* @__PURE__ */ jsxDEV22(Section, {
11752
12248
  title: "Latest Saved Session",
11753
- children: latestSession ? /* @__PURE__ */ jsxDEV21(Fragment2, {
12249
+ children: latestSession ? /* @__PURE__ */ jsxDEV22(Fragment2, {
11754
12250
  children: [
11755
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12251
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11756
12252
  label: "Session",
11757
12253
  value: latestSession.id
11758
12254
  }, undefined, false, undefined, this),
11759
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12255
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11760
12256
  label: "Title",
11761
12257
  value: latestSession.title ?? latestSession.latestUserPromptPreview
11762
12258
  }, undefined, false, undefined, this),
11763
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12259
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11764
12260
  label: "Messages",
11765
12261
  value: latestSession.messageCount
11766
12262
  }, undefined, false, undefined, this),
11767
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12263
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11768
12264
  label: "Updated",
11769
12265
  value: formatDate2(latestSession.updatedAt)
11770
12266
  }, undefined, false, undefined, this)
11771
12267
  ]
11772
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV21("text", {
12268
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV22("text", {
11773
12269
  fg: cliTheme.text.muted,
11774
12270
  children: "No saved sessions yet."
11775
12271
  }, undefined, false, undefined, this)
@@ -11778,36 +12274,36 @@ function renderStatus(payload, routeState) {
11778
12274
  }, undefined, true, undefined, this);
11779
12275
  }
11780
12276
  function renderDoctor(payload) {
11781
- return /* @__PURE__ */ jsxDEV21(Section, {
12277
+ return /* @__PURE__ */ jsxDEV22(Section, {
11782
12278
  title: `Overall: ${payload.status}`,
11783
- children: payload.checks.map((check) => /* @__PURE__ */ jsxDEV21("box", {
12279
+ children: payload.checks.map((check) => /* @__PURE__ */ jsxDEV22("box", {
11784
12280
  flexDirection: "column",
11785
12281
  paddingY: 1,
11786
12282
  children: [
11787
- /* @__PURE__ */ jsxDEV21("box", {
12283
+ /* @__PURE__ */ jsxDEV22("box", {
11788
12284
  flexDirection: "row",
11789
12285
  gap: 1,
11790
12286
  children: [
11791
- /* @__PURE__ */ jsxDEV21("text", {
12287
+ /* @__PURE__ */ jsxDEV22("text", {
11792
12288
  fg: getStatusColor2(check.status),
11793
- attributes: TextAttributes15.BOLD,
12289
+ attributes: TextAttributes16.BOLD,
11794
12290
  width: 8,
11795
12291
  children: check.status.toUpperCase()
11796
12292
  }, undefined, false, undefined, this),
11797
- /* @__PURE__ */ jsxDEV21("text", {
12293
+ /* @__PURE__ */ jsxDEV22("text", {
11798
12294
  fg: cliTheme.text.primary,
11799
- attributes: TextAttributes15.BOLD,
12295
+ attributes: TextAttributes16.BOLD,
11800
12296
  children: check.label
11801
12297
  }, undefined, false, undefined, this)
11802
12298
  ]
11803
12299
  }, undefined, true, undefined, this),
11804
- /* @__PURE__ */ jsxDEV21("text", {
12300
+ /* @__PURE__ */ jsxDEV22("text", {
11805
12301
  fg: cliTheme.text.secondary,
11806
12302
  children: check.summary
11807
12303
  }, undefined, false, undefined, this),
11808
- check.details.map((detail) => /* @__PURE__ */ jsxDEV21("text", {
12304
+ check.details.map((detail) => /* @__PURE__ */ jsxDEV22("text", {
11809
12305
  fg: cliTheme.text.muted,
11810
- attributes: TextAttributes15.DIM,
12306
+ attributes: TextAttributes16.DIM,
11811
12307
  children: detail
11812
12308
  }, detail, false, undefined, this))
11813
12309
  ]
@@ -11817,111 +12313,111 @@ function renderDoctor(payload) {
11817
12313
  function renderPermissions(payload, routeState) {
11818
12314
  const parsedRouteState = diagnosticsRouteStateSchema.safeParse(routeState);
11819
12315
  const currentSession = parsedRouteState.success ? parsedRouteState.data : {};
11820
- return /* @__PURE__ */ jsxDEV21(Fragment2, {
12316
+ return /* @__PURE__ */ jsxDEV22(Fragment2, {
11821
12317
  children: [
11822
- /* @__PURE__ */ jsxDEV21(Section, {
12318
+ /* @__PURE__ */ jsxDEV22(Section, {
11823
12319
  title: "Current Chat",
11824
12320
  children: [
11825
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12321
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11826
12322
  label: "Mode",
11827
12323
  value: currentSession.mode ?? payload.defaultMode
11828
12324
  }, undefined, false, undefined, this),
11829
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12325
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11830
12326
  label: "Permission mode",
11831
12327
  value: currentSession.permissionMode ?? payload.effectivePermissionMode
11832
12328
  }, undefined, false, undefined, this),
11833
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12329
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11834
12330
  label: "Pending approvals",
11835
12331
  value: currentSession.pendingApprovalCount ?? 0,
11836
12332
  tone: (currentSession.pendingApprovalCount ?? 0) > 0 ? "warn" : "ok"
11837
12333
  }, undefined, false, undefined, this),
11838
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12334
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11839
12335
  label: "Persisted pending",
11840
12336
  value: payload.pendingInteractions.total,
11841
12337
  tone: payload.pendingInteractions.total > 0 ? "warn" : "ok"
11842
12338
  }, undefined, false, undefined, this)
11843
12339
  ]
11844
12340
  }, undefined, true, undefined, this),
11845
- /* @__PURE__ */ jsxDEV21(Section, {
12341
+ /* @__PURE__ */ jsxDEV22(Section, {
11846
12342
  title: "Policy",
11847
12343
  children: [
11848
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12344
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11849
12345
  label: "Default mode",
11850
12346
  value: payload.defaultMode
11851
12347
  }, undefined, false, undefined, this),
11852
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12348
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11853
12349
  label: "Configured permission",
11854
12350
  value: payload.configuredPermissionMode
11855
12351
  }, undefined, false, undefined, this),
11856
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12352
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11857
12353
  label: "Effective permission",
11858
12354
  value: payload.effectivePermissionMode
11859
12355
  }, undefined, false, undefined, this),
11860
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12356
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11861
12357
  label: "Allowed tools",
11862
12358
  value: payload.allowedTools ? payload.allowedTools.join(", ") : "all policy-allowed tools"
11863
12359
  }, undefined, false, undefined, this),
11864
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12360
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11865
12361
  label: "Allow rules",
11866
12362
  value: payload.rules.allow?.length ?? 0
11867
12363
  }, undefined, false, undefined, this),
11868
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12364
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11869
12365
  label: "Ask rules",
11870
12366
  value: payload.rules.ask?.length ?? 0
11871
12367
  }, undefined, false, undefined, this),
11872
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12368
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11873
12369
  label: "Deny rules",
11874
12370
  value: payload.rules.deny?.length ?? 0
11875
12371
  }, undefined, false, undefined, this)
11876
12372
  ]
11877
12373
  }, undefined, true, undefined, this),
11878
- /* @__PURE__ */ jsxDEV21(Section, {
12374
+ /* @__PURE__ */ jsxDEV22(Section, {
11879
12375
  title: "Recovery",
11880
12376
  children: [
11881
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12377
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11882
12378
  label: "Tool approvals",
11883
12379
  value: payload.pendingInteractions.toolApprovals
11884
12380
  }, undefined, false, undefined, this),
11885
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12381
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11886
12382
  label: "User prompts",
11887
12383
  value: payload.pendingInteractions.userPrompts
11888
12384
  }, undefined, false, undefined, this),
11889
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12385
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11890
12386
  label: "Stale pending",
11891
12387
  value: payload.pendingInteractions.stale,
11892
12388
  tone: payload.pendingInteractions.stale > 0 ? "warn" : "ok"
11893
12389
  }, undefined, false, undefined, this),
11894
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12390
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11895
12391
  label: "Recoverable",
11896
12392
  value: payload.pendingInteractions.recoverable
11897
12393
  }, undefined, false, undefined, this)
11898
12394
  ]
11899
12395
  }, undefined, true, undefined, this),
11900
- /* @__PURE__ */ jsxDEV21(Section, {
12396
+ /* @__PURE__ */ jsxDEV22(Section, {
11901
12397
  title: "Sandbox",
11902
12398
  children: [
11903
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12399
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11904
12400
  label: "Enabled",
11905
12401
  value: payload.sandbox.enabled ? "yes" : "no"
11906
12402
  }, undefined, false, undefined, this),
11907
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12403
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11908
12404
  label: "Mode",
11909
12405
  value: payload.sandbox.mode
11910
12406
  }, undefined, false, undefined, this),
11911
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12407
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11912
12408
  label: "Supported",
11913
12409
  value: payload.sandbox.supported ? "yes" : "no",
11914
12410
  tone: payload.sandbox.supported ? "ok" : "warn"
11915
12411
  }, undefined, false, undefined, this),
11916
- payload.sandbox.unsupportedReason ? /* @__PURE__ */ jsxDEV21("text", {
12412
+ payload.sandbox.unsupportedReason ? /* @__PURE__ */ jsxDEV22("text", {
11917
12413
  fg: cliTheme.semantic.warning,
11918
12414
  children: payload.sandbox.unsupportedReason
11919
12415
  }, undefined, false, undefined, this) : null
11920
12416
  ]
11921
12417
  }, undefined, true, undefined, this),
11922
- /* @__PURE__ */ jsxDEV21(Section, {
12418
+ /* @__PURE__ */ jsxDEV22(Section, {
11923
12419
  title: "Notes",
11924
- children: payload.notes.map((note) => /* @__PURE__ */ jsxDEV21("text", {
12420
+ children: payload.notes.map((note) => /* @__PURE__ */ jsxDEV22("text", {
11925
12421
  fg: cliTheme.text.muted,
11926
12422
  children: note
11927
12423
  }, note, false, undefined, this))
@@ -11930,62 +12426,62 @@ function renderPermissions(payload, routeState) {
11930
12426
  }, undefined, true, undefined, this);
11931
12427
  }
11932
12428
  function renderTools(payload) {
11933
- return /* @__PURE__ */ jsxDEV21(Fragment2, {
12429
+ return /* @__PURE__ */ jsxDEV22(Fragment2, {
11934
12430
  children: [
11935
- /* @__PURE__ */ jsxDEV21(Section, {
12431
+ /* @__PURE__ */ jsxDEV22(Section, {
11936
12432
  title: "Summary",
11937
12433
  children: [
11938
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12434
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11939
12435
  label: "Total",
11940
12436
  value: payload.summary.total
11941
12437
  }, undefined, false, undefined, this),
11942
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12438
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11943
12439
  label: "Read-only",
11944
12440
  value: payload.summary.readOnly
11945
12441
  }, undefined, false, undefined, this),
11946
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12442
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11947
12443
  label: "Workspace-write",
11948
12444
  value: payload.summary.workspaceWrite
11949
12445
  }, undefined, false, undefined, this),
11950
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12446
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11951
12447
  label: "Danger-full-access",
11952
12448
  value: payload.summary.dangerFullAccess
11953
12449
  }, undefined, false, undefined, this),
11954
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12450
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
11955
12451
  label: "Schema status",
11956
12452
  value: payload.summary.providerSchemaStatus,
11957
12453
  tone: payload.summary.providerSchemaStatus
11958
12454
  }, undefined, false, undefined, this)
11959
12455
  ]
11960
12456
  }, undefined, true, undefined, this),
11961
- /* @__PURE__ */ jsxDEV21(Section, {
12457
+ /* @__PURE__ */ jsxDEV22(Section, {
11962
12458
  title: "Tool Catalog",
11963
- children: payload.tools.map((tool2) => /* @__PURE__ */ jsxDEV21("box", {
12459
+ children: payload.tools.map((tool2) => /* @__PURE__ */ jsxDEV22("box", {
11964
12460
  flexDirection: "column",
11965
12461
  paddingY: 1,
11966
12462
  children: [
11967
- /* @__PURE__ */ jsxDEV21("box", {
12463
+ /* @__PURE__ */ jsxDEV22("box", {
11968
12464
  flexDirection: "row",
11969
12465
  justifyContent: "space-between",
11970
12466
  children: [
11971
- /* @__PURE__ */ jsxDEV21("text", {
12467
+ /* @__PURE__ */ jsxDEV22("text", {
11972
12468
  fg: cliTheme.text.primary,
11973
- attributes: TextAttributes15.BOLD,
12469
+ attributes: TextAttributes16.BOLD,
11974
12470
  children: tool2.name
11975
12471
  }, undefined, false, undefined, this),
11976
- /* @__PURE__ */ jsxDEV21("text", {
12472
+ /* @__PURE__ */ jsxDEV22("text", {
11977
12473
  fg: getStatusColor2(tool2.providerSchemaStatus),
11978
12474
  children: tool2.permissionMode
11979
12475
  }, undefined, false, undefined, this)
11980
12476
  ]
11981
12477
  }, undefined, true, undefined, this),
11982
- /* @__PURE__ */ jsxDEV21("text", {
12478
+ /* @__PURE__ */ jsxDEV22("text", {
11983
12479
  fg: cliTheme.text.secondary,
11984
12480
  children: tool2.description
11985
12481
  }, undefined, false, undefined, this),
11986
- /* @__PURE__ */ jsxDEV21("text", {
12482
+ /* @__PURE__ */ jsxDEV22("text", {
11987
12483
  fg: cliTheme.text.muted,
11988
- attributes: TextAttributes15.DIM,
12484
+ attributes: TextAttributes16.DIM,
11989
12485
  children: [
11990
12486
  "Modes: ",
11991
12487
  tool2.activeInModes.join(", ") || "none",
@@ -12000,51 +12496,51 @@ function renderTools(payload) {
12000
12496
  }, undefined, true, undefined, this);
12001
12497
  }
12002
12498
  function renderConfig(payload) {
12003
- return /* @__PURE__ */ jsxDEV21(Fragment2, {
12499
+ return /* @__PURE__ */ jsxDEV22(Fragment2, {
12004
12500
  children: [
12005
- /* @__PURE__ */ jsxDEV21(Section, {
12501
+ /* @__PURE__ */ jsxDEV22(Section, {
12006
12502
  title: "Effective Config",
12007
12503
  children: [
12008
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12504
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12009
12505
  label: "Provider",
12010
12506
  value: payload.selectedProvider
12011
12507
  }, undefined, false, undefined, this),
12012
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12508
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12013
12509
  label: "Configured model",
12014
12510
  value: payload.configuredModel
12015
12511
  }, undefined, false, undefined, this),
12016
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12512
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12017
12513
  label: "Selected model",
12018
12514
  value: payload.selectedModel
12019
12515
  }, undefined, false, undefined, this),
12020
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12516
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12021
12517
  label: "Base URL",
12022
12518
  value: payload.baseUrl ? "configured" : "default"
12023
12519
  }, undefined, false, undefined, this),
12024
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12520
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12025
12521
  label: "Default mode",
12026
12522
  value: payload.defaultMode
12027
12523
  }, undefined, false, undefined, this),
12028
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12524
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12029
12525
  label: "Permission mode",
12030
12526
  value: payload.permissionMode
12031
12527
  }, undefined, false, undefined, this),
12032
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12528
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12033
12529
  label: "Max output tokens",
12034
12530
  value: payload.maxOutputTokens
12035
12531
  }, undefined, false, undefined, this),
12036
- /* @__PURE__ */ jsxDEV21(InfoRow, {
12532
+ /* @__PURE__ */ jsxDEV22(InfoRow, {
12037
12533
  label: "Max steps",
12038
12534
  value: payload.maxSteps
12039
12535
  }, undefined, false, undefined, this)
12040
12536
  ]
12041
12537
  }, undefined, true, undefined, this),
12042
- /* @__PURE__ */ jsxDEV21(Section, {
12538
+ /* @__PURE__ */ jsxDEV22(Section, {
12043
12539
  title: "Loaded Files",
12044
- children: payload.loadedFiles.map((file) => /* @__PURE__ */ jsxDEV21("box", {
12540
+ children: payload.loadedFiles.map((file) => /* @__PURE__ */ jsxDEV22("box", {
12045
12541
  flexDirection: "column",
12046
12542
  children: [
12047
- /* @__PURE__ */ jsxDEV21("text", {
12543
+ /* @__PURE__ */ jsxDEV22("text", {
12048
12544
  fg: file.loaded ? cliTheme.semantic.success : cliTheme.text.muted,
12049
12545
  children: [
12050
12546
  file.scope,
@@ -12052,17 +12548,17 @@ function renderConfig(payload) {
12052
12548
  file.loaded ? "loaded" : file.exists ? "not loaded" : "missing"
12053
12549
  ]
12054
12550
  }, undefined, true, undefined, this),
12055
- /* @__PURE__ */ jsxDEV21("text", {
12551
+ /* @__PURE__ */ jsxDEV22("text", {
12056
12552
  fg: cliTheme.text.muted,
12057
- attributes: TextAttributes15.DIM,
12553
+ attributes: TextAttributes16.DIM,
12058
12554
  children: file.path
12059
12555
  }, undefined, false, undefined, this)
12060
12556
  ]
12061
12557
  }, `${file.scope}:${file.path}`, true, undefined, this))
12062
12558
  }, undefined, false, undefined, this),
12063
- payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV21(Section, {
12559
+ payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV22(Section, {
12064
12560
  title: "Credential Hints",
12065
- children: payload.missingCredentialHints.map((hint) => /* @__PURE__ */ jsxDEV21("text", {
12561
+ children: payload.missingCredentialHints.map((hint) => /* @__PURE__ */ jsxDEV22("text", {
12066
12562
  fg: cliTheme.semantic.warning,
12067
12563
  children: hint
12068
12564
  }, hint, false, undefined, this))
@@ -12136,36 +12632,36 @@ function DiagnosticsScreen({ kind }) {
12136
12632
  }
12137
12633
  return renderConfig(payload);
12138
12634
  }, [kind, location.state, payload]);
12139
- return /* @__PURE__ */ jsxDEV21("box", {
12635
+ return /* @__PURE__ */ jsxDEV22("box", {
12140
12636
  width: "100%",
12141
12637
  height: "100%",
12142
12638
  flexDirection: "column",
12143
12639
  gap: 1,
12144
12640
  children: [
12145
- /* @__PURE__ */ jsxDEV21("box", {
12641
+ /* @__PURE__ */ jsxDEV22("box", {
12146
12642
  flexDirection: "row",
12147
12643
  justifyContent: "space-between",
12148
12644
  paddingX: 1,
12149
12645
  children: [
12150
- /* @__PURE__ */ jsxDEV21("text", {
12646
+ /* @__PURE__ */ jsxDEV22("text", {
12151
12647
  fg: cliTheme.text.primary,
12152
- attributes: TextAttributes15.BOLD,
12648
+ attributes: TextAttributes16.BOLD,
12153
12649
  children: title
12154
12650
  }, undefined, false, undefined, this),
12155
- /* @__PURE__ */ jsxDEV21("text", {
12651
+ /* @__PURE__ */ jsxDEV22("text", {
12156
12652
  fg: cliTheme.text.muted,
12157
12653
  children: isLoading ? "loading..." : "press r to refresh"
12158
12654
  }, undefined, false, undefined, this)
12159
12655
  ]
12160
12656
  }, undefined, true, undefined, this),
12161
- errorMessage ? /* @__PURE__ */ jsxDEV21("box", {
12657
+ errorMessage ? /* @__PURE__ */ jsxDEV22("box", {
12162
12658
  paddingX: 1,
12163
- children: /* @__PURE__ */ jsxDEV21("text", {
12659
+ children: /* @__PURE__ */ jsxDEV22("text", {
12164
12660
  fg: cliTheme.semantic.error,
12165
12661
  children: errorMessage
12166
12662
  }, undefined, false, undefined, this)
12167
12663
  }, undefined, false, undefined, this) : null,
12168
- /* @__PURE__ */ jsxDEV21("scrollbox", {
12664
+ /* @__PURE__ */ jsxDEV22("scrollbox", {
12169
12665
  flexGrow: 1,
12170
12666
  flexDirection: "column",
12171
12667
  gap: 1,
@@ -12198,12 +12694,12 @@ var init_diagnostics_screen = __esm(() => {
12198
12694
  });
12199
12695
 
12200
12696
  // apps/cli/src/components/home-ascii-art.tsx
12201
- import { jsxDEV as jsxDEV22 } from "@opentui/react/jsx-dev-runtime";
12697
+ import { jsxDEV as jsxDEV23 } from "@opentui/react/jsx-dev-runtime";
12202
12698
  function HomeAsciiArt() {
12203
- return /* @__PURE__ */ jsxDEV22("box", {
12699
+ return /* @__PURE__ */ jsxDEV23("box", {
12204
12700
  flexDirection: "column",
12205
12701
  alignItems: "center",
12206
- children: /* @__PURE__ */ jsxDEV22("ascii-font", {
12702
+ children: /* @__PURE__ */ jsxDEV23("ascii-font", {
12207
12703
  text: "lightcode",
12208
12704
  font: "block",
12209
12705
  color: logoColors
@@ -12221,7 +12717,7 @@ var init_home_ascii_art = __esm(() => {
12221
12717
  import { useKeyboard as useKeyboard6 } from "@opentui/react";
12222
12718
  import { useState as useState12 } from "react";
12223
12719
  import { useNavigate as useNavigate2 } from "react-router";
12224
- import { jsxDEV as jsxDEV23 } from "@opentui/react/jsx-dev-runtime";
12720
+ import { jsxDEV as jsxDEV24 } from "@opentui/react/jsx-dev-runtime";
12225
12721
  function HomeTextArea() {
12226
12722
  const navigate = useNavigate2();
12227
12723
  const {
@@ -12299,14 +12795,14 @@ function HomeTextArea() {
12299
12795
  setIsCreatingSession(false);
12300
12796
  }
12301
12797
  }
12302
- return /* @__PURE__ */ jsxDEV23("box", {
12798
+ return /* @__PURE__ */ jsxDEV24("box", {
12303
12799
  width: "66%",
12304
12800
  maxWidth: 104,
12305
12801
  minWidth: 64,
12306
12802
  flexDirection: "column",
12307
12803
  gap: 1,
12308
12804
  children: [
12309
- /* @__PURE__ */ jsxDEV23(ChatTextArea, {
12805
+ /* @__PURE__ */ jsxDEV24(ChatTextArea, {
12310
12806
  containerHeight: 7,
12311
12807
  allowEmpty: true,
12312
12808
  trimOnSubmit: false,
@@ -12316,7 +12812,7 @@ function HomeTextArea() {
12316
12812
  modeToggleHint: true,
12317
12813
  slashMenuOpen,
12318
12814
  onTextChange: syncSlashMenuFromInput,
12319
- beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV23(SlashPageMenu, {
12815
+ beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV24(SlashPageMenu, {
12320
12816
  query: slashMenuQuery,
12321
12817
  selectedIndex,
12322
12818
  routes: slashRoutes
@@ -12324,21 +12820,21 @@ function HomeTextArea() {
12324
12820
  onSubmit: (text) => {
12325
12821
  createSessionAndNavigate(text);
12326
12822
  },
12327
- footer: /* @__PURE__ */ jsxDEV23("box", {
12823
+ footer: /* @__PURE__ */ jsxDEV24("box", {
12328
12824
  flexDirection: "row",
12329
12825
  justifyContent: "space-between",
12330
12826
  children: [
12331
- /* @__PURE__ */ jsxDEV23("text", {
12827
+ /* @__PURE__ */ jsxDEV24("text", {
12332
12828
  fg: cliTheme.text.muted,
12333
12829
  children: isCreatingSession ? "Creating session..." : "Tab/Ctrl+T switch mode | Ctrl+P commands"
12334
12830
  }, undefined, false, undefined, this),
12335
- /* @__PURE__ */ jsxDEV23("text", {
12831
+ /* @__PURE__ */ jsxDEV24("text", {
12336
12832
  children: [
12337
- /* @__PURE__ */ jsxDEV23("span", {
12833
+ /* @__PURE__ */ jsxDEV24("span", {
12338
12834
  fg: cliTheme.accent.primary,
12339
12835
  children: modeDefinition.label
12340
12836
  }, undefined, false, undefined, this),
12341
- /* @__PURE__ */ jsxDEV23("span", {
12837
+ /* @__PURE__ */ jsxDEV24("span", {
12342
12838
  fg: cliTheme.text.muted,
12343
12839
  children: " mode"
12344
12840
  }, undefined, false, undefined, this)
@@ -12347,9 +12843,9 @@ function HomeTextArea() {
12347
12843
  ]
12348
12844
  }, undefined, true, undefined, this)
12349
12845
  }, undefined, false, undefined, this),
12350
- sessionCreateError ? /* @__PURE__ */ jsxDEV23("box", {
12846
+ sessionCreateError ? /* @__PURE__ */ jsxDEV24("box", {
12351
12847
  paddingX: 1,
12352
- children: /* @__PURE__ */ jsxDEV23("text", {
12848
+ children: /* @__PURE__ */ jsxDEV24("text", {
12353
12849
  fg: cliTheme.semantic.error,
12354
12850
  children: sessionCreateError
12355
12851
  }, undefined, false, undefined, this)
@@ -12370,12 +12866,12 @@ var init_home_text_area = __esm(() => {
12370
12866
 
12371
12867
  // apps/cli/src/components/server-status.tsx
12372
12868
  import { useEffect as useEffect10, useState as useState13 } from "react";
12373
- import { jsxDEV as jsxDEV24 } from "@opentui/react/jsx-dev-runtime";
12869
+ import { jsxDEV as jsxDEV25 } from "@opentui/react/jsx-dev-runtime";
12374
12870
  function ServerStatus() {
12375
12871
  const [status, setStatus] = useState13("checking");
12376
12872
  useEffect10(() => {
12377
12873
  let cancelled = false;
12378
- async function checkServer() {
12874
+ async function checkServer2() {
12379
12875
  try {
12380
12876
  if (!cancelled) {
12381
12877
  const response = await fetch(apiBaseUrl);
@@ -12387,12 +12883,12 @@ function ServerStatus() {
12387
12883
  }
12388
12884
  }
12389
12885
  }
12390
- checkServer();
12886
+ checkServer2();
12391
12887
  return () => {
12392
12888
  cancelled = true;
12393
12889
  };
12394
12890
  }, []);
12395
- return /* @__PURE__ */ jsxDEV24("text", {
12891
+ return /* @__PURE__ */ jsxDEV25("text", {
12396
12892
  fg: serverStatusColors[status],
12397
12893
  children: [
12398
12894
  "Server: ",
@@ -12406,9 +12902,9 @@ var init_server_status = __esm(() => {
12406
12902
  });
12407
12903
 
12408
12904
  // apps/cli/src/screens/home-screen.tsx
12409
- import { jsxDEV as jsxDEV25 } from "@opentui/react/jsx-dev-runtime";
12905
+ import { jsxDEV as jsxDEV26 } from "@opentui/react/jsx-dev-runtime";
12410
12906
  function HomeScreen() {
12411
- return /* @__PURE__ */ jsxDEV25("box", {
12907
+ return /* @__PURE__ */ jsxDEV26("box", {
12412
12908
  width: "100%",
12413
12909
  height: "100%",
12414
12910
  flexDirection: "column",
@@ -12419,9 +12915,9 @@ function HomeScreen() {
12419
12915
  paddingY: 1,
12420
12916
  backgroundColor: cliTheme.surfaces.base,
12421
12917
  children: [
12422
- /* @__PURE__ */ jsxDEV25(HomeAsciiArt, {}, undefined, false, undefined, this),
12423
- /* @__PURE__ */ jsxDEV25(ServerStatus, {}, undefined, false, undefined, this),
12424
- /* @__PURE__ */ jsxDEV25(HomeTextArea, {}, undefined, false, undefined, this)
12918
+ /* @__PURE__ */ jsxDEV26(HomeAsciiArt, {}, undefined, false, undefined, this),
12919
+ /* @__PURE__ */ jsxDEV26(ServerStatus, {}, undefined, false, undefined, this),
12920
+ /* @__PURE__ */ jsxDEV26(HomeTextArea, {}, undefined, false, undefined, this)
12425
12921
  ]
12426
12922
  }, undefined, true, undefined, this);
12427
12923
  }
@@ -12433,10 +12929,10 @@ var init_home_screen = __esm(() => {
12433
12929
  });
12434
12930
 
12435
12931
  // apps/cli/src/screens/model-screen.tsx
12436
- import { TextAttributes as TextAttributes16 } from "@opentui/core";
12932
+ import { TextAttributes as TextAttributes17 } from "@opentui/core";
12437
12933
  import { useKeyboard as useKeyboard7 } from "@opentui/react";
12438
12934
  import { useCallback as useCallback6, useEffect as useEffect11, useState as useState14 } from "react";
12439
- import { jsxDEV as jsxDEV26 } from "@opentui/react/jsx-dev-runtime";
12935
+ import { jsxDEV as jsxDEV27 } from "@opentui/react/jsx-dev-runtime";
12440
12936
  function ModelScreen() {
12441
12937
  const [isLoading, setIsLoading] = useState14(true);
12442
12938
  const [payload, setPayload] = useState14(null);
@@ -12468,42 +12964,42 @@ function ModelScreen() {
12468
12964
  keyEvent.stopPropagation();
12469
12965
  loadModelConfig();
12470
12966
  });
12471
- return /* @__PURE__ */ jsxDEV26("box", {
12967
+ return /* @__PURE__ */ jsxDEV27("box", {
12472
12968
  width: "100%",
12473
12969
  height: "100%",
12474
12970
  flexDirection: "column",
12475
12971
  gap: 1,
12476
12972
  paddingX: 1,
12477
12973
  children: [
12478
- /* @__PURE__ */ jsxDEV26("box", {
12974
+ /* @__PURE__ */ jsxDEV27("box", {
12479
12975
  flexDirection: "row",
12480
12976
  justifyContent: "space-between",
12481
12977
  children: [
12482
- /* @__PURE__ */ jsxDEV26("text", {
12978
+ /* @__PURE__ */ jsxDEV27("text", {
12483
12979
  fg: cliTheme.text.primary,
12484
- attributes: TextAttributes16.BOLD,
12980
+ attributes: TextAttributes17.BOLD,
12485
12981
  children: "Model"
12486
12982
  }, undefined, false, undefined, this),
12487
- /* @__PURE__ */ jsxDEV26("text", {
12983
+ /* @__PURE__ */ jsxDEV27("text", {
12488
12984
  fg: cliTheme.text.muted,
12489
12985
  children: isLoading ? "loading..." : "press r to refresh"
12490
12986
  }, undefined, false, undefined, this)
12491
12987
  ]
12492
12988
  }, undefined, true, undefined, this),
12493
- errorMessage ? /* @__PURE__ */ jsxDEV26("box", {
12494
- children: /* @__PURE__ */ jsxDEV26("text", {
12989
+ errorMessage ? /* @__PURE__ */ jsxDEV27("box", {
12990
+ children: /* @__PURE__ */ jsxDEV27("text", {
12495
12991
  fg: cliTheme.semantic.error,
12496
12992
  children: errorMessage
12497
12993
  }, undefined, false, undefined, this)
12498
- }, undefined, false, undefined, this) : payload ? /* @__PURE__ */ jsxDEV26("scrollbox", {
12994
+ }, undefined, false, undefined, this) : payload ? /* @__PURE__ */ jsxDEV27("scrollbox", {
12499
12995
  flexGrow: 1,
12500
12996
  flexDirection: "column",
12501
12997
  gap: 1,
12502
12998
  children: [
12503
- /* @__PURE__ */ jsxDEV26(ModelInfo, {
12999
+ /* @__PURE__ */ jsxDEV27(ModelInfo, {
12504
13000
  payload
12505
13001
  }, undefined, false, undefined, this),
12506
- payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV26(CredentialHints, {
13002
+ payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV27(CredentialHints, {
12507
13003
  hints: payload.missingCredentialHints
12508
13004
  }, undefined, false, undefined, this) : null
12509
13005
  ]
@@ -12513,7 +13009,7 @@ function ModelScreen() {
12513
13009
  }
12514
13010
  function ModelInfo({ payload }) {
12515
13011
  const baseUrlStatus = payload.baseUrl ? "configured" : "default";
12516
- return /* @__PURE__ */ jsxDEV26("box", {
13012
+ return /* @__PURE__ */ jsxDEV27("box", {
12517
13013
  flexDirection: "column",
12518
13014
  gap: 2,
12519
13015
  paddingY: 1,
@@ -12521,19 +13017,19 @@ function ModelInfo({ payload }) {
12521
13017
  borderColor: cliTheme.borders.subtle,
12522
13018
  backgroundColor: cliTheme.surfaces.panel,
12523
13019
  children: [
12524
- /* @__PURE__ */ jsxDEV26(InfoRow2, {
13020
+ /* @__PURE__ */ jsxDEV27(InfoRow2, {
12525
13021
  label: "Provider",
12526
13022
  value: payload.selectedProvider ?? "not set"
12527
13023
  }, undefined, false, undefined, this),
12528
- /* @__PURE__ */ jsxDEV26(InfoRow2, {
13024
+ /* @__PURE__ */ jsxDEV27(InfoRow2, {
12529
13025
  label: "Model",
12530
13026
  value: payload.selectedModel ?? "not set"
12531
13027
  }, undefined, false, undefined, this),
12532
- /* @__PURE__ */ jsxDEV26(InfoRow2, {
13028
+ /* @__PURE__ */ jsxDEV27(InfoRow2, {
12533
13029
  label: "Base URL",
12534
13030
  value: `${baseUrlStatus}${payload.baseUrl ? ` (${payload.baseUrl})` : ""}`
12535
13031
  }, undefined, false, undefined, this),
12536
- /* @__PURE__ */ jsxDEV26(InfoRow2, {
13032
+ /* @__PURE__ */ jsxDEV27(InfoRow2, {
12537
13033
  label: "Configured model",
12538
13034
  value: payload.configuredModel ?? "not set"
12539
13035
  }, undefined, false, undefined, this)
@@ -12541,16 +13037,16 @@ function ModelInfo({ payload }) {
12541
13037
  }, undefined, true, undefined, this);
12542
13038
  }
12543
13039
  function InfoRow2({ label, value }) {
12544
- return /* @__PURE__ */ jsxDEV26("box", {
13040
+ return /* @__PURE__ */ jsxDEV27("box", {
12545
13041
  flexDirection: "row",
12546
13042
  gap: 2,
12547
13043
  children: [
12548
- /* @__PURE__ */ jsxDEV26("text", {
13044
+ /* @__PURE__ */ jsxDEV27("text", {
12549
13045
  fg: cliTheme.text.muted,
12550
13046
  width: 18,
12551
13047
  children: label
12552
13048
  }, undefined, false, undefined, this),
12553
- /* @__PURE__ */ jsxDEV26("text", {
13049
+ /* @__PURE__ */ jsxDEV27("text", {
12554
13050
  fg: cliTheme.text.primary,
12555
13051
  children: value
12556
13052
  }, undefined, false, undefined, this)
@@ -12558,7 +13054,7 @@ function InfoRow2({ label, value }) {
12558
13054
  }, undefined, true, undefined, this);
12559
13055
  }
12560
13056
  function CredentialHints({ hints }) {
12561
- return /* @__PURE__ */ jsxDEV26("box", {
13057
+ return /* @__PURE__ */ jsxDEV27("box", {
12562
13058
  flexDirection: "column",
12563
13059
  gap: 1,
12564
13060
  paddingY: 1,
@@ -12566,12 +13062,12 @@ function CredentialHints({ hints }) {
12566
13062
  borderColor: cliTheme.borders.subtle,
12567
13063
  backgroundColor: cliTheme.surfaces.panel,
12568
13064
  children: [
12569
- /* @__PURE__ */ jsxDEV26("text", {
13065
+ /* @__PURE__ */ jsxDEV27("text", {
12570
13066
  fg: cliTheme.semantic.warning,
12571
- attributes: TextAttributes16.BOLD,
13067
+ attributes: TextAttributes17.BOLD,
12572
13068
  children: "Missing Credentials"
12573
13069
  }, undefined, false, undefined, this),
12574
- hints.map((hint, index) => /* @__PURE__ */ jsxDEV26("text", {
13070
+ hints.map((hint, index) => /* @__PURE__ */ jsxDEV27("text", {
12575
13071
  fg: cliTheme.text.secondary,
12576
13072
  children: [
12577
13073
  "- ",
@@ -12588,23 +13084,302 @@ var init_model_screen = __esm(() => {
12588
13084
  init_text_utils();
12589
13085
  });
12590
13086
 
12591
- // apps/cli/src/screens/onboarding-screen.tsx
12592
- import { TextAttributes as TextAttributes17 } from "@opentui/core";
13087
+ // apps/cli/src/components/chat/model-selector.tsx
13088
+ import { TextAttributes as TextAttributes18 } from "@opentui/core";
12593
13089
  import { useKeyboard as useKeyboard8 } from "@opentui/react";
12594
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
12595
- import os5 from "os";
12596
- import path16 from "path";
12597
- import { useCallback as useCallback7, useState as useState15 } from "react";
13090
+ import { useEffect as useEffect12, useMemo as useMemo6, useState as useState15 } from "react";
13091
+ import { jsxDEV as jsxDEV28 } from "@opentui/react/jsx-dev-runtime";
13092
+ function formatContextLength(contextLength) {
13093
+ if (!contextLength) {
13094
+ return "ctx ?";
13095
+ }
13096
+ return `ctx ${Math.round(contextLength / 1000)}k`;
13097
+ }
13098
+ function ModelSelector({ onClose, notify }) {
13099
+ const [models, setModels] = useState15([]);
13100
+ const [currentModel, setCurrentModel] = useState15(null);
13101
+ const [loadError, setLoadError] = useState15(null);
13102
+ const [isLoading, setIsLoading] = useState15(true);
13103
+ const [isApplying, setIsApplying] = useState15(false);
13104
+ const [filter, setFilter] = useState15("");
13105
+ const [selectedIndex, setSelectedIndex] = useState15(0);
13106
+ useEffect12(() => {
13107
+ let cancelled = false;
13108
+ async function loadModels() {
13109
+ try {
13110
+ const [modelsResponse, statusResponse] = await Promise.all([
13111
+ client.config.models.$get({ query: { provider: "openrouter" } }),
13112
+ client.config.status.$get()
13113
+ ]);
13114
+ if (!modelsResponse.ok) {
13115
+ throw new Error(`Server returned HTTP ${modelsResponse.status}`);
13116
+ }
13117
+ const payload = await modelsResponse.json();
13118
+ const loadedModels = "models" in payload ? payload.models : [];
13119
+ let activeModel = null;
13120
+ if (statusResponse.ok) {
13121
+ const status = await statusResponse.json();
13122
+ activeModel = status.selectedModel ?? null;
13123
+ }
13124
+ if (!cancelled) {
13125
+ setModels(loadedModels);
13126
+ setCurrentModel(activeModel);
13127
+ setIsLoading(false);
13128
+ }
13129
+ } catch (error) {
13130
+ if (!cancelled) {
13131
+ setLoadError(error instanceof Error ? error.message : "Unable to load models.");
13132
+ setIsLoading(false);
13133
+ }
13134
+ }
13135
+ }
13136
+ loadModels();
13137
+ return () => {
13138
+ cancelled = true;
13139
+ };
13140
+ }, []);
13141
+ const filteredModels = useMemo6(() => {
13142
+ const normalized = filter.trim().toLowerCase();
13143
+ if (!normalized) {
13144
+ return models;
13145
+ }
13146
+ return models.filter((model) => model.id.toLowerCase().includes(normalized) || model.name.toLowerCase().includes(normalized));
13147
+ }, [filter, models]);
13148
+ useEffect12(() => {
13149
+ setSelectedIndex((current) => Math.min(current, Math.max(filteredModels.length - 1, 0)));
13150
+ }, [filteredModels.length]);
13151
+ const applyModel = async (model) => {
13152
+ setIsApplying(true);
13153
+ try {
13154
+ const response = await client.config.model.$put({
13155
+ json: { provider: "openrouter", model: model.id }
13156
+ });
13157
+ if (!response.ok) {
13158
+ const body = await response.json().catch(() => null);
13159
+ throw new Error(body?.error ?? `Server returned HTTP ${response.status}`);
13160
+ }
13161
+ notify(`Model switched to ${model.id} (saved to settings.json).`);
13162
+ onClose();
13163
+ } catch (error) {
13164
+ notify(`Model switch failed: ${error instanceof Error ? error.message : "unknown error"}`, "error");
13165
+ setIsApplying(false);
13166
+ }
13167
+ };
13168
+ useKeyboard8((keyEvent) => {
13169
+ const keyName = keyEvent.name.toLowerCase();
13170
+ if (isEscapeKey(keyName)) {
13171
+ keyEvent.preventDefault();
13172
+ keyEvent.stopPropagation();
13173
+ onClose();
13174
+ return;
13175
+ }
13176
+ if (isApplying) {
13177
+ return;
13178
+ }
13179
+ if (isDownKey(keyName)) {
13180
+ keyEvent.preventDefault();
13181
+ keyEvent.stopPropagation();
13182
+ setSelectedIndex((current) => Math.min(current + 1, Math.max(filteredModels.length - 1, 0)));
13183
+ return;
13184
+ }
13185
+ if (isUpKey(keyName)) {
13186
+ keyEvent.preventDefault();
13187
+ keyEvent.stopPropagation();
13188
+ setSelectedIndex((current) => Math.max(current - 1, 0));
13189
+ return;
13190
+ }
13191
+ if (isEnterKey(keyName)) {
13192
+ keyEvent.preventDefault();
13193
+ keyEvent.stopPropagation();
13194
+ const selected = filteredModels[selectedIndex];
13195
+ if (selected) {
13196
+ applyModel(selected);
13197
+ }
13198
+ return;
13199
+ }
13200
+ if (isBackspaceKey(keyName, keyEvent.sequence)) {
13201
+ keyEvent.preventDefault();
13202
+ keyEvent.stopPropagation();
13203
+ setFilter((current) => current.slice(0, -1));
13204
+ return;
13205
+ }
13206
+ const sequence = keyEvent.sequence ?? "";
13207
+ if (sequence.length === 1 && !keyEvent.ctrl && !keyEvent.meta && sequence >= " " && sequence !== "\x7F") {
13208
+ keyEvent.preventDefault();
13209
+ keyEvent.stopPropagation();
13210
+ setFilter((current) => current + sequence);
13211
+ }
13212
+ });
13213
+ const windowStart = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisibleRows / 2), filteredModels.length - maxVisibleRows));
13214
+ const visibleModels = filteredModels.slice(windowStart, windowStart + maxVisibleRows);
13215
+ return /* @__PURE__ */ jsxDEV28("box", {
13216
+ width: "100%",
13217
+ flexDirection: "column",
13218
+ borderStyle: "single",
13219
+ borderColor: cliTheme.overlay.border,
13220
+ backgroundColor: cliTheme.overlay.surface,
13221
+ children: [
13222
+ /* @__PURE__ */ jsxDEV28("box", {
13223
+ width: "100%",
13224
+ flexDirection: "row",
13225
+ justifyContent: "space-between",
13226
+ paddingX: 2,
13227
+ paddingY: 1,
13228
+ borderStyle: "single",
13229
+ border: ["bottom"],
13230
+ borderColor: cliTheme.overlay.border,
13231
+ children: [
13232
+ /* @__PURE__ */ jsxDEV28("text", {
13233
+ fg: cliTheme.accent.primary,
13234
+ attributes: TextAttributes18.BOLD,
13235
+ children: "Switch Model (OpenRouter)"
13236
+ }, undefined, false, undefined, this),
13237
+ /* @__PURE__ */ jsxDEV28("text", {
13238
+ fg: cliTheme.overlay.footerText,
13239
+ children: "type to filter | \u2191/\u2193 select | Enter confirm | Esc cancel"
13240
+ }, undefined, false, undefined, this)
13241
+ ]
13242
+ }, undefined, true, undefined, this),
13243
+ /* @__PURE__ */ jsxDEV28("box", {
13244
+ width: "100%",
13245
+ paddingX: 2,
13246
+ paddingY: 1,
13247
+ children: /* @__PURE__ */ jsxDEV28("text", {
13248
+ fg: cliTheme.text.primary,
13249
+ children: `Filter: ${filter}\u2588`
13250
+ }, undefined, false, undefined, this)
13251
+ }, undefined, false, undefined, this),
13252
+ isLoading ? /* @__PURE__ */ jsxDEV28("box", {
13253
+ paddingX: 2,
13254
+ paddingY: 1,
13255
+ children: /* @__PURE__ */ jsxDEV28("text", {
13256
+ fg: cliTheme.text.muted,
13257
+ children: "Loading models..."
13258
+ }, undefined, false, undefined, this)
13259
+ }, undefined, false, undefined, this) : loadError ? /* @__PURE__ */ jsxDEV28("box", {
13260
+ paddingX: 2,
13261
+ paddingY: 1,
13262
+ children: /* @__PURE__ */ jsxDEV28("text", {
13263
+ fg: cliTheme.semantic.error,
13264
+ children: loadError
13265
+ }, undefined, false, undefined, this)
13266
+ }, undefined, false, undefined, this) : filteredModels.length === 0 ? /* @__PURE__ */ jsxDEV28("box", {
13267
+ paddingX: 2,
13268
+ paddingY: 1,
13269
+ children: /* @__PURE__ */ jsxDEV28("text", {
13270
+ fg: cliTheme.text.muted,
13271
+ children: `No models match "${filter}".`
13272
+ }, undefined, false, undefined, this)
13273
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV28("box", {
13274
+ flexDirection: "column",
13275
+ paddingY: 1,
13276
+ children: [
13277
+ visibleModels.map((model, visibleIndex) => {
13278
+ const index = windowStart + visibleIndex;
13279
+ const isSelected = index === selectedIndex;
13280
+ const rowColors = getOverlayRowColors(isSelected);
13281
+ return /* @__PURE__ */ jsxDEV28("box", {
13282
+ width: "100%",
13283
+ flexDirection: "row",
13284
+ paddingX: 2,
13285
+ backgroundColor: rowColors.backgroundColor,
13286
+ children: [
13287
+ /* @__PURE__ */ jsxDEV28("text", {
13288
+ width: 2,
13289
+ fg: isSelected ? cliTheme.accent.primary : cliTheme.text.muted,
13290
+ attributes: TextAttributes18.BOLD,
13291
+ children: isSelected ? ">" : " "
13292
+ }, undefined, false, undefined, this),
13293
+ /* @__PURE__ */ jsxDEV28("text", {
13294
+ fg: isSelected ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
13295
+ attributes: isSelected ? TextAttributes18.BOLD : TextAttributes18.NONE,
13296
+ children: model.id
13297
+ }, undefined, false, undefined, this),
13298
+ /* @__PURE__ */ jsxDEV28("text", {
13299
+ fg: cliTheme.text.muted,
13300
+ marginLeft: 2,
13301
+ children: `${formatContextLength(model.contextLength)}${model.supportsTools ? " | tools" : ""}${model.supportsReasoning ? " | reasoning" : ""}`
13302
+ }, undefined, false, undefined, this),
13303
+ model.id === currentModel && /* @__PURE__ */ jsxDEV28("text", {
13304
+ fg: cliTheme.accent.primary,
13305
+ marginLeft: 2,
13306
+ children: "(current)"
13307
+ }, undefined, false, undefined, this)
13308
+ ]
13309
+ }, model.id, true, undefined, this);
13310
+ }),
13311
+ /* @__PURE__ */ jsxDEV28("box", {
13312
+ paddingX: 2,
13313
+ marginTop: 1,
13314
+ children: /* @__PURE__ */ jsxDEV28("text", {
13315
+ fg: cliTheme.overlay.footerText,
13316
+ children: `${filteredModels.length} model${filteredModels.length === 1 ? "" : "s"}${isApplying ? " | switching..." : ""}`
13317
+ }, undefined, false, undefined, this)
13318
+ }, undefined, false, undefined, this)
13319
+ ]
13320
+ }, undefined, true, undefined, this)
13321
+ ]
13322
+ }, undefined, true, undefined, this);
13323
+ }
13324
+ var maxVisibleRows = 8;
13325
+ var init_model_selector = __esm(() => {
13326
+ init_client();
13327
+ init_cli_theme();
13328
+ });
13329
+
13330
+ // apps/cli/src/screens/model-select-screen.tsx
13331
+ import { TextAttributes as TextAttributes19 } from "@opentui/core";
13332
+ import { useState as useState16 } from "react";
12598
13333
  import { useNavigate as useNavigate3 } from "react-router";
12599
- import { jsxDEV as jsxDEV27 } from "@opentui/react/jsx-dev-runtime";
13334
+ import { jsxDEV as jsxDEV29 } from "@opentui/react/jsx-dev-runtime";
13335
+ function ModelSelectScreen() {
13336
+ const navigate = useNavigate3();
13337
+ const [notice, setNotice] = useState16(null);
13338
+ return /* @__PURE__ */ jsxDEV29("box", {
13339
+ width: "100%",
13340
+ height: "100%",
13341
+ flexDirection: "column",
13342
+ gap: 1,
13343
+ paddingX: 1,
13344
+ children: [
13345
+ /* @__PURE__ */ jsxDEV29("text", {
13346
+ fg: cliTheme.text.primary,
13347
+ attributes: TextAttributes19.BOLD,
13348
+ children: "Switch Model"
13349
+ }, undefined, false, undefined, this),
13350
+ notice ? /* @__PURE__ */ jsxDEV29("text", {
13351
+ fg: notice.tone === "error" ? cliTheme.semantic.error : cliTheme.accent.primary,
13352
+ children: notice.text
13353
+ }, undefined, false, undefined, this) : null,
13354
+ /* @__PURE__ */ jsxDEV29(ModelSelector, {
13355
+ notify: (text, tone = "info") => setNotice({ text, tone }),
13356
+ onClose: () => navigate(-1)
13357
+ }, undefined, false, undefined, this)
13358
+ ]
13359
+ }, undefined, true, undefined, this);
13360
+ }
13361
+ var init_model_select_screen = __esm(() => {
13362
+ init_model_selector();
13363
+ init_cli_theme();
13364
+ });
13365
+
13366
+ // apps/cli/src/screens/onboarding-screen.tsx
13367
+ import { TextAttributes as TextAttributes20 } from "@opentui/core";
13368
+ import { useKeyboard as useKeyboard9 } from "@opentui/react";
13369
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
13370
+ import os5 from "os";
13371
+ import path17 from "path";
13372
+ import { useCallback as useCallback7, useState as useState17 } from "react";
13373
+ import { useNavigate as useNavigate4 } from "react-router";
13374
+ import { jsxDEV as jsxDEV30 } from "@opentui/react/jsx-dev-runtime";
12600
13375
  function getUserSettingsPath() {
12601
13376
  const configured = process.env.LIGHTCODE_USER_CONFIG?.trim();
12602
- return configured ?? path16.join(os5.homedir(), ".lightcode", "settings.json");
13377
+ return configured ?? path17.join(os5.homedir(), ".lightcode", "settings.json");
12603
13378
  }
12604
13379
  function mergeUserSettings(update) {
12605
13380
  const settingsPath = getUserSettingsPath();
12606
13381
  let existing = {};
12607
- if (existsSync7(settingsPath)) {
13382
+ if (existsSync8(settingsPath)) {
12608
13383
  try {
12609
13384
  const parsed = JSON.parse(readFileSync5(settingsPath, "utf8"));
12610
13385
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -12612,20 +13387,20 @@ function mergeUserSettings(update) {
12612
13387
  }
12613
13388
  } catch {}
12614
13389
  }
12615
- mkdirSync3(path16.dirname(settingsPath), { recursive: true });
12616
- writeFileSync2(settingsPath, `${JSON.stringify({ ...existing, ...update }, null, 2)}
13390
+ mkdirSync5(path17.dirname(settingsPath), { recursive: true });
13391
+ writeFileSync3(settingsPath, `${JSON.stringify({ ...existing, ...update }, null, 2)}
12617
13392
  `, "utf8");
12618
13393
  return settingsPath;
12619
13394
  }
12620
13395
  function OnboardingScreen() {
12621
- const navigate = useNavigate3();
13396
+ const navigate = useNavigate4();
12622
13397
  const { bumpConfigRefresh } = useAppState();
12623
- const [step, setStep] = useState15("provider");
12624
- const [selectedIndex, setSelectedIndex] = useState15(0);
12625
- const [apiKey, setApiKey] = useState15("");
12626
- const [model, setModel] = useState15("");
12627
- const [errorText, setErrorText] = useState15(null);
12628
- const [serverRestarted, setServerRestarted] = useState15(false);
13398
+ const [step, setStep] = useState17("provider");
13399
+ const [selectedIndex, setSelectedIndex] = useState17(0);
13400
+ const [apiKey, setApiKey] = useState17("");
13401
+ const [model, setModel] = useState17("");
13402
+ const [errorText, setErrorText] = useState17(null);
13403
+ const [serverRestarted, setServerRestarted] = useState17(false);
12629
13404
  const provider = providerChoices[selectedIndex];
12630
13405
  const completeSetup = useCallback7(async ({
12631
13406
  finalModel,
@@ -12650,7 +13425,7 @@ function OnboardingScreen() {
12650
13425
  setStep("apiKey");
12651
13426
  }
12652
13427
  }, [bumpConfigRefresh, provider]);
12653
- useKeyboard8((keyEvent) => {
13428
+ useKeyboard9((keyEvent) => {
12654
13429
  if (step === "provider") {
12655
13430
  if (isDownKey(keyEvent.name)) {
12656
13431
  keyEvent.preventDefault();
@@ -12669,36 +13444,36 @@ function OnboardingScreen() {
12669
13444
  navigate("/");
12670
13445
  }
12671
13446
  });
12672
- return /* @__PURE__ */ jsxDEV27("box", {
13447
+ return /* @__PURE__ */ jsxDEV30("box", {
12673
13448
  width: "100%",
12674
13449
  height: "100%",
12675
13450
  flexDirection: "column",
12676
13451
  alignItems: "center",
12677
13452
  paddingTop: 2,
12678
- children: /* @__PURE__ */ jsxDEV27("box", {
13453
+ children: /* @__PURE__ */ jsxDEV30("box", {
12679
13454
  width: 72,
12680
13455
  flexDirection: "column",
12681
13456
  gap: 1,
12682
13457
  children: [
12683
- /* @__PURE__ */ jsxDEV27("text", {
13458
+ /* @__PURE__ */ jsxDEV30("text", {
12684
13459
  fg: cliTheme.accent.primary,
12685
- attributes: TextAttributes17.BOLD,
13460
+ attributes: TextAttributes20.BOLD,
12686
13461
  children: "Welcome to Lightcode"
12687
13462
  }, undefined, false, undefined, this),
12688
- /* @__PURE__ */ jsxDEV27("text", {
13463
+ /* @__PURE__ */ jsxDEV30("text", {
12689
13464
  fg: cliTheme.text.secondary,
12690
13465
  children: "One-time setup: pick a model provider and store an API key."
12691
13466
  }, undefined, false, undefined, this),
12692
- /* @__PURE__ */ jsxDEV27("text", {
13467
+ /* @__PURE__ */ jsxDEV30("text", {
12693
13468
  fg: cliTheme.text.muted,
12694
- attributes: TextAttributes17.DIM,
13469
+ attributes: TextAttributes20.DIM,
12695
13470
  children: "Keys are stored in ~/.lightcode/credentials.json (environment variables always take precedence)."
12696
13471
  }, undefined, false, undefined, this),
12697
- errorText ? /* @__PURE__ */ jsxDEV27("text", {
13472
+ errorText ? /* @__PURE__ */ jsxDEV30("text", {
12698
13473
  fg: cliTheme.semantic.error,
12699
13474
  children: errorText
12700
13475
  }, undefined, false, undefined, this) : null,
12701
- step === "provider" ? /* @__PURE__ */ jsxDEV27("box", {
13476
+ step === "provider" ? /* @__PURE__ */ jsxDEV30("box", {
12702
13477
  flexDirection: "column",
12703
13478
  borderStyle: "single",
12704
13479
  borderColor: cliTheme.borders.default,
@@ -12706,53 +13481,53 @@ function OnboardingScreen() {
12706
13481
  paddingX: 1,
12707
13482
  paddingY: 1,
12708
13483
  children: [
12709
- /* @__PURE__ */ jsxDEV27("text", {
13484
+ /* @__PURE__ */ jsxDEV30("text", {
12710
13485
  fg: cliTheme.text.primary,
12711
- attributes: TextAttributes17.BOLD,
13486
+ attributes: TextAttributes20.BOLD,
12712
13487
  children: "Choose a provider"
12713
13488
  }, undefined, false, undefined, this),
12714
- providerChoices.map((choice, index) => /* @__PURE__ */ jsxDEV27("box", {
13489
+ providerChoices.map((choice, index) => /* @__PURE__ */ jsxDEV30("box", {
12715
13490
  flexDirection: "row",
12716
13491
  gap: 1,
12717
13492
  backgroundColor: index === selectedIndex ? cliTheme.overlay.selectedRowBackground : undefined,
12718
13493
  paddingX: 1,
12719
13494
  children: [
12720
- /* @__PURE__ */ jsxDEV27("text", {
13495
+ /* @__PURE__ */ jsxDEV30("text", {
12721
13496
  fg: index === selectedIndex ? cliTheme.accent.primary : cliTheme.text.muted,
12722
13497
  children: index === selectedIndex ? ">" : " "
12723
13498
  }, undefined, false, undefined, this),
12724
- /* @__PURE__ */ jsxDEV27("text", {
13499
+ /* @__PURE__ */ jsxDEV30("text", {
12725
13500
  fg: index === selectedIndex ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
12726
- attributes: index === selectedIndex ? TextAttributes17.BOLD : TextAttributes17.NONE,
13501
+ attributes: index === selectedIndex ? TextAttributes20.BOLD : TextAttributes20.NONE,
12727
13502
  children: choice.label
12728
13503
  }, undefined, false, undefined, this),
12729
- /* @__PURE__ */ jsxDEV27("text", {
13504
+ /* @__PURE__ */ jsxDEV30("text", {
12730
13505
  fg: cliTheme.overlay.description,
12731
13506
  children: choice.description
12732
13507
  }, undefined, false, undefined, this)
12733
13508
  ]
12734
13509
  }, choice.id, true, undefined, this)),
12735
- /* @__PURE__ */ jsxDEV27("text", {
13510
+ /* @__PURE__ */ jsxDEV30("text", {
12736
13511
  fg: cliTheme.text.muted,
12737
- attributes: TextAttributes17.DIM,
13512
+ attributes: TextAttributes20.DIM,
12738
13513
  children: "Up/Down select - Enter continue"
12739
13514
  }, undefined, false, undefined, this)
12740
13515
  ]
12741
13516
  }, undefined, true, undefined, this) : null,
12742
- step === "apiKey" ? /* @__PURE__ */ jsxDEV27("box", {
13517
+ step === "apiKey" ? /* @__PURE__ */ jsxDEV30("box", {
12743
13518
  flexDirection: "column",
12744
13519
  gap: 1,
12745
13520
  children: [
12746
- /* @__PURE__ */ jsxDEV27("text", {
13521
+ /* @__PURE__ */ jsxDEV30("text", {
12747
13522
  fg: cliTheme.text.primary,
12748
13523
  children: `Paste your ${provider.label} API key and press Enter`
12749
13524
  }, undefined, false, undefined, this),
12750
- /* @__PURE__ */ jsxDEV27("text", {
13525
+ /* @__PURE__ */ jsxDEV30("text", {
12751
13526
  fg: cliTheme.text.muted,
12752
- attributes: TextAttributes17.DIM,
13527
+ attributes: TextAttributes20.DIM,
12753
13528
  children: provider.keyHint
12754
13529
  }, undefined, false, undefined, this),
12755
- /* @__PURE__ */ jsxDEV27(ChatTextArea, {
13530
+ /* @__PURE__ */ jsxDEV30(ChatTextArea, {
12756
13531
  placeholder: "API key...",
12757
13532
  onSubmit: (text) => {
12758
13533
  const trimmedKey = text.trim();
@@ -12769,15 +13544,15 @@ function OnboardingScreen() {
12769
13544
  }, undefined, false, undefined, this)
12770
13545
  ]
12771
13546
  }, undefined, true, undefined, this) : null,
12772
- step === "model" ? /* @__PURE__ */ jsxDEV27("box", {
13547
+ step === "model" ? /* @__PURE__ */ jsxDEV30("box", {
12773
13548
  flexDirection: "column",
12774
13549
  gap: 1,
12775
13550
  children: [
12776
- /* @__PURE__ */ jsxDEV27("text", {
13551
+ /* @__PURE__ */ jsxDEV30("text", {
12777
13552
  fg: cliTheme.text.primary,
12778
13553
  children: `Model id for ${provider.label} (e.g. ${provider.id === "openrouter" ? "anthropic/claude-sonnet-4-6" : "your model id"})`
12779
13554
  }, undefined, false, undefined, this),
12780
- /* @__PURE__ */ jsxDEV27(ChatTextArea, {
13555
+ /* @__PURE__ */ jsxDEV30(ChatTextArea, {
12781
13556
  placeholder: "Model id...",
12782
13557
  onSubmit: (text) => {
12783
13558
  const trimmedModel = text.trim();
@@ -12798,15 +13573,15 @@ function OnboardingScreen() {
12798
13573
  }, undefined, false, undefined, this)
12799
13574
  ]
12800
13575
  }, undefined, true, undefined, this) : null,
12801
- step === "baseUrl" ? /* @__PURE__ */ jsxDEV27("box", {
13576
+ step === "baseUrl" ? /* @__PURE__ */ jsxDEV30("box", {
12802
13577
  flexDirection: "column",
12803
13578
  gap: 1,
12804
13579
  children: [
12805
- /* @__PURE__ */ jsxDEV27("text", {
13580
+ /* @__PURE__ */ jsxDEV30("text", {
12806
13581
  fg: cliTheme.text.primary,
12807
13582
  children: "Base URL of the OpenAI-compatible endpoint"
12808
13583
  }, undefined, false, undefined, this),
12809
- /* @__PURE__ */ jsxDEV27(ChatTextArea, {
13584
+ /* @__PURE__ */ jsxDEV30(ChatTextArea, {
12810
13585
  placeholder: "https://...",
12811
13586
  onSubmit: (text) => {
12812
13587
  const trimmedBaseUrl = text.trim();
@@ -12822,24 +13597,24 @@ function OnboardingScreen() {
12822
13597
  }, undefined, false, undefined, this)
12823
13598
  ]
12824
13599
  }, undefined, true, undefined, this) : null,
12825
- step === "saving" ? /* @__PURE__ */ jsxDEV27("text", {
13600
+ step === "saving" ? /* @__PURE__ */ jsxDEV30("text", {
12826
13601
  fg: cliTheme.semantic.info,
12827
13602
  children: "Saving configuration and restarting the server..."
12828
13603
  }, undefined, false, undefined, this) : null,
12829
- step === "done" ? /* @__PURE__ */ jsxDEV27("box", {
13604
+ step === "done" ? /* @__PURE__ */ jsxDEV30("box", {
12830
13605
  flexDirection: "column",
12831
13606
  gap: 1,
12832
13607
  children: [
12833
- /* @__PURE__ */ jsxDEV27("text", {
13608
+ /* @__PURE__ */ jsxDEV30("text", {
12834
13609
  fg: cliTheme.semantic.success,
12835
- attributes: TextAttributes17.BOLD,
13610
+ attributes: TextAttributes20.BOLD,
12836
13611
  children: "Setup complete"
12837
13612
  }, undefined, false, undefined, this),
12838
- /* @__PURE__ */ jsxDEV27("text", {
13613
+ /* @__PURE__ */ jsxDEV30("text", {
12839
13614
  fg: cliTheme.text.secondary,
12840
13615
  children: serverRestarted ? "The local server was restarted with your new configuration." : "Restart the Lightcode server to pick up the new configuration."
12841
13616
  }, undefined, false, undefined, this),
12842
- /* @__PURE__ */ jsxDEV27("text", {
13617
+ /* @__PURE__ */ jsxDEV30("text", {
12843
13618
  fg: cliTheme.text.muted,
12844
13619
  children: "Press Enter to start coding."
12845
13620
  }, undefined, false, undefined, this)
@@ -12898,12 +13673,11 @@ var init_onboarding_screen = __esm(() => {
12898
13673
  });
12899
13674
 
12900
13675
  // apps/cli/src/screens/session-list-screen.tsx
12901
- import { TextAttributes as TextAttributes18 } from "@opentui/core";
12902
- import { useKeyboard as useKeyboard9 } from "@opentui/react";
12903
- import path17 from "path";
12904
- import { useCallback as useCallback8, useEffect as useEffect12, useMemo as useMemo6, useState as useState16 } from "react";
12905
- import { useNavigate as useNavigate4 } from "react-router";
12906
- import { jsxDEV as jsxDEV28 } from "@opentui/react/jsx-dev-runtime";
13676
+ import { TextAttributes as TextAttributes21 } from "@opentui/core";
13677
+ import { useKeyboard as useKeyboard10 } from "@opentui/react";
13678
+ import { useCallback as useCallback8, useEffect as useEffect13, useMemo as useMemo7, useState as useState18 } from "react";
13679
+ import { useNavigate as useNavigate5 } from "react-router";
13680
+ import { jsxDEV as jsxDEV31 } from "@opentui/react/jsx-dev-runtime";
12907
13681
  function getSessionLabel(session) {
12908
13682
  return session.title ?? session.latestUserPromptPreview ?? session.id;
12909
13683
  }
@@ -12915,20 +13689,25 @@ function sessionMatchesFilter(session, query) {
12915
13689
  return (session.title ?? "").toLowerCase().includes(normalized) || (session.latestUserPromptPreview ?? "").toLowerCase().includes(normalized) || session.id.toLowerCase().includes(normalized);
12916
13690
  }
12917
13691
  function SessionListScreen() {
12918
- const navigate = useNavigate4();
12919
- const [sessions, setSessions] = useState16([]);
12920
- const [selectedIndex, setSelectedIndex] = useState16(0);
12921
- const [isLoading, setIsLoading] = useState16(true);
12922
- const [errorMessage, setErrorMessage] = useState16(null);
12923
- const [actionMessage, setActionMessage] = useState16(null);
12924
- const [pendingDeleteId, setPendingDeleteId] = useState16(null);
12925
- const [filterQuery, setFilterQuery] = useState16("");
12926
- const [filterOpen, setFilterOpen] = useState16(false);
12927
- const [renamingSessionId, setRenamingSessionId] = useState16(null);
12928
- const visibleSessions = useMemo6(() => sessions.filter((session) => sessionMatchesFilter(session, filterQuery)), [filterQuery, sessions]);
13692
+ const navigate = useNavigate5();
13693
+ const [sessions, setSessions] = useState18([]);
13694
+ const [selectedIndex, setSelectedIndex] = useState18(0);
13695
+ const [isLoading, setIsLoading] = useState18(true);
13696
+ const [errorMessage, setErrorMessage] = useState18(null);
13697
+ const [actionMessage, setActionMessage] = useState18(null);
13698
+ const [pendingDeleteId, setPendingDeleteId] = useState18(null);
13699
+ const [filterQuery, setFilterQuery] = useState18("");
13700
+ const [filterOpen, setFilterOpen] = useState18(false);
13701
+ const [renamingSessionId, setRenamingSessionId] = useState18(null);
13702
+ const visibleSessions = useMemo7(() => sessions.filter((session) => sessionMatchesFilter(session, filterQuery)), [filterQuery, sessions]);
12929
13703
  const safeSelectedIndex = Math.min(selectedIndex, Math.max(visibleSessions.length - 1, 0));
12930
13704
  const selectedSession = visibleSessions[safeSelectedIndex] ?? null;
12931
13705
  const inputActive = filterOpen || renamingSessionId !== null;
13706
+ const applyLoadedSessions = useCallback8((loadedSessions, droppedCount) => {
13707
+ setSessions(loadedSessions);
13708
+ setSelectedIndex((currentIndex) => Math.min(currentIndex, Math.max(loadedSessions.length - 1, 0)));
13709
+ setErrorMessage(droppedCount > 0 ? `${droppedCount} session${droppedCount === 1 ? "" : "s"} could not be read and ${droppedCount === 1 ? "was" : "were"} skipped.` : null);
13710
+ }, []);
12932
13711
  const loadSessions = useCallback8(async () => {
12933
13712
  setIsLoading(true);
12934
13713
  setErrorMessage(null);
@@ -12939,17 +13718,31 @@ function SessionListScreen() {
12939
13718
  }
12940
13719
  const rawPayload = await response.json();
12941
13720
  const parsedPayload = sessionListResponseSchema.safeParse(rawPayload);
12942
- if (!parsedPayload.success) {
13721
+ if (parsedPayload.success) {
13722
+ applyLoadedSessions(parsedPayload.data.sessions, 0);
13723
+ return;
13724
+ }
13725
+ const rawSessions = rawPayload && typeof rawPayload === "object" && Array.isArray(rawPayload.sessions) ? rawPayload.sessions : null;
13726
+ if (!rawSessions) {
12943
13727
  throw new Error("Server returned an invalid session list response.");
12944
13728
  }
12945
- setSessions(parsedPayload.data.sessions);
12946
- setSelectedIndex((currentIndex) => Math.min(currentIndex, Math.max(parsedPayload.data.sessions.length - 1, 0)));
13729
+ const validSessions = [];
13730
+ let droppedCount = 0;
13731
+ for (const rawSession of rawSessions) {
13732
+ const parsedSession = sessionSummarySchema.safeParse(rawSession);
13733
+ if (parsedSession.success) {
13734
+ validSessions.push(parsedSession.data);
13735
+ } else {
13736
+ droppedCount += 1;
13737
+ }
13738
+ }
13739
+ applyLoadedSessions(validSessions, droppedCount);
12947
13740
  } catch (error) {
12948
- setErrorMessage(getErrorMessage(error, "Unable to list sessions."));
13741
+ setErrorMessage(getErrorMessage(error, "Unable to reach the Lightcode server."));
12949
13742
  } finally {
12950
13743
  setIsLoading(false);
12951
13744
  }
12952
- }, []);
13745
+ }, [applyLoadedSessions]);
12953
13746
  const resumeSelectedSession = useCallback8(() => {
12954
13747
  if (!selectedSession) {
12955
13748
  setActionMessage("No session selected.");
@@ -13009,9 +13802,9 @@ function SessionListScreen() {
13009
13802
  if (!parsedPayload.success) {
13010
13803
  throw new Error("Server returned an invalid export response.");
13011
13804
  }
13012
- const exportPath = path17.join(process.cwd(), `lightcode-session-${selectedSession.id}.json`);
13013
- await Bun.write(exportPath, JSON.stringify(parsedPayload.data, null, 2));
13014
- setActionMessage(`Exported JSON to ${exportPath}`);
13805
+ const exportPath = getExportPath(parsedPayload.data.session.title, parsedPayload.data.session.id);
13806
+ await Bun.write(exportPath, sessionToMarkdown(parsedPayload.data));
13807
+ setActionMessage(`Exported Markdown to ${exportPath.replace(/\\/g, "/")}`);
13015
13808
  } catch (error) {
13016
13809
  setActionMessage(getErrorMessage(error, "Unable to export session."));
13017
13810
  }
@@ -13062,10 +13855,10 @@ function SessionListScreen() {
13062
13855
  setActionMessage(getErrorMessage(error, "Unable to rename session."));
13063
13856
  }
13064
13857
  }, [loadSessions]);
13065
- useEffect12(() => {
13858
+ useEffect13(() => {
13066
13859
  loadSessions();
13067
13860
  }, [loadSessions]);
13068
- useKeyboard9((keyEvent) => {
13861
+ useKeyboard10((keyEvent) => {
13069
13862
  const keyName = keyEvent.name.toLowerCase();
13070
13863
  if (inputActive) {
13071
13864
  if (isEscapeKey(keyName)) {
@@ -13147,55 +13940,55 @@ function SessionListScreen() {
13147
13940
  exportSelectedSession();
13148
13941
  }
13149
13942
  });
13150
- const footerText = useMemo6(() => {
13943
+ const footerText = useMemo7(() => {
13151
13944
  if (sessions.length === 0) {
13152
13945
  return `u refresh | l resume latest | ${BACK_SHORTCUT_LABEL} back`;
13153
13946
  }
13154
13947
  return "Enter resume | r rename | f fork | / filter | e export | d delete | u refresh";
13155
13948
  }, [sessions.length]);
13156
- return /* @__PURE__ */ jsxDEV28("box", {
13949
+ return /* @__PURE__ */ jsxDEV31("box", {
13157
13950
  width: "100%",
13158
13951
  height: "100%",
13159
13952
  flexDirection: "column",
13160
13953
  gap: 1,
13161
13954
  children: [
13162
- /* @__PURE__ */ jsxDEV28("box", {
13955
+ /* @__PURE__ */ jsxDEV31("box", {
13163
13956
  flexDirection: "row",
13164
13957
  justifyContent: "space-between",
13165
13958
  paddingX: 1,
13166
13959
  children: [
13167
- /* @__PURE__ */ jsxDEV28("text", {
13960
+ /* @__PURE__ */ jsxDEV31("text", {
13168
13961
  fg: cliTheme.text.primary,
13169
- attributes: TextAttributes18.BOLD,
13962
+ attributes: TextAttributes21.BOLD,
13170
13963
  children: "Sessions"
13171
13964
  }, undefined, false, undefined, this),
13172
- /* @__PURE__ */ jsxDEV28("text", {
13965
+ /* @__PURE__ */ jsxDEV31("text", {
13173
13966
  fg: cliTheme.text.muted,
13174
13967
  children: isLoading ? "loading..." : `${sessions.length} saved`
13175
13968
  }, undefined, false, undefined, this)
13176
13969
  ]
13177
13970
  }, undefined, true, undefined, this),
13178
- errorMessage ? /* @__PURE__ */ jsxDEV28("box", {
13971
+ errorMessage ? /* @__PURE__ */ jsxDEV31("box", {
13179
13972
  paddingX: 1,
13180
- children: /* @__PURE__ */ jsxDEV28("text", {
13973
+ children: /* @__PURE__ */ jsxDEV31("text", {
13181
13974
  fg: cliTheme.semantic.error,
13182
13975
  children: errorMessage
13183
13976
  }, undefined, false, undefined, this)
13184
13977
  }, undefined, false, undefined, this) : null,
13185
- filterQuery && !filterOpen ? /* @__PURE__ */ jsxDEV28("box", {
13978
+ filterQuery && !filterOpen ? /* @__PURE__ */ jsxDEV31("box", {
13186
13979
  paddingX: 1,
13187
- children: /* @__PURE__ */ jsxDEV28("text", {
13980
+ children: /* @__PURE__ */ jsxDEV31("text", {
13188
13981
  fg: cliTheme.accent.primary,
13189
13982
  children: `Filter: "${filterQuery}" (${visibleSessions.length} match${visibleSessions.length === 1 ? "" : "es"}) - Esc clears`
13190
13983
  }, undefined, false, undefined, this)
13191
13984
  }, undefined, false, undefined, this) : null,
13192
- /* @__PURE__ */ jsxDEV28("box", {
13985
+ /* @__PURE__ */ jsxDEV31("box", {
13193
13986
  flexDirection: "column",
13194
13987
  flexGrow: 1,
13195
13988
  children: [
13196
- visibleSessions.length === 0 && !isLoading ? /* @__PURE__ */ jsxDEV28("box", {
13989
+ visibleSessions.length === 0 && !isLoading ? /* @__PURE__ */ jsxDEV31("box", {
13197
13990
  paddingX: 1,
13198
- children: /* @__PURE__ */ jsxDEV28("text", {
13991
+ children: /* @__PURE__ */ jsxDEV31("text", {
13199
13992
  fg: cliTheme.text.muted,
13200
13993
  children: sessions.length === 0 ? "No saved sessions yet." : "No sessions match the filter."
13201
13994
  }, undefined, false, undefined, this)
@@ -13204,34 +13997,34 @@ function SessionListScreen() {
13204
13997
  const isSelected = index === safeSelectedIndex;
13205
13998
  const rowColors = getOverlayRowColors(isSelected);
13206
13999
  const selectedForDelete = pendingDeleteId === session.id;
13207
- return /* @__PURE__ */ jsxDEV28("box", {
14000
+ return /* @__PURE__ */ jsxDEV31("box", {
13208
14001
  flexDirection: "column",
13209
14002
  paddingX: 1,
13210
14003
  backgroundColor: rowColors.backgroundColor,
13211
14004
  children: [
13212
- /* @__PURE__ */ jsxDEV28("box", {
14005
+ /* @__PURE__ */ jsxDEV31("box", {
13213
14006
  flexDirection: "row",
13214
14007
  justifyContent: "space-between",
13215
14008
  children: [
13216
- /* @__PURE__ */ jsxDEV28("text", {
14009
+ /* @__PURE__ */ jsxDEV31("text", {
13217
14010
  fg: selectedForDelete ? cliTheme.semantic.error : rowColors.primaryTextColor,
13218
- attributes: TextAttributes18.BOLD,
14011
+ attributes: TextAttributes21.BOLD,
13219
14012
  children: [
13220
14013
  index + 1,
13221
14014
  ". ",
13222
14015
  truncateInline2(getSessionLabel(session), 64)
13223
14016
  ]
13224
14017
  }, undefined, true, undefined, this),
13225
- /* @__PURE__ */ jsxDEV28("text", {
14018
+ /* @__PURE__ */ jsxDEV31("text", {
13226
14019
  fg: rowColors.secondaryTextColor,
13227
- attributes: TextAttributes18.DIM,
14020
+ attributes: TextAttributes21.DIM,
13228
14021
  children: `${session.messageCount} msgs - ${session.mode} - ${formatDate2(session.updatedAt)}`
13229
14022
  }, undefined, false, undefined, this)
13230
14023
  ]
13231
14024
  }, undefined, true, undefined, this),
13232
- isSelected && session.latestUserPromptPreview ? /* @__PURE__ */ jsxDEV28("text", {
14025
+ isSelected && session.latestUserPromptPreview ? /* @__PURE__ */ jsxDEV31("text", {
13233
14026
  fg: rowColors.secondaryTextColor,
13234
- attributes: TextAttributes18.DIM,
14027
+ attributes: TextAttributes21.DIM,
13235
14028
  children: truncateInline2(session.latestUserPromptPreview, 110)
13236
14029
  }, undefined, false, undefined, this) : null
13237
14030
  ]
@@ -13239,28 +14032,28 @@ function SessionListScreen() {
13239
14032
  })
13240
14033
  ]
13241
14034
  }, undefined, true, undefined, this),
13242
- actionMessage ? /* @__PURE__ */ jsxDEV28("box", {
14035
+ actionMessage ? /* @__PURE__ */ jsxDEV31("box", {
13243
14036
  paddingX: 1,
13244
- children: /* @__PURE__ */ jsxDEV28("text", {
14037
+ children: /* @__PURE__ */ jsxDEV31("text", {
13245
14038
  fg: cliTheme.semantic.info,
13246
14039
  children: actionMessage
13247
14040
  }, undefined, false, undefined, this)
13248
14041
  }, undefined, false, undefined, this) : null,
13249
- filterOpen ? /* @__PURE__ */ jsxDEV28(ChatTextArea, {
14042
+ filterOpen ? /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13250
14043
  placeholder: "Filter sessions...",
13251
14044
  allowEmpty: true,
13252
14045
  onTextChange: setFilterQuery,
13253
14046
  onSubmit: () => setFilterOpen(false)
13254
14047
  }, undefined, false, undefined, this) : null,
13255
- renamingSessionId !== null ? /* @__PURE__ */ jsxDEV28(ChatTextArea, {
14048
+ renamingSessionId !== null ? /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13256
14049
  placeholder: "New session title...",
13257
14050
  onSubmit: (title) => void renameSession(renamingSessionId, title)
13258
14051
  }, undefined, false, undefined, this) : null,
13259
- /* @__PURE__ */ jsxDEV28("box", {
14052
+ /* @__PURE__ */ jsxDEV31("box", {
13260
14053
  paddingX: 1,
13261
- children: /* @__PURE__ */ jsxDEV28("text", {
14054
+ children: /* @__PURE__ */ jsxDEV31("text", {
13262
14055
  fg: cliTheme.text.muted,
13263
- attributes: TextAttributes18.DIM,
14056
+ attributes: TextAttributes21.DIM,
13264
14057
  children: inputActive ? "Enter confirm | Esc cancel" : footerText
13265
14058
  }, undefined, false, undefined, this)
13266
14059
  }, undefined, false, undefined, this)
@@ -13271,16 +14064,17 @@ var init_session_list_screen = __esm(() => {
13271
14064
  init_src2();
13272
14065
  init_client();
13273
14066
  init_keymap();
14067
+ init_export_session_markdown();
13274
14068
  init_chat_text_area();
13275
14069
  init_cli_theme();
13276
14070
  init_text_utils();
13277
14071
  });
13278
14072
 
13279
14073
  // apps/cli/src/hooks/use-config-badge.ts
13280
- import { useEffect as useEffect13, useState as useState17 } from "react";
14074
+ import { useEffect as useEffect14, useState as useState19 } from "react";
13281
14075
  function useConfigBadge(refreshNonce = 0) {
13282
- const [state, setState] = useState17({ status: "loading" });
13283
- useEffect13(() => {
14076
+ const [state, setState] = useState19({ status: "loading" });
14077
+ useEffect14(() => {
13284
14078
  let cancelled = false;
13285
14079
  async function loadConfig() {
13286
14080
  try {
@@ -13326,11 +14120,11 @@ var exports_app = {};
13326
14120
  __export(exports_app, {
13327
14121
  App: () => App
13328
14122
  });
13329
- import { TextAttributes as TextAttributes19 } from "@opentui/core";
13330
- import { useKeyboard as useKeyboard10, useRenderer } from "@opentui/react";
13331
- import { useEffect as useEffect14, useState as useState18 } from "react";
13332
- import { MemoryRouter, Navigate, Route, Routes, useLocation as useLocation3, useNavigate as useNavigate5 } from "react-router";
13333
- import { jsxDEV as jsxDEV29 } from "@opentui/react/jsx-dev-runtime";
14123
+ import { TextAttributes as TextAttributes22 } from "@opentui/core";
14124
+ import { useKeyboard as useKeyboard11, useRenderer as useRenderer2 } from "@opentui/react";
14125
+ import { useEffect as useEffect15, useRef as useRef5, useState as useState20 } from "react";
14126
+ import { MemoryRouter, Navigate, Route, Routes, useLocation as useLocation3, useNavigate as useNavigate6 } from "react-router";
14127
+ import { jsxDEV as jsxDEV32 } from "@opentui/react/jsx-dev-runtime";
13334
14128
  function getCurrentViewLabel(pathname) {
13335
14129
  if (pathname === "/" || pathname === "/home") {
13336
14130
  return "home";
@@ -13356,14 +14150,17 @@ function getCurrentViewLabel(pathname) {
13356
14150
  if (pathname === "/config") {
13357
14151
  return "config";
13358
14152
  }
13359
- if (pathname === "/model") {
14153
+ if (pathname === "/model-info") {
13360
14154
  return "model";
13361
14155
  }
14156
+ if (pathname === "/model") {
14157
+ return "model-select";
14158
+ }
13362
14159
  return pathname;
13363
14160
  }
13364
14161
  function AppContent() {
13365
- const renderer = useRenderer();
13366
- const navigate = useNavigate5();
14162
+ const renderer = useRenderer2();
14163
+ const navigate = useNavigate6();
13367
14164
  const location = useLocation3();
13368
14165
  const {
13369
14166
  paletteOpen,
@@ -13383,7 +14180,45 @@ function AppContent() {
13383
14180
  configRefreshNonce,
13384
14181
  toggleToolOutputExpansion
13385
14182
  } = useAppState();
13386
- const [helpOpen, setHelpOpen] = useState18(false);
14183
+ const [helpOpen, setHelpOpen] = useState20(false);
14184
+ const [ctrlCNotice, setCtrlCNotice] = useState20(null);
14185
+ const ctrlCArmedRef = useRef5(false);
14186
+ const ctrlCTimerRef = useRef5(null);
14187
+ const showCtrlCNotice = (text) => {
14188
+ setCtrlCNotice(text);
14189
+ if (ctrlCTimerRef.current) {
14190
+ clearTimeout(ctrlCTimerRef.current);
14191
+ }
14192
+ ctrlCTimerRef.current = setTimeout(() => {
14193
+ setCtrlCNotice(null);
14194
+ ctrlCArmedRef.current = false;
14195
+ ctrlCTimerRef.current = null;
14196
+ }, 2000);
14197
+ };
14198
+ const handleCtrlC = () => {
14199
+ const selection = renderer.getSelection();
14200
+ const selectedText = selection ? selection.getSelectedText() : "";
14201
+ if (selectedText.trim().length > 0) {
14202
+ copyText(renderer, selectedText);
14203
+ renderer.clearSelection();
14204
+ ctrlCArmedRef.current = false;
14205
+ showCtrlCNotice(`Copied ${selectedText.length} chars to clipboard`);
14206
+ return;
14207
+ }
14208
+ if (ctrlCArmedRef.current) {
14209
+ renderer.destroy();
14210
+ return;
14211
+ }
14212
+ ctrlCArmedRef.current = true;
14213
+ showCtrlCNotice("Press Ctrl+C again to exit");
14214
+ };
14215
+ useEffect15(() => {
14216
+ return () => {
14217
+ if (ctrlCTimerRef.current) {
14218
+ clearTimeout(ctrlCTimerRef.current);
14219
+ }
14220
+ };
14221
+ }, []);
13387
14222
  const canGoBack = location.pathname !== "/" && location.pathname !== "/home";
13388
14223
  const currentView = getCurrentViewLabel(location.pathname);
13389
14224
  const inChatSession = location.pathname.startsWith("/sessions/");
@@ -13494,7 +14329,11 @@ function AppContent() {
13494
14329
  closeSlashMenu();
13495
14330
  }
13496
14331
  };
13497
- useKeyboard10((keyEvent) => {
14332
+ useKeyboard11((keyEvent) => {
14333
+ if (keyEvent.ctrl && keyEvent.name === "c") {
14334
+ handleCtrlC();
14335
+ return;
14336
+ }
13498
14337
  if (slashMenuOpen) {
13499
14338
  handleSlashMenuKeyDown(keyEvent);
13500
14339
  return;
@@ -13504,10 +14343,6 @@ function AppContent() {
13504
14343
  return;
13505
14344
  }
13506
14345
  const normalizedKey = normalizeKeyName(keyEvent.name, keyEvent.ctrl, false, false);
13507
- if (keyEvent.ctrl && keyEvent.name === "c") {
13508
- handleAction("system:quit");
13509
- return;
13510
- }
13511
14346
  if (isEscapeKey2(keyEvent)) {
13512
14347
  handleAction("system:cancel");
13513
14348
  return;
@@ -13524,7 +14359,7 @@ function AppContent() {
13524
14359
  });
13525
14360
  const configBadge = useConfigBadge(configRefreshNonce);
13526
14361
  const needsOnboarding = configBadge.status === "available" && configBadge.missingCredentialHints.length > 0;
13527
- useEffect14(() => {
14362
+ useEffect15(() => {
13528
14363
  if (needsOnboarding && location.pathname !== "/onboarding") {
13529
14364
  navigate("/onboarding");
13530
14365
  }
@@ -13536,12 +14371,12 @@ function AppContent() {
13536
14371
  const backHint = canGoBack ? ` | Esc/${BACK_SHORTCUT_LABEL} Back` : "";
13537
14372
  return "/ Pages | Ctrl+P Cmd | F1 Help | Ctrl+C Quit" + backHint;
13538
14373
  };
13539
- return /* @__PURE__ */ jsxDEV29("box", {
14374
+ return /* @__PURE__ */ jsxDEV32("box", {
13540
14375
  flexDirection: "column",
13541
14376
  flexGrow: 1,
13542
14377
  backgroundColor: cliTheme.surfaces.base,
13543
14378
  children: [
13544
- /* @__PURE__ */ jsxDEV29("box", {
14379
+ /* @__PURE__ */ jsxDEV32("box", {
13545
14380
  flexDirection: "row",
13546
14381
  justifyContent: "space-between",
13547
14382
  paddingLeft: 1,
@@ -13550,17 +14385,17 @@ function AppContent() {
13550
14385
  border: ["bottom"],
13551
14386
  borderColor: cliTheme.borders.default,
13552
14387
  children: [
13553
- /* @__PURE__ */ jsxDEV29("box", {
14388
+ /* @__PURE__ */ jsxDEV32("box", {
13554
14389
  flexDirection: "row",
13555
14390
  gap: 2,
13556
14391
  alignItems: "center",
13557
14392
  children: [
13558
- /* @__PURE__ */ jsxDEV29("text", {
14393
+ /* @__PURE__ */ jsxDEV32("text", {
13559
14394
  fg: cliTheme.text.primary,
13560
- attributes: TextAttributes19.BOLD,
14395
+ attributes: TextAttributes22.BOLD,
13561
14396
  children: "Lightcode"
13562
14397
  }, undefined, false, undefined, this),
13563
- configBadge.status === "available" ? /* @__PURE__ */ jsxDEV29("text", {
14398
+ configBadge.status === "available" ? /* @__PURE__ */ jsxDEV32("text", {
13564
14399
  fg: cliTheme.text.secondary,
13565
14400
  children: [
13566
14401
  "| ",
@@ -13568,122 +14403,126 @@ function AppContent() {
13568
14403
  " | ",
13569
14404
  configBadge.model
13570
14405
  ]
13571
- }, undefined, true, undefined, this) : configBadge.status === "loading" ? /* @__PURE__ */ jsxDEV29("text", {
14406
+ }, undefined, true, undefined, this) : configBadge.status === "loading" ? /* @__PURE__ */ jsxDEV32("text", {
13572
14407
  fg: cliTheme.text.muted,
13573
14408
  children: "| loading..."
13574
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV29("text", {
14409
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV32("text", {
13575
14410
  fg: cliTheme.text.muted,
13576
14411
  children: "| provider unavailable"
13577
14412
  }, undefined, false, undefined, this)
13578
14413
  ]
13579
14414
  }, undefined, true, undefined, this),
13580
- /* @__PURE__ */ jsxDEV29("text", {
14415
+ /* @__PURE__ */ jsxDEV32("text", {
13581
14416
  fg: cliTheme.text.muted,
13582
14417
  children: currentView
13583
14418
  }, undefined, false, undefined, this)
13584
14419
  ]
13585
14420
  }, undefined, true, undefined, this),
13586
- /* @__PURE__ */ jsxDEV29("box", {
14421
+ /* @__PURE__ */ jsxDEV32("box", {
13587
14422
  flexGrow: 1,
13588
14423
  padding: 1,
13589
14424
  position: "relative",
13590
14425
  children: [
13591
- /* @__PURE__ */ jsxDEV29(Routes, {
14426
+ /* @__PURE__ */ jsxDEV32(Routes, {
13592
14427
  children: [
13593
- /* @__PURE__ */ jsxDEV29(Route, {
14428
+ /* @__PURE__ */ jsxDEV32(Route, {
13594
14429
  path: "/",
13595
- element: /* @__PURE__ */ jsxDEV29(HomeScreen, {}, undefined, false, undefined, this)
14430
+ element: /* @__PURE__ */ jsxDEV32(HomeScreen, {}, undefined, false, undefined, this)
13596
14431
  }, undefined, false, undefined, this),
13597
- /* @__PURE__ */ jsxDEV29(Route, {
14432
+ /* @__PURE__ */ jsxDEV32(Route, {
13598
14433
  path: "/home",
13599
- element: /* @__PURE__ */ jsxDEV29(Navigate, {
14434
+ element: /* @__PURE__ */ jsxDEV32(Navigate, {
13600
14435
  to: "/",
13601
14436
  replace: true
13602
14437
  }, undefined, false, undefined, this)
13603
14438
  }, undefined, false, undefined, this),
13604
- /* @__PURE__ */ jsxDEV29(Route, {
14439
+ /* @__PURE__ */ jsxDEV32(Route, {
13605
14440
  path: "/status",
13606
- element: /* @__PURE__ */ jsxDEV29(DiagnosticsScreen, {
14441
+ element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
13607
14442
  kind: "status"
13608
14443
  }, undefined, false, undefined, this)
13609
14444
  }, undefined, false, undefined, this),
13610
- /* @__PURE__ */ jsxDEV29(Route, {
14445
+ /* @__PURE__ */ jsxDEV32(Route, {
13611
14446
  path: "/doctor",
13612
- element: /* @__PURE__ */ jsxDEV29(DiagnosticsScreen, {
14447
+ element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
13613
14448
  kind: "doctor"
13614
14449
  }, undefined, false, undefined, this)
13615
14450
  }, undefined, false, undefined, this),
13616
- /* @__PURE__ */ jsxDEV29(Route, {
14451
+ /* @__PURE__ */ jsxDEV32(Route, {
13617
14452
  path: "/permissions",
13618
- element: /* @__PURE__ */ jsxDEV29(DiagnosticsScreen, {
14453
+ element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
13619
14454
  kind: "permissions"
13620
14455
  }, undefined, false, undefined, this)
13621
14456
  }, undefined, false, undefined, this),
13622
- /* @__PURE__ */ jsxDEV29(Route, {
14457
+ /* @__PURE__ */ jsxDEV32(Route, {
13623
14458
  path: "/sessions",
13624
- element: /* @__PURE__ */ jsxDEV29(SessionListScreen, {}, undefined, false, undefined, this)
14459
+ element: /* @__PURE__ */ jsxDEV32(SessionListScreen, {}, undefined, false, undefined, this)
13625
14460
  }, undefined, false, undefined, this),
13626
- /* @__PURE__ */ jsxDEV29(Route, {
14461
+ /* @__PURE__ */ jsxDEV32(Route, {
13627
14462
  path: "/sessions/:id",
13628
- element: /* @__PURE__ */ jsxDEV29(ChatScreen, {}, undefined, false, undefined, this)
14463
+ element: /* @__PURE__ */ jsxDEV32(ChatScreen, {}, undefined, false, undefined, this)
13629
14464
  }, undefined, false, undefined, this),
13630
- /* @__PURE__ */ jsxDEV29(Route, {
14465
+ /* @__PURE__ */ jsxDEV32(Route, {
13631
14466
  path: "/tools",
13632
- element: /* @__PURE__ */ jsxDEV29(DiagnosticsScreen, {
14467
+ element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
13633
14468
  kind: "tools"
13634
14469
  }, undefined, false, undefined, this)
13635
14470
  }, undefined, false, undefined, this),
13636
- /* @__PURE__ */ jsxDEV29(Route, {
14471
+ /* @__PURE__ */ jsxDEV32(Route, {
13637
14472
  path: "/config",
13638
- element: /* @__PURE__ */ jsxDEV29(DiagnosticsScreen, {
14473
+ element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
13639
14474
  kind: "config"
13640
14475
  }, undefined, false, undefined, this)
13641
14476
  }, undefined, false, undefined, this),
13642
- /* @__PURE__ */ jsxDEV29(Route, {
14477
+ /* @__PURE__ */ jsxDEV32(Route, {
14478
+ path: "/model-info",
14479
+ element: /* @__PURE__ */ jsxDEV32(ModelScreen, {}, undefined, false, undefined, this)
14480
+ }, undefined, false, undefined, this),
14481
+ /* @__PURE__ */ jsxDEV32(Route, {
13643
14482
  path: "/model",
13644
- element: /* @__PURE__ */ jsxDEV29(ModelScreen, {}, undefined, false, undefined, this)
14483
+ element: /* @__PURE__ */ jsxDEV32(ModelSelectScreen, {}, undefined, false, undefined, this)
13645
14484
  }, undefined, false, undefined, this),
13646
- /* @__PURE__ */ jsxDEV29(Route, {
14485
+ /* @__PURE__ */ jsxDEV32(Route, {
13647
14486
  path: "/onboarding",
13648
- element: /* @__PURE__ */ jsxDEV29(OnboardingScreen, {}, undefined, false, undefined, this)
14487
+ element: /* @__PURE__ */ jsxDEV32(OnboardingScreen, {}, undefined, false, undefined, this)
13649
14488
  }, undefined, false, undefined, this),
13650
- /* @__PURE__ */ jsxDEV29(Route, {
14489
+ /* @__PURE__ */ jsxDEV32(Route, {
13651
14490
  path: "*",
13652
- element: /* @__PURE__ */ jsxDEV29(Navigate, {
14491
+ element: /* @__PURE__ */ jsxDEV32(Navigate, {
13653
14492
  to: "/",
13654
14493
  replace: true
13655
14494
  }, undefined, false, undefined, this)
13656
14495
  }, undefined, false, undefined, this)
13657
14496
  ]
13658
14497
  }, undefined, true, undefined, this),
13659
- paletteOpen ? /* @__PURE__ */ jsxDEV29(CommandPalette, {
14498
+ paletteOpen ? /* @__PURE__ */ jsxDEV32(CommandPalette, {
13660
14499
  query: paletteQuery,
13661
14500
  setQuery: setPaletteQuery,
13662
14501
  selectedIndex: paletteSelected
13663
14502
  }, undefined, false, undefined, this) : null,
13664
- slashMenuOpen && !inputHostsSlashMenu ? /* @__PURE__ */ jsxDEV29("box", {
14503
+ slashMenuOpen && !inputHostsSlashMenu ? /* @__PURE__ */ jsxDEV32("box", {
13665
14504
  position: "absolute",
13666
14505
  top: 1,
13667
14506
  left: 2,
13668
14507
  right: 2,
13669
14508
  zIndex: 20,
13670
- children: /* @__PURE__ */ jsxDEV29(SlashPageMenu, {
14509
+ children: /* @__PURE__ */ jsxDEV32(SlashPageMenu, {
13671
14510
  query: slashMenuQuery,
13672
14511
  selectedIndex: selectedSlashRouteIndex,
13673
14512
  routes: filteredSlashRoutes
13674
14513
  }, undefined, false, undefined, this)
13675
14514
  }, undefined, false, undefined, this) : null,
13676
- helpOpen ? /* @__PURE__ */ jsxDEV29("box", {
14515
+ helpOpen ? /* @__PURE__ */ jsxDEV32("box", {
13677
14516
  position: "absolute",
13678
14517
  top: 1,
13679
14518
  left: 4,
13680
14519
  right: 4,
13681
14520
  zIndex: 30,
13682
- children: /* @__PURE__ */ jsxDEV29(HelpOverlay, {}, undefined, false, undefined, this)
14521
+ children: /* @__PURE__ */ jsxDEV32(HelpOverlay, {}, undefined, false, undefined, this)
13683
14522
  }, undefined, false, undefined, this) : null
13684
14523
  ]
13685
14524
  }, undefined, true, undefined, this),
13686
- /* @__PURE__ */ jsxDEV29("box", {
14525
+ /* @__PURE__ */ jsxDEV32("box", {
13687
14526
  flexDirection: "row",
13688
14527
  justifyContent: "center",
13689
14528
  gap: 2,
@@ -13692,19 +14531,19 @@ function AppContent() {
13692
14531
  borderStyle: "single",
13693
14532
  border: ["top"],
13694
14533
  borderColor: cliTheme.borders.default,
13695
- children: /* @__PURE__ */ jsxDEV29("text", {
13696
- fg: cliTheme.text.muted,
13697
- children: getFooterStatus()
14534
+ children: /* @__PURE__ */ jsxDEV32("text", {
14535
+ fg: ctrlCNotice ? cliTheme.semantic.warning : cliTheme.text.muted,
14536
+ children: ctrlCNotice ?? getFooterStatus()
13698
14537
  }, undefined, false, undefined, this)
13699
14538
  }, undefined, false, undefined, this)
13700
14539
  ]
13701
14540
  }, undefined, true, undefined, this);
13702
14541
  }
13703
14542
  function App() {
13704
- return /* @__PURE__ */ jsxDEV29(MemoryRouter, {
14543
+ return /* @__PURE__ */ jsxDEV32(MemoryRouter, {
13705
14544
  initialEntries: ["/"],
13706
- children: /* @__PURE__ */ jsxDEV29(AppStateProvider, {
13707
- children: /* @__PURE__ */ jsxDEV29(AppContent, {}, undefined, false, undefined, this)
14545
+ children: /* @__PURE__ */ jsxDEV32(AppStateProvider, {
14546
+ children: /* @__PURE__ */ jsxDEV32(AppContent, {}, undefined, false, undefined, this)
13708
14547
  }, undefined, false, undefined, this)
13709
14548
  }, undefined, false, undefined, this);
13710
14549
  }
@@ -13720,16 +14559,18 @@ var init_app = __esm(() => {
13720
14559
  init_diagnostics_screen();
13721
14560
  init_home_screen();
13722
14561
  init_model_screen();
14562
+ init_model_select_screen();
13723
14563
  init_onboarding_screen();
13724
14564
  init_session_list_screen();
13725
14565
  init_app_state();
13726
14566
  init_use_config_badge();
13727
14567
  init_cli_theme();
14568
+ init_clipboard();
13728
14569
  });
13729
14570
 
13730
14571
  // apps/cli/src/index.tsx
13731
14572
  init_src();
13732
- import { jsxDEV as jsxDEV30 } from "@opentui/react/jsx-dev-runtime";
14573
+ import { jsxDEV as jsxDEV33 } from "@opentui/react/jsx-dev-runtime";
13733
14574
  var cliArgs = Bun.argv.slice(2);
13734
14575
  if (cliArgs.includes("--version") || cliArgs.includes("-v")) {
13735
14576
  console.log(`${productName} v${lightcodeVersion}`);
@@ -13737,11 +14578,17 @@ if (cliArgs.includes("--version") || cliArgs.includes("-v")) {
13737
14578
  }
13738
14579
  var { ensureServerRunning: ensureServerRunning2 } = await Promise.resolve().then(() => (init_server_launcher(), exports_server_launcher));
13739
14580
  var serverLaunch = await ensureServerRunning2();
14581
+ if (serverLaunch.error) {
14582
+ console.error(`
14583
+ ${serverLaunch.error}
14584
+ `);
14585
+ process.exit(1);
14586
+ }
13740
14587
  var { createCliRenderer } = await import("@opentui/core");
13741
14588
  var { createRoot } = await import("@opentui/react");
13742
14589
  var { App: App2 } = await Promise.resolve().then(() => (init_app(), exports_app));
13743
14590
  var renderer = await createCliRenderer({
13744
- exitOnCtrlC: true
14591
+ exitOnCtrlC: false
13745
14592
  });
13746
14593
  if (serverLaunch.ownedProcess) {
13747
14594
  const ownedProcess = serverLaunch.ownedProcess;
@@ -13751,4 +14598,4 @@ if (serverLaunch.ownedProcess) {
13751
14598
  } catch {}
13752
14599
  });
13753
14600
  }
13754
- createRoot(renderer).render(/* @__PURE__ */ jsxDEV30(App2, {}, undefined, false, undefined, this));
14601
+ createRoot(renderer).render(/* @__PURE__ */ jsxDEV33(App2, {}, undefined, false, undefined, this));