@kmugalkhod/lightcode 0.4.0 → 0.6.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 (5) hide show
  1. package/README.md +20 -1
  2. package/cli.js +1575 -935
  3. package/lightcode.cjs +63 -18
  4. package/package.json +2 -1
  5. package/server.js +695 -238
package/cli.js CHANGED
@@ -124,7 +124,7 @@ var init_logger = __esm(() => {
124
124
  });
125
125
 
126
126
  // packages/shared/src/version.ts
127
- var lightcodeVersion = "0.4.0";
127
+ var lightcodeVersion = "0.6.0";
128
128
 
129
129
  // packages/shared/src/index.ts
130
130
  var productName = "Lightcode";
@@ -226,7 +226,8 @@ async function ensureServerRunning() {
226
226
  stderr: logFile,
227
227
  env: {
228
228
  ...process.env,
229
- PORT: port
229
+ PORT: port,
230
+ LIGHTCODE_PARENT_PID: String(process.pid)
230
231
  }
231
232
  });
232
233
  for (let attempt = 0;attempt < startupPollAttempts; attempt += 1) {
@@ -279,7 +280,8 @@ var init_keymap = __esm(() => {
279
280
  "ctrl+q": { sequence: "ctrl+q", action: "system:quit", label: "Quit", category: "system" },
280
281
  f1: { sequence: "f1", action: "system:help", label: "Help", category: "system" },
281
282
  "ctrl+/": { sequence: "ctrl+/", action: "system:help", label: "Help", category: "system" },
282
- "ctrl+o": { sequence: "ctrl+o", action: "system:toggleToolOutput", label: "Expand Tool Output", category: "action" }
283
+ "ctrl+o": { sequence: "ctrl+o", action: "system:toggleToolOutput", label: "Expand Tool Output", category: "action" },
284
+ "ctrl+r": { sequence: "ctrl+r", action: "system:toggleReasoning", label: "Show Reasoning", category: "action" }
283
285
  }
284
286
  };
285
287
  });
@@ -319,7 +321,7 @@ function getOverlayRowColors(selected) {
319
321
  secondaryTextColor: cliTheme.overlay.description
320
322
  };
321
323
  }
322
- var toolStateToneMap, cliTheme, serverStatusColors;
324
+ var toolStateToneMap, cliTheme, space, borderStyleFor, glyphs, serverStatusColors;
323
325
  var init_cli_theme = __esm(() => {
324
326
  toolStateToneMap = {
325
327
  "input-streaming": "muted",
@@ -340,7 +342,7 @@ var init_cli_theme = __esm(() => {
340
342
  borders: {
341
343
  default: "#273241",
342
344
  subtle: "#202A36",
343
- active: "#80D0C7"
345
+ active: "#F2A65A"
344
346
  },
345
347
  text: {
346
348
  primary: "#EEF3F8",
@@ -348,57 +350,66 @@ var init_cli_theme = __esm(() => {
348
350
  muted: "#7A8796"
349
351
  },
350
352
  accent: {
351
- primary: "#80D0C7",
352
- softBackground: "#17333A",
353
- softText: "#BCEDE8"
353
+ primary: "#F2A65A",
354
+ softBackground: "#33260F",
355
+ softText: "#F6C99A"
354
356
  },
355
357
  semantic: {
356
358
  success: "#8BD49C",
357
- warning: "#F1C27D",
359
+ warning: "#E5C07B",
358
360
  error: "#F28B82",
359
361
  info: "#9CBDFD"
360
362
  },
363
+ diff: {
364
+ added: "#8BD49C",
365
+ addedBg: "#13271B",
366
+ removed: "#F28B82",
367
+ removedBg: "#2B1619"
368
+ },
361
369
  messageRoles: {
362
370
  user: {
363
371
  labelColor: "#9CBDFD",
364
372
  borderColor: "#22314A",
365
- backgroundColor: "#111A27"
373
+ backgroundColor: "#111A27",
374
+ glyph: "\u25B8"
366
375
  },
367
376
  assistant: {
368
377
  labelColor: "#8BD49C",
369
378
  borderColor: "#203A2A",
370
- backgroundColor: "#111C18"
379
+ backgroundColor: "#111C18",
380
+ glyph: "\u25C6"
371
381
  },
372
382
  system: {
373
383
  labelColor: "#C3CDD9",
374
384
  borderColor: "#273241",
375
- backgroundColor: "#131820"
385
+ backgroundColor: "#131820",
386
+ glyph: "\u25A0"
376
387
  }
377
388
  },
378
389
  overlay: {
379
390
  surface: "#121820",
380
391
  border: "#273241",
381
- title: "#80D0C7",
382
- selectedRowBackground: "#17333A",
392
+ title: "#F2A65A",
393
+ selectedRowBackground: "#33260F",
383
394
  selectedRowText: "#EEF3F8",
384
395
  inputSurface: "#10151C",
385
396
  inputText: "#EEF3F8",
386
397
  mutedText: "#7A8796",
387
398
  footerText: "#B6C2CF",
388
- selectedBorder: "#80D0C7",
399
+ selectedBorder: "#F2A65A",
389
400
  badgeBackground: "#18212B",
390
401
  badgeText: "#B6C2CF",
391
402
  description: "#9AA8B7",
392
403
  sectionDivider: "#202A36",
393
404
  shortcutHint: "#9AA8B7",
394
405
  headerMuted: "#9AA8B7",
395
- countBadge: "#17333A",
406
+ countBadge: "#33260F",
396
407
  hoverBackground: "#18212B"
397
408
  },
398
409
  input: {
399
410
  container: "#121820",
400
411
  field: "#10151C",
401
- focusedBorder: "#80D0C7",
412
+ focusedBorder: "#F2A65A",
402
413
  blurredBorder: "#273241",
403
414
  placeholder: "#7A8796",
404
415
  text: "#EEF3F8",
@@ -408,12 +419,56 @@ var init_cli_theme = __esm(() => {
408
419
  scroll: {
409
420
  rail: "#0D1117",
410
421
  thumb: "#273241",
411
- thumbActive: "#80D0C7"
422
+ thumbActive: "#F2A65A"
412
423
  },
413
424
  markdown: {
414
425
  tableBorder: "#273241"
415
426
  }
416
427
  };
428
+ space = {
429
+ none: 0,
430
+ xs: 1,
431
+ sm: 1,
432
+ md: 2,
433
+ lg: 3
434
+ };
435
+ borderStyleFor = {
436
+ card: "rounded",
437
+ modal: "heavy",
438
+ chrome: "single"
439
+ };
440
+ glyphs = {
441
+ unicode: {
442
+ roleUser: "\u25B8",
443
+ roleAssistant: "\u25C6",
444
+ roleSystem: "\u25A0",
445
+ toolRunning: "\u25B8",
446
+ toolOk: "\u2713",
447
+ toolError: "\u2717",
448
+ toolDenied: "\u2298",
449
+ toolApproval: "\u25CF",
450
+ statusDot: "\u25CF",
451
+ indent: "\u23BF",
452
+ back: "\u25C0",
453
+ breadcrumb: "\u203A",
454
+ bullet: "\xB7"
455
+ },
456
+ ascii: {
457
+ roleUser: ">",
458
+ roleAssistant: "*",
459
+ roleSystem: "#",
460
+ toolRunning: ">",
461
+ toolOk: "+",
462
+ toolError: "x",
463
+ toolDenied: "/",
464
+ toolApproval: "o",
465
+ statusDot: "o",
466
+ indent: ">",
467
+ back: "<",
468
+ breadcrumb: ">",
469
+ bullet: "-"
470
+ }
471
+ };
417
472
  serverStatusColors = {
418
473
  checking: cliTheme.text.secondary,
419
474
  online: cliTheme.semantic.success,
@@ -433,7 +488,7 @@ function HelpOverlay() {
433
488
  return /* @__PURE__ */ jsxDEV("box", {
434
489
  width: "100%",
435
490
  flexDirection: "column",
436
- borderStyle: "single",
491
+ borderStyle: borderStyleFor.modal,
437
492
  borderColor: cliTheme.accent.primary,
438
493
  backgroundColor: cliTheme.overlay.surface,
439
494
  paddingX: 2,
@@ -493,7 +548,7 @@ var init_help_overlay = __esm(() => {
493
548
  init_cli_theme();
494
549
  screenHints = [
495
550
  { context: "Chat", hint: "Tab/Ctrl+T switch mode | @ attach files | /compact, /undo actions" },
496
- { context: "Chat", hint: "Ctrl+O expand tool output | Enter send | Ctrl+Enter newline" },
551
+ { context: "Chat", hint: "Ctrl+O expand tool output | Ctrl+R show reasoning | Enter send | Ctrl+Enter newline" },
497
552
  { context: "Copy", hint: "Ctrl+Y copy mode (\u2191/\u2193 pick, Enter copy, c code) | /copy [last|code|all]" },
498
553
  { context: "Sessions", hint: "Enter resume | r rename | f fork | / filter | e export (.md) | d delete" }
499
554
  ];
@@ -637,7 +692,7 @@ function CommandPalette({ query, setQuery, selectedIndex }) {
637
692
  minHeight: 20,
638
693
  flexDirection: "column",
639
694
  backgroundColor: cliTheme.overlay.surface,
640
- borderStyle: "single",
695
+ borderStyle: borderStyleFor.modal,
641
696
  border: ["top", "bottom", "left", "right"],
642
697
  borderColor: cliTheme.overlay.border,
643
698
  children: [
@@ -874,7 +929,7 @@ function SlashPageMenu({
874
929
  width: "100%",
875
930
  flexDirection: "column",
876
931
  backgroundColor: cliTheme.overlay.surface,
877
- borderStyle: "single",
932
+ borderStyle: borderStyleFor.modal,
878
933
  borderColor: cliTheme.overlay.border,
879
934
  children: [
880
935
  /* @__PURE__ */ jsxDEV3("box", {
@@ -1617,9 +1672,10 @@ var init_schema = __esm(() => {
1617
1672
 
1618
1673
  // packages/ai/src/edit-file/schema.ts
1619
1674
  import { z as z6 } from "zod";
1620
- var editFileDescription = "Edit a file via literal search and replace. Use this for focused, small edits.", editFileInputSchema, editFileProviderInputSchema, editFileOutputSchema;
1675
+ var editFileDescription, editFileInputSchema, editFileProviderInputSchema, editFileOutputSchema;
1621
1676
  var init_schema2 = __esm(() => {
1622
1677
  init_base_schemas();
1678
+ editFileDescription = "Edit a file via search and replace. Use this for focused, small edits. " + "Line endings (CRLF/LF) and surrounding whitespace are matched tolerantly, so " + "copy the lines as shown by read_file. The search text must match exactly one " + "place \u2014 include enough surrounding context to make it unique, or set " + "replaceAll: true to change every occurrence.";
1623
1679
  editFileInputSchema = z6.object({
1624
1680
  path: boundedPathSchema,
1625
1681
  search: z6.string().min(1),
@@ -2466,7 +2522,8 @@ var init_agent_tools = __esm(() => {
2466
2522
  permissionMode: permissionModeSchema.optional(),
2467
2523
  allowedTools: z21.array(codingAgentToolNameSchema).optional(),
2468
2524
  permissionRules: permissionRulesSchema.optional(),
2469
- sandbox: sandboxConfigSchema.optional()
2525
+ sandbox: sandboxConfigSchema.optional(),
2526
+ environmentContext: z21.string().optional()
2470
2527
  });
2471
2528
  });
2472
2529
 
@@ -2491,7 +2548,8 @@ function parseChatStreamError(message) {
2491
2548
  kind: parsed.kind,
2492
2549
  statusCode: typeof parsed.statusCode === "number" ? parsed.statusCode : undefined,
2493
2550
  retryable: parsed.retryable,
2494
- message: parsed.message
2551
+ message: parsed.message,
2552
+ contextLimitTokens: typeof parsed.contextLimitTokens === "number" ? parsed.contextLimitTokens : undefined
2495
2553
  };
2496
2554
  } catch {
2497
2555
  return null;
@@ -2501,6 +2559,15 @@ function describeChatStreamError(info) {
2501
2559
  const status = info.statusCode ? ` (HTTP ${info.statusCode})` : "";
2502
2560
  return `${chatErrorKindLabels[info.kind]}${status}: ${info.message}`;
2503
2561
  }
2562
+ function retryReasonLabel(reason) {
2563
+ if (reason === "stall") {
2564
+ return "Stalled";
2565
+ }
2566
+ if (reason === undefined || reason === "network") {
2567
+ return "Connection dropped";
2568
+ }
2569
+ return chatErrorKindLabels[reason];
2570
+ }
2504
2571
  var chatStreamErrorPrefix = "LIGHTCODE_ERROR:", maxTransportMessageLength = 500, chatErrorKinds, chatErrorKindLabels;
2505
2572
  var init_chat_error = __esm(() => {
2506
2573
  chatErrorKinds = new Set([
@@ -2841,7 +2908,8 @@ function buildCodingAgentSystemPrompt({
2841
2908
  cwd,
2842
2909
  override,
2843
2910
  mode = defaultCodingAgentMode,
2844
- includeToolDiscipline = false
2911
+ includeToolDiscipline = false,
2912
+ environmentContext
2845
2913
  }) {
2846
2914
  const promptTemplate = override?.trim() ? override : defaultCodingAgentSystemPrompt;
2847
2915
  const normalizedPrompt = normalizePromptTemplate(promptTemplate, cwd);
@@ -2852,12 +2920,14 @@ function buildCodingAgentSystemPrompt({
2852
2920
  ` + `Mode purpose: ${modeDefinition.purpose}
2853
2921
  ` + `${modeDefinition.instructions}` + (includeToolDiscipline ? `
2854
2922
 
2855
- ${toolCallingDisciplineAddendum}` : "");
2923
+ ${toolCallingDisciplineAddendum}` : "") + (environmentContext?.trim() ? `
2924
+
2925
+ ${environmentContext.trim()}` : "");
2856
2926
  }
2857
2927
  var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
2858
2928
  var init_coding_agent_prompt = __esm(() => {
2859
2929
  init_coding_agent_modes();
2860
- defaultCodingAgentSystemPrompt = "You are a coding agent. Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under this working directory: {cwd}. " + "Prefer dedicated tools \u2014 glob_search for paths, grep for text search, structured git tools for repository inspection \u2014 and bash only when those are not enough. " + `Use todo_write to keep multi-step work visible and current, inspect context before risky operations, and emit a brief, natural progress note before major tool actions and after important findings.
2930
+ defaultCodingAgentSystemPrompt = "You are a coding agent working inside the user's project at this working directory: {cwd}. " + 'You can already see the project \u2014 proactively read it. When the user asks you to review, explain, or work on "my code"/"this", inspect the working directory yourself with list_files, glob_search, grep, and read_file; NEVER ask the user to paste or share code that lives in this directory \u2014 open it. ' + "Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under the working directory. " + "Prefer dedicated tools \u2014 glob_search for paths, grep for text search, structured git tools for repository inspection \u2014 and bash only when those are not enough. " + `Use todo_write to keep multi-step work visible and current, inspect context before risky operations, and emit a brief, natural progress note before major tool actions and after important findings.
2861
2931
 
2862
2932
  ` + `You are an agent \u2014 keep working until the request is fully resolved. Never stop with a partial answer; if you say you will do something, do it in the same turn by calling a tool. Do not finish while any todo is pending or in_progress \u2014 end your turn only when the task is complete or you are blocked on input that only the user can provide.
2863
2933
 
@@ -3179,78 +3249,7 @@ function selectCodingAgentIntentTools({
3179
3249
  if (!normalizedText || casualPromptPattern.test(normalizedText)) {
3180
3250
  return [];
3181
3251
  }
3182
- const selectedTools = [];
3183
- const hasCodeIntent = includesAny(normalizedText, [
3184
- "file",
3185
- "folder",
3186
- "code",
3187
- "repo",
3188
- "project",
3189
- "implement",
3190
- "fix",
3191
- "add",
3192
- "build",
3193
- "refactor",
3194
- "change",
3195
- "create",
3196
- "update",
3197
- "edit",
3198
- "delete",
3199
- "run",
3200
- "test",
3201
- "typecheck",
3202
- "debug",
3203
- "error",
3204
- "issue",
3205
- "problem",
3206
- "fails",
3207
- "failed",
3208
- "failing",
3209
- "bug",
3210
- "crash",
3211
- "slow",
3212
- "slowness",
3213
- "not working",
3214
- "epic",
3215
- "ticket"
3216
- ]);
3217
- const hasReadIntent = includesAny(normalizedText, [
3218
- "read",
3219
- "list",
3220
- "find",
3221
- "search",
3222
- "grep",
3223
- "glob",
3224
- "inspect",
3225
- "analyse",
3226
- "analyze",
3227
- "check"
3228
- ]);
3229
- const hasWriteIntent = mode === "build" && includesAny(normalizedText, [
3230
- "write",
3231
- "edit",
3232
- "change",
3233
- "create",
3234
- "implement",
3235
- "fix",
3236
- "update",
3237
- "delete",
3238
- "remove",
3239
- "rename"
3240
- ]);
3241
- const hasShellIntent = mode === "build" && includesAny(normalizedText, [
3242
- "run",
3243
- "command",
3244
- "terminal",
3245
- "shell",
3246
- "bash",
3247
- "powershell",
3248
- "bun",
3249
- "tests",
3250
- "run test",
3251
- "run tests",
3252
- "typecheck"
3253
- ]);
3252
+ const situational = [];
3254
3253
  const hasGitIntent = includesAny(normalizedText, [
3255
3254
  "git",
3256
3255
  "diff",
@@ -3261,95 +3260,64 @@ function selectCodingAgentIntentTools({
3261
3260
  "revision",
3262
3261
  "changes"
3263
3262
  ]);
3264
- const hasTodoIntent = includesAny(normalizedText, [
3265
- "todo",
3266
- "tasks",
3267
- "checklist",
3268
- "steps",
3269
- "epic",
3270
- "ticket"
3271
- ]);
3272
- const hasWebIntent = includesAny(normalizedText, [
3273
- "http://",
3274
- "https://",
3275
- "url",
3276
- "fetch",
3277
- "website",
3278
- "web",
3279
- "online",
3280
- "search internet",
3281
- "latest"
3282
- ]);
3283
- const explicitToolSearch = includesAny(normalizedText, [
3284
- "tool_search",
3285
- "tool search",
3286
- "find tool",
3287
- "available tool"
3288
- ]);
3289
- const hasSkillIntent = includesAny(normalizedText, [
3290
- "skill",
3291
- "skills",
3292
- "load skill"
3293
- ]);
3294
- const hasMcpIntent = includesAny(normalizedText, [
3295
- "mcp",
3296
- "resource",
3297
- "resources",
3298
- "mcp tool",
3299
- "mcp server"
3300
- ]);
3301
- if (hasCodeIntent || hasReadIntent && !hasGitIntent && !hasWebIntent && !explicitToolSearch) {
3302
- for (const toolName of baseReadTools) {
3303
- pushUniqueTool(selectedTools, toolName);
3304
- }
3305
- }
3306
3263
  if (hasGitIntent) {
3307
3264
  if (includesAny(normalizedText, ["status", "state", "dirty", "working tree"]) || !includesAny(normalizedText, ["diff", "log", "show", "history", "commit"])) {
3308
- pushUniqueTool(selectedTools, "git_status");
3265
+ pushUniqueTool(situational, "git_status");
3309
3266
  }
3310
3267
  if (includesAny(normalizedText, ["diff", "changes", "patch"])) {
3311
- pushUniqueTool(selectedTools, "git_diff");
3268
+ pushUniqueTool(situational, "git_diff");
3312
3269
  }
3313
3270
  if (includesAny(normalizedText, ["log", "history", "commits"])) {
3314
- pushUniqueTool(selectedTools, "git_log");
3271
+ pushUniqueTool(situational, "git_log");
3315
3272
  }
3316
3273
  if (includesAny(normalizedText, ["show", "revision", "commit"])) {
3317
- pushUniqueTool(selectedTools, "git_show");
3274
+ pushUniqueTool(situational, "git_show");
3318
3275
  }
3319
3276
  }
3320
- if (hasTodoIntent && mode === "build") {
3321
- pushUniqueTool(selectedTools, "todo_write");
3277
+ if (mode === "build" && includesAny(normalizedText, ["todo", "tasks", "checklist", "steps", "epic", "ticket"])) {
3278
+ pushUniqueTool(situational, "todo_write");
3322
3279
  }
3323
- if (explicitToolSearch) {
3324
- pushUniqueTool(selectedTools, "tool_search");
3280
+ if (includesAny(normalizedText, ["tool_search", "tool search", "find tool", "available tool"])) {
3281
+ pushUniqueTool(situational, "tool_search");
3325
3282
  }
3326
- if (hasSkillIntent) {
3327
- pushUniqueTool(selectedTools, "skill");
3283
+ if (includesAny(normalizedText, ["skill", "skills", "load skill"])) {
3284
+ pushUniqueTool(situational, "skill");
3328
3285
  }
3329
- if (hasMcpIntent) {
3330
- pushUniqueTool(selectedTools, "list_mcp_resources");
3286
+ if (includesAny(normalizedText, ["mcp", "resource", "resources", "mcp tool", "mcp server"])) {
3287
+ pushUniqueTool(situational, "list_mcp_resources");
3331
3288
  if (includesAny(normalizedText, ["read", "resource", "resources"])) {
3332
- pushUniqueTool(selectedTools, "read_mcp_resource");
3289
+ pushUniqueTool(situational, "read_mcp_resource");
3333
3290
  }
3334
3291
  if (mode === "build" && includesAny(normalizedText, ["call", "execute", "run"])) {
3335
- pushUniqueTool(selectedTools, "call_mcp_tool");
3292
+ pushUniqueTool(situational, "call_mcp_tool");
3336
3293
  }
3337
3294
  }
3338
- if (hasWriteIntent) {
3339
- pushUniqueTool(selectedTools, "write_file");
3340
- pushUniqueTool(selectedTools, "edit_file");
3295
+ if (includesAny(normalizedText, [
3296
+ "http://",
3297
+ "https://",
3298
+ "url",
3299
+ "fetch",
3300
+ "website",
3301
+ "web",
3302
+ "online",
3303
+ "search internet",
3304
+ "latest"
3305
+ ])) {
3306
+ pushUniqueTool(situational, "web_fetch");
3307
+ pushUniqueTool(situational, "web_search");
3341
3308
  }
3342
- if (hasShellIntent) {
3343
- pushUniqueTool(selectedTools, "bash");
3309
+ if (mode === "plan" && includesAny(normalizedText, ["question", "ask", "clarify"])) {
3310
+ pushUniqueTool(situational, "request_user_input");
3344
3311
  }
3345
- if (hasWebIntent) {
3346
- pushUniqueTool(selectedTools, "web_fetch");
3347
- pushUniqueTool(selectedTools, "web_search");
3312
+ const ordered = [...baseReadTools, ...situational];
3313
+ if (mode === "build") {
3314
+ ordered.push("write_file", "edit_file", "bash");
3348
3315
  }
3349
- if (mode === "plan" && includesAny(normalizedText, ["question", "ask", "clarify"])) {
3350
- pushUniqueTool(selectedTools, "request_user_input");
3316
+ const selected = [];
3317
+ for (const toolName of ordered) {
3318
+ pushUniqueTool(selected, toolName);
3351
3319
  }
3352
- return selectedTools;
3320
+ return selected.slice(0, maxProviderActiveTools);
3353
3321
  }
3354
3322
  function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
3355
3323
  if (tools.length <= maxTools) {
@@ -3552,7 +3520,8 @@ function createCodingAgent({
3552
3520
  cwd: options.cwd,
3553
3521
  override: promptOverride,
3554
3522
  mode,
3555
- includeToolDiscipline
3523
+ includeToolDiscipline,
3524
+ environmentContext: options.environmentContext
3556
3525
  })
3557
3526
  };
3558
3527
  }
@@ -3633,17 +3602,33 @@ function normalizeContextOptimizerConfig(config) {
3633
3602
  }
3634
3603
  function resolveContextWindowTokens({
3635
3604
  config,
3636
- modelContextWindow
3605
+ modelContextWindow,
3606
+ endpointLimitTokens
3607
+ }) {
3608
+ const resolved = config.contextWindowOverride ?? modelContextWindow ?? DEFAULT_CONTEXT_WINDOW_TOKENS;
3609
+ if (typeof endpointLimitTokens === "number" && Number.isFinite(endpointLimitTokens) && endpointLimitTokens > 0) {
3610
+ return Math.min(resolved, endpointLimitTokens);
3611
+ }
3612
+ return resolved;
3613
+ }
3614
+ function resolveInputBudgetTokens({
3615
+ contextWindow,
3616
+ reservedOutputTokens,
3617
+ safetyFraction = DEFAULT_INPUT_BUDGET_SAFETY_FRACTION
3637
3618
  }) {
3638
- return config.contextWindowOverride ?? modelContextWindow ?? DEFAULT_CONTEXT_WINDOW_TOKENS;
3619
+ const reserved = typeof reservedOutputTokens === "number" && Number.isFinite(reservedOutputTokens) && reservedOutputTokens > 0 ? reservedOutputTokens : 0;
3620
+ const clampedFraction = Math.min(1, Math.max(0.1, safetyFraction));
3621
+ const usable = Math.max(0, contextWindow - reserved);
3622
+ return Math.max(0, Math.floor(usable * clampedFraction));
3639
3623
  }
3640
- var DEFAULT_CONTEXT_WINDOW_TOKENS = 128000, contextOptimizerConfigSchema, resolvedContextOptimizerConfigSchema, defaultContextOptimizerConfig;
3624
+ var DEFAULT_CONTEXT_WINDOW_TOKENS = 128000, contextOptimizerConfigSchema, resolvedContextOptimizerConfigSchema, defaultContextOptimizerConfig, DEFAULT_INPUT_BUDGET_SAFETY_FRACTION = 0.92;
3641
3625
  var init_config3 = __esm(() => {
3642
3626
  contextOptimizerConfigSchema = z26.object({
3643
3627
  autoCompact: z26.boolean().optional(),
3644
3628
  compactAtFraction: z26.number().min(0.3).max(0.95).optional(),
3645
3629
  pruneAtFraction: z26.number().min(0.2).max(0.9).optional(),
3646
3630
  preserveRecentMessages: z26.number().int().min(2).max(100).optional(),
3631
+ maxCoverageTokensPerCompaction: z26.number().int().min(2000).max(200000).optional(),
3647
3632
  contextWindowOverride: z26.number().int().min(8000).max(1e7).optional(),
3648
3633
  summaryMaxChars: z26.number().int().min(200).max(50000).optional(),
3649
3634
  aggressivePruneWhenUncached: z26.boolean().optional(),
@@ -3657,6 +3642,7 @@ var init_config3 = __esm(() => {
3657
3642
  compactAtFraction: z26.number().min(0.3).max(0.95),
3658
3643
  pruneAtFraction: z26.number().min(0.2).max(0.9),
3659
3644
  preserveRecentMessages: z26.number().int().min(2).max(100),
3645
+ maxCoverageTokensPerCompaction: z26.number().int().min(2000).max(200000),
3660
3646
  contextWindowOverride: z26.number().int().min(8000).max(1e7).nullable(),
3661
3647
  summaryMaxChars: z26.number().int().min(200).max(50000),
3662
3648
  aggressivePruneWhenUncached: z26.boolean(),
@@ -3669,6 +3655,7 @@ var init_config3 = __esm(() => {
3669
3655
  compactAtFraction: 0.7,
3670
3656
  pruneAtFraction: 0.6,
3671
3657
  preserveRecentMessages: 6,
3658
+ maxCoverageTokensPerCompaction: 12000,
3672
3659
  contextWindowOverride: null,
3673
3660
  summaryMaxChars: 4000,
3674
3661
  aggressivePruneWhenUncached: true,
@@ -3748,10 +3735,61 @@ var init_estimate = __esm(() => {
3748
3735
  totalTokens: z27.number().nonnegative().optional()
3749
3736
  }),
3750
3737
  modelId: z27.string().optional(),
3751
- finishReason: languageModelFinishReasonSchema.optional()
3738
+ finishReason: languageModelFinishReasonSchema.optional(),
3739
+ context: z27.object({
3740
+ inputTokens: z27.number().nonnegative(),
3741
+ contextWindow: z27.number().positive(),
3742
+ compactedMessages: z27.number().nonnegative().optional()
3743
+ }).optional()
3752
3744
  }).loose();
3753
3745
  });
3754
3746
 
3747
+ // packages/ai/src/common/output-utils.ts
3748
+ function omissionMarker(omitted) {
3749
+ return `
3750
+
3751
+ ... [${omitted} characters omitted; re-read a line range with read_file or raise maxChars] ...
3752
+
3753
+ `;
3754
+ }
3755
+ function truncateText(text, maxChars = DEFAULT_TOOL_TEXT_OUTPUT_CHARS) {
3756
+ if (text.length <= maxChars) {
3757
+ return {
3758
+ text,
3759
+ truncated: false
3760
+ };
3761
+ }
3762
+ const markerReserve = omissionMarker(text.length).length;
3763
+ const usable = Math.max(0, maxChars - markerReserve);
3764
+ const headChars = Math.floor(usable * 0.7);
3765
+ const tailChars = usable - headChars;
3766
+ const head = text.slice(0, headChars);
3767
+ const tail = tailChars > 0 ? text.slice(text.length - tailChars) : "";
3768
+ const omitted = text.length - head.length - tail.length;
3769
+ return {
3770
+ text: `${head}${omissionMarker(omitted)}${tail}`,
3771
+ truncated: true
3772
+ };
3773
+ }
3774
+ function toSingleLinePreview(value, maxChars = 140) {
3775
+ let text;
3776
+ if (typeof value === "string") {
3777
+ text = value;
3778
+ } else {
3779
+ try {
3780
+ text = JSON.stringify(value);
3781
+ } catch {
3782
+ text = String(value);
3783
+ }
3784
+ }
3785
+ const normalized = text.replace(/\s+/g, " ").trim();
3786
+ if (normalized.length <= maxChars) {
3787
+ return normalized;
3788
+ }
3789
+ return `${normalized.slice(0, maxChars - 3)}...`;
3790
+ }
3791
+ var init_output_utils = () => {};
3792
+
3755
3793
  // packages/ai/src/context/heuristic-summary.ts
3756
3794
  function truncateInline(text, maxChars) {
3757
3795
  const normalized = text.replace(/\s+/g, " ").trim();
@@ -4101,6 +4139,127 @@ var init_tier1_prune = __esm(() => {
4101
4139
  init_message_parts();
4102
4140
  });
4103
4141
 
4142
+ // packages/ai/src/context/fit-to-budget.ts
4143
+ function estimateStructuralTokens(messages) {
4144
+ return messages.reduce((total, message) => total + estimateMessageTokens(message), 0);
4145
+ }
4146
+ function getPartState2(part) {
4147
+ const state = isRecord3(part) ? Reflect.get(part, "state") : undefined;
4148
+ return typeof state === "string" ? state : null;
4149
+ }
4150
+ function elideOutput(part, originalChars) {
4151
+ const stub = {
4152
+ lightcodeElided: true,
4153
+ note: "Tool output elided to fit the model's context budget. Re-run the tool if this output is needed again.",
4154
+ originalChars
4155
+ };
4156
+ return { ...part, output: stub };
4157
+ }
4158
+ function elideToolOutputsInMessage(message) {
4159
+ let elided = 0;
4160
+ const parts = message.parts.map((part) => {
4161
+ if (!getToolNameFromPart(part) || getPartState2(part) !== "output-available") {
4162
+ return part;
4163
+ }
4164
+ const output = isRecord3(part) ? Reflect.get(part, "output") : undefined;
4165
+ if (output === undefined || isElidedToolOutput(output)) {
4166
+ return part;
4167
+ }
4168
+ elided += 1;
4169
+ return elideOutput(part, safeStringify(output).length);
4170
+ });
4171
+ return elided > 0 ? { message: { ...message, parts }, elided } : { message, elided: 0 };
4172
+ }
4173
+ function truncateTextPartsInMessage(message, capChars) {
4174
+ let truncated = 0;
4175
+ const parts = message.parts.map((part) => {
4176
+ if (!isRecord3(part)) {
4177
+ return part;
4178
+ }
4179
+ const text = Reflect.get(part, "text");
4180
+ if (typeof text !== "string" || text.length <= capChars) {
4181
+ return part;
4182
+ }
4183
+ const result = truncateText(text, capChars);
4184
+ if (!result.truncated) {
4185
+ return part;
4186
+ }
4187
+ truncated += 1;
4188
+ return { ...part, text: result.text };
4189
+ });
4190
+ return truncated > 0 ? { message: { ...message, parts }, truncated } : { message, truncated: 0 };
4191
+ }
4192
+ function fitMessagesToBudget(messages, options) {
4193
+ const budget = Math.max(0, Math.floor(options.inputBudgetTokens));
4194
+ const preserveRecentMessages = Math.max(0, options.preserveRecentMessages);
4195
+ let working = [...messages];
4196
+ let elidedToolOutputs = 0;
4197
+ let droppedMessages = 0;
4198
+ let truncatedTextParts = 0;
4199
+ const fits = () => estimateStructuralTokens(working) <= budget;
4200
+ if (fits()) {
4201
+ return {
4202
+ messages: working,
4203
+ elidedToolOutputs: 0,
4204
+ droppedMessages: 0,
4205
+ truncatedTextParts: 0,
4206
+ estimatedTokens: estimateStructuralTokens(working),
4207
+ fitted: false,
4208
+ withinBudget: true
4209
+ };
4210
+ }
4211
+ const preserveFrom = Math.max(0, working.length - preserveRecentMessages);
4212
+ for (let index = 0;index < preserveFrom && !fits(); index += 1) {
4213
+ const { message, elided } = elideToolOutputsInMessage(working[index]);
4214
+ if (elided > 0) {
4215
+ working[index] = message;
4216
+ elidedToolOutputs += elided;
4217
+ }
4218
+ }
4219
+ const firstIsSummary = isSummaryMessage(working[0]);
4220
+ let dropFrom = firstIsSummary ? 1 : 0;
4221
+ while (!fits() && working.length - dropFrom > preserveRecentMessages && dropFrom < working.length) {
4222
+ working.splice(dropFrom, 1);
4223
+ droppedMessages += 1;
4224
+ }
4225
+ if (!fits()) {
4226
+ working = working.map((message) => {
4227
+ const { message: elidedMessage, elided } = elideToolOutputsInMessage(message);
4228
+ elidedToolOutputs += elided;
4229
+ return elidedMessage;
4230
+ });
4231
+ let cap = MAX_TOOL_TEXT_OUTPUT_CHARS;
4232
+ while (!fits() && cap >= MIN_TRUNCATION_CAP_CHARS) {
4233
+ working = working.map((message) => {
4234
+ const { message: truncatedMessage, truncated } = truncateTextPartsInMessage(message, cap);
4235
+ truncatedTextParts += truncated;
4236
+ return truncatedMessage;
4237
+ });
4238
+ cap = Math.floor(cap / 2);
4239
+ }
4240
+ }
4241
+ return {
4242
+ messages: working,
4243
+ elidedToolOutputs,
4244
+ droppedMessages,
4245
+ truncatedTextParts,
4246
+ estimatedTokens: estimateStructuralTokens(working),
4247
+ fitted: elidedToolOutputs > 0 || droppedMessages > 0 || truncatedTextParts > 0,
4248
+ withinBudget: fits()
4249
+ };
4250
+ }
4251
+ function isSummaryMessage(message) {
4252
+ return message?.id === contextSummaryMessageId;
4253
+ }
4254
+ var MIN_TRUNCATION_CAP_CHARS = 256;
4255
+ var init_fit_to_budget = __esm(() => {
4256
+ init_output_utils();
4257
+ init_estimate();
4258
+ init_heuristic_summary();
4259
+ init_message_parts();
4260
+ init_tier1_prune();
4261
+ });
4262
+
4104
4263
  // packages/ai/src/context/build-provider-view.ts
4105
4264
  function computeCompactionCutoffIndex(messages, preserveRecentMessages, afterAnchorIndex) {
4106
4265
  const candidate = messages.length - preserveRecentMessages;
@@ -4114,11 +4273,32 @@ function computeCompactionCutoffIndex(messages, preserveRecentMessages, afterAnc
4114
4273
  }
4115
4274
  return afterAnchorIndex;
4116
4275
  }
4276
+ function capCoverageToTokenBudget(coveredMessages, maxTokens) {
4277
+ if (coveredMessages.length <= 1) {
4278
+ return [...coveredMessages];
4279
+ }
4280
+ let total = 0;
4281
+ const capped = [];
4282
+ for (const message of coveredMessages) {
4283
+ const messageTokens = estimateStructuralTokens([message]);
4284
+ if (capped.length > 0 && total + messageTokens > maxTokens) {
4285
+ break;
4286
+ }
4287
+ capped.push(message);
4288
+ total += messageTokens;
4289
+ }
4290
+ return capped;
4291
+ }
4117
4292
  function buildProviderView(options) {
4118
4293
  const { messages, contextState, config } = options;
4119
4294
  const contextWindow = resolveContextWindowTokens({
4120
4295
  config,
4121
- modelContextWindow: options.modelContextWindow
4296
+ modelContextWindow: options.modelContextWindow,
4297
+ endpointLimitTokens: options.endpointLimitTokens
4298
+ });
4299
+ const inputBudgetTokens = resolveInputBudgetTokens({
4300
+ contextWindow,
4301
+ reservedOutputTokens: options.reservedOutputTokens
4122
4302
  });
4123
4303
  let anchorResolved = false;
4124
4304
  let afterAnchorIndex = 0;
@@ -4142,7 +4322,8 @@ function buildProviderView(options) {
4142
4322
  let tier1 = null;
4143
4323
  const uncachedActive = options.cacheActive === false && config.aggressivePruneWhenUncached;
4144
4324
  const pruneFraction = uncachedActive ? config.uncachedPruneAtFraction : config.pruneAtFraction;
4145
- if (estimate.tokens > pruneFraction * contextWindow) {
4325
+ const triggerTokens = (currentEstimateTokens) => Math.max(currentEstimateTokens, estimateStructuralTokens(providerMessages), typeof options.measuredInputTokens === "number" ? options.measuredInputTokens : 0);
4326
+ if (triggerTokens(estimate.tokens) > pruneFraction * inputBudgetTokens) {
4146
4327
  tier1 = pruneToolOutputs(providerMessages, {
4147
4328
  preserveRecentMessages: config.preserveRecentMessages,
4148
4329
  minOutputChars: uncachedActive ? config.uncachedPruneMinOutputChars : undefined,
@@ -4157,15 +4338,15 @@ function buildProviderView(options) {
4157
4338
  }
4158
4339
  }
4159
4340
  const compactionCutoffIndex = computeCompactionCutoffIndex(messages, config.preserveRecentMessages, afterAnchorIndex);
4160
- const coveredMessages = messages.slice(afterAnchorIndex, compactionCutoffIndex);
4161
- const overCompactThreshold = estimate.tokens > config.compactAtFraction * contextWindow;
4341
+ const coveredMessages = capCoverageToTokenBudget(messages.slice(afterAnchorIndex, compactionCutoffIndex), config.maxCoverageTokensPerCompaction);
4342
+ const overCompactThreshold = triggerTokens(estimate.tokens) > config.compactAtFraction * inputBudgetTokens;
4162
4343
  let compactionBlockedReason = null;
4163
4344
  if (overCompactThreshold) {
4164
4345
  if (coveredMessages.length === 0) {
4165
4346
  compactionBlockedReason = "not_enough_messages";
4166
4347
  } else if ((options.pendingInteractionCount ?? 0) > 0) {
4167
4348
  compactionBlockedReason = "pending_interactions";
4168
- } else if (hasUnresolvedToolWork(messages)) {
4349
+ } else if (hasUnresolvedToolWork(coveredMessages)) {
4169
4350
  compactionBlockedReason = "unresolved_tool_work";
4170
4351
  }
4171
4352
  }
@@ -4173,6 +4354,7 @@ function buildProviderView(options) {
4173
4354
  providerMessages,
4174
4355
  estimate,
4175
4356
  contextWindow,
4357
+ inputBudgetTokens,
4176
4358
  needsCompaction: overCompactThreshold && compactionBlockedReason === null,
4177
4359
  compactionBlockedReason,
4178
4360
  anchorResolved,
@@ -4184,6 +4366,7 @@ function buildProviderView(options) {
4184
4366
  var init_build_provider_view = __esm(() => {
4185
4367
  init_config3();
4186
4368
  init_estimate();
4369
+ init_fit_to_budget();
4187
4370
  init_heuristic_summary();
4188
4371
  init_message_parts();
4189
4372
  init_tier1_prune();
@@ -4232,6 +4415,7 @@ var init_context = __esm(() => {
4232
4415
  init_config3();
4233
4416
  init_context_state();
4234
4417
  init_estimate();
4418
+ init_fit_to_budget();
4235
4419
  init_heuristic_summary();
4236
4420
  init_message_parts();
4237
4421
  init_normalize_provider_messages();
@@ -4461,8 +4645,8 @@ var init_lightcode_config = __esm(() => {
4461
4645
  defaultAutoContinueConfig = {
4462
4646
  enabled: true,
4463
4647
  maxAutoContinues: 50,
4464
- maxErrorRetries: 5,
4465
- stallTimeoutSeconds: 180
4648
+ maxErrorRetries: 8,
4649
+ stallTimeoutSeconds: 300
4466
4650
  };
4467
4651
  headroomConfigSchema = z28.object({
4468
4652
  enabled: z28.boolean().optional(),
@@ -4505,7 +4689,7 @@ var init_lightcode_config = __esm(() => {
4505
4689
  context: defaultContextOptimizerConfig,
4506
4690
  maxOutputTokens: 32768,
4507
4691
  maxSteps: 30,
4508
- maxRetries: 5,
4692
+ maxRetries: 3,
4509
4693
  autoContinue: defaultAutoContinueConfig,
4510
4694
  headroom: defaultHeadroomConfig
4511
4695
  };
@@ -5038,6 +5222,10 @@ async function extractToolCalls(text, knownToolNames) {
5038
5222
  while ((match = DEEPSEEK_WRAPPER_RE.exec(text)) !== null) {
5039
5223
  segments.push({ start: match.index, end: match.index + match[0].length, call: null });
5040
5224
  }
5225
+ FUNCTION_CALLS_WRAPPER_RE.lastIndex = 0;
5226
+ while ((match = FUNCTION_CALLS_WRAPPER_RE.exec(text)) !== null) {
5227
+ segments.push({ start: match.index, end: match.index + match[0].length, call: null });
5228
+ }
5041
5229
  const covered = (idx) => segments.some((s) => idx >= s.start && idx < s.end);
5042
5230
  const orphanIdx = findOrphanMarker(text, covered);
5043
5231
  if (orphanIdx !== -1) {
@@ -5125,7 +5313,7 @@ async function parseBareTrailingJson(text, knownToolNames) {
5125
5313
  return null;
5126
5314
  return { start: idx, payload };
5127
5315
  }
5128
- var TOOL_CALL_OPEN_MARKERS, MAX_MARKER_LENGTH, MARKER_CONFIRMATION_WINDOW = 256, BLOCK_CLOSE_MARKERS, CLOSED_BLOCK_RES, DEEPSEEK_WRAPPER_RE, PARAMETER_TAG_RE, PARAMETER_EQ_RE;
5316
+ var TOOL_CALL_OPEN_MARKERS, MAX_MARKER_LENGTH, MARKER_CONFIRMATION_WINDOW = 256, BLOCK_CLOSE_MARKERS, CLOSED_BLOCK_RES, DEEPSEEK_WRAPPER_RE, FUNCTION_CALLS_WRAPPER_RE, PARAMETER_TAG_RE, PARAMETER_EQ_RE;
5129
5317
  var init_xml_tool_call_parser = __esm(() => {
5130
5318
  init_tool_call_json_repair();
5131
5319
  TOOL_CALL_OPEN_MARKERS = [
@@ -5152,6 +5340,7 @@ var init_xml_tool_call_parser = __esm(() => {
5152
5340
  deepseek: /<\uFF5Ctool\u2581call\u2581begin\uFF5C>\s*(?:function)?\s*(?:<\uFF5Ctool\u2581sep\uFF5C>)?\s*([\w.-]+)\s*\n?([\s\S]*?)<\uFF5Ctool\u2581call\u2581end\uFF5C>/g
5153
5341
  };
5154
5342
  DEEPSEEK_WRAPPER_RE = /<\uFF5Ctool\u2581calls\u2581(?:begin|end)\uFF5C>/g;
5343
+ FUNCTION_CALLS_WRAPPER_RE = /<\/?function_calls\s*>/gi;
5155
5344
  PARAMETER_TAG_RE = /<parameter\s+name=["']?([\w.-]+)["']?\s*>([\s\S]*?)(?:<\/parameter>|(?=<parameter\s)|$)/gi;
5156
5345
  PARAMETER_EQ_RE = /<parameter=([\w.-]+)>([\s\S]*?)(?=<parameter=|<\/function>|$)/gi;
5157
5346
  });
@@ -5968,52 +6157,6 @@ var init_resolve_within_workspace = __esm(() => {
5968
6157
  WORKSPACE = defaultWorkspaceContext.root;
5969
6158
  });
5970
6159
 
5971
- // packages/ai/src/common/output-utils.ts
5972
- function omissionMarker(omitted) {
5973
- return `
5974
-
5975
- ... [${omitted} characters omitted; re-read a line range with read_file or raise maxChars] ...
5976
-
5977
- `;
5978
- }
5979
- function truncateText(text, maxChars = DEFAULT_TOOL_TEXT_OUTPUT_CHARS) {
5980
- if (text.length <= maxChars) {
5981
- return {
5982
- text,
5983
- truncated: false
5984
- };
5985
- }
5986
- const markerReserve = omissionMarker(text.length).length;
5987
- const usable = Math.max(0, maxChars - markerReserve);
5988
- const headChars = Math.floor(usable * 0.7);
5989
- const tailChars = usable - headChars;
5990
- const head = text.slice(0, headChars);
5991
- const tail = tailChars > 0 ? text.slice(text.length - tailChars) : "";
5992
- const omitted = text.length - head.length - tail.length;
5993
- return {
5994
- text: `${head}${omissionMarker(omitted)}${tail}`,
5995
- truncated: true
5996
- };
5997
- }
5998
- function toSingleLinePreview(value, maxChars = 140) {
5999
- let text;
6000
- if (typeof value === "string") {
6001
- text = value;
6002
- } else {
6003
- try {
6004
- text = JSON.stringify(value);
6005
- } catch {
6006
- text = String(value);
6007
- }
6008
- }
6009
- const normalized = text.replace(/\s+/g, " ").trim();
6010
- if (normalized.length <= maxChars) {
6011
- return normalized;
6012
- }
6013
- return `${normalized.slice(0, maxChars - 3)}...`;
6014
- }
6015
- var init_output_utils = () => {};
6016
-
6017
6160
  // packages/ai/src/bash/runtime.ts
6018
6161
  import { exec } from "child_process";
6019
6162
  import { promisify } from "util";
@@ -6202,24 +6345,129 @@ function createUnifiedDiff(filePath, oldText, newText, { maxChars = DEFAULT_DIFF
6202
6345
  var DEFAULT_DIFF_MAX_CHARS = 6000, DIFF_CONTEXT_LINES = 3;
6203
6346
  var init_unified_diff = () => {};
6204
6347
 
6205
- // packages/ai/src/edit-file/runtime.ts
6206
- import { promises as fs2 } from "fs";
6207
- function countOccurrences(haystack, needle) {
6208
- if (!needle) {
6209
- return 0;
6348
+ // packages/ai/src/edit-file/match.ts
6349
+ function escapeRegExp(value) {
6350
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6351
+ }
6352
+ function detectEol(content) {
6353
+ const crlf = (content.match(/\r\n/g) ?? []).length;
6354
+ if (crlf === 0) {
6355
+ return `
6356
+ `;
6210
6357
  }
6211
- let count = 0;
6212
- let startIndex = 0;
6213
- while (true) {
6214
- const index = haystack.indexOf(needle, startIndex);
6358
+ const lfOnly = (content.match(/(?<!\r)\n/g) ?? []).length;
6359
+ return crlf >= lfOnly ? `\r
6360
+ ` : `
6361
+ `;
6362
+ }
6363
+ function toLf(value) {
6364
+ return value.replace(/\r\n/g, `
6365
+ `);
6366
+ }
6367
+ function spliceRanges(content, ranges, replacement) {
6368
+ let out = "";
6369
+ let cursor = 0;
6370
+ for (const range of ranges) {
6371
+ out += content.slice(cursor, range.start) + replacement;
6372
+ cursor = range.end;
6373
+ }
6374
+ return out + content.slice(cursor);
6375
+ }
6376
+ function literalRanges(content, search) {
6377
+ const ranges = [];
6378
+ let from = 0;
6379
+ for (;; ) {
6380
+ const index = content.indexOf(search, from);
6215
6381
  if (index === -1) {
6216
6382
  break;
6217
6383
  }
6218
- count += 1;
6219
- startIndex = index + needle.length;
6384
+ ranges.push({ start: index, end: index + search.length });
6385
+ from = index + search.length;
6220
6386
  }
6221
- return count;
6387
+ return ranges;
6388
+ }
6389
+ function buildFlexibleRegex(searchLf) {
6390
+ if (!searchLf.trim()) {
6391
+ return null;
6392
+ }
6393
+ const linePatterns = searchLf.split(`
6394
+ `).map((line) => {
6395
+ const core = line.replace(/^[ \t]+/, "").replace(/[ \t]+$/, "");
6396
+ return `[ \\t]*${escapeRegExp(core)}[ \\t]*`;
6397
+ });
6398
+ return new RegExp(`(?<=^|\\n)${linePatterns.join("\\n")}(?=\\n|$)`, "g");
6399
+ }
6400
+ function regexRanges(content, regex) {
6401
+ const ranges = [];
6402
+ regex.lastIndex = 0;
6403
+ for (;; ) {
6404
+ const match = regex.exec(content);
6405
+ if (!match) {
6406
+ break;
6407
+ }
6408
+ if (match[0].length === 0) {
6409
+ regex.lastIndex += 1;
6410
+ continue;
6411
+ }
6412
+ ranges.push({ start: match.index, end: match.index + match[0].length });
6413
+ regex.lastIndex = match.index + match[0].length;
6414
+ }
6415
+ return ranges;
6416
+ }
6417
+ function applyRanges(content, ranges, replacement, replaceAll, strategy) {
6418
+ if (ranges.length === 0) {
6419
+ return null;
6420
+ }
6421
+ if (!replaceAll && ranges.length > 1) {
6422
+ throw new EditMatchError(`The search text matches ${ranges.length} places. Add surrounding lines to make it unique, or pass replaceAll: true to change every occurrence.`);
6423
+ }
6424
+ const targetRanges = replaceAll ? ranges : [ranges[0]];
6425
+ return {
6426
+ updatedContent: spliceRanges(content, targetRanges, replacement),
6427
+ replacements: targetRanges.length,
6428
+ strategy
6429
+ };
6222
6430
  }
6431
+ function applyEdit(original, search, replace, replaceAll) {
6432
+ if (!search) {
6433
+ throw new EditMatchError("Search text must not be empty.");
6434
+ }
6435
+ const exact = applyRanges(original, literalRanges(original, search), replace, replaceAll, "exact");
6436
+ if (exact) {
6437
+ return exact;
6438
+ }
6439
+ const eol = detectEol(original);
6440
+ const originalLf = toLf(original);
6441
+ const searchLf = toLf(search);
6442
+ const replaceLf = toLf(replace);
6443
+ const restoreEol = (lf) => eol === `\r
6444
+ ` ? lf.replace(/\n/g, `\r
6445
+ `) : lf;
6446
+ const normalized = applyRanges(originalLf, literalRanges(originalLf, searchLf), replaceLf, replaceAll, "eol-normalized");
6447
+ if (normalized) {
6448
+ return { ...normalized, updatedContent: restoreEol(normalized.updatedContent) };
6449
+ }
6450
+ const flexibleRegex = buildFlexibleRegex(searchLf);
6451
+ if (flexibleRegex) {
6452
+ const flexible = applyRanges(originalLf, regexRanges(originalLf, flexibleRegex), replaceLf, replaceAll, "whitespace-flexible");
6453
+ if (flexible) {
6454
+ return { ...flexible, updatedContent: restoreEol(flexible.updatedContent) };
6455
+ }
6456
+ }
6457
+ throw new EditMatchError("No match for the search text. Line-ending and surrounding-whitespace differences are tolerated automatically, so a remaining mismatch means the file content itself differs \u2014 re-read the file and copy the exact lines you want to change.");
6458
+ }
6459
+ var EditMatchError;
6460
+ var init_match = __esm(() => {
6461
+ EditMatchError = class EditMatchError extends Error {
6462
+ constructor(message) {
6463
+ super(message);
6464
+ this.name = "EditMatchError";
6465
+ }
6466
+ };
6467
+ });
6468
+
6469
+ // packages/ai/src/edit-file/runtime.ts
6470
+ import { promises as fs2 } from "fs";
6223
6471
  async function executeEditFile(input, workspaceContext = getDefaultWorkspaceContext(), editContext = {}) {
6224
6472
  const parsedInput = editFileInputSchema.parse(input);
6225
6473
  const resolvedPath = resolveWithinWorkspace(parsedInput.path, {
@@ -6227,11 +6475,7 @@ async function executeEditFile(input, workspaceContext = getDefaultWorkspaceCont
6227
6475
  });
6228
6476
  const relativePath = toWorkspaceRelativePath(resolvedPath, workspaceContext);
6229
6477
  const originalContent = await fs2.readFile(resolvedPath, "utf8");
6230
- const replacements = parsedInput.replaceAll ? countOccurrences(originalContent, parsedInput.search) : originalContent.includes(parsedInput.search) ? 1 : 0;
6231
- if (replacements === 0) {
6232
- throw new Error(`No matches found for the provided search text in "${parsedInput.path}".`);
6233
- }
6234
- const updatedContent = parsedInput.replaceAll ? originalContent.split(parsedInput.search).join(parsedInput.replace) : originalContent.replace(parsedInput.search, parsedInput.replace);
6478
+ const { updatedContent, replacements } = applyEdit(originalContent, parsedInput.search, parsedInput.replace, parsedInput.replaceAll);
6235
6479
  if (editContext.sessionId && editContext.turnKey) {
6236
6480
  await recordFileCheckpoint({
6237
6481
  sessionId: editContext.sessionId,
@@ -6253,6 +6497,7 @@ var init_runtime6 = __esm(() => {
6253
6497
  init_runtime5();
6254
6498
  init_resolve_within_workspace();
6255
6499
  init_unified_diff();
6500
+ init_match();
6256
6501
  init_schema2();
6257
6502
  });
6258
6503
 
@@ -7639,11 +7884,13 @@ __export(exports_runtime, {
7639
7884
  sandboxConfigSchema: () => sandboxConfigSchema,
7640
7885
  safeStringify: () => safeStringify,
7641
7886
  runPluginHooks: () => runPluginHooks,
7887
+ retryReasonLabel: () => retryReasonLabel,
7642
7888
  resolvedHeadroomConfigSchema: () => resolvedHeadroomConfigSchema,
7643
7889
  resolvedContextOptimizerConfigSchema: () => resolvedContextOptimizerConfigSchema,
7644
7890
  resolvedAutoContinueConfigSchema: () => resolvedAutoContinueConfigSchema,
7645
7891
  resolveWithinWorkspace: () => resolveWithinWorkspace,
7646
7892
  resolveMaxOutputTokens: () => resolveMaxOutputTokens,
7893
+ resolveInputBudgetTokens: () => resolveInputBudgetTokens,
7647
7894
  resolveDanglingToolParts: () => resolveDanglingToolParts,
7648
7895
  resolveContextWindowTokens: () => resolveContextWindowTokens,
7649
7896
  resolveCodingPermissionMode: () => resolveCodingPermissionMode,
@@ -7721,6 +7968,7 @@ __export(exports_runtime, {
7721
7968
  getActiveCodingToolsForPolicy: () => getActiveCodingToolsForPolicy,
7722
7969
  formatPermissionDecision: () => formatPermissionDecision,
7723
7970
  formatChatStreamError: () => formatChatStreamError,
7971
+ fitMessagesToBudget: () => fitMessagesToBudget,
7724
7972
  extractProposedPlanBlocks: () => extractProposedPlanBlocks,
7725
7973
  extractLatestProposedPlan: () => extractLatestProposedPlan,
7726
7974
  executeWriteFile: () => executeWriteFile,
@@ -7744,6 +7992,7 @@ __export(exports_runtime, {
7744
7992
  executeBash: () => executeBash,
7745
7993
  evaluateCodingToolPermission: () => evaluateCodingToolPermission,
7746
7994
  estimateTextTokens: () => estimateTextTokens,
7995
+ estimateStructuralTokens: () => estimateStructuralTokens,
7747
7996
  estimateMessageTokens: () => estimateMessageTokens,
7748
7997
  estimateContextTokens: () => estimateContextTokens,
7749
7998
  diagnosticsToolsResponseSchema: () => diagnosticsToolsResponseSchema,
@@ -7810,6 +8059,7 @@ __export(exports_runtime, {
7810
8059
  chatInteractionListQuerySchema: () => chatInteractionListQuerySchema,
7811
8060
  chatInteractionKindSchema: () => chatInteractionKindSchema,
7812
8061
  chatInteractionKindOrder: () => chatInteractionKindOrder,
8062
+ capCoverageToTokenBudget: () => capCoverageToTokenBudget,
7813
8063
  callMcpToolProviderInputSchema: () => callMcpToolProviderInputSchema,
7814
8064
  callMcpToolOutputSchema: () => callMcpToolOutputSchema,
7815
8065
  callMcpToolInputSchema: () => callMcpToolInputSchema,
@@ -7830,6 +8080,7 @@ __export(exports_runtime, {
7830
8080
  ESTIMATED_CHARS_PER_TOKEN: () => ESTIMATED_CHARS_PER_TOKEN,
7831
8081
  DEFAULT_PRUNE_QUANTIZE_USER_TURNS: () => DEFAULT_PRUNE_QUANTIZE_USER_TURNS,
7832
8082
  DEFAULT_PRUNE_MIN_OUTPUT_CHARS: () => DEFAULT_PRUNE_MIN_OUTPUT_CHARS,
8083
+ DEFAULT_INPUT_BUDGET_SAFETY_FRACTION: () => DEFAULT_INPUT_BUDGET_SAFETY_FRACTION,
7833
8084
  DEFAULT_CONTEXT_WINDOW_TOKENS: () => DEFAULT_CONTEXT_WINDOW_TOKENS,
7834
8085
  ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET: () => ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET
7835
8086
  });
@@ -8164,6 +8415,22 @@ import {
8164
8415
  safeValidateUIMessages
8165
8416
  } from "ai";
8166
8417
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
8418
+ function decideErrorRetry({
8419
+ attempt,
8420
+ kind,
8421
+ elapsedMs,
8422
+ maxErrorRetries
8423
+ }) {
8424
+ const cap = kind === "rate_limit" ? Math.min(rateLimitMaxRetries, maxErrorRetries) : maxErrorRetries;
8425
+ if (attempt > cap) {
8426
+ return { retry: false, delayMs: 0 };
8427
+ }
8428
+ const delayMs = Math.min(errorRetryBaseDelayMs * 2 ** (attempt - 1), errorRetryMaxDelayMs);
8429
+ if (elapsedMs + delayMs > errorRetryTimeBudgetMs) {
8430
+ return { retry: false, delayMs: 0 };
8431
+ }
8432
+ return { retry: true, delayMs };
8433
+ }
8167
8434
  function getErrorMessage2(error, fallback) {
8168
8435
  if (error instanceof Error) {
8169
8436
  return error.message;
@@ -8454,6 +8721,7 @@ function useCodingSessionChat({
8454
8721
  const autoContinuesRef = useRef(0);
8455
8722
  const errorRetriesRef = useRef(0);
8456
8723
  const errorRetryTimerRef = useRef(null);
8724
+ const errorEpisodeStartRef = useRef(null);
8457
8725
  const lastDecidedAssistantIdRef = useRef(null);
8458
8726
  const lastProgressMarkerRef = useRef(0);
8459
8727
  const noProgressRetriesRef = useRef(0);
@@ -8464,6 +8732,7 @@ function useCodingSessionChat({
8464
8732
  turnKeyRef.current = `turn-${Date.now()}-${turnCounterRef.current}`;
8465
8733
  autoContinuesRef.current = 0;
8466
8734
  errorRetriesRef.current = 0;
8735
+ errorEpisodeStartRef.current = null;
8467
8736
  lastProgressMarkerRef.current = 0;
8468
8737
  noProgressRetriesRef.current = 0;
8469
8738
  taskStuckRef.current = false;
@@ -8860,6 +9129,7 @@ function useCodingSessionChat({
8860
9129
  useEffect(() => {
8861
9130
  if (status === "ready" && !error) {
8862
9131
  errorRetriesRef.current = 0;
9132
+ errorEpisodeStartRef.current = null;
8863
9133
  }
8864
9134
  }, [error, status]);
8865
9135
  useEffect(() => {
@@ -8897,7 +9167,8 @@ function useCodingSessionChat({
8897
9167
  errorRetriesRef.current += 1;
8898
9168
  setAutoContinueState({
8899
9169
  attempt: errorRetriesRef.current,
8900
- kind: "retry-error"
9170
+ kind: "retry-error",
9171
+ retryReason: "stall"
8901
9172
  });
8902
9173
  (async () => {
8903
9174
  try {
@@ -8972,12 +9243,28 @@ function useCodingSessionChat({
8972
9243
  if (taskStuckRef.current || pendingApprovals.length > 0 || pendingUserPrompts.length > 0 || errorRetryTimerRef.current || stallRecoveryInFlightRef.current) {
8973
9244
  return;
8974
9245
  }
8975
- if (errorRetriesRef.current >= maxErrorRetriesRef.current) {
8976
- return;
8977
- }
8978
9246
  const retryMessages = sanitizeMessagesForRetry(messages);
9247
+ const retryReason = parseChatStreamError(error.message)?.kind ?? "network";
9248
+ if (errorEpisodeStartRef.current === null) {
9249
+ errorEpisodeStartRef.current = Date.now();
9250
+ }
8979
9251
  const attempt = errorRetriesRef.current + 1;
8980
- const delay = Math.min(errorRetryBaseDelayMs * 2 ** (attempt - 1), errorRetryMaxDelayMs);
9252
+ const decision2 = decideErrorRetry({
9253
+ attempt,
9254
+ kind: retryReason,
9255
+ elapsedMs: Date.now() - errorEpisodeStartRef.current,
9256
+ maxErrorRetries: maxErrorRetriesRef.current
9257
+ });
9258
+ if (!decision2.retry) {
9259
+ return;
9260
+ }
9261
+ const delay = decision2.delayMs;
9262
+ setAutoContinueState({
9263
+ attempt,
9264
+ kind: "retry-error",
9265
+ retryReason,
9266
+ retryAtMs: Date.now() + delay
9267
+ });
8981
9268
  errorRetryTimerRef.current = setTimeout(() => {
8982
9269
  errorRetryTimerRef.current = null;
8983
9270
  if (!registerRetryProgress(messagesRef.current)) {
@@ -8989,7 +9276,7 @@ function useCodingSessionChat({
8989
9276
  return;
8990
9277
  }
8991
9278
  errorRetriesRef.current = attempt;
8992
- setAutoContinueState({ attempt, kind: "retry-error" });
9279
+ setAutoContinueState({ attempt, kind: "retry-error", retryReason });
8993
9280
  setMessages(retryMessages);
8994
9281
  clearError();
8995
9282
  setToolExecutionError(null);
@@ -9207,7 +9494,7 @@ function useCodingSessionChat({
9207
9494
  isSessionIdValid
9208
9495
  };
9209
9496
  }
9210
- 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, terminalToolPartStates;
9497
+ 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, rateLimitMaxRetries = 6, errorRetryTimeBudgetMs = 180000, disconnectErrorPattern, transientProviderErrorPattern, terminalToolPartStates;
9211
9498
  var init_use_coding_session_chat = __esm(() => {
9212
9499
  init_agent_tools();
9213
9500
  init_chat_error();
@@ -9383,7 +9670,7 @@ function ContextSummaryFrame({ children }) {
9383
9670
  return /* @__PURE__ */ jsxDEV4("box", {
9384
9671
  width: "100%",
9385
9672
  flexDirection: "column",
9386
- borderStyle: "single",
9673
+ borderStyle: borderStyleFor.card,
9387
9674
  borderColor: cliTheme.semantic.info,
9388
9675
  backgroundColor: cliTheme.surfaces.panel,
9389
9676
  paddingX: 1,
@@ -9508,7 +9795,7 @@ function ChatProposedPlanCard({ content }) {
9508
9795
  return /* @__PURE__ */ jsxDEV5("box", {
9509
9796
  width: "100%",
9510
9797
  flexDirection: "column",
9511
- borderStyle: "single",
9798
+ borderStyle: borderStyleFor.card,
9512
9799
  borderColor: cliTheme.borders.active,
9513
9800
  backgroundColor: cliTheme.surfaces.panel,
9514
9801
  paddingX: 1,
@@ -9717,7 +10004,7 @@ function ChatInteractionPopup({
9717
10004
  return /* @__PURE__ */ jsxDEV6("box", {
9718
10005
  width: "100%",
9719
10006
  flexDirection: "column",
9720
- borderStyle: "single",
10007
+ borderStyle: borderStyleFor.modal,
9721
10008
  borderColor: cliTheme.borders.active,
9722
10009
  backgroundColor: cliTheme.surfaces.panel,
9723
10010
  paddingX: 1,
@@ -9841,21 +10128,146 @@ var init_chat_message_error_part = __esm(() => {
9841
10128
  init_cli_theme();
9842
10129
  });
9843
10130
 
9844
- // apps/cli/src/components/chat/chat-message-reasoning-part.tsx
10131
+ // apps/cli/src/state/app-state.tsx
10132
+ import { createContext, useCallback as useCallback2, useContext, useMemo as useMemo3, useState as useState3 } from "react";
9845
10133
  import { jsxDEV as jsxDEV8 } from "@opentui/react/jsx-dev-runtime";
10134
+ function AppStateProvider({ children }) {
10135
+ const [paletteOpen, setPaletteOpen] = useState3(false);
10136
+ const [paletteQuery, setPaletteQuery] = useState3("");
10137
+ const [paletteSelected, setPaletteSelected] = useState3(0);
10138
+ const [slashMenuOpen, setSlashMenuOpen] = useState3(false);
10139
+ const [slashMenuQuery, setSlashMenuQuery] = useState3("/");
10140
+ const [slashMenuSelected, setSlashMenuSelected] = useState3(0);
10141
+ const openPalette = useCallback2((initialQuery = "") => {
10142
+ setPaletteOpen(true);
10143
+ setPaletteQuery(initialQuery);
10144
+ setPaletteSelected(0);
10145
+ setSlashMenuOpen(false);
10146
+ }, []);
10147
+ const closePalette = useCallback2(() => {
10148
+ setPaletteOpen(false);
10149
+ setPaletteQuery("");
10150
+ setPaletteSelected(0);
10151
+ }, []);
10152
+ const openSlashMenu = useCallback2(() => {
10153
+ setSlashMenuOpen(true);
10154
+ setSlashMenuQuery("/");
10155
+ setSlashMenuSelected(0);
10156
+ setPaletteOpen(false);
10157
+ }, []);
10158
+ const closeSlashMenu = useCallback2(() => {
10159
+ setSlashMenuOpen(false);
10160
+ setSlashMenuQuery("/");
10161
+ setSlashMenuSelected(0);
10162
+ }, []);
10163
+ const [requestedChatActionId, setRequestedChatActionId] = useState3(null);
10164
+ const requestChatAction = useCallback2((id) => {
10165
+ setRequestedChatActionId(id);
10166
+ }, []);
10167
+ const clearRequestedChatAction = useCallback2(() => {
10168
+ setRequestedChatActionId(null);
10169
+ }, []);
10170
+ const [configRefreshNonce, setConfigRefreshNonce] = useState3(0);
10171
+ const bumpConfigRefresh = useCallback2(() => {
10172
+ setConfigRefreshNonce((nonce) => nonce + 1);
10173
+ }, []);
10174
+ const [expandedToolOutput, setExpandedToolOutput] = useState3(false);
10175
+ const toggleToolOutputExpansion = useCallback2(() => {
10176
+ setExpandedToolOutput((expanded) => !expanded);
10177
+ }, []);
10178
+ const [expandedReasoning, setExpandedReasoning] = useState3(false);
10179
+ const toggleReasoningExpansion = useCallback2(() => {
10180
+ setExpandedReasoning((expanded) => !expanded);
10181
+ }, []);
10182
+ const value = useMemo3(() => ({
10183
+ paletteOpen,
10184
+ paletteQuery,
10185
+ setPaletteQuery,
10186
+ paletteSelected,
10187
+ setPaletteSelected,
10188
+ openPalette,
10189
+ closePalette,
10190
+ slashMenuOpen,
10191
+ slashMenuQuery,
10192
+ setSlashMenuQuery,
10193
+ slashMenuSelected,
10194
+ setSlashMenuSelected,
10195
+ openSlashMenu,
10196
+ closeSlashMenu,
10197
+ requestedChatActionId,
10198
+ requestChatAction,
10199
+ clearRequestedChatAction,
10200
+ configRefreshNonce,
10201
+ bumpConfigRefresh,
10202
+ expandedToolOutput,
10203
+ toggleToolOutputExpansion,
10204
+ expandedReasoning,
10205
+ toggleReasoningExpansion
10206
+ }), [
10207
+ paletteOpen,
10208
+ paletteQuery,
10209
+ paletteSelected,
10210
+ openPalette,
10211
+ closePalette,
10212
+ slashMenuOpen,
10213
+ slashMenuQuery,
10214
+ slashMenuSelected,
10215
+ openSlashMenu,
10216
+ closeSlashMenu,
10217
+ requestedChatActionId,
10218
+ requestChatAction,
10219
+ clearRequestedChatAction,
10220
+ configRefreshNonce,
10221
+ bumpConfigRefresh,
10222
+ expandedToolOutput,
10223
+ toggleToolOutputExpansion,
10224
+ expandedReasoning,
10225
+ toggleReasoningExpansion
10226
+ ]);
10227
+ return /* @__PURE__ */ jsxDEV8(AppStateContext.Provider, {
10228
+ value,
10229
+ children
10230
+ }, undefined, false, undefined, this);
10231
+ }
10232
+ function useAppState() {
10233
+ const context2 = useContext(AppStateContext);
10234
+ if (!context2) {
10235
+ throw new Error("useAppState must be used within AppStateProvider");
10236
+ }
10237
+ return context2;
10238
+ }
10239
+ var AppStateContext;
10240
+ var init_app_state = __esm(() => {
10241
+ AppStateContext = createContext(null);
10242
+ });
10243
+
10244
+ // apps/cli/src/components/chat/chat-message-reasoning-part.tsx
10245
+ import { TextAttributes as TextAttributes7 } from "@opentui/core";
10246
+ import { jsxDEV as jsxDEV9 } from "@opentui/react/jsx-dev-runtime";
9846
10247
  function ChatMessageReasoningPart({ part }) {
10248
+ const { expandedReasoning } = useAppState();
9847
10249
  if (!part.text) {
9848
10250
  return null;
9849
10251
  }
9850
- return /* @__PURE__ */ jsxDEV8("box", {
10252
+ if (!expandedReasoning) {
10253
+ return /* @__PURE__ */ jsxDEV9("box", {
10254
+ paddingX: 1,
10255
+ children: /* @__PURE__ */ jsxDEV9("text", {
10256
+ fg: cliTheme.text.muted,
10257
+ attributes: TextAttributes7.DIM,
10258
+ children: "Thinking\u2026 (Ctrl+R to view)"
10259
+ }, undefined, false, undefined, this)
10260
+ }, undefined, false, undefined, this);
10261
+ }
10262
+ return /* @__PURE__ */ jsxDEV9("box", {
9851
10263
  flexDirection: "column",
9852
10264
  gap: 1,
9853
10265
  children: [
9854
- /* @__PURE__ */ jsxDEV8("text", {
10266
+ /* @__PURE__ */ jsxDEV9("text", {
9855
10267
  fg: cliTheme.semantic.info,
9856
10268
  children: "Thinking:"
9857
10269
  }, undefined, false, undefined, this),
9858
- /* @__PURE__ */ jsxDEV8("text", {
10270
+ /* @__PURE__ */ jsxDEV9("text", {
9859
10271
  fg: cliTheme.text.secondary,
9860
10272
  children: part.text
9861
10273
  }, undefined, false, undefined, this)
@@ -9863,6 +10275,7 @@ function ChatMessageReasoningPart({ part }) {
9863
10275
  }, undefined, true, undefined, this);
9864
10276
  }
9865
10277
  var init_chat_message_reasoning_part = __esm(() => {
10278
+ init_app_state();
9866
10279
  init_cli_theme();
9867
10280
  });
9868
10281
 
@@ -9929,13 +10342,13 @@ var init_code_syntax_style = __esm(() => {
9929
10342
  });
9930
10343
 
9931
10344
  // apps/cli/src/components/chat/chat-message-text-part.tsx
9932
- import { jsxDEV as jsxDEV9 } from "@opentui/react/jsx-dev-runtime";
10345
+ import { jsxDEV as jsxDEV10 } from "@opentui/react/jsx-dev-runtime";
9933
10346
  function renderMarkdown(content, key) {
9934
10347
  const trimmedContent = content.trim();
9935
10348
  if (trimmedContent.length === 0) {
9936
10349
  return null;
9937
10350
  }
9938
- return /* @__PURE__ */ jsxDEV9("markdown", {
10351
+ return /* @__PURE__ */ jsxDEV10("markdown", {
9939
10352
  width: "100%",
9940
10353
  content: trimmedContent,
9941
10354
  syntaxStyle: markdownSyntaxStyle,
@@ -9960,7 +10373,7 @@ function ChatMessageTextPart({ part }) {
9960
10373
  const renderedSegments = segments.map((segment, index) => {
9961
10374
  const key = `${segment.type}:${index}`;
9962
10375
  if (segment.type === "plan") {
9963
- return /* @__PURE__ */ jsxDEV9(ChatProposedPlanCard, {
10376
+ return /* @__PURE__ */ jsxDEV10(ChatProposedPlanCard, {
9964
10377
  content: segment.text
9965
10378
  }, key, false, undefined, this);
9966
10379
  }
@@ -9969,7 +10382,7 @@ function ChatMessageTextPart({ part }) {
9969
10382
  if (renderedSegments.length === 0) {
9970
10383
  return null;
9971
10384
  }
9972
- return /* @__PURE__ */ jsxDEV9("box", {
10385
+ return /* @__PURE__ */ jsxDEV10("box", {
9973
10386
  width: "100%",
9974
10387
  flexDirection: "column",
9975
10388
  gap: 1,
@@ -9984,113 +10397,35 @@ var init_chat_message_text_part = __esm(() => {
9984
10397
  markdownSyntaxStyle = codeSyntaxStyle;
9985
10398
  });
9986
10399
 
9987
- // apps/cli/src/state/app-state.tsx
9988
- import { createContext, useCallback as useCallback2, useContext, useMemo as useMemo3, useState as useState3 } from "react";
9989
- import { jsxDEV as jsxDEV10 } from "@opentui/react/jsx-dev-runtime";
9990
- function AppStateProvider({ children }) {
9991
- const [paletteOpen, setPaletteOpen] = useState3(false);
9992
- const [paletteQuery, setPaletteQuery] = useState3("");
9993
- const [paletteSelected, setPaletteSelected] = useState3(0);
9994
- const [slashMenuOpen, setSlashMenuOpen] = useState3(false);
9995
- const [slashMenuQuery, setSlashMenuQuery] = useState3("/");
9996
- const [slashMenuSelected, setSlashMenuSelected] = useState3(0);
9997
- const openPalette = useCallback2((initialQuery = "") => {
9998
- setPaletteOpen(true);
9999
- setPaletteQuery(initialQuery);
10000
- setPaletteSelected(0);
10001
- setSlashMenuOpen(false);
10002
- }, []);
10003
- const closePalette = useCallback2(() => {
10004
- setPaletteOpen(false);
10005
- setPaletteQuery("");
10006
- setPaletteSelected(0);
10007
- }, []);
10008
- const openSlashMenu = useCallback2(() => {
10009
- setSlashMenuOpen(true);
10010
- setSlashMenuQuery("/");
10011
- setSlashMenuSelected(0);
10012
- setPaletteOpen(false);
10013
- }, []);
10014
- const closeSlashMenu = useCallback2(() => {
10015
- setSlashMenuOpen(false);
10016
- setSlashMenuQuery("/");
10017
- setSlashMenuSelected(0);
10018
- }, []);
10019
- const [requestedChatActionId, setRequestedChatActionId] = useState3(null);
10020
- const requestChatAction = useCallback2((id) => {
10021
- setRequestedChatActionId(id);
10022
- }, []);
10023
- const clearRequestedChatAction = useCallback2(() => {
10024
- setRequestedChatActionId(null);
10025
- }, []);
10026
- const [configRefreshNonce, setConfigRefreshNonce] = useState3(0);
10027
- const bumpConfigRefresh = useCallback2(() => {
10028
- setConfigRefreshNonce((nonce) => nonce + 1);
10029
- }, []);
10030
- const [expandedToolOutput, setExpandedToolOutput] = useState3(false);
10031
- const toggleToolOutputExpansion = useCallback2(() => {
10032
- setExpandedToolOutput((expanded) => !expanded);
10033
- }, []);
10034
- const value = useMemo3(() => ({
10035
- paletteOpen,
10036
- paletteQuery,
10037
- setPaletteQuery,
10038
- paletteSelected,
10039
- setPaletteSelected,
10040
- openPalette,
10041
- closePalette,
10042
- slashMenuOpen,
10043
- slashMenuQuery,
10044
- setSlashMenuQuery,
10045
- slashMenuSelected,
10046
- setSlashMenuSelected,
10047
- openSlashMenu,
10048
- closeSlashMenu,
10049
- requestedChatActionId,
10050
- requestChatAction,
10051
- clearRequestedChatAction,
10052
- configRefreshNonce,
10053
- bumpConfigRefresh,
10054
- expandedToolOutput,
10055
- toggleToolOutputExpansion
10056
- }), [
10057
- paletteOpen,
10058
- paletteQuery,
10059
- paletteSelected,
10060
- openPalette,
10061
- closePalette,
10062
- slashMenuOpen,
10063
- slashMenuQuery,
10064
- slashMenuSelected,
10065
- openSlashMenu,
10066
- closeSlashMenu,
10067
- requestedChatActionId,
10068
- requestChatAction,
10069
- clearRequestedChatAction,
10070
- configRefreshNonce,
10071
- bumpConfigRefresh,
10072
- expandedToolOutput,
10073
- toggleToolOutputExpansion
10074
- ]);
10075
- return /* @__PURE__ */ jsxDEV10(AppStateContext.Provider, {
10076
- value,
10077
- children
10078
- }, undefined, false, undefined, this);
10079
- }
10080
- function useAppState() {
10081
- const context2 = useContext(AppStateContext);
10082
- if (!context2) {
10083
- throw new Error("useAppState must be used within AppStateProvider");
10400
+ // apps/cli/src/ui/cli-theme-capabilities.ts
10401
+ function isUtf8Locale(env) {
10402
+ const locale = env.LC_ALL || env.LC_CTYPE || env.LANG || "";
10403
+ if (!locale) {
10404
+ return true;
10084
10405
  }
10085
- return context2;
10406
+ return /utf-?8/i.test(locale);
10086
10407
  }
10087
- var AppStateContext;
10088
- var init_app_state = __esm(() => {
10089
- AppStateContext = createContext(null);
10408
+ function detectTerminalCapabilities(env = process.env) {
10409
+ const noColor = Boolean(env.NO_COLOR);
10410
+ const dumbTerm = env.TERM === "dumb";
10411
+ return {
10412
+ color: !noColor && !dumbTerm,
10413
+ unicode: isUtf8Locale(env),
10414
+ reducedMotion: Boolean(env.NO_COLOR || env.LIGHTCODE_REDUCED_MOTION)
10415
+ };
10416
+ }
10417
+ function resolveGlyphs(caps) {
10418
+ return caps.unicode ? glyphs.unicode : glyphs.ascii;
10419
+ }
10420
+ var terminalCapabilities, activeGlyphs;
10421
+ var init_cli_theme_capabilities = __esm(() => {
10422
+ init_cli_theme();
10423
+ terminalCapabilities = detectTerminalCapabilities();
10424
+ activeGlyphs = resolveGlyphs(terminalCapabilities);
10090
10425
  });
10091
10426
 
10092
10427
  // apps/cli/src/components/chat/chat-diff-card.tsx
10093
- import { TextAttributes as TextAttributes7 } from "@opentui/core";
10428
+ import { TextAttributes as TextAttributes8 } from "@opentui/core";
10094
10429
  import { jsxDEV as jsxDEV11 } from "@opentui/react/jsx-dev-runtime";
10095
10430
  function collapseDiff(diff) {
10096
10431
  const lines = diff.split(`
@@ -10120,7 +10455,7 @@ function ChatDiffCard({ path: path16, diff }) {
10120
10455
  return /* @__PURE__ */ jsxDEV11("box", {
10121
10456
  width: "100%",
10122
10457
  flexDirection: "column",
10123
- borderStyle: "single",
10458
+ borderStyle: borderStyleFor.card,
10124
10459
  borderColor: cliTheme.borders.subtle,
10125
10460
  backgroundColor: cliTheme.surfaces.inset,
10126
10461
  children: [
@@ -10128,7 +10463,7 @@ function ChatDiffCard({ path: path16, diff }) {
10128
10463
  paddingX: 1,
10129
10464
  children: /* @__PURE__ */ jsxDEV11("text", {
10130
10465
  fg: cliTheme.text.secondary,
10131
- attributes: TextAttributes7.BOLD,
10466
+ attributes: TextAttributes8.BOLD,
10132
10467
  children: path16
10133
10468
  }, undefined, false, undefined, this)
10134
10469
  }, undefined, false, undefined, this),
@@ -10139,32 +10474,32 @@ function ChatDiffCard({ path: path16, diff }) {
10139
10474
  filetype: inferFiletype(path16),
10140
10475
  syntaxStyle: codeSyntaxStyle,
10141
10476
  showLineNumbers: true,
10142
- addedBg,
10143
- removedBg,
10477
+ addedBg: cliTheme.diff.addedBg,
10478
+ removedBg: cliTheme.diff.removedBg,
10144
10479
  lineNumberFg: cliTheme.text.muted,
10145
10480
  lineNumberBg: cliTheme.surfaces.inset,
10146
- addedSignColor: cliTheme.semantic.success,
10147
- removedSignColor: cliTheme.semantic.error
10481
+ addedSignColor: cliTheme.diff.added,
10482
+ removedSignColor: cliTheme.diff.removed
10148
10483
  }, undefined, false, undefined, this),
10149
10484
  hiddenLines > 0 || wasPreTruncated ? /* @__PURE__ */ jsxDEV11("box", {
10150
10485
  paddingX: 1,
10151
10486
  children: /* @__PURE__ */ jsxDEV11("text", {
10152
10487
  fg: cliTheme.text.muted,
10153
- attributes: TextAttributes7.DIM,
10488
+ attributes: TextAttributes8.DIM,
10154
10489
  children: hiddenLines > 0 ? `+${hiddenLines} more diff lines` : "diff truncated"
10155
10490
  }, undefined, false, undefined, this)
10156
10491
  }, undefined, false, undefined, this) : null
10157
10492
  ]
10158
10493
  }, undefined, true, undefined, this);
10159
10494
  }
10160
- var COLLAPSED_MAX_LINES = 20, TRUNCATION_MARKER = "... diff truncated ...", addedBg = "#13271B", removedBg = "#2B1619";
10495
+ var COLLAPSED_MAX_LINES = 20, TRUNCATION_MARKER = "... diff truncated ...";
10161
10496
  var init_chat_diff_card = __esm(() => {
10162
10497
  init_cli_theme();
10163
10498
  init_code_syntax_style();
10164
10499
  });
10165
10500
 
10166
10501
  // apps/cli/src/components/chat/chat-message-tool-invocation-part.tsx
10167
- import { TextAttributes as TextAttributes8 } from "@opentui/core";
10502
+ import { TextAttributes as TextAttributes9 } from "@opentui/core";
10168
10503
  import {
10169
10504
  getToolName
10170
10505
  } from "ai";
@@ -10250,6 +10585,78 @@ function getOutputPreviewLines(part, maxLines) {
10250
10585
  }
10251
10586
  return [];
10252
10587
  }
10588
+ function basename(filePath) {
10589
+ const parts = filePath.split(/[\\/]/).filter(Boolean);
10590
+ return parts[parts.length - 1] ?? filePath;
10591
+ }
10592
+ function countTextLines(text) {
10593
+ const trimmed = text.replace(/[\r\n]+$/, "");
10594
+ return trimmed.length === 0 ? 0 : trimmed.split(/\r?\n/).length;
10595
+ }
10596
+ function countDiffStats(diff) {
10597
+ let added = 0;
10598
+ let removed = 0;
10599
+ for (const line of diff.split(/\r?\n/)) {
10600
+ if (line.startsWith("+") && !line.startsWith("+++")) {
10601
+ added += 1;
10602
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
10603
+ removed += 1;
10604
+ }
10605
+ }
10606
+ return { added, removed };
10607
+ }
10608
+ function pluralize(count, noun) {
10609
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
10610
+ }
10611
+ function summarizeToolOutput(part) {
10612
+ if (part.state !== "output-available") {
10613
+ return null;
10614
+ }
10615
+ const output = part.output;
10616
+ if (typeof output === "string") {
10617
+ const lines = countTextLines(output);
10618
+ return lines > 0 ? { text: pluralize(lines, "line"), isError: false } : null;
10619
+ }
10620
+ if (!output || typeof output !== "object") {
10621
+ return null;
10622
+ }
10623
+ const exitCode = Reflect.get(output, "exitCode");
10624
+ if (typeof exitCode === "number") {
10625
+ const stdout = Reflect.get(output, "stdout");
10626
+ const stdoutText = typeof stdout === "string" ? stdout : "";
10627
+ const testMatch = /(\d+)\s+(passed|failed|passing|failing)/i.exec(stdoutText);
10628
+ if (testMatch) {
10629
+ return { text: `Tests: ${testMatch[0]}`, isError: exitCode !== 0 };
10630
+ }
10631
+ const lines = countTextLines(stdoutText);
10632
+ const tail = lines > 0 ? ` \xB7 ${pluralize(lines, "line")}` : "";
10633
+ return { text: `exit ${exitCode}${tail}`, isError: exitCode !== 0 };
10634
+ }
10635
+ for (const key of ["content", "text"]) {
10636
+ const value = Reflect.get(output, key);
10637
+ if (typeof value === "string" && value.trim()) {
10638
+ return { text: pluralize(countTextLines(value), "line"), isError: false };
10639
+ }
10640
+ }
10641
+ const matches = Reflect.get(output, "matches");
10642
+ if (Array.isArray(matches)) {
10643
+ return { text: pluralize(matches.length, "match"), isError: false };
10644
+ }
10645
+ for (const key of ["paths", "files", "results", "entries"]) {
10646
+ const arr = Reflect.get(output, key);
10647
+ if (Array.isArray(arr)) {
10648
+ const noun = key === "results" || key === "entries" ? "item" : "file";
10649
+ return { text: pluralize(arr.length, noun), isError: false };
10650
+ }
10651
+ }
10652
+ for (const key of ["summary", "message"]) {
10653
+ const value = Reflect.get(output, key);
10654
+ if (typeof value === "string" && value.trim()) {
10655
+ return { text: truncateInline2(value, 100), isError: false };
10656
+ }
10657
+ }
10658
+ return null;
10659
+ }
10253
10660
  function ChatMessageToolInvocationPart({
10254
10661
  part,
10255
10662
  pendingApprovalIds
@@ -10259,12 +10666,18 @@ function ChatMessageToolInvocationPart({
10259
10666
  const target = normalizeToolTarget(getToolTarget(part.input));
10260
10667
  const label = target ?? humanizeToolName(toolName);
10261
10668
  const isPendingApproval = part.state === "input-available" && pendingApprovalIds?.has(part.toolCallId);
10262
- const stateView = isPendingApproval ? { symbol: "!", verb: "Approval pending for" } : TOOL_STATE_VIEW[part.state];
10669
+ const stateView = isPendingApproval ? { symbol: activeGlyphs.toolApproval, verb: "Approval pending for" } : TOOL_STATE_VIEW[part.state];
10263
10670
  const stateColor = isPendingApproval ? cliTheme.semantic.warning : getToolToneColor(part.state);
10264
10671
  const line = `${stateView.symbol} ${stateView.verb} [${humanizeToolName(toolName)}] ${target ?? ""}`.trimEnd();
10265
10672
  const fileEditDiff = getFileEditDiff(part);
10266
10673
  const secondaryParams = getSecondaryParams(part.input, target);
10267
- const outputPreviewLines = fileEditDiff ? [] : getOutputPreviewLines(part, expandedToolOutput ? EXPANDED_OUTPUT_LINES : COLLAPSED_OUTPUT_LINES);
10674
+ const editSummary = fileEditDiff ? (() => {
10675
+ const { added, removed } = countDiffStats(fileEditDiff.diff);
10676
+ return `Edited ${basename(fileEditDiff.path)} (+${added} -${removed})`;
10677
+ })() : null;
10678
+ const resultSummary = fileEditDiff ? null : summarizeToolOutput(part);
10679
+ const rawPreviewLines = !fileEditDiff && expandedToolOutput ? getOutputPreviewLines(part, EXPANDED_OUTPUT_LINES) : [];
10680
+ const hasCollapsedRawOutput = !fileEditDiff && !expandedToolOutput && getOutputPreviewLines(part, 1).length > 0;
10268
10681
  return /* @__PURE__ */ jsxDEV12("box", {
10269
10682
  flexDirection: "column",
10270
10683
  children: [
@@ -10274,9 +10687,24 @@ function ChatMessageToolInvocationPart({
10274
10687
  }, undefined, false, undefined, this),
10275
10688
  secondaryParams ? /* @__PURE__ */ jsxDEV12("text", {
10276
10689
  fg: cliTheme.text.muted,
10277
- attributes: TextAttributes8.DIM,
10690
+ attributes: TextAttributes9.DIM,
10278
10691
  children: ` ${secondaryParams}`
10279
10692
  }, undefined, false, undefined, this) : null,
10693
+ editSummary ? /* @__PURE__ */ jsxDEV12("text", {
10694
+ fg: cliTheme.text.muted,
10695
+ children: ` ${activeGlyphs.indent} ${editSummary}`
10696
+ }, undefined, false, undefined, this) : null,
10697
+ resultSummary ? /* @__PURE__ */ jsxDEV12("text", {
10698
+ fg: resultSummary.isError ? cliTheme.semantic.warning : cliTheme.text.muted,
10699
+ children: [
10700
+ ` ${activeGlyphs.indent} ${resultSummary.text}`,
10701
+ hasCollapsedRawOutput ? " \xB7 Ctrl+O" : ""
10702
+ ]
10703
+ }, undefined, true, undefined, this) : hasCollapsedRawOutput ? /* @__PURE__ */ jsxDEV12("text", {
10704
+ fg: cliTheme.text.muted,
10705
+ attributes: TextAttributes9.DIM,
10706
+ children: ` ${activeGlyphs.indent} Ctrl+O to expand`
10707
+ }, undefined, false, undefined, this) : null,
10280
10708
  part.state === "output-error" ? /* @__PURE__ */ jsxDEV12("text", {
10281
10709
  fg: cliTheme.text.secondary,
10282
10710
  children: part.errorText
@@ -10288,22 +10716,15 @@ function ChatMessageToolInvocationPart({
10288
10716
  part.approval.reason
10289
10717
  ]
10290
10718
  }, undefined, true, undefined, this) : null,
10291
- outputPreviewLines.length > 0 ? /* @__PURE__ */ jsxDEV12("box", {
10719
+ rawPreviewLines.length > 0 ? /* @__PURE__ */ jsxDEV12("box", {
10292
10720
  flexDirection: "column",
10293
10721
  paddingX: 1,
10294
10722
  backgroundColor: cliTheme.surfaces.inset,
10295
- children: [
10296
- outputPreviewLines.map((previewLine, index) => /* @__PURE__ */ jsxDEV12("text", {
10297
- fg: cliTheme.text.muted,
10298
- children: previewLine || " "
10299
- }, `${part.toolCallId}-preview-${index}`, false, undefined, this)),
10300
- !expandedToolOutput ? /* @__PURE__ */ jsxDEV12("text", {
10301
- fg: cliTheme.text.muted,
10302
- attributes: TextAttributes8.DIM,
10303
- children: "Ctrl+O expand"
10304
- }, undefined, false, undefined, this) : null
10305
- ]
10306
- }, undefined, true, undefined, this) : null,
10723
+ children: rawPreviewLines.map((previewLine, index) => /* @__PURE__ */ jsxDEV12("text", {
10724
+ fg: cliTheme.text.muted,
10725
+ children: previewLine || " "
10726
+ }, `${part.toolCallId}-preview-${index}`, false, undefined, this))
10727
+ }, undefined, false, undefined, this) : null,
10307
10728
  fileEditDiff ? /* @__PURE__ */ jsxDEV12(ChatDiffCard, {
10308
10729
  path: fileEditDiff.path,
10309
10730
  diff: fileEditDiff.diff
@@ -10311,10 +10732,11 @@ function ChatMessageToolInvocationPart({
10311
10732
  ]
10312
10733
  }, undefined, true, undefined, this);
10313
10734
  }
10314
- var COLLAPSED_OUTPUT_LINES = 3, EXPANDED_OUTPUT_LINES = 18, OUTPUT_LINE_MAX_CHARS = 160, previewOutputKeys, TOOL_STATE_VIEW;
10735
+ var EXPANDED_OUTPUT_LINES = 18, OUTPUT_LINE_MAX_CHARS = 160, previewOutputKeys, TOOL_STATE_VIEW;
10315
10736
  var init_chat_message_tool_invocation_part = __esm(() => {
10316
10737
  init_app_state();
10317
10738
  init_cli_theme();
10739
+ init_cli_theme_capabilities();
10318
10740
  init_text_utils();
10319
10741
  init_chat_diff_card();
10320
10742
  previewOutputKeys = [
@@ -10326,19 +10748,19 @@ var init_chat_message_tool_invocation_part = __esm(() => {
10326
10748
  "message"
10327
10749
  ];
10328
10750
  TOOL_STATE_VIEW = {
10329
- "input-streaming": { symbol: ">", verb: "Preparing" },
10330
- "input-available": { symbol: ">", verb: "Running" },
10331
- "approval-requested": { symbol: "!", verb: "Approval needed for" },
10332
- "approval-responded": { symbol: ">", verb: "Approval received for" },
10333
- "output-available": { symbol: "OK", verb: "Completed" },
10334
- "output-error": { symbol: "ERR", verb: "Failed" },
10335
- "output-denied": { symbol: "NO", verb: "Denied" }
10751
+ "input-streaming": { symbol: activeGlyphs.toolRunning, verb: "Preparing" },
10752
+ "input-available": { symbol: activeGlyphs.toolRunning, verb: "Running" },
10753
+ "approval-requested": { symbol: activeGlyphs.toolApproval, verb: "Approval needed for" },
10754
+ "approval-responded": { symbol: activeGlyphs.toolRunning, verb: "Approval received for" },
10755
+ "output-available": { symbol: activeGlyphs.toolOk, verb: "Completed" },
10756
+ "output-error": { symbol: activeGlyphs.toolError, verb: "Failed" },
10757
+ "output-denied": { symbol: activeGlyphs.toolDenied, verb: "Denied" }
10336
10758
  };
10337
10759
  });
10338
10760
 
10339
10761
  // apps/cli/src/components/chat/chat-message.tsx
10340
10762
  import { isToolUIPart as isToolUIPart3 } from "ai";
10341
- import { TextAttributes as TextAttributes9 } from "@opentui/core";
10763
+ import { TextAttributes as TextAttributes10 } from "@opentui/core";
10342
10764
  import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
10343
10765
  function ChatMessage({ message, pendingApprovalIds }) {
10344
10766
  const roleStyle = getMessageRoleTheme(message.role);
@@ -10374,43 +10796,45 @@ function ChatMessage({ message, pendingApprovalIds }) {
10374
10796
  return /* @__PURE__ */ jsxDEV13("box", {
10375
10797
  width: "100%",
10376
10798
  flexDirection: "column",
10377
- borderStyle: "single",
10378
- borderColor: roleStyle.borderColor,
10379
- backgroundColor: roleStyle.backgroundColor,
10380
- paddingX: 1,
10381
- paddingY: 1,
10382
- gap: 1,
10799
+ gap: space.xs,
10383
10800
  children: [
10384
10801
  /* @__PURE__ */ jsxDEV13("text", {
10385
10802
  fg: roleStyle.labelColor,
10386
- attributes: TextAttributes9.BOLD,
10387
- children: ROLE_LABELS[message.role]
10803
+ attributes: TextAttributes10.BOLD,
10804
+ children: `${ROLE_GLYPHS[message.role]} ${ROLE_LABELS[message.role]}`
10388
10805
  }, undefined, false, undefined, this),
10389
10806
  /* @__PURE__ */ jsxDEV13("box", {
10390
10807
  width: "100%",
10391
10808
  flexDirection: "column",
10392
- gap: 1,
10809
+ gap: space.xs,
10810
+ paddingLeft: space.md,
10393
10811
  children: renderedParts
10394
10812
  }, undefined, false, undefined, this)
10395
10813
  ]
10396
10814
  }, undefined, true, undefined, this);
10397
10815
  }
10398
- var ROLE_LABELS;
10816
+ var ROLE_LABELS, ROLE_GLYPHS;
10399
10817
  var init_chat_message = __esm(() => {
10400
10818
  init_chat_message_error_part();
10401
10819
  init_chat_message_reasoning_part();
10402
10820
  init_chat_message_text_part();
10403
10821
  init_chat_message_tool_invocation_part();
10404
10822
  init_cli_theme();
10823
+ init_cli_theme_capabilities();
10405
10824
  ROLE_LABELS = {
10406
- user: "You",
10407
- assistant: "Assistant",
10408
- system: "System"
10825
+ user: "you",
10826
+ assistant: "assistant",
10827
+ system: "system"
10828
+ };
10829
+ ROLE_GLYPHS = {
10830
+ user: activeGlyphs.roleUser,
10831
+ assistant: activeGlyphs.roleAssistant,
10832
+ system: activeGlyphs.roleSystem
10409
10833
  };
10410
10834
  });
10411
10835
 
10412
10836
  // apps/cli/src/components/chat/chat-shell.tsx
10413
- import { TextAttributes as TextAttributes10 } from "@opentui/core";
10837
+ import { TextAttributes as TextAttributes11 } from "@opentui/core";
10414
10838
  import { jsxDEV as jsxDEV14 } from "@opentui/react/jsx-dev-runtime";
10415
10839
  function ChatShell({
10416
10840
  title = "Conversation",
@@ -10436,7 +10860,7 @@ function ChatShell({
10436
10860
  children: [
10437
10861
  /* @__PURE__ */ jsxDEV14("text", {
10438
10862
  fg: cliTheme.text.secondary,
10439
- attributes: TextAttributes10.BOLD,
10863
+ attributes: TextAttributes11.BOLD,
10440
10864
  children: title
10441
10865
  }, undefined, false, undefined, this),
10442
10866
  /* @__PURE__ */ jsxDEV14("text", {
@@ -10463,7 +10887,7 @@ function ChatShell({
10463
10887
  contentOptions: {
10464
10888
  width: "100%",
10465
10889
  flexDirection: "column",
10466
- gap: 1
10890
+ gap: space.md
10467
10891
  },
10468
10892
  scrollbarOptions: {
10469
10893
  trackOptions: {
@@ -10477,10 +10901,29 @@ function ChatShell({
10477
10901
  backgroundColor: cliTheme.scroll.rail
10478
10902
  }
10479
10903
  },
10480
- children: hasMessages ? children : /* @__PURE__ */ jsxDEV14("text", {
10481
- fg: cliTheme.text.muted,
10482
- children: emptyStateLabel
10483
- }, undefined, false, undefined, this)
10904
+ children: hasMessages ? children : /* @__PURE__ */ jsxDEV14("box", {
10905
+ width: "100%",
10906
+ height: "100%",
10907
+ flexDirection: "column",
10908
+ alignItems: "center",
10909
+ justifyContent: "center",
10910
+ gap: space.xs,
10911
+ children: [
10912
+ /* @__PURE__ */ jsxDEV14("text", {
10913
+ fg: cliTheme.accent.primary,
10914
+ attributes: TextAttributes11.BOLD,
10915
+ children: "\u25C6 ready"
10916
+ }, undefined, false, undefined, this),
10917
+ /* @__PURE__ */ jsxDEV14("text", {
10918
+ fg: cliTheme.text.secondary,
10919
+ children: emptyStateLabel
10920
+ }, undefined, false, undefined, this),
10921
+ /* @__PURE__ */ jsxDEV14("text", {
10922
+ fg: cliTheme.text.muted,
10923
+ children: "Type a message \xB7 / for commands \xB7 @ to attach files"
10924
+ }, undefined, false, undefined, this)
10925
+ ]
10926
+ }, undefined, true, undefined, this)
10484
10927
  }, undefined, false, undefined, this)
10485
10928
  }, undefined, false, undefined, this),
10486
10929
  errorMessage ? /* @__PURE__ */ jsxDEV14(ChatMessageErrorPart, {
@@ -10497,7 +10940,7 @@ var init_chat_shell = __esm(() => {
10497
10940
  });
10498
10941
 
10499
10942
  // apps/cli/src/components/chat/permission-mode-selector.tsx
10500
- import { TextAttributes as TextAttributes11 } from "@opentui/core";
10943
+ import { TextAttributes as TextAttributes12 } from "@opentui/core";
10501
10944
  import { useKeyboard as useKeyboard2 } from "@opentui/react";
10502
10945
  import { useEffect as useEffect2, useState as useState4 } from "react";
10503
10946
  import { jsxDEV as jsxDEV15 } from "@opentui/react/jsx-dev-runtime";
@@ -10549,7 +10992,7 @@ function PermissionModeSelector({
10549
10992
  return /* @__PURE__ */ jsxDEV15("box", {
10550
10993
  width: "100%",
10551
10994
  flexDirection: "column",
10552
- borderStyle: "single",
10995
+ borderStyle: borderStyleFor.modal,
10553
10996
  borderColor: cliTheme.overlay.border,
10554
10997
  backgroundColor: cliTheme.overlay.surface,
10555
10998
  children: [
@@ -10565,7 +11008,7 @@ function PermissionModeSelector({
10565
11008
  children: [
10566
11009
  /* @__PURE__ */ jsxDEV15("text", {
10567
11010
  fg: cliTheme.accent.primary,
10568
- attributes: TextAttributes11.BOLD,
11011
+ attributes: TextAttributes12.BOLD,
10569
11012
  children: "Permission Mode"
10570
11013
  }, undefined, false, undefined, this),
10571
11014
  /* @__PURE__ */ jsxDEV15("text", {
@@ -10598,12 +11041,12 @@ function PermissionModeSelector({
10598
11041
  /* @__PURE__ */ jsxDEV15("text", {
10599
11042
  width: 3,
10600
11043
  fg: isSelected ? cliTheme.accent.primary : cliTheme.text.muted,
10601
- attributes: TextAttributes11.BOLD,
11044
+ attributes: TextAttributes12.BOLD,
10602
11045
  children: isSelected ? "> " : " "
10603
11046
  }, undefined, false, undefined, this),
10604
11047
  /* @__PURE__ */ jsxDEV15("text", {
10605
11048
  fg: isSelected ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
10606
- attributes: isSelected ? TextAttributes11.BOLD : TextAttributes11.NONE,
11049
+ attributes: isSelected ? TextAttributes12.BOLD : TextAttributes12.NONE,
10607
11050
  children: modeOption.label
10608
11051
  }, undefined, false, undefined, this),
10609
11052
  modeOption.mode === currentMode && /* @__PURE__ */ jsxDEV15("text", {
@@ -10625,7 +11068,7 @@ function PermissionModeSelector({
10625
11068
  marginTop: 1,
10626
11069
  children: modeOption.risks.map((risk) => /* @__PURE__ */ jsxDEV15("text", {
10627
11070
  fg: modeOption.mode === "danger-full-access" ? cliTheme.semantic.error : cliTheme.text.muted,
10628
- attributes: TextAttributes11.DIM,
11071
+ attributes: TextAttributes12.DIM,
10629
11072
  children: [
10630
11073
  "\u2022 ",
10631
11074
  risk
@@ -10648,7 +11091,7 @@ function PermissionModeSelector({
10648
11091
  children: [
10649
11092
  /* @__PURE__ */ jsxDEV15("text", {
10650
11093
  fg: cliTheme.overlay.selectedRowText,
10651
- attributes: TextAttributes11.BOLD,
11094
+ attributes: TextAttributes12.BOLD,
10652
11095
  children: [
10653
11096
  "Selected: ",
10654
11097
  currentSelection.label
@@ -10724,7 +11167,7 @@ var init_fuzzy_match = __esm(() => {
10724
11167
  });
10725
11168
 
10726
11169
  // apps/cli/src/components/chat/chat-text-area.tsx
10727
- import { TextAttributes as TextAttributes12 } from "@opentui/core";
11170
+ import { TextAttributes as TextAttributes13 } from "@opentui/core";
10728
11171
  import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2, useState as useState5 } from "react";
10729
11172
  import { jsxDEV as jsxDEV16 } from "@opentui/react/jsx-dev-runtime";
10730
11173
  function getTrailingMentionQuery(text) {
@@ -10741,7 +11184,6 @@ function ChatTextArea({
10741
11184
  containerHeight,
10742
11185
  beforeInput,
10743
11186
  footer,
10744
- modeToggleHint = false,
10745
11187
  slashMenuOpen = false,
10746
11188
  onTextChange,
10747
11189
  mentionCandidates
@@ -10752,7 +11194,6 @@ function ChatTextArea({
10752
11194
  const [mentionQuery, setMentionQuery] = useState5(null);
10753
11195
  const [mentionSelected, setMentionSelected] = useState5(0);
10754
11196
  const isFocused = focused && !disabled;
10755
- const inputHint = modeToggleHint ? "Enter to send | Ctrl+Enter for newline | Tab/Ctrl+T to switch mode" : "Enter to send | Ctrl+Enter for newline";
10756
11197
  const mentionMatches = mentionQuery !== null && mentionCandidates && mentionCandidates.length > 0 ? fuzzyFilter(mentionQuery, mentionCandidates) : [];
10757
11198
  const selectedMentionIndex = Math.min(mentionSelected, Math.max(mentionMatches.length - 1, 0));
10758
11199
  const acceptMention = useCallback3((mentionPath) => {
@@ -10847,12 +11288,12 @@ function ChatTextArea({
10847
11288
  mentionMatches.map((mentionPath, index) => /* @__PURE__ */ jsxDEV16("text", {
10848
11289
  fg: index === selectedMentionIndex ? cliTheme.overlay.selectedRowText : cliTheme.text.secondary,
10849
11290
  bg: index === selectedMentionIndex ? cliTheme.overlay.selectedRowBackground : undefined,
10850
- attributes: index === selectedMentionIndex ? TextAttributes12.BOLD : TextAttributes12.NONE,
11291
+ attributes: index === selectedMentionIndex ? TextAttributes13.BOLD : TextAttributes13.NONE,
10851
11292
  children: `${index === selectedMentionIndex ? "> " : " "}${mentionPath}`
10852
11293
  }, mentionPath, false, undefined, this)),
10853
11294
  /* @__PURE__ */ jsxDEV16("text", {
10854
11295
  fg: cliTheme.overlay.footerText,
10855
- attributes: TextAttributes12.DIM,
11296
+ attributes: TextAttributes13.DIM,
10856
11297
  children: "Tab attach | Up/Down select | Esc dismiss"
10857
11298
  }, undefined, false, undefined, this)
10858
11299
  ]
@@ -10885,10 +11326,6 @@ function ChatTextArea({
10885
11326
  placeholderColor: cliTheme.input.placeholder,
10886
11327
  focused: isFocused
10887
11328
  }, undefined, false, undefined, this),
10888
- /* @__PURE__ */ jsxDEV16("text", {
10889
- fg: cliTheme.input.hint,
10890
- children: inputHint
10891
- }, undefined, false, undefined, this),
10892
11329
  footer
10893
11330
  ]
10894
11331
  }, undefined, true, undefined, this)
@@ -10910,7 +11347,7 @@ var init_chat_text_area = __esm(() => {
10910
11347
  });
10911
11348
 
10912
11349
  // apps/cli/src/components/chat/chat-todo-status-card.tsx
10913
- import { TextAttributes as TextAttributes13 } from "@opentui/core";
11350
+ import { TextAttributes as TextAttributes14 } from "@opentui/core";
10914
11351
  import { jsxDEV as jsxDEV17 } from "@opentui/react/jsx-dev-runtime";
10915
11352
  function getStatusColor(status) {
10916
11353
  if (status === "completed") {
@@ -10932,7 +11369,7 @@ function ChatTodoStatusCard({ todos }) {
10932
11369
  return /* @__PURE__ */ jsxDEV17("box", {
10933
11370
  width: "100%",
10934
11371
  flexDirection: "column",
10935
- borderStyle: "single",
11372
+ borderStyle: borderStyleFor.card,
10936
11373
  borderColor: cliTheme.semantic.info,
10937
11374
  backgroundColor: cliTheme.surfaces.panel,
10938
11375
  paddingX: 1,
@@ -10946,7 +11383,7 @@ function ChatTodoStatusCard({ todos }) {
10946
11383
  children: [
10947
11384
  /* @__PURE__ */ jsxDEV17("text", {
10948
11385
  fg: cliTheme.semantic.info,
10949
- attributes: TextAttributes13.BOLD,
11386
+ attributes: TextAttributes14.BOLD,
10950
11387
  children: "Session Todo"
10951
11388
  }, undefined, false, undefined, this),
10952
11389
  /* @__PURE__ */ jsxDEV17("text", {
@@ -10978,7 +11415,7 @@ function ChatTodoStatusCard({ todos }) {
10978
11415
  }, undefined, false, undefined, this),
10979
11416
  /* @__PURE__ */ jsxDEV17("text", {
10980
11417
  fg: getStatusColor(todo.status),
10981
- attributes: TextAttributes13.DIM,
11418
+ attributes: TextAttributes14.DIM,
10982
11419
  children: todoStatusLabel[todo.status]
10983
11420
  }, undefined, false, undefined, this)
10984
11421
  ]
@@ -10986,7 +11423,7 @@ function ChatTodoStatusCard({ todos }) {
10986
11423
  }, undefined, false, undefined, this),
10987
11424
  todos.length > 6 ? /* @__PURE__ */ jsxDEV17("text", {
10988
11425
  fg: cliTheme.text.muted,
10989
- attributes: TextAttributes13.DIM,
11426
+ attributes: TextAttributes14.DIM,
10990
11427
  children: [
10991
11428
  todos.length - 6,
10992
11429
  " more tasks hidden"
@@ -11008,7 +11445,7 @@ var init_chat_todo_status_card = __esm(() => {
11008
11445
  });
11009
11446
 
11010
11447
  // apps/cli/src/components/chat/chat-tool-approval-card.tsx
11011
- import { TextAttributes as TextAttributes14 } from "@opentui/core";
11448
+ import { TextAttributes as TextAttributes15 } from "@opentui/core";
11012
11449
  import { useKeyboard as useKeyboard3 } from "@opentui/react";
11013
11450
  import { useEffect as useEffect4, useState as useState6 } from "react";
11014
11451
  import { jsxDEV as jsxDEV18 } from "@opentui/react/jsx-dev-runtime";
@@ -11121,7 +11558,7 @@ function ChatToolApprovalCard({
11121
11558
  children: [
11122
11559
  /* @__PURE__ */ jsxDEV18("text", {
11123
11560
  fg: cliTheme.semantic.warning,
11124
- attributes: TextAttributes14.BOLD,
11561
+ attributes: TextAttributes15.BOLD,
11125
11562
  children: "Tool Approval"
11126
11563
  }, undefined, false, undefined, this),
11127
11564
  /* @__PURE__ */ jsxDEV18("text", {
@@ -11144,7 +11581,7 @@ function ChatToolApprovalCard({
11144
11581
  children: [
11145
11582
  /* @__PURE__ */ jsxDEV18("text", {
11146
11583
  fg: rowColors.primaryTextColor,
11147
- attributes: keyboardActive && index === safeSelectedIndex ? TextAttributes14.BOLD : TextAttributes14.NONE,
11584
+ attributes: keyboardActive && index === safeSelectedIndex ? TextAttributes15.BOLD : TextAttributes15.NONE,
11148
11585
  children: [
11149
11586
  keyboardActive && index === safeSelectedIndex ? "> " : " ",
11150
11587
  index + 1,
@@ -11156,7 +11593,7 @@ function ChatToolApprovalCard({
11156
11593
  }, undefined, true, undefined, this),
11157
11594
  /* @__PURE__ */ jsxDEV18("text", {
11158
11595
  fg: rowColors.secondaryTextColor,
11159
- attributes: TextAttributes14.DIM,
11596
+ attributes: TextAttributes15.DIM,
11160
11597
  children: getApprovalDescription(approval)
11161
11598
  }, undefined, false, undefined, this)
11162
11599
  ]
@@ -11170,7 +11607,7 @@ function ChatToolApprovalCard({
11170
11607
  children: [
11171
11608
  /* @__PURE__ */ jsxDEV18("text", {
11172
11609
  fg: cliTheme.text.muted,
11173
- attributes: TextAttributes14.DIM,
11610
+ attributes: TextAttributes15.DIM,
11174
11611
  children: keyboardActive ? "Enter approve | d deny | a approve all | Esc release focus" : "Down focus card | or type approve/deny below"
11175
11612
  }, undefined, false, undefined, this),
11176
11613
  /* @__PURE__ */ jsxDEV18("text", {
@@ -11187,30 +11624,80 @@ var init_chat_tool_approval_card = __esm(() => {
11187
11624
  init_cli_theme();
11188
11625
  });
11189
11626
 
11627
+ // apps/cli/src/ui/hooks/use-spinner.ts
11628
+ import { useEffect as useEffect5, useState as useState7 } from "react";
11629
+ function spinnerGlyph(tick, { active, reducedMotion = false, ascii = false }) {
11630
+ if (reducedMotion || !active) {
11631
+ return STATIC_GLYPH;
11632
+ }
11633
+ const frames = ascii ? ASCII_SPINNER_FRAMES : SPINNER_FRAMES;
11634
+ const index = (tick % frames.length + frames.length) % frames.length;
11635
+ return frames[index];
11636
+ }
11637
+ function useSpinner(options) {
11638
+ const { active, reducedMotion = false } = options;
11639
+ const [tick, setTick] = useState7(0);
11640
+ useEffect5(() => {
11641
+ if (!active || reducedMotion) {
11642
+ return;
11643
+ }
11644
+ const intervalId = setInterval(() => {
11645
+ setTick((current) => current + 1);
11646
+ }, FRAME_INTERVAL_MS);
11647
+ return () => clearInterval(intervalId);
11648
+ }, [active, reducedMotion]);
11649
+ return spinnerGlyph(tick, options);
11650
+ }
11651
+ var SPINNER_FRAMES, ASCII_SPINNER_FRAMES, STATIC_GLYPH = "\u25CF", FRAME_INTERVAL_MS = 100;
11652
+ var init_use_spinner = __esm(() => {
11653
+ SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
11654
+ ASCII_SPINNER_FRAMES = ["|", "/", "-", "\\"];
11655
+ });
11656
+
11190
11657
  // apps/cli/src/components/chat/loading-timer.tsx
11191
11658
  import { jsxDEV as jsxDEV19 } from "@opentui/react/jsx-dev-runtime";
11192
- function LoadingTimer({ elapsedSeconds }) {
11659
+ function formatTokens(tokens) {
11660
+ return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : `${tokens}`;
11661
+ }
11662
+ function LoadingTimer({ elapsedSeconds, outputTokens }) {
11663
+ const spinner = useSpinner({
11664
+ active: true,
11665
+ reducedMotion: terminalCapabilities.reducedMotion,
11666
+ ascii: !terminalCapabilities.unicode
11667
+ });
11193
11668
  const hours = Math.floor(elapsedSeconds / 3600);
11194
11669
  const minutes = Math.floor(elapsedSeconds % 3600 / 60);
11195
11670
  const seconds = Math.floor(elapsedSeconds % 60);
11196
11671
  const formattedTime = hours > 0 ? `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}` : `${minutes}:${seconds.toString().padStart(2, "0")}`;
11672
+ const tokenSuffix = typeof outputTokens === "number" && outputTokens > 0 ? ` \xB7 ${formatTokens(outputTokens)} tok` : "";
11197
11673
  return /* @__PURE__ */ jsxDEV19("box", {
11198
11674
  paddingX: 1,
11199
- children: /* @__PURE__ */ jsxDEV19("text", {
11200
- fg: cliTheme.semantic.info,
11201
- children: [
11202
- "Thinking ",
11203
- formattedTime
11204
- ]
11205
- }, undefined, true, undefined, this)
11206
- }, undefined, false, undefined, this);
11675
+ flexDirection: "row",
11676
+ gap: 1,
11677
+ children: [
11678
+ /* @__PURE__ */ jsxDEV19("text", {
11679
+ fg: cliTheme.accent.primary,
11680
+ children: spinner
11681
+ }, undefined, false, undefined, this),
11682
+ /* @__PURE__ */ jsxDEV19("text", {
11683
+ fg: cliTheme.semantic.info,
11684
+ children: [
11685
+ "Thinking ",
11686
+ formattedTime,
11687
+ tokenSuffix
11688
+ ]
11689
+ }, undefined, true, undefined, this)
11690
+ ]
11691
+ }, undefined, true, undefined, this);
11207
11692
  }
11208
11693
  var init_loading_timer = __esm(() => {
11209
11694
  init_cli_theme();
11695
+ init_cli_theme_capabilities();
11696
+ init_use_spinner();
11210
11697
  });
11211
11698
 
11212
11699
  // apps/cli/src/components/chat/copy-mode-overlay.tsx
11213
- import { TextAttributes as TextAttributes15 } from "@opentui/core";
11700
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
11214
11701
  import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
11215
11702
  function previewOf(message) {
11216
11703
  const text = collectMessageText(message).replace(/\s+/g, " ").trim();
@@ -11231,7 +11718,7 @@ function CopyModeOverlay({ messages, selectedIndex }) {
11231
11718
  children: [
11232
11719
  /* @__PURE__ */ jsxDEV20("text", {
11233
11720
  fg: cliTheme.accent.primary,
11234
- attributes: TextAttributes15.BOLD,
11721
+ attributes: TextAttributes16.BOLD,
11235
11722
  children: "Copy mode \u2014 \u2191/\u2193 select \xB7 Enter copy \xB7 c code blocks \xB7 Esc exit"
11236
11723
  }, undefined, false, undefined, this),
11237
11724
  visible.map((message, offset) => {
@@ -11250,7 +11737,7 @@ function CopyModeOverlay({ messages, selectedIndex }) {
11250
11737
  }),
11251
11738
  total > visible.length ? /* @__PURE__ */ jsxDEV20("text", {
11252
11739
  fg: cliTheme.text.muted,
11253
- attributes: TextAttributes15.DIM,
11740
+ attributes: TextAttributes16.DIM,
11254
11741
  children: `${selectedIndex + 1}/${total}`
11255
11742
  }, undefined, false, undefined, this) : null
11256
11743
  ]
@@ -11264,10 +11751,10 @@ var init_copy_mode_overlay = __esm(() => {
11264
11751
  });
11265
11752
 
11266
11753
  // apps/cli/src/hooks/use-auto-continue-config.ts
11267
- import { useEffect as useEffect5, useState as useState7 } from "react";
11754
+ import { useEffect as useEffect6, useState as useState8 } from "react";
11268
11755
  function useAutoContinueConfig() {
11269
- const [config4, setConfig] = useState7(defaultAutoContinueConfig);
11270
- useEffect5(() => {
11756
+ const [config4, setConfig] = useState8(defaultAutoContinueConfig);
11757
+ useEffect6(() => {
11271
11758
  let cancelled = false;
11272
11759
  async function loadConfig() {
11273
11760
  try {
@@ -11294,10 +11781,10 @@ var init_use_auto_continue_config = __esm(() => {
11294
11781
  });
11295
11782
 
11296
11783
  // apps/cli/src/hooks/use-context-window.ts
11297
- import { useEffect as useEffect6, useState as useState8 } from "react";
11784
+ import { useEffect as useEffect7, useState as useState9 } from "react";
11298
11785
  function useContextWindow() {
11299
- const [contextWindow, setContextWindow] = useState8(DEFAULT_CONTEXT_WINDOW_TOKENS);
11300
- useEffect6(() => {
11786
+ const [contextWindow, setContextWindow] = useState9(DEFAULT_CONTEXT_WINDOW_TOKENS);
11787
+ useEffect7(() => {
11301
11788
  let cancelled = false;
11302
11789
  async function loadContextWindow() {
11303
11790
  try {
@@ -11324,12 +11811,12 @@ var init_use_context_window = __esm(() => {
11324
11811
  });
11325
11812
 
11326
11813
  // apps/cli/src/hooks/use-loading-timer.ts
11327
- import { useEffect as useEffect7, useRef as useRef3, useState as useState9 } from "react";
11814
+ import { useEffect as useEffect8, useRef as useRef3, useState as useState10 } from "react";
11328
11815
  function useLoadingTimer(isActive) {
11329
- const [elapsedSeconds, setElapsedSeconds] = useState9(0);
11816
+ const [elapsedSeconds, setElapsedSeconds] = useState10(0);
11330
11817
  const intervalRef = useRef3(null);
11331
11818
  const startTimeRef = useRef3(null);
11332
- useEffect7(() => {
11819
+ useEffect8(() => {
11333
11820
  if (!isActive) {
11334
11821
  if (intervalRef.current) {
11335
11822
  clearInterval(intervalRef.current);
@@ -11380,19 +11867,33 @@ var init_route_state = __esm(() => {
11380
11867
  });
11381
11868
 
11382
11869
  // apps/cli/src/utils/chat-context-utils.ts
11870
+ function latestServerContext(messages) {
11871
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
11872
+ const context2 = getMessageUsageMetadata(messages[index])?.context;
11873
+ if (context2) {
11874
+ return {
11875
+ inputTokens: context2.inputTokens,
11876
+ contextWindow: context2.contextWindow
11877
+ };
11878
+ }
11879
+ }
11880
+ return null;
11881
+ }
11383
11882
  function formatTokenCount(tokens) {
11384
11883
  return tokens >= 1000 ? `${Math.round(tokens / 1000)}k` : `${tokens}`;
11385
11884
  }
11386
11885
  function estimateContextUsage(messages, contextWindow) {
11387
- const estimate2 = estimateContextTokens(messages);
11388
- const percentage = Math.min(100, Math.round(estimate2.tokens / Math.max(1, contextWindow) * 100));
11886
+ const serverContext = latestServerContext(messages);
11887
+ const estimate2 = serverContext ? { tokens: serverContext.inputTokens, basis: "usage_calibrated" } : estimateContextTokens(messages);
11888
+ const effectiveWindow = serverContext?.contextWindow ?? contextWindow;
11889
+ const percentage = Math.min(100, Math.round(estimate2.tokens / Math.max(1, effectiveWindow) * 100));
11389
11890
  const level = percentage >= CRITICAL_PERCENTAGE ? "critical" : percentage >= WARNING_PERCENTAGE ? "warning" : "normal";
11390
11891
  return {
11391
11892
  tokens: estimate2.tokens,
11392
11893
  basis: estimate2.basis,
11393
11894
  percentage,
11394
11895
  level,
11395
- displayText: `${formatTokenCount(estimate2.tokens)}/${formatTokenCount(contextWindow)} ${percentage}%`
11896
+ displayText: `${formatTokenCount(estimate2.tokens)}/${formatTokenCount(effectiveWindow)} ${percentage}%`
11396
11897
  };
11397
11898
  }
11398
11899
  var WARNING_PERCENTAGE = 60, CRITICAL_PERCENTAGE = 85;
@@ -11481,11 +11982,35 @@ var init_file_mentions = __esm(() => {
11481
11982
  };
11482
11983
  });
11483
11984
 
11985
+ // apps/cli/src/ui/components/badge.tsx
11986
+ import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
11987
+ function Badge({ tone = "neutral", label }) {
11988
+ return /* @__PURE__ */ jsxDEV21("box", {
11989
+ backgroundColor: cliTheme.overlay.badgeBackground,
11990
+ paddingX: 1,
11991
+ children: /* @__PURE__ */ jsxDEV21("text", {
11992
+ fg: toneColor[tone],
11993
+ children: label
11994
+ }, undefined, false, undefined, this)
11995
+ }, undefined, false, undefined, this);
11996
+ }
11997
+ var toneColor;
11998
+ var init_badge = __esm(() => {
11999
+ init_cli_theme();
12000
+ toneColor = {
12001
+ neutral: cliTheme.text.secondary,
12002
+ accent: cliTheme.accent.primary,
12003
+ success: cliTheme.semantic.success,
12004
+ warning: cliTheme.semantic.warning,
12005
+ error: cliTheme.semantic.error
12006
+ };
12007
+ });
12008
+
11484
12009
  // apps/cli/src/screens/chat-screen.tsx
11485
12010
  import { useKeyboard as useKeyboard4, useRenderer } from "@opentui/react";
11486
- import { Fragment, useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef4, useState as useState10 } from "react";
12011
+ import { Fragment, useCallback as useCallback4, useEffect as useEffect9, useMemo as useMemo4, useRef as useRef4, useState as useState11 } from "react";
11487
12012
  import { useLocation, useNavigate, useParams } from "react-router";
11488
- import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
12013
+ import { jsxDEV as jsxDEV22 } from "@opentui/react/jsx-dev-runtime";
11489
12014
  function shouldImplementApprovedPlan(response) {
11490
12015
  if (response.selectedValue === "yes") {
11491
12016
  return true;
@@ -11546,14 +12071,14 @@ function ChatScreen() {
11546
12071
  const skipHistoryLoad = locationState.skipHistoryLoad ?? false;
11547
12072
  const sessionId = parsedRouteParams.success ? parsedRouteParams.data.id : "";
11548
12073
  const isSessionIdValid = parsedRouteParams.success;
11549
- const [mode, setMode] = useState10(locationState.mode ?? defaultCodingAgentMode);
11550
- const [permissionMode, setPermissionMode] = useState10(locationState.permissionMode);
11551
- const [planConfirmationMessageId, setPlanConfirmationMessageId] = useState10(null);
11552
- const [handledPlanMessageIds, setHandledPlanMessageIds] = useState10([]);
11553
- const [queuedImplementationInstruction, setQueuedImplementationInstruction] = useState10(null);
11554
- const [contextState, setContextState] = useState10(null);
11555
- const [actionNotice, setActionNotice] = useState10(null);
11556
- const [permissionSelectorOpen, setPermissionSelectorOpen] = useState10(false);
12074
+ const [mode, setMode] = useState11(locationState.mode ?? defaultCodingAgentMode);
12075
+ const [permissionMode, setPermissionMode] = useState11(locationState.permissionMode);
12076
+ const [planConfirmationMessageId, setPlanConfirmationMessageId] = useState11(null);
12077
+ const [handledPlanMessageIds, setHandledPlanMessageIds] = useState11([]);
12078
+ const [queuedImplementationInstruction, setQueuedImplementationInstruction] = useState11(null);
12079
+ const [contextState, setContextState] = useState11(null);
12080
+ const [actionNotice, setActionNotice] = useState11(null);
12081
+ const [permissionSelectorOpen, setPermissionSelectorOpen] = useState11(false);
11557
12082
  const slashRoutes = getSlashMenuItems(slashMenuQuery, {
11558
12083
  includeChatActions: true
11559
12084
  });
@@ -11578,12 +12103,12 @@ function ChatScreen() {
11578
12103
  setSlashMenuSelected,
11579
12104
  slashMenuOpen
11580
12105
  ]);
11581
- useEffect8(() => {
12106
+ useEffect9(() => {
11582
12107
  const nextMode = locationState.mode ?? defaultCodingAgentMode;
11583
12108
  setMode((currentMode) => currentMode === nextMode ? currentMode : nextMode);
11584
12109
  setPermissionMode((currentPermissionMode) => currentPermissionMode === locationState.permissionMode ? currentPermissionMode : locationState.permissionMode);
11585
12110
  }, [locationState.mode, locationState.permissionMode, sessionId]);
11586
- useEffect8(() => {
12111
+ useEffect9(() => {
11587
12112
  setPlanConfirmationMessageId(null);
11588
12113
  setHandledPlanMessageIds([]);
11589
12114
  setQueuedImplementationInstruction(null);
@@ -11687,8 +12212,26 @@ function ChatScreen() {
11687
12212
  });
11688
12213
  const elapsedSeconds = useLoadingTimer(isLoading || isStreaming);
11689
12214
  const contextWindow = useContextWindow();
11690
- const [mentionCandidates, setMentionCandidates] = useState10([]);
11691
- useEffect8(() => {
12215
+ const [mentionCandidates, setMentionCandidates] = useState11([]);
12216
+ const [retryNowMs, setRetryNowMs] = useState11(() => Date.now());
12217
+ const retryAtMs = autoContinueState?.kind === "retry-error" ? autoContinueState.retryAtMs : undefined;
12218
+ useEffect9(() => {
12219
+ if (!retryAtMs) {
12220
+ return;
12221
+ }
12222
+ const intervalId = setInterval(() => setRetryNowMs(Date.now()), 500);
12223
+ return () => clearInterval(intervalId);
12224
+ }, [retryAtMs]);
12225
+ const retryNoticeText = (() => {
12226
+ if (autoContinueState?.kind !== "retry-error") {
12227
+ return "";
12228
+ }
12229
+ const label = retryReasonLabel(autoContinueState.retryReason);
12230
+ const secondsLeft = retryAtMs ? Math.max(0, Math.ceil((retryAtMs - retryNowMs) / 1000)) : 0;
12231
+ const timing = secondsLeft > 0 ? `in ${secondsLeft}s` : "now";
12232
+ return `${label} \u2014 retrying ${timing} (${autoContinueState.attempt})...`;
12233
+ })();
12234
+ useEffect9(() => {
11692
12235
  let cancelled = false;
11693
12236
  async function loadMentionCandidates() {
11694
12237
  try {
@@ -11719,7 +12262,7 @@ function ChatScreen() {
11719
12262
  } catch {}
11720
12263
  }, [sessionId]);
11721
12264
  const wasStreamingRef = useRef4(false);
11722
- useEffect8(() => {
12265
+ useEffect9(() => {
11723
12266
  if (wasStreamingRef.current && !isStreaming) {
11724
12267
  refreshContextState();
11725
12268
  }
@@ -11729,8 +12272,8 @@ function ChatScreen() {
11729
12272
  setActionNotice({ text, tone });
11730
12273
  }, []);
11731
12274
  const renderer = useRenderer();
11732
- const [copyModeOpen, setCopyModeOpen] = useState10(false);
11733
- const [copyModeIndex, setCopyModeIndex] = useState10(0);
12275
+ const [copyModeOpen, setCopyModeOpen] = useState11(false);
12276
+ const [copyModeIndex, setCopyModeIndex] = useState11(0);
11734
12277
  const copyableMessages = useMemo4(() => messages.filter((message) => (message.role === "user" || message.role === "assistant") && collectMessageText(message).trim().length > 0), [messages]);
11735
12278
  const copySelectedMessage = useCallback4(async (kind) => {
11736
12279
  const message = copyableMessages[copyModeIndex];
@@ -11782,7 +12325,7 @@ function ChatScreen() {
11782
12325
  const setPermissionModeToState = useCallback4((newMode) => {
11783
12326
  setPermissionMode(newMode);
11784
12327
  }, []);
11785
- useEffect8(() => {
12328
+ useEffect9(() => {
11786
12329
  if (!requestedChatActionId) {
11787
12330
  return;
11788
12331
  }
@@ -11861,13 +12404,24 @@ function ChatScreen() {
11861
12404
  const contextMeterColor = contextEstimate.level === "critical" ? cliTheme.semantic.error : contextEstimate.level === "warning" ? cliTheme.semantic.warning : cliTheme.text.muted;
11862
12405
  const anchorVisibleInMessages = contextState !== null && messages.some((message) => message.id === contextState.anchorMessageId);
11863
12406
  const lastAssistantMessage2 = getLatestAssistantMessage(messages);
12407
+ const liveOutputTokens = (() => {
12408
+ if (!lastAssistantMessage2) {
12409
+ return 0;
12410
+ }
12411
+ const reported = getMessageUsageMetadata(lastAssistantMessage2)?.usage?.outputTokens;
12412
+ if (typeof reported === "number" && reported > 0) {
12413
+ return reported;
12414
+ }
12415
+ const text = collectMessageText(lastAssistantMessage2);
12416
+ return text ? Math.ceil(text.length / 4) : 0;
12417
+ })();
11864
12418
  const modelHasToolCallXmlLeak = !isLoading && !isStreaming && lastAssistantMessage2 ? hasToolCallXmlLeak(lastAssistantMessage2) : false;
11865
12419
  const activeUserPrompt = pendingUserPrompts[0] ?? null;
11866
12420
  const pendingApprovalIds = useMemo4(() => new Set(pendingApprovals.map((approval) => approval.toolCallId)), [pendingApprovals]);
11867
12421
  const planConfirmationOpen = mode === "plan" && activeUserPrompt === null && planConfirmationMessageId !== null;
11868
12422
  const hasBlockingPopup = activeUserPrompt !== null || planConfirmationOpen;
11869
12423
  const canTypeInChat = !isSessionIdValid ? false : hasBlockingPopup ? false : pendingApprovals.length > 0 ? true : !isLoading;
11870
- useEffect8(() => {
12424
+ useEffect9(() => {
11871
12425
  if (mode !== "plan") {
11872
12426
  setPlanConfirmationMessageId(null);
11873
12427
  return;
@@ -11893,7 +12447,7 @@ function ChatScreen() {
11893
12447
  isStreaming,
11894
12448
  pendingUserPrompts.length
11895
12449
  ]);
11896
- useEffect8(() => {
12450
+ useEffect9(() => {
11897
12451
  if (mode !== "build" || !queuedImplementationInstruction) {
11898
12452
  return;
11899
12453
  }
@@ -11958,113 +12512,110 @@ function ChatScreen() {
11958
12512
  keyEvent.stopPropagation();
11959
12513
  setMode((currentMode) => cycleCodingAgentMode(currentMode));
11960
12514
  });
11961
- return /* @__PURE__ */ jsxDEV21("box", {
12515
+ return /* @__PURE__ */ jsxDEV22("box", {
11962
12516
  width: "100%",
11963
12517
  height: "100%",
11964
- children: /* @__PURE__ */ jsxDEV21(ChatShell, {
12518
+ children: /* @__PURE__ */ jsxDEV22(ChatShell, {
11965
12519
  hasMessages: messages.length > 0 || isStreaming || pendingApprovals.length > 0 || activeUserPrompt !== null || planConfirmationOpen,
11966
12520
  messageCount: messages.length,
11967
12521
  errorMessage,
11968
- inputArea: /* @__PURE__ */ jsxDEV21(ChatTextArea, {
12522
+ inputArea: /* @__PURE__ */ jsxDEV22(ChatTextArea, {
11969
12523
  placeholder: isLoading ? "Waiting for response..." : "Reply... (@ to attach files)",
11970
12524
  focused: canTypeInChat && !copyModeOpen,
11971
12525
  disabled: !canTypeInChat || copyModeOpen,
11972
12526
  slashMenuOpen,
11973
12527
  mentionCandidates,
11974
12528
  onTextChange: syncSlashMenuFromInput,
11975
- beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV21(SlashPageMenu, {
12529
+ beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV22(SlashPageMenu, {
11976
12530
  query: slashMenuQuery,
11977
12531
  selectedIndex: selectedSlashRouteIndex,
11978
12532
  routes: slashRoutes
11979
12533
  }, undefined, false, undefined, this) : null,
11980
- footer: /* @__PURE__ */ jsxDEV21("box", {
12534
+ footer: /* @__PURE__ */ jsxDEV22("box", {
11981
12535
  flexDirection: "row",
11982
12536
  justifyContent: "space-between",
11983
12537
  children: [
11984
- copyModeOpen ? /* @__PURE__ */ jsxDEV21("text", {
12538
+ copyModeOpen ? /* @__PURE__ */ jsxDEV22("text", {
11985
12539
  fg: cliTheme.text.muted,
11986
12540
  children: "Copy mode: \u2191/\u2193 select \xB7 Enter copy \xB7 c code \xB7 Esc exit"
11987
- }, undefined, false, undefined, this) : canRetryRecoverableResponse ? /* @__PURE__ */ jsxDEV21("text", {
12541
+ }, undefined, false, undefined, this) : canRetryRecoverableResponse ? /* @__PURE__ */ jsxDEV22("text", {
11988
12542
  fg: cliTheme.text.muted,
11989
- children: "Type retry or regenerate to try again"
11990
- }, undefined, false, undefined, this) : pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV21("text", {
12543
+ children: errorMessage && /rate.?limit/i.test(errorMessage) ? "Rate-limited \u2014 wait a moment, /model to switch, or add provider credits" : "Type retry or regenerate to try again"
12544
+ }, undefined, false, undefined, this) : pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV22("text", {
11991
12545
  fg: cliTheme.text.muted,
11992
12546
  children: "Use approval card or approve/deny commands"
11993
- }, undefined, false, undefined, this) : hasBlockingPopup ? /* @__PURE__ */ jsxDEV21("text", {
12547
+ }, undefined, false, undefined, this) : hasBlockingPopup ? /* @__PURE__ */ jsxDEV22("text", {
11994
12548
  fg: cliTheme.text.muted,
11995
12549
  children: "Complete the inline prompt to continue"
11996
- }, undefined, false, undefined, this) : slashMenuOpen ? /* @__PURE__ */ jsxDEV21("text", {
12550
+ }, undefined, false, undefined, this) : slashMenuOpen ? /* @__PURE__ */ jsxDEV22("text", {
11997
12551
  fg: cliTheme.text.muted,
11998
12552
  children: "Choose a page or press Esc"
11999
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV21("text", {
12553
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV22("text", {
12000
12554
  fg: cliTheme.text.muted,
12001
- children: "Tab/Ctrl+T mode | Ctrl+Y copy | Ctrl+P commands"
12555
+ children: "Enter send \xB7 Ctrl+Enter newline"
12002
12556
  }, undefined, false, undefined, this),
12003
- /* @__PURE__ */ jsxDEV21("box", {
12557
+ /* @__PURE__ */ jsxDEV22("box", {
12004
12558
  flexDirection: "row",
12005
12559
  gap: 1,
12006
12560
  alignItems: "center",
12007
12561
  children: [
12008
- /* @__PURE__ */ jsxDEV21("text", {
12562
+ /* @__PURE__ */ jsxDEV22("text", {
12009
12563
  fg: contextMeterColor,
12010
- children: contextEstimate.displayText
12564
+ children: truncateInline2(contextEstimate.displayText, 24)
12011
12565
  }, undefined, false, undefined, this),
12012
- /* @__PURE__ */ jsxDEV21("text", {
12566
+ /* @__PURE__ */ jsxDEV22("text", {
12013
12567
  children: [
12014
- /* @__PURE__ */ jsxDEV21("span", {
12568
+ /* @__PURE__ */ jsxDEV22("span", {
12015
12569
  fg: cliTheme.accent.primary,
12016
12570
  children: modeDefinition.label
12017
12571
  }, undefined, false, undefined, this),
12018
- /* @__PURE__ */ jsxDEV21("span", {
12572
+ /* @__PURE__ */ jsxDEV22("span", {
12019
12573
  fg: cliTheme.text.muted,
12020
12574
  children: " mode"
12021
12575
  }, undefined, false, undefined, this)
12022
12576
  ]
12023
12577
  }, undefined, true, undefined, this),
12024
- /* @__PURE__ */ jsxDEV21("text", {
12025
- children: /* @__PURE__ */ jsxDEV21("span", {
12026
- fg: cliTheme.semantic.warning,
12027
- children: "|"
12578
+ /* @__PURE__ */ jsxDEV22("text", {
12579
+ children: /* @__PURE__ */ jsxDEV22("span", {
12580
+ fg: cliTheme.text.muted,
12581
+ children: "\xB7"
12028
12582
  }, undefined, false, undefined, this)
12029
12583
  }, undefined, false, undefined, this),
12030
- /* @__PURE__ */ jsxDEV21("text", {
12031
- children: /* @__PURE__ */ jsxDEV21("span", {
12032
- fg: permissionMode === "danger-full-access" ? cliTheme.semantic.error : permissionMode === "read-only" ? cliTheme.text.muted : cliTheme.text.primary,
12033
- children: permissionMode ?? "default"
12034
- }, undefined, false, undefined, this)
12584
+ /* @__PURE__ */ jsxDEV22(Badge, {
12585
+ tone: permissionMode === "danger-full-access" ? "error" : permissionMode === "read-only" ? "neutral" : "accent",
12586
+ label: permissionMode ?? "default"
12035
12587
  }, undefined, false, undefined, this)
12036
12588
  ]
12037
12589
  }, undefined, true, undefined, this)
12038
12590
  ]
12039
12591
  }, undefined, true, undefined, this),
12040
- modeToggleHint: true,
12041
12592
  onSubmit: submitChatInput
12042
12593
  }, undefined, false, undefined, this),
12043
12594
  children: [
12044
- contextState && !anchorVisibleInMessages ? /* @__PURE__ */ jsxDEV21(ChatContextStateCard, {
12595
+ contextState && !anchorVisibleInMessages ? /* @__PURE__ */ jsxDEV22(ChatContextStateCard, {
12045
12596
  contextState
12046
12597
  }, undefined, false, undefined, this) : null,
12047
- messages.map((message) => /* @__PURE__ */ jsxDEV21(Fragment, {
12598
+ messages.map((message) => /* @__PURE__ */ jsxDEV22(Fragment, {
12048
12599
  children: [
12049
- isAutoContinueMessage(message) ? null : isContextSummaryMessage(message) ? /* @__PURE__ */ jsxDEV21(ChatContextSummaryCard, {
12600
+ isAutoContinueMessage(message) ? null : isContextSummaryMessage(message) ? /* @__PURE__ */ jsxDEV22(ChatContextSummaryCard, {
12050
12601
  message
12051
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV21(ChatMessage, {
12602
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV22(ChatMessage, {
12052
12603
  message,
12053
12604
  pendingApprovalIds
12054
12605
  }, undefined, false, undefined, this),
12055
- contextState && contextState.anchorMessageId === message.id ? /* @__PURE__ */ jsxDEV21(ChatContextStateCard, {
12606
+ contextState && contextState.anchorMessageId === message.id ? /* @__PURE__ */ jsxDEV22(ChatContextStateCard, {
12056
12607
  contextState
12057
12608
  }, undefined, false, undefined, this) : null
12058
12609
  ]
12059
12610
  }, message.id, true, undefined, this)),
12060
- actionNotice ? /* @__PURE__ */ jsxDEV21("box", {
12611
+ actionNotice ? /* @__PURE__ */ jsxDEV22("box", {
12061
12612
  paddingX: 1,
12062
- children: /* @__PURE__ */ jsxDEV21("text", {
12613
+ children: /* @__PURE__ */ jsxDEV22("text", {
12063
12614
  fg: actionNotice.tone === "error" ? cliTheme.semantic.error : cliTheme.semantic.info,
12064
12615
  children: actionNotice.text
12065
12616
  }, undefined, false, undefined, this)
12066
12617
  }, undefined, false, undefined, this) : null,
12067
- modelHasToolCallXmlLeak ? /* @__PURE__ */ jsxDEV21("box", {
12618
+ modelHasToolCallXmlLeak ? /* @__PURE__ */ jsxDEV22("box", {
12068
12619
  paddingX: 1,
12069
12620
  paddingY: 1,
12070
12621
  borderStyle: "single",
@@ -12072,47 +12623,41 @@ function ChatScreen() {
12072
12623
  flexDirection: "column",
12073
12624
  gap: 1,
12074
12625
  children: [
12075
- /* @__PURE__ */ jsxDEV21("text", {
12626
+ /* @__PURE__ */ jsxDEV22("text", {
12076
12627
  fg: cliTheme.semantic.warning,
12077
12628
  children: "Model compatibility issue: this model output raw tool call XML instead of invoking tools."
12078
12629
  }, undefined, false, undefined, this),
12079
- /* @__PURE__ */ jsxDEV21("text", {
12630
+ /* @__PURE__ */ jsxDEV22("text", {
12080
12631
  fg: cliTheme.text.muted,
12081
12632
  children: "Switch to a model with native function calling (Claude, GPT-4, Llama 3) via /model."
12082
12633
  }, undefined, false, undefined, this)
12083
12634
  ]
12084
- }, undefined, true, undefined, this) : autoContinueState?.guardTripped ? /* @__PURE__ */ jsxDEV21("box", {
12635
+ }, undefined, true, undefined, this) : autoContinueState?.guardTripped ? /* @__PURE__ */ jsxDEV22("box", {
12085
12636
  paddingX: 1,
12086
- children: /* @__PURE__ */ jsxDEV21("text", {
12637
+ children: /* @__PURE__ */ jsxDEV22("text", {
12087
12638
  fg: cliTheme.semantic.warning,
12088
12639
  children: autoContinueState.guardTripped === "doom-loop" ? "Agent appears stuck repeating itself \u2014 automatic continuation stopped. Send a message to resume." : autoContinueState.guardTripped === "no-progress" ? "The connection keeps dropping without making progress \u2014 automatic retry stopped. Send a message to resume." : `Reached the automatic continuation limit (${autoContinueState.attempt}). Send a message to resume.`
12089
12640
  }, undefined, false, undefined, this)
12090
- }, undefined, false, undefined, this) : autoContinueState ? /* @__PURE__ */ jsxDEV21("box", {
12641
+ }, undefined, false, undefined, this) : autoContinueState ? /* @__PURE__ */ jsxDEV22("box", {
12091
12642
  paddingX: 1,
12092
- children: /* @__PURE__ */ jsxDEV21("text", {
12643
+ children: /* @__PURE__ */ jsxDEV22("text", {
12093
12644
  fg: cliTheme.semantic.info,
12094
- 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})...`
12645
+ children: autoContinueState.kind === "continue-length" ? `Output limit hit \u2014 continuing automatically (${autoContinueState.attempt})...` : autoContinueState.kind === "retry-error" ? retryNoticeText : `Agent stopped early \u2014 continuing automatically (${autoContinueState.attempt})...`
12095
12646
  }, undefined, false, undefined, this)
12096
12647
  }, undefined, false, undefined, this) : null,
12097
- isLoading || isStreaming ? /* @__PURE__ */ jsxDEV21(LoadingTimer, {
12098
- elapsedSeconds
12099
- }, undefined, false, undefined, this) : null,
12100
- isStreaming ? /* @__PURE__ */ jsxDEV21("box", {
12101
- paddingX: 1,
12102
- children: /* @__PURE__ */ jsxDEV21("text", {
12103
- fg: cliTheme.semantic.info,
12104
- children: "Assistant is thinking..."
12105
- }, undefined, false, undefined, this)
12648
+ isLoading || isStreaming ? /* @__PURE__ */ jsxDEV22(LoadingTimer, {
12649
+ elapsedSeconds,
12650
+ outputTokens: liveOutputTokens
12106
12651
  }, undefined, false, undefined, this) : null,
12107
- pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV21(ChatToolApprovalCard, {
12652
+ pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV22(ChatToolApprovalCard, {
12108
12653
  approvals: pendingApprovals,
12109
12654
  onResolve: resolveToolApproval,
12110
12655
  onResolveAll: resolveAllToolApprovals
12111
12656
  }, undefined, false, undefined, this) : null,
12112
- todos.length > 0 ? /* @__PURE__ */ jsxDEV21(ChatTodoStatusCard, {
12657
+ todos.length > 0 ? /* @__PURE__ */ jsxDEV22(ChatTodoStatusCard, {
12113
12658
  todos
12114
12659
  }, undefined, false, undefined, this) : null,
12115
- activeUserPrompt ? /* @__PURE__ */ jsxDEV21(ChatInteractionPopup, {
12660
+ activeUserPrompt ? /* @__PURE__ */ jsxDEV22(ChatInteractionPopup, {
12116
12661
  title: activeUserPrompt.header ?? "Question",
12117
12662
  question: activeUserPrompt.question,
12118
12663
  options: activeUserPrompt.options.map((option) => ({
@@ -12143,7 +12688,7 @@ function ChatScreen() {
12143
12688
  });
12144
12689
  }
12145
12690
  }, undefined, false, undefined, this) : null,
12146
- planConfirmationOpen ? /* @__PURE__ */ jsxDEV21(ChatInteractionPopup, {
12691
+ planConfirmationOpen ? /* @__PURE__ */ jsxDEV22(ChatInteractionPopup, {
12147
12692
  title: "Plan review",
12148
12693
  question: "Implement this plan now?",
12149
12694
  options: [
@@ -12191,12 +12736,12 @@ function ChatScreen() {
12191
12736
  sendDirectMessage(defaultPlanRevisionRequest);
12192
12737
  }
12193
12738
  }, undefined, false, undefined, this) : null,
12194
- permissionSelectorOpen ? /* @__PURE__ */ jsxDEV21(PermissionModeSelector, {
12739
+ permissionSelectorOpen ? /* @__PURE__ */ jsxDEV22(PermissionModeSelector, {
12195
12740
  currentMode: permissionMode,
12196
12741
  onSelect: updatePermissionMode,
12197
12742
  onClose: () => setPermissionSelectorOpen(false)
12198
12743
  }, undefined, false, undefined, this) : null,
12199
- copyModeOpen ? /* @__PURE__ */ jsxDEV21(CopyModeOverlay, {
12744
+ copyModeOpen ? /* @__PURE__ */ jsxDEV22(CopyModeOverlay, {
12200
12745
  messages: copyableMessages,
12201
12746
  selectedIndex: Math.min(copyModeIndex, Math.max(copyableMessages.length - 1, 0))
12202
12747
  }, undefined, false, undefined, this) : null
@@ -12234,6 +12779,8 @@ var init_chat_screen = __esm(() => {
12234
12779
  init_chat_context_utils();
12235
12780
  init_file_mentions();
12236
12781
  init_markdown_code();
12782
+ init_text_utils();
12783
+ init_badge();
12237
12784
  planConfirmationAcceptKeywords = [
12238
12785
  "yes",
12239
12786
  "y",
@@ -12261,12 +12808,12 @@ var init_chat_screen = __esm(() => {
12261
12808
  });
12262
12809
 
12263
12810
  // apps/cli/src/screens/diagnostics-screen.tsx
12264
- import { TextAttributes as TextAttributes16 } from "@opentui/core";
12811
+ import { TextAttributes as TextAttributes17 } from "@opentui/core";
12265
12812
  import { useKeyboard as useKeyboard5 } from "@opentui/react";
12266
- import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo5, useState as useState11 } from "react";
12813
+ import { useCallback as useCallback5, useEffect as useEffect10, useMemo as useMemo5, useState as useState12 } from "react";
12267
12814
  import { useLocation as useLocation2 } from "react-router";
12268
12815
  import { z as z35 } from "zod";
12269
- import { jsxDEV as jsxDEV22, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
12816
+ import { jsxDEV as jsxDEV23, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
12270
12817
  function getStatusColor2(status) {
12271
12818
  if (status === "ok") {
12272
12819
  return cliTheme.semantic.success;
@@ -12288,16 +12835,16 @@ function InfoRow({
12288
12835
  tone
12289
12836
  }) {
12290
12837
  const valueColor = tone === "muted" || tone === undefined ? cliTheme.text.secondary : getStatusColor2(tone);
12291
- return /* @__PURE__ */ jsxDEV22("box", {
12838
+ return /* @__PURE__ */ jsxDEV23("box", {
12292
12839
  flexDirection: "row",
12293
12840
  gap: 1,
12294
12841
  children: [
12295
- /* @__PURE__ */ jsxDEV22("text", {
12842
+ /* @__PURE__ */ jsxDEV23("text", {
12296
12843
  fg: cliTheme.text.muted,
12297
12844
  width: 24,
12298
12845
  children: label
12299
12846
  }, undefined, false, undefined, this),
12300
- /* @__PURE__ */ jsxDEV22("text", {
12847
+ /* @__PURE__ */ jsxDEV23("text", {
12301
12848
  fg: valueColor,
12302
12849
  children: formatNullable(value)
12303
12850
  }, undefined, false, undefined, this)
@@ -12308,7 +12855,7 @@ function Section({
12308
12855
  title,
12309
12856
  children
12310
12857
  }) {
12311
- return /* @__PURE__ */ jsxDEV22("box", {
12858
+ return /* @__PURE__ */ jsxDEV23("box", {
12312
12859
  flexDirection: "column",
12313
12860
  gap: 1,
12314
12861
  paddingX: 1,
@@ -12317,9 +12864,9 @@ function Section({
12317
12864
  borderColor: cliTheme.borders.subtle,
12318
12865
  backgroundColor: cliTheme.surfaces.panel,
12319
12866
  children: [
12320
- /* @__PURE__ */ jsxDEV22("text", {
12867
+ /* @__PURE__ */ jsxDEV23("text", {
12321
12868
  fg: cliTheme.accent.primary,
12322
- attributes: TextAttributes16.BOLD,
12869
+ attributes: TextAttributes17.BOLD,
12323
12870
  children: title
12324
12871
  }, undefined, false, undefined, this),
12325
12872
  children
@@ -12330,123 +12877,123 @@ function renderStatus(payload, routeState) {
12330
12877
  const parsedRouteState = diagnosticsRouteStateSchema.safeParse(routeState);
12331
12878
  const currentSession = parsedRouteState.success ? parsedRouteState.data : {};
12332
12879
  const latestSession = payload.sessions.latest;
12333
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
12880
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12334
12881
  children: [
12335
- /* @__PURE__ */ jsxDEV22(Section, {
12882
+ /* @__PURE__ */ jsxDEV23(Section, {
12336
12883
  title: "Current Chat",
12337
12884
  children: [
12338
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12885
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12339
12886
  label: "Session",
12340
12887
  value: currentSession.sessionId ?? "not in chat"
12341
12888
  }, undefined, false, undefined, this),
12342
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12889
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12343
12890
  label: "Mode",
12344
12891
  value: currentSession.mode ?? "not set"
12345
12892
  }, undefined, false, undefined, this),
12346
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12893
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12347
12894
  label: "Permission mode",
12348
12895
  value: currentSession.permissionMode ?? "not set"
12349
12896
  }, undefined, false, undefined, this),
12350
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12897
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12351
12898
  label: "CWD",
12352
12899
  value: currentSession.cwd ?? payload.server.cwd
12353
12900
  }, undefined, false, undefined, this),
12354
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12901
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12355
12902
  label: "Messages",
12356
12903
  value: currentSession.messageCount ?? null
12357
12904
  }, undefined, false, undefined, this),
12358
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12905
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12359
12906
  label: "Pending approvals",
12360
12907
  value: currentSession.pendingApprovalCount ?? 0,
12361
12908
  tone: (currentSession.pendingApprovalCount ?? 0) > 0 ? "warn" : "ok"
12362
12909
  }, undefined, false, undefined, this)
12363
12910
  ]
12364
12911
  }, undefined, true, undefined, this),
12365
- /* @__PURE__ */ jsxDEV22(Section, {
12912
+ /* @__PURE__ */ jsxDEV23(Section, {
12366
12913
  title: "Runtime",
12367
12914
  children: [
12368
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12915
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12369
12916
  label: "Server",
12370
12917
  value: payload.server.status,
12371
12918
  tone: payload.server.status
12372
12919
  }, undefined, false, undefined, this),
12373
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12920
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12374
12921
  label: "Provider",
12375
12922
  value: payload.provider.provider,
12376
12923
  tone: payload.provider.status
12377
12924
  }, undefined, false, undefined, this),
12378
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12925
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12379
12926
  label: "Model",
12380
12927
  value: payload.provider.model
12381
12928
  }, undefined, false, undefined, this),
12382
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12929
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12383
12930
  label: "Database",
12384
12931
  value: payload.database.status,
12385
12932
  tone: payload.database.status
12386
12933
  }, undefined, false, undefined, this),
12387
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12934
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12388
12935
  label: "Tool count",
12389
12936
  value: payload.tools.total
12390
12937
  }, undefined, false, undefined, this),
12391
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12938
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12392
12939
  label: "Schema status",
12393
12940
  value: payload.tools.providerSchemaStatus,
12394
12941
  tone: payload.tools.providerSchemaStatus
12395
12942
  }, undefined, false, undefined, this)
12396
12943
  ]
12397
12944
  }, undefined, true, undefined, this),
12398
- /* @__PURE__ */ jsxDEV22(Section, {
12945
+ /* @__PURE__ */ jsxDEV23(Section, {
12399
12946
  title: "Extensions",
12400
12947
  children: [
12401
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12948
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12402
12949
  label: "Skills",
12403
12950
  value: payload.extensions.skills.count
12404
12951
  }, undefined, false, undefined, this),
12405
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12952
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12406
12953
  label: "MCP servers",
12407
12954
  value: payload.extensions.mcp.configuredServers
12408
12955
  }, undefined, false, undefined, this),
12409
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12956
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12410
12957
  label: "MCP running",
12411
12958
  value: payload.extensions.mcp.runningServers
12412
12959
  }, undefined, false, undefined, this),
12413
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12960
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12414
12961
  label: "MCP degraded",
12415
12962
  value: payload.extensions.mcp.degradedServers,
12416
12963
  tone: payload.extensions.mcp.degradedServers > 0 ? "warn" : "ok"
12417
12964
  }, undefined, false, undefined, this),
12418
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12965
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12419
12966
  label: "Plugins",
12420
12967
  value: payload.extensions.plugins.count
12421
12968
  }, undefined, false, undefined, this),
12422
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12969
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12423
12970
  label: "Plugin hooks",
12424
12971
  value: payload.extensions.plugins.hooks
12425
12972
  }, undefined, false, undefined, this)
12426
12973
  ]
12427
12974
  }, undefined, true, undefined, this),
12428
- /* @__PURE__ */ jsxDEV22(Section, {
12975
+ /* @__PURE__ */ jsxDEV23(Section, {
12429
12976
  title: "Latest Saved Session",
12430
- children: latestSession ? /* @__PURE__ */ jsxDEV22(Fragment2, {
12977
+ children: latestSession ? /* @__PURE__ */ jsxDEV23(Fragment2, {
12431
12978
  children: [
12432
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12979
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12433
12980
  label: "Session",
12434
12981
  value: latestSession.id
12435
12982
  }, undefined, false, undefined, this),
12436
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12983
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12437
12984
  label: "Title",
12438
12985
  value: latestSession.title ?? latestSession.latestUserPromptPreview
12439
12986
  }, undefined, false, undefined, this),
12440
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12987
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12441
12988
  label: "Messages",
12442
12989
  value: latestSession.messageCount
12443
12990
  }, undefined, false, undefined, this),
12444
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12991
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12445
12992
  label: "Updated",
12446
12993
  value: formatDate2(latestSession.updatedAt)
12447
12994
  }, undefined, false, undefined, this)
12448
12995
  ]
12449
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV22("text", {
12996
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV23("text", {
12450
12997
  fg: cliTheme.text.muted,
12451
12998
  children: "No saved sessions yet."
12452
12999
  }, undefined, false, undefined, this)
@@ -12455,36 +13002,36 @@ function renderStatus(payload, routeState) {
12455
13002
  }, undefined, true, undefined, this);
12456
13003
  }
12457
13004
  function renderDoctor(payload) {
12458
- return /* @__PURE__ */ jsxDEV22(Section, {
13005
+ return /* @__PURE__ */ jsxDEV23(Section, {
12459
13006
  title: `Overall: ${payload.status}`,
12460
- children: payload.checks.map((check) => /* @__PURE__ */ jsxDEV22("box", {
13007
+ children: payload.checks.map((check) => /* @__PURE__ */ jsxDEV23("box", {
12461
13008
  flexDirection: "column",
12462
13009
  paddingY: 1,
12463
13010
  children: [
12464
- /* @__PURE__ */ jsxDEV22("box", {
13011
+ /* @__PURE__ */ jsxDEV23("box", {
12465
13012
  flexDirection: "row",
12466
13013
  gap: 1,
12467
13014
  children: [
12468
- /* @__PURE__ */ jsxDEV22("text", {
13015
+ /* @__PURE__ */ jsxDEV23("text", {
12469
13016
  fg: getStatusColor2(check.status),
12470
- attributes: TextAttributes16.BOLD,
13017
+ attributes: TextAttributes17.BOLD,
12471
13018
  width: 8,
12472
13019
  children: check.status.toUpperCase()
12473
13020
  }, undefined, false, undefined, this),
12474
- /* @__PURE__ */ jsxDEV22("text", {
13021
+ /* @__PURE__ */ jsxDEV23("text", {
12475
13022
  fg: cliTheme.text.primary,
12476
- attributes: TextAttributes16.BOLD,
13023
+ attributes: TextAttributes17.BOLD,
12477
13024
  children: check.label
12478
13025
  }, undefined, false, undefined, this)
12479
13026
  ]
12480
13027
  }, undefined, true, undefined, this),
12481
- /* @__PURE__ */ jsxDEV22("text", {
13028
+ /* @__PURE__ */ jsxDEV23("text", {
12482
13029
  fg: cliTheme.text.secondary,
12483
13030
  children: check.summary
12484
13031
  }, undefined, false, undefined, this),
12485
- check.details.map((detail) => /* @__PURE__ */ jsxDEV22("text", {
13032
+ check.details.map((detail) => /* @__PURE__ */ jsxDEV23("text", {
12486
13033
  fg: cliTheme.text.muted,
12487
- attributes: TextAttributes16.DIM,
13034
+ attributes: TextAttributes17.DIM,
12488
13035
  children: detail
12489
13036
  }, detail, false, undefined, this))
12490
13037
  ]
@@ -12494,111 +13041,111 @@ function renderDoctor(payload) {
12494
13041
  function renderPermissions(payload, routeState) {
12495
13042
  const parsedRouteState = diagnosticsRouteStateSchema.safeParse(routeState);
12496
13043
  const currentSession = parsedRouteState.success ? parsedRouteState.data : {};
12497
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
13044
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12498
13045
  children: [
12499
- /* @__PURE__ */ jsxDEV22(Section, {
13046
+ /* @__PURE__ */ jsxDEV23(Section, {
12500
13047
  title: "Current Chat",
12501
13048
  children: [
12502
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13049
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12503
13050
  label: "Mode",
12504
13051
  value: currentSession.mode ?? payload.defaultMode
12505
13052
  }, undefined, false, undefined, this),
12506
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13053
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12507
13054
  label: "Permission mode",
12508
13055
  value: currentSession.permissionMode ?? payload.effectivePermissionMode
12509
13056
  }, undefined, false, undefined, this),
12510
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13057
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12511
13058
  label: "Pending approvals",
12512
13059
  value: currentSession.pendingApprovalCount ?? 0,
12513
13060
  tone: (currentSession.pendingApprovalCount ?? 0) > 0 ? "warn" : "ok"
12514
13061
  }, undefined, false, undefined, this),
12515
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13062
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12516
13063
  label: "Persisted pending",
12517
13064
  value: payload.pendingInteractions.total,
12518
13065
  tone: payload.pendingInteractions.total > 0 ? "warn" : "ok"
12519
13066
  }, undefined, false, undefined, this)
12520
13067
  ]
12521
13068
  }, undefined, true, undefined, this),
12522
- /* @__PURE__ */ jsxDEV22(Section, {
13069
+ /* @__PURE__ */ jsxDEV23(Section, {
12523
13070
  title: "Policy",
12524
13071
  children: [
12525
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13072
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12526
13073
  label: "Default mode",
12527
13074
  value: payload.defaultMode
12528
13075
  }, undefined, false, undefined, this),
12529
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13076
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12530
13077
  label: "Configured permission",
12531
13078
  value: payload.configuredPermissionMode
12532
13079
  }, undefined, false, undefined, this),
12533
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13080
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12534
13081
  label: "Effective permission",
12535
13082
  value: payload.effectivePermissionMode
12536
13083
  }, undefined, false, undefined, this),
12537
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13084
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12538
13085
  label: "Allowed tools",
12539
13086
  value: payload.allowedTools ? payload.allowedTools.join(", ") : "all policy-allowed tools"
12540
13087
  }, undefined, false, undefined, this),
12541
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13088
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12542
13089
  label: "Allow rules",
12543
13090
  value: payload.rules.allow?.length ?? 0
12544
13091
  }, undefined, false, undefined, this),
12545
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13092
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12546
13093
  label: "Ask rules",
12547
13094
  value: payload.rules.ask?.length ?? 0
12548
13095
  }, undefined, false, undefined, this),
12549
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13096
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12550
13097
  label: "Deny rules",
12551
13098
  value: payload.rules.deny?.length ?? 0
12552
13099
  }, undefined, false, undefined, this)
12553
13100
  ]
12554
13101
  }, undefined, true, undefined, this),
12555
- /* @__PURE__ */ jsxDEV22(Section, {
13102
+ /* @__PURE__ */ jsxDEV23(Section, {
12556
13103
  title: "Recovery",
12557
13104
  children: [
12558
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13105
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12559
13106
  label: "Tool approvals",
12560
13107
  value: payload.pendingInteractions.toolApprovals
12561
13108
  }, undefined, false, undefined, this),
12562
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13109
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12563
13110
  label: "User prompts",
12564
13111
  value: payload.pendingInteractions.userPrompts
12565
13112
  }, undefined, false, undefined, this),
12566
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13113
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12567
13114
  label: "Stale pending",
12568
13115
  value: payload.pendingInteractions.stale,
12569
13116
  tone: payload.pendingInteractions.stale > 0 ? "warn" : "ok"
12570
13117
  }, undefined, false, undefined, this),
12571
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13118
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12572
13119
  label: "Recoverable",
12573
13120
  value: payload.pendingInteractions.recoverable
12574
13121
  }, undefined, false, undefined, this)
12575
13122
  ]
12576
13123
  }, undefined, true, undefined, this),
12577
- /* @__PURE__ */ jsxDEV22(Section, {
13124
+ /* @__PURE__ */ jsxDEV23(Section, {
12578
13125
  title: "Sandbox",
12579
13126
  children: [
12580
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13127
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12581
13128
  label: "Enabled",
12582
13129
  value: payload.sandbox.enabled ? "yes" : "no"
12583
13130
  }, undefined, false, undefined, this),
12584
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13131
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12585
13132
  label: "Mode",
12586
13133
  value: payload.sandbox.mode
12587
13134
  }, undefined, false, undefined, this),
12588
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13135
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12589
13136
  label: "Supported",
12590
13137
  value: payload.sandbox.supported ? "yes" : "no",
12591
13138
  tone: payload.sandbox.supported ? "ok" : "warn"
12592
13139
  }, undefined, false, undefined, this),
12593
- payload.sandbox.unsupportedReason ? /* @__PURE__ */ jsxDEV22("text", {
13140
+ payload.sandbox.unsupportedReason ? /* @__PURE__ */ jsxDEV23("text", {
12594
13141
  fg: cliTheme.semantic.warning,
12595
13142
  children: payload.sandbox.unsupportedReason
12596
13143
  }, undefined, false, undefined, this) : null
12597
13144
  ]
12598
13145
  }, undefined, true, undefined, this),
12599
- /* @__PURE__ */ jsxDEV22(Section, {
13146
+ /* @__PURE__ */ jsxDEV23(Section, {
12600
13147
  title: "Notes",
12601
- children: payload.notes.map((note) => /* @__PURE__ */ jsxDEV22("text", {
13148
+ children: payload.notes.map((note) => /* @__PURE__ */ jsxDEV23("text", {
12602
13149
  fg: cliTheme.text.muted,
12603
13150
  children: note
12604
13151
  }, note, false, undefined, this))
@@ -12607,62 +13154,62 @@ function renderPermissions(payload, routeState) {
12607
13154
  }, undefined, true, undefined, this);
12608
13155
  }
12609
13156
  function renderTools(payload) {
12610
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
13157
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12611
13158
  children: [
12612
- /* @__PURE__ */ jsxDEV22(Section, {
13159
+ /* @__PURE__ */ jsxDEV23(Section, {
12613
13160
  title: "Summary",
12614
13161
  children: [
12615
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13162
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12616
13163
  label: "Total",
12617
13164
  value: payload.summary.total
12618
13165
  }, undefined, false, undefined, this),
12619
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13166
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12620
13167
  label: "Read-only",
12621
13168
  value: payload.summary.readOnly
12622
13169
  }, undefined, false, undefined, this),
12623
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13170
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12624
13171
  label: "Workspace-write",
12625
13172
  value: payload.summary.workspaceWrite
12626
13173
  }, undefined, false, undefined, this),
12627
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13174
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12628
13175
  label: "Danger-full-access",
12629
13176
  value: payload.summary.dangerFullAccess
12630
13177
  }, undefined, false, undefined, this),
12631
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13178
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12632
13179
  label: "Schema status",
12633
13180
  value: payload.summary.providerSchemaStatus,
12634
13181
  tone: payload.summary.providerSchemaStatus
12635
13182
  }, undefined, false, undefined, this)
12636
13183
  ]
12637
13184
  }, undefined, true, undefined, this),
12638
- /* @__PURE__ */ jsxDEV22(Section, {
13185
+ /* @__PURE__ */ jsxDEV23(Section, {
12639
13186
  title: "Tool Catalog",
12640
- children: payload.tools.map((tool2) => /* @__PURE__ */ jsxDEV22("box", {
13187
+ children: payload.tools.map((tool2) => /* @__PURE__ */ jsxDEV23("box", {
12641
13188
  flexDirection: "column",
12642
13189
  paddingY: 1,
12643
13190
  children: [
12644
- /* @__PURE__ */ jsxDEV22("box", {
13191
+ /* @__PURE__ */ jsxDEV23("box", {
12645
13192
  flexDirection: "row",
12646
13193
  justifyContent: "space-between",
12647
13194
  children: [
12648
- /* @__PURE__ */ jsxDEV22("text", {
13195
+ /* @__PURE__ */ jsxDEV23("text", {
12649
13196
  fg: cliTheme.text.primary,
12650
- attributes: TextAttributes16.BOLD,
13197
+ attributes: TextAttributes17.BOLD,
12651
13198
  children: tool2.name
12652
13199
  }, undefined, false, undefined, this),
12653
- /* @__PURE__ */ jsxDEV22("text", {
13200
+ /* @__PURE__ */ jsxDEV23("text", {
12654
13201
  fg: getStatusColor2(tool2.providerSchemaStatus),
12655
13202
  children: tool2.permissionMode
12656
13203
  }, undefined, false, undefined, this)
12657
13204
  ]
12658
13205
  }, undefined, true, undefined, this),
12659
- /* @__PURE__ */ jsxDEV22("text", {
13206
+ /* @__PURE__ */ jsxDEV23("text", {
12660
13207
  fg: cliTheme.text.secondary,
12661
13208
  children: tool2.description
12662
13209
  }, undefined, false, undefined, this),
12663
- /* @__PURE__ */ jsxDEV22("text", {
13210
+ /* @__PURE__ */ jsxDEV23("text", {
12664
13211
  fg: cliTheme.text.muted,
12665
- attributes: TextAttributes16.DIM,
13212
+ attributes: TextAttributes17.DIM,
12666
13213
  children: [
12667
13214
  "Modes: ",
12668
13215
  tool2.activeInModes.join(", ") || "none",
@@ -12677,51 +13224,51 @@ function renderTools(payload) {
12677
13224
  }, undefined, true, undefined, this);
12678
13225
  }
12679
13226
  function renderConfig(payload) {
12680
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
13227
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12681
13228
  children: [
12682
- /* @__PURE__ */ jsxDEV22(Section, {
13229
+ /* @__PURE__ */ jsxDEV23(Section, {
12683
13230
  title: "Effective Config",
12684
13231
  children: [
12685
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13232
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12686
13233
  label: "Provider",
12687
13234
  value: payload.selectedProvider
12688
13235
  }, undefined, false, undefined, this),
12689
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13236
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12690
13237
  label: "Configured model",
12691
13238
  value: payload.configuredModel
12692
13239
  }, undefined, false, undefined, this),
12693
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13240
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12694
13241
  label: "Selected model",
12695
13242
  value: payload.selectedModel
12696
13243
  }, undefined, false, undefined, this),
12697
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13244
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12698
13245
  label: "Base URL",
12699
13246
  value: payload.baseUrl ? "configured" : "default"
12700
13247
  }, undefined, false, undefined, this),
12701
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13248
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12702
13249
  label: "Default mode",
12703
13250
  value: payload.defaultMode
12704
13251
  }, undefined, false, undefined, this),
12705
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13252
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12706
13253
  label: "Permission mode",
12707
13254
  value: payload.permissionMode
12708
13255
  }, undefined, false, undefined, this),
12709
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13256
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12710
13257
  label: "Max output tokens",
12711
13258
  value: payload.maxOutputTokens
12712
13259
  }, undefined, false, undefined, this),
12713
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13260
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12714
13261
  label: "Max steps",
12715
13262
  value: payload.maxSteps
12716
13263
  }, undefined, false, undefined, this)
12717
13264
  ]
12718
13265
  }, undefined, true, undefined, this),
12719
- /* @__PURE__ */ jsxDEV22(Section, {
13266
+ /* @__PURE__ */ jsxDEV23(Section, {
12720
13267
  title: "Loaded Files",
12721
- children: payload.loadedFiles.map((file) => /* @__PURE__ */ jsxDEV22("box", {
13268
+ children: payload.loadedFiles.map((file) => /* @__PURE__ */ jsxDEV23("box", {
12722
13269
  flexDirection: "column",
12723
13270
  children: [
12724
- /* @__PURE__ */ jsxDEV22("text", {
13271
+ /* @__PURE__ */ jsxDEV23("text", {
12725
13272
  fg: file.loaded ? cliTheme.semantic.success : cliTheme.text.muted,
12726
13273
  children: [
12727
13274
  file.scope,
@@ -12729,17 +13276,17 @@ function renderConfig(payload) {
12729
13276
  file.loaded ? "loaded" : file.exists ? "not loaded" : "missing"
12730
13277
  ]
12731
13278
  }, undefined, true, undefined, this),
12732
- /* @__PURE__ */ jsxDEV22("text", {
13279
+ /* @__PURE__ */ jsxDEV23("text", {
12733
13280
  fg: cliTheme.text.muted,
12734
- attributes: TextAttributes16.DIM,
13281
+ attributes: TextAttributes17.DIM,
12735
13282
  children: file.path
12736
13283
  }, undefined, false, undefined, this)
12737
13284
  ]
12738
13285
  }, `${file.scope}:${file.path}`, true, undefined, this))
12739
13286
  }, undefined, false, undefined, this),
12740
- payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV22(Section, {
13287
+ payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV23(Section, {
12741
13288
  title: "Credential Hints",
12742
- children: payload.missingCredentialHints.map((hint) => /* @__PURE__ */ jsxDEV22("text", {
13289
+ children: payload.missingCredentialHints.map((hint) => /* @__PURE__ */ jsxDEV23("text", {
12743
13290
  fg: cliTheme.semantic.warning,
12744
13291
  children: hint
12745
13292
  }, hint, false, undefined, this))
@@ -12749,9 +13296,9 @@ function renderConfig(payload) {
12749
13296
  }
12750
13297
  function DiagnosticsScreen({ kind }) {
12751
13298
  const location = useLocation2();
12752
- const [payload, setPayload] = useState11(null);
12753
- const [isLoading, setIsLoading] = useState11(true);
12754
- const [errorMessage, setErrorMessage] = useState11(null);
13299
+ const [payload, setPayload] = useState12(null);
13300
+ const [isLoading, setIsLoading] = useState12(true);
13301
+ const [errorMessage, setErrorMessage] = useState12(null);
12755
13302
  const title = pageTitles[kind];
12756
13303
  const loadDiagnostics = useCallback5(async () => {
12757
13304
  setIsLoading(true);
@@ -12784,7 +13331,7 @@ function DiagnosticsScreen({ kind }) {
12784
13331
  setIsLoading(false);
12785
13332
  }
12786
13333
  }, [kind, title]);
12787
- useEffect9(() => {
13334
+ useEffect10(() => {
12788
13335
  loadDiagnostics();
12789
13336
  }, [loadDiagnostics]);
12790
13337
  useKeyboard5((keyEvent) => {
@@ -12813,36 +13360,36 @@ function DiagnosticsScreen({ kind }) {
12813
13360
  }
12814
13361
  return renderConfig(payload);
12815
13362
  }, [kind, location.state, payload]);
12816
- return /* @__PURE__ */ jsxDEV22("box", {
13363
+ return /* @__PURE__ */ jsxDEV23("box", {
12817
13364
  width: "100%",
12818
13365
  height: "100%",
12819
13366
  flexDirection: "column",
12820
13367
  gap: 1,
12821
13368
  children: [
12822
- /* @__PURE__ */ jsxDEV22("box", {
13369
+ /* @__PURE__ */ jsxDEV23("box", {
12823
13370
  flexDirection: "row",
12824
13371
  justifyContent: "space-between",
12825
13372
  paddingX: 1,
12826
13373
  children: [
12827
- /* @__PURE__ */ jsxDEV22("text", {
13374
+ /* @__PURE__ */ jsxDEV23("text", {
12828
13375
  fg: cliTheme.text.primary,
12829
- attributes: TextAttributes16.BOLD,
13376
+ attributes: TextAttributes17.BOLD,
12830
13377
  children: title
12831
13378
  }, undefined, false, undefined, this),
12832
- /* @__PURE__ */ jsxDEV22("text", {
13379
+ /* @__PURE__ */ jsxDEV23("text", {
12833
13380
  fg: cliTheme.text.muted,
12834
13381
  children: isLoading ? "loading..." : "press r to refresh"
12835
13382
  }, undefined, false, undefined, this)
12836
13383
  ]
12837
13384
  }, undefined, true, undefined, this),
12838
- errorMessage ? /* @__PURE__ */ jsxDEV22("box", {
13385
+ errorMessage ? /* @__PURE__ */ jsxDEV23("box", {
12839
13386
  paddingX: 1,
12840
- children: /* @__PURE__ */ jsxDEV22("text", {
13387
+ children: /* @__PURE__ */ jsxDEV23("text", {
12841
13388
  fg: cliTheme.semantic.error,
12842
13389
  children: errorMessage
12843
13390
  }, undefined, false, undefined, this)
12844
13391
  }, undefined, false, undefined, this) : null,
12845
- /* @__PURE__ */ jsxDEV22("scrollbox", {
13392
+ /* @__PURE__ */ jsxDEV23("scrollbox", {
12846
13393
  flexGrow: 1,
12847
13394
  flexDirection: "column",
12848
13395
  gap: 1,
@@ -12875,17 +13422,24 @@ var init_diagnostics_screen = __esm(() => {
12875
13422
  });
12876
13423
 
12877
13424
  // apps/cli/src/components/home-ascii-art.tsx
12878
- import { jsxDEV as jsxDEV23 } from "@opentui/react/jsx-dev-runtime";
13425
+ import { jsxDEV as jsxDEV24 } from "@opentui/react/jsx-dev-runtime";
12879
13426
  function HomeAsciiArt() {
12880
- return /* @__PURE__ */ jsxDEV23("box", {
13427
+ return /* @__PURE__ */ jsxDEV24("box", {
12881
13428
  flexDirection: "column",
12882
13429
  alignItems: "center",
12883
- children: /* @__PURE__ */ jsxDEV23("ascii-font", {
12884
- text: "lightcode",
12885
- font: "block",
12886
- color: logoColors
12887
- }, undefined, false, undefined, this)
12888
- }, undefined, false, undefined, this);
13430
+ gap: space.xs,
13431
+ children: [
13432
+ /* @__PURE__ */ jsxDEV24("ascii-font", {
13433
+ text: "lightcode",
13434
+ font: "block",
13435
+ color: logoColors
13436
+ }, undefined, false, undefined, this),
13437
+ /* @__PURE__ */ jsxDEV24("text", {
13438
+ fg: cliTheme.text.secondary,
13439
+ children: "Agentic coding in your terminal"
13440
+ }, undefined, false, undefined, this)
13441
+ ]
13442
+ }, undefined, true, undefined, this);
12889
13443
  }
12890
13444
  var logoColors;
12891
13445
  var init_home_ascii_art = __esm(() => {
@@ -12896,9 +13450,9 @@ var init_home_ascii_art = __esm(() => {
12896
13450
 
12897
13451
  // apps/cli/src/components/home-text-area.tsx
12898
13452
  import { useKeyboard as useKeyboard6 } from "@opentui/react";
12899
- import { useState as useState12 } from "react";
13453
+ import { useState as useState13 } from "react";
12900
13454
  import { useNavigate as useNavigate2 } from "react-router";
12901
- import { jsxDEV as jsxDEV24 } from "@opentui/react/jsx-dev-runtime";
13455
+ import { jsxDEV as jsxDEV25 } from "@opentui/react/jsx-dev-runtime";
12902
13456
  function HomeTextArea() {
12903
13457
  const navigate = useNavigate2();
12904
13458
  const {
@@ -12910,9 +13464,9 @@ function HomeTextArea() {
12910
13464
  openSlashMenu,
12911
13465
  closeSlashMenu
12912
13466
  } = useAppState();
12913
- const [sessionCreateError, setSessionCreateError] = useState12(null);
12914
- const [isCreatingSession, setIsCreatingSession] = useState12(false);
12915
- const [mode, setMode] = useState12(defaultCodingAgentMode);
13467
+ const [sessionCreateError, setSessionCreateError] = useState13(null);
13468
+ const [isCreatingSession, setIsCreatingSession] = useState13(false);
13469
+ const [mode, setMode] = useState13(defaultCodingAgentMode);
12916
13470
  const slashRoutes = getSlashPageRoutes(slashMenuQuery);
12917
13471
  const selectedIndex = Math.min(slashMenuSelected, Math.max(slashRoutes.length - 1, 0));
12918
13472
  const modeDefinition = codingAgentModes[mode];
@@ -12976,24 +13530,23 @@ function HomeTextArea() {
12976
13530
  setIsCreatingSession(false);
12977
13531
  }
12978
13532
  }
12979
- return /* @__PURE__ */ jsxDEV24("box", {
13533
+ return /* @__PURE__ */ jsxDEV25("box", {
12980
13534
  width: "66%",
12981
13535
  maxWidth: 104,
12982
13536
  minWidth: 64,
12983
13537
  flexDirection: "column",
12984
13538
  gap: 1,
12985
13539
  children: [
12986
- /* @__PURE__ */ jsxDEV24(ChatTextArea, {
13540
+ /* @__PURE__ */ jsxDEV25(ChatTextArea, {
12987
13541
  containerHeight: 7,
12988
13542
  allowEmpty: true,
12989
13543
  trimOnSubmit: false,
12990
13544
  placeholder: 'Ask anything... "What is the tech stack of this project?"',
12991
13545
  focused: !isCreatingSession,
12992
13546
  disabled: isCreatingSession,
12993
- modeToggleHint: true,
12994
13547
  slashMenuOpen,
12995
13548
  onTextChange: syncSlashMenuFromInput,
12996
- beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV24(SlashPageMenu, {
13549
+ beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV25(SlashPageMenu, {
12997
13550
  query: slashMenuQuery,
12998
13551
  selectedIndex,
12999
13552
  routes: slashRoutes
@@ -13001,21 +13554,21 @@ function HomeTextArea() {
13001
13554
  onSubmit: (text) => {
13002
13555
  createSessionAndNavigate(text);
13003
13556
  },
13004
- footer: /* @__PURE__ */ jsxDEV24("box", {
13557
+ footer: /* @__PURE__ */ jsxDEV25("box", {
13005
13558
  flexDirection: "row",
13006
13559
  justifyContent: "space-between",
13007
13560
  children: [
13008
- /* @__PURE__ */ jsxDEV24("text", {
13561
+ /* @__PURE__ */ jsxDEV25("text", {
13009
13562
  fg: cliTheme.text.muted,
13010
13563
  children: isCreatingSession ? "Creating session..." : "Tab/Ctrl+T switch mode | Ctrl+P commands"
13011
13564
  }, undefined, false, undefined, this),
13012
- /* @__PURE__ */ jsxDEV24("text", {
13565
+ /* @__PURE__ */ jsxDEV25("text", {
13013
13566
  children: [
13014
- /* @__PURE__ */ jsxDEV24("span", {
13567
+ /* @__PURE__ */ jsxDEV25("span", {
13015
13568
  fg: cliTheme.accent.primary,
13016
13569
  children: modeDefinition.label
13017
13570
  }, undefined, false, undefined, this),
13018
- /* @__PURE__ */ jsxDEV24("span", {
13571
+ /* @__PURE__ */ jsxDEV25("span", {
13019
13572
  fg: cliTheme.text.muted,
13020
13573
  children: " mode"
13021
13574
  }, undefined, false, undefined, this)
@@ -13024,9 +13577,9 @@ function HomeTextArea() {
13024
13577
  ]
13025
13578
  }, undefined, true, undefined, this)
13026
13579
  }, undefined, false, undefined, this),
13027
- sessionCreateError ? /* @__PURE__ */ jsxDEV24("box", {
13580
+ sessionCreateError ? /* @__PURE__ */ jsxDEV25("box", {
13028
13581
  paddingX: 1,
13029
- children: /* @__PURE__ */ jsxDEV24("text", {
13582
+ children: /* @__PURE__ */ jsxDEV25("text", {
13030
13583
  fg: cliTheme.semantic.error,
13031
13584
  children: sessionCreateError
13032
13585
  }, undefined, false, undefined, this)
@@ -13046,11 +13599,11 @@ var init_home_text_area = __esm(() => {
13046
13599
  });
13047
13600
 
13048
13601
  // apps/cli/src/components/server-status.tsx
13049
- import { useEffect as useEffect10, useState as useState13 } from "react";
13050
- import { jsxDEV as jsxDEV25 } from "@opentui/react/jsx-dev-runtime";
13602
+ import { useEffect as useEffect11, useState as useState14 } from "react";
13603
+ import { jsxDEV as jsxDEV26 } from "@opentui/react/jsx-dev-runtime";
13051
13604
  function ServerStatus() {
13052
- const [status, setStatus] = useState13("checking");
13053
- useEffect10(() => {
13605
+ const [status, setStatus] = useState14("checking");
13606
+ useEffect11(() => {
13054
13607
  let cancelled = false;
13055
13608
  async function checkServer2() {
13056
13609
  try {
@@ -13069,7 +13622,7 @@ function ServerStatus() {
13069
13622
  cancelled = true;
13070
13623
  };
13071
13624
  }, []);
13072
- return /* @__PURE__ */ jsxDEV25("text", {
13625
+ return /* @__PURE__ */ jsxDEV26("text", {
13073
13626
  fg: serverStatusColors[status],
13074
13627
  children: [
13075
13628
  "Server: ",
@@ -13083,9 +13636,9 @@ var init_server_status = __esm(() => {
13083
13636
  });
13084
13637
 
13085
13638
  // apps/cli/src/screens/home-screen.tsx
13086
- import { jsxDEV as jsxDEV26 } from "@opentui/react/jsx-dev-runtime";
13639
+ import { jsxDEV as jsxDEV27 } from "@opentui/react/jsx-dev-runtime";
13087
13640
  function HomeScreen() {
13088
- return /* @__PURE__ */ jsxDEV26("box", {
13641
+ return /* @__PURE__ */ jsxDEV27("box", {
13089
13642
  width: "100%",
13090
13643
  height: "100%",
13091
13644
  flexDirection: "column",
@@ -13096,9 +13649,9 @@ function HomeScreen() {
13096
13649
  paddingY: 1,
13097
13650
  backgroundColor: cliTheme.surfaces.base,
13098
13651
  children: [
13099
- /* @__PURE__ */ jsxDEV26(HomeAsciiArt, {}, undefined, false, undefined, this),
13100
- /* @__PURE__ */ jsxDEV26(ServerStatus, {}, undefined, false, undefined, this),
13101
- /* @__PURE__ */ jsxDEV26(HomeTextArea, {}, undefined, false, undefined, this)
13652
+ /* @__PURE__ */ jsxDEV27(HomeAsciiArt, {}, undefined, false, undefined, this),
13653
+ /* @__PURE__ */ jsxDEV27(ServerStatus, {}, undefined, false, undefined, this),
13654
+ /* @__PURE__ */ jsxDEV27(HomeTextArea, {}, undefined, false, undefined, this)
13102
13655
  ]
13103
13656
  }, undefined, true, undefined, this);
13104
13657
  }
@@ -13110,14 +13663,14 @@ var init_home_screen = __esm(() => {
13110
13663
  });
13111
13664
 
13112
13665
  // apps/cli/src/screens/model-screen.tsx
13113
- import { TextAttributes as TextAttributes17 } from "@opentui/core";
13666
+ import { TextAttributes as TextAttributes18 } from "@opentui/core";
13114
13667
  import { useKeyboard as useKeyboard7 } from "@opentui/react";
13115
- import { useCallback as useCallback6, useEffect as useEffect11, useState as useState14 } from "react";
13116
- import { jsxDEV as jsxDEV27 } from "@opentui/react/jsx-dev-runtime";
13668
+ import { useCallback as useCallback6, useEffect as useEffect12, useState as useState15 } from "react";
13669
+ import { jsxDEV as jsxDEV28 } from "@opentui/react/jsx-dev-runtime";
13117
13670
  function ModelScreen() {
13118
- const [isLoading, setIsLoading] = useState14(true);
13119
- const [payload, setPayload] = useState14(null);
13120
- const [errorMessage, setErrorMessage] = useState14(null);
13671
+ const [isLoading, setIsLoading] = useState15(true);
13672
+ const [payload, setPayload] = useState15(null);
13673
+ const [errorMessage, setErrorMessage] = useState15(null);
13121
13674
  const loadModelConfig = useCallback6(async () => {
13122
13675
  setIsLoading(true);
13123
13676
  try {
@@ -13134,7 +13687,7 @@ function ModelScreen() {
13134
13687
  setIsLoading(false);
13135
13688
  }
13136
13689
  }, []);
13137
- useEffect11(() => {
13690
+ useEffect12(() => {
13138
13691
  loadModelConfig();
13139
13692
  }, [loadModelConfig]);
13140
13693
  useKeyboard7((keyEvent) => {
@@ -13145,42 +13698,42 @@ function ModelScreen() {
13145
13698
  keyEvent.stopPropagation();
13146
13699
  loadModelConfig();
13147
13700
  });
13148
- return /* @__PURE__ */ jsxDEV27("box", {
13701
+ return /* @__PURE__ */ jsxDEV28("box", {
13149
13702
  width: "100%",
13150
13703
  height: "100%",
13151
13704
  flexDirection: "column",
13152
13705
  gap: 1,
13153
13706
  paddingX: 1,
13154
13707
  children: [
13155
- /* @__PURE__ */ jsxDEV27("box", {
13708
+ /* @__PURE__ */ jsxDEV28("box", {
13156
13709
  flexDirection: "row",
13157
13710
  justifyContent: "space-between",
13158
13711
  children: [
13159
- /* @__PURE__ */ jsxDEV27("text", {
13712
+ /* @__PURE__ */ jsxDEV28("text", {
13160
13713
  fg: cliTheme.text.primary,
13161
- attributes: TextAttributes17.BOLD,
13714
+ attributes: TextAttributes18.BOLD,
13162
13715
  children: "Model"
13163
13716
  }, undefined, false, undefined, this),
13164
- /* @__PURE__ */ jsxDEV27("text", {
13717
+ /* @__PURE__ */ jsxDEV28("text", {
13165
13718
  fg: cliTheme.text.muted,
13166
13719
  children: isLoading ? "loading..." : "press r to refresh"
13167
13720
  }, undefined, false, undefined, this)
13168
13721
  ]
13169
13722
  }, undefined, true, undefined, this),
13170
- errorMessage ? /* @__PURE__ */ jsxDEV27("box", {
13171
- children: /* @__PURE__ */ jsxDEV27("text", {
13723
+ errorMessage ? /* @__PURE__ */ jsxDEV28("box", {
13724
+ children: /* @__PURE__ */ jsxDEV28("text", {
13172
13725
  fg: cliTheme.semantic.error,
13173
13726
  children: errorMessage
13174
13727
  }, undefined, false, undefined, this)
13175
- }, undefined, false, undefined, this) : payload ? /* @__PURE__ */ jsxDEV27("scrollbox", {
13728
+ }, undefined, false, undefined, this) : payload ? /* @__PURE__ */ jsxDEV28("scrollbox", {
13176
13729
  flexGrow: 1,
13177
13730
  flexDirection: "column",
13178
13731
  gap: 1,
13179
13732
  children: [
13180
- /* @__PURE__ */ jsxDEV27(ModelInfo, {
13733
+ /* @__PURE__ */ jsxDEV28(ModelInfo, {
13181
13734
  payload
13182
13735
  }, undefined, false, undefined, this),
13183
- payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV27(CredentialHints, {
13736
+ payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV28(CredentialHints, {
13184
13737
  hints: payload.missingCredentialHints
13185
13738
  }, undefined, false, undefined, this) : null
13186
13739
  ]
@@ -13190,7 +13743,7 @@ function ModelScreen() {
13190
13743
  }
13191
13744
  function ModelInfo({ payload }) {
13192
13745
  const baseUrlStatus = payload.baseUrl ? "configured" : "default";
13193
- return /* @__PURE__ */ jsxDEV27("box", {
13746
+ return /* @__PURE__ */ jsxDEV28("box", {
13194
13747
  flexDirection: "column",
13195
13748
  gap: 2,
13196
13749
  paddingY: 1,
@@ -13198,19 +13751,19 @@ function ModelInfo({ payload }) {
13198
13751
  borderColor: cliTheme.borders.subtle,
13199
13752
  backgroundColor: cliTheme.surfaces.panel,
13200
13753
  children: [
13201
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13754
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13202
13755
  label: "Provider",
13203
13756
  value: payload.selectedProvider ?? "not set"
13204
13757
  }, undefined, false, undefined, this),
13205
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13758
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13206
13759
  label: "Model",
13207
13760
  value: payload.selectedModel ?? "not set"
13208
13761
  }, undefined, false, undefined, this),
13209
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13762
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13210
13763
  label: "Base URL",
13211
13764
  value: `${baseUrlStatus}${payload.baseUrl ? ` (${payload.baseUrl})` : ""}`
13212
13765
  }, undefined, false, undefined, this),
13213
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13766
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13214
13767
  label: "Configured model",
13215
13768
  value: payload.configuredModel ?? "not set"
13216
13769
  }, undefined, false, undefined, this)
@@ -13218,16 +13771,16 @@ function ModelInfo({ payload }) {
13218
13771
  }, undefined, true, undefined, this);
13219
13772
  }
13220
13773
  function InfoRow2({ label, value }) {
13221
- return /* @__PURE__ */ jsxDEV27("box", {
13774
+ return /* @__PURE__ */ jsxDEV28("box", {
13222
13775
  flexDirection: "row",
13223
13776
  gap: 2,
13224
13777
  children: [
13225
- /* @__PURE__ */ jsxDEV27("text", {
13778
+ /* @__PURE__ */ jsxDEV28("text", {
13226
13779
  fg: cliTheme.text.muted,
13227
13780
  width: 18,
13228
13781
  children: label
13229
13782
  }, undefined, false, undefined, this),
13230
- /* @__PURE__ */ jsxDEV27("text", {
13783
+ /* @__PURE__ */ jsxDEV28("text", {
13231
13784
  fg: cliTheme.text.primary,
13232
13785
  children: value
13233
13786
  }, undefined, false, undefined, this)
@@ -13235,7 +13788,7 @@ function InfoRow2({ label, value }) {
13235
13788
  }, undefined, true, undefined, this);
13236
13789
  }
13237
13790
  function CredentialHints({ hints }) {
13238
- return /* @__PURE__ */ jsxDEV27("box", {
13791
+ return /* @__PURE__ */ jsxDEV28("box", {
13239
13792
  flexDirection: "column",
13240
13793
  gap: 1,
13241
13794
  paddingY: 1,
@@ -13243,12 +13796,12 @@ function CredentialHints({ hints }) {
13243
13796
  borderColor: cliTheme.borders.subtle,
13244
13797
  backgroundColor: cliTheme.surfaces.panel,
13245
13798
  children: [
13246
- /* @__PURE__ */ jsxDEV27("text", {
13799
+ /* @__PURE__ */ jsxDEV28("text", {
13247
13800
  fg: cliTheme.semantic.warning,
13248
- attributes: TextAttributes17.BOLD,
13801
+ attributes: TextAttributes18.BOLD,
13249
13802
  children: "Missing Credentials"
13250
13803
  }, undefined, false, undefined, this),
13251
- hints.map((hint, index) => /* @__PURE__ */ jsxDEV27("text", {
13804
+ hints.map((hint, index) => /* @__PURE__ */ jsxDEV28("text", {
13252
13805
  fg: cliTheme.text.secondary,
13253
13806
  children: [
13254
13807
  "- ",
@@ -13266,10 +13819,10 @@ var init_model_screen = __esm(() => {
13266
13819
  });
13267
13820
 
13268
13821
  // apps/cli/src/components/chat/model-selector.tsx
13269
- import { TextAttributes as TextAttributes18 } from "@opentui/core";
13822
+ import { TextAttributes as TextAttributes19 } from "@opentui/core";
13270
13823
  import { useKeyboard as useKeyboard8 } from "@opentui/react";
13271
- import { useEffect as useEffect12, useMemo as useMemo6, useState as useState15 } from "react";
13272
- import { jsxDEV as jsxDEV28 } from "@opentui/react/jsx-dev-runtime";
13824
+ import { useEffect as useEffect13, useMemo as useMemo6, useState as useState16 } from "react";
13825
+ import { jsxDEV as jsxDEV29 } from "@opentui/react/jsx-dev-runtime";
13273
13826
  function formatContextLength(contextLength) {
13274
13827
  if (!contextLength) {
13275
13828
  return "ctx ?";
@@ -13277,14 +13830,14 @@ function formatContextLength(contextLength) {
13277
13830
  return `ctx ${Math.round(contextLength / 1000)}k`;
13278
13831
  }
13279
13832
  function ModelSelector({ onClose, notify }) {
13280
- const [models, setModels] = useState15([]);
13281
- const [currentModel, setCurrentModel] = useState15(null);
13282
- const [loadError, setLoadError] = useState15(null);
13283
- const [isLoading, setIsLoading] = useState15(true);
13284
- const [isApplying, setIsApplying] = useState15(false);
13285
- const [filter, setFilter] = useState15("");
13286
- const [selectedIndex, setSelectedIndex] = useState15(0);
13287
- useEffect12(() => {
13833
+ const [models, setModels] = useState16([]);
13834
+ const [currentModel, setCurrentModel] = useState16(null);
13835
+ const [loadError, setLoadError] = useState16(null);
13836
+ const [isLoading, setIsLoading] = useState16(true);
13837
+ const [isApplying, setIsApplying] = useState16(false);
13838
+ const [filter, setFilter] = useState16("");
13839
+ const [selectedIndex, setSelectedIndex] = useState16(0);
13840
+ useEffect13(() => {
13288
13841
  let cancelled = false;
13289
13842
  async function loadModels() {
13290
13843
  try {
@@ -13326,7 +13879,7 @@ function ModelSelector({ onClose, notify }) {
13326
13879
  }
13327
13880
  return models.filter((model) => model.id.toLowerCase().includes(normalized) || model.name.toLowerCase().includes(normalized));
13328
13881
  }, [filter, models]);
13329
- useEffect12(() => {
13882
+ useEffect13(() => {
13330
13883
  setSelectedIndex((current) => Math.min(current, Math.max(filteredModels.length - 1, 0)));
13331
13884
  }, [filteredModels.length]);
13332
13885
  const applyModel = async (model) => {
@@ -13393,14 +13946,14 @@ function ModelSelector({ onClose, notify }) {
13393
13946
  });
13394
13947
  const windowStart = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisibleRows / 2), filteredModels.length - maxVisibleRows));
13395
13948
  const visibleModels = filteredModels.slice(windowStart, windowStart + maxVisibleRows);
13396
- return /* @__PURE__ */ jsxDEV28("box", {
13949
+ return /* @__PURE__ */ jsxDEV29("box", {
13397
13950
  width: "100%",
13398
13951
  flexDirection: "column",
13399
- borderStyle: "single",
13952
+ borderStyle: borderStyleFor.modal,
13400
13953
  borderColor: cliTheme.overlay.border,
13401
13954
  backgroundColor: cliTheme.overlay.surface,
13402
13955
  children: [
13403
- /* @__PURE__ */ jsxDEV28("box", {
13956
+ /* @__PURE__ */ jsxDEV29("box", {
13404
13957
  width: "100%",
13405
13958
  flexDirection: "row",
13406
13959
  justifyContent: "space-between",
@@ -13410,48 +13963,48 @@ function ModelSelector({ onClose, notify }) {
13410
13963
  border: ["bottom"],
13411
13964
  borderColor: cliTheme.overlay.border,
13412
13965
  children: [
13413
- /* @__PURE__ */ jsxDEV28("text", {
13966
+ /* @__PURE__ */ jsxDEV29("text", {
13414
13967
  fg: cliTheme.accent.primary,
13415
- attributes: TextAttributes18.BOLD,
13968
+ attributes: TextAttributes19.BOLD,
13416
13969
  children: "Switch Model (OpenRouter)"
13417
13970
  }, undefined, false, undefined, this),
13418
- /* @__PURE__ */ jsxDEV28("text", {
13971
+ /* @__PURE__ */ jsxDEV29("text", {
13419
13972
  fg: cliTheme.overlay.footerText,
13420
13973
  children: "type to filter | \u2191/\u2193 select | Enter confirm | Esc cancel"
13421
13974
  }, undefined, false, undefined, this)
13422
13975
  ]
13423
13976
  }, undefined, true, undefined, this),
13424
- /* @__PURE__ */ jsxDEV28("box", {
13977
+ /* @__PURE__ */ jsxDEV29("box", {
13425
13978
  width: "100%",
13426
13979
  paddingX: 2,
13427
13980
  paddingY: 1,
13428
- children: /* @__PURE__ */ jsxDEV28("text", {
13981
+ children: /* @__PURE__ */ jsxDEV29("text", {
13429
13982
  fg: cliTheme.text.primary,
13430
13983
  children: `Filter: ${filter}\u2588`
13431
13984
  }, undefined, false, undefined, this)
13432
13985
  }, undefined, false, undefined, this),
13433
- isLoading ? /* @__PURE__ */ jsxDEV28("box", {
13986
+ isLoading ? /* @__PURE__ */ jsxDEV29("box", {
13434
13987
  paddingX: 2,
13435
13988
  paddingY: 1,
13436
- children: /* @__PURE__ */ jsxDEV28("text", {
13989
+ children: /* @__PURE__ */ jsxDEV29("text", {
13437
13990
  fg: cliTheme.text.muted,
13438
13991
  children: "Loading models..."
13439
13992
  }, undefined, false, undefined, this)
13440
- }, undefined, false, undefined, this) : loadError ? /* @__PURE__ */ jsxDEV28("box", {
13993
+ }, undefined, false, undefined, this) : loadError ? /* @__PURE__ */ jsxDEV29("box", {
13441
13994
  paddingX: 2,
13442
13995
  paddingY: 1,
13443
- children: /* @__PURE__ */ jsxDEV28("text", {
13996
+ children: /* @__PURE__ */ jsxDEV29("text", {
13444
13997
  fg: cliTheme.semantic.error,
13445
13998
  children: loadError
13446
13999
  }, undefined, false, undefined, this)
13447
- }, undefined, false, undefined, this) : filteredModels.length === 0 ? /* @__PURE__ */ jsxDEV28("box", {
14000
+ }, undefined, false, undefined, this) : filteredModels.length === 0 ? /* @__PURE__ */ jsxDEV29("box", {
13448
14001
  paddingX: 2,
13449
14002
  paddingY: 1,
13450
- children: /* @__PURE__ */ jsxDEV28("text", {
14003
+ children: /* @__PURE__ */ jsxDEV29("text", {
13451
14004
  fg: cliTheme.text.muted,
13452
14005
  children: `No models match "${filter}".`
13453
14006
  }, undefined, false, undefined, this)
13454
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV28("box", {
14007
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV29("box", {
13455
14008
  flexDirection: "column",
13456
14009
  paddingY: 1,
13457
14010
  children: [
@@ -13459,29 +14012,29 @@ function ModelSelector({ onClose, notify }) {
13459
14012
  const index = windowStart + visibleIndex;
13460
14013
  const isSelected = index === selectedIndex;
13461
14014
  const rowColors = getOverlayRowColors(isSelected);
13462
- return /* @__PURE__ */ jsxDEV28("box", {
14015
+ return /* @__PURE__ */ jsxDEV29("box", {
13463
14016
  width: "100%",
13464
14017
  flexDirection: "row",
13465
14018
  paddingX: 2,
13466
14019
  backgroundColor: rowColors.backgroundColor,
13467
14020
  children: [
13468
- /* @__PURE__ */ jsxDEV28("text", {
14021
+ /* @__PURE__ */ jsxDEV29("text", {
13469
14022
  width: 2,
13470
14023
  fg: isSelected ? cliTheme.accent.primary : cliTheme.text.muted,
13471
- attributes: TextAttributes18.BOLD,
14024
+ attributes: TextAttributes19.BOLD,
13472
14025
  children: isSelected ? ">" : " "
13473
14026
  }, undefined, false, undefined, this),
13474
- /* @__PURE__ */ jsxDEV28("text", {
14027
+ /* @__PURE__ */ jsxDEV29("text", {
13475
14028
  fg: isSelected ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
13476
- attributes: isSelected ? TextAttributes18.BOLD : TextAttributes18.NONE,
14029
+ attributes: isSelected ? TextAttributes19.BOLD : TextAttributes19.NONE,
13477
14030
  children: model.id
13478
14031
  }, undefined, false, undefined, this),
13479
- /* @__PURE__ */ jsxDEV28("text", {
14032
+ /* @__PURE__ */ jsxDEV29("text", {
13480
14033
  fg: cliTheme.text.muted,
13481
14034
  marginLeft: 2,
13482
14035
  children: `${formatContextLength(model.contextLength)}${model.supportsTools ? " | tools" : ""}${model.supportsReasoning ? " | reasoning" : ""}`
13483
14036
  }, undefined, false, undefined, this),
13484
- model.id === currentModel && /* @__PURE__ */ jsxDEV28("text", {
14037
+ model.id === currentModel && /* @__PURE__ */ jsxDEV29("text", {
13485
14038
  fg: cliTheme.accent.primary,
13486
14039
  marginLeft: 2,
13487
14040
  children: "(current)"
@@ -13489,10 +14042,10 @@ function ModelSelector({ onClose, notify }) {
13489
14042
  ]
13490
14043
  }, model.id, true, undefined, this);
13491
14044
  }),
13492
- /* @__PURE__ */ jsxDEV28("box", {
14045
+ /* @__PURE__ */ jsxDEV29("box", {
13493
14046
  paddingX: 2,
13494
14047
  marginTop: 1,
13495
- children: /* @__PURE__ */ jsxDEV28("text", {
14048
+ children: /* @__PURE__ */ jsxDEV29("text", {
13496
14049
  fg: cliTheme.overlay.footerText,
13497
14050
  children: `${filteredModels.length} model${filteredModels.length === 1 ? "" : "s"}${isApplying ? " | switching..." : ""}`
13498
14051
  }, undefined, false, undefined, this)
@@ -13509,30 +14062,30 @@ var init_model_selector = __esm(() => {
13509
14062
  });
13510
14063
 
13511
14064
  // apps/cli/src/screens/model-select-screen.tsx
13512
- import { TextAttributes as TextAttributes19 } from "@opentui/core";
13513
- import { useState as useState16 } from "react";
14065
+ import { TextAttributes as TextAttributes20 } from "@opentui/core";
14066
+ import { useState as useState17 } from "react";
13514
14067
  import { useNavigate as useNavigate3 } from "react-router";
13515
- import { jsxDEV as jsxDEV29 } from "@opentui/react/jsx-dev-runtime";
14068
+ import { jsxDEV as jsxDEV30 } from "@opentui/react/jsx-dev-runtime";
13516
14069
  function ModelSelectScreen() {
13517
14070
  const navigate = useNavigate3();
13518
- const [notice, setNotice] = useState16(null);
13519
- return /* @__PURE__ */ jsxDEV29("box", {
14071
+ const [notice, setNotice] = useState17(null);
14072
+ return /* @__PURE__ */ jsxDEV30("box", {
13520
14073
  width: "100%",
13521
14074
  height: "100%",
13522
14075
  flexDirection: "column",
13523
14076
  gap: 1,
13524
14077
  paddingX: 1,
13525
14078
  children: [
13526
- /* @__PURE__ */ jsxDEV29("text", {
14079
+ /* @__PURE__ */ jsxDEV30("text", {
13527
14080
  fg: cliTheme.text.primary,
13528
- attributes: TextAttributes19.BOLD,
14081
+ attributes: TextAttributes20.BOLD,
13529
14082
  children: "Switch Model"
13530
14083
  }, undefined, false, undefined, this),
13531
- notice ? /* @__PURE__ */ jsxDEV29("text", {
14084
+ notice ? /* @__PURE__ */ jsxDEV30("text", {
13532
14085
  fg: notice.tone === "error" ? cliTheme.semantic.error : cliTheme.accent.primary,
13533
14086
  children: notice.text
13534
14087
  }, undefined, false, undefined, this) : null,
13535
- /* @__PURE__ */ jsxDEV29(ModelSelector, {
14088
+ /* @__PURE__ */ jsxDEV30(ModelSelector, {
13536
14089
  notify: (text, tone = "info") => setNotice({ text, tone }),
13537
14090
  onClose: () => navigate(-1)
13538
14091
  }, undefined, false, undefined, this)
@@ -13545,14 +14098,14 @@ var init_model_select_screen = __esm(() => {
13545
14098
  });
13546
14099
 
13547
14100
  // apps/cli/src/screens/onboarding-screen.tsx
13548
- import { TextAttributes as TextAttributes20 } from "@opentui/core";
14101
+ import { TextAttributes as TextAttributes21 } from "@opentui/core";
13549
14102
  import { useKeyboard as useKeyboard9 } from "@opentui/react";
13550
14103
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
13551
14104
  import os5 from "os";
13552
14105
  import path17 from "path";
13553
- import { useCallback as useCallback7, useState as useState17 } from "react";
14106
+ import { useCallback as useCallback7, useState as useState18 } from "react";
13554
14107
  import { useNavigate as useNavigate4 } from "react-router";
13555
- import { jsxDEV as jsxDEV30 } from "@opentui/react/jsx-dev-runtime";
14108
+ import { jsxDEV as jsxDEV31 } from "@opentui/react/jsx-dev-runtime";
13556
14109
  function getUserSettingsPath() {
13557
14110
  const configured = process.env.LIGHTCODE_USER_CONFIG?.trim();
13558
14111
  return configured ?? path17.join(os5.homedir(), ".lightcode", "settings.json");
@@ -13576,12 +14129,12 @@ function mergeUserSettings(update) {
13576
14129
  function OnboardingScreen() {
13577
14130
  const navigate = useNavigate4();
13578
14131
  const { bumpConfigRefresh } = useAppState();
13579
- const [step, setStep] = useState17("provider");
13580
- const [selectedIndex, setSelectedIndex] = useState17(0);
13581
- const [apiKey, setApiKey] = useState17("");
13582
- const [model, setModel] = useState17("");
13583
- const [errorText, setErrorText] = useState17(null);
13584
- const [serverRestarted, setServerRestarted] = useState17(false);
14132
+ const [step, setStep] = useState18("provider");
14133
+ const [selectedIndex, setSelectedIndex] = useState18(0);
14134
+ const [apiKey, setApiKey] = useState18("");
14135
+ const [model, setModel] = useState18("");
14136
+ const [errorText, setErrorText] = useState18(null);
14137
+ const [serverRestarted, setServerRestarted] = useState18(false);
13585
14138
  const provider = providerChoices[selectedIndex];
13586
14139
  const completeSetup = useCallback7(async ({
13587
14140
  finalModel,
@@ -13598,7 +14151,12 @@ function OnboardingScreen() {
13598
14151
  ...finalBaseUrl ? { baseUrl: finalBaseUrl } : {}
13599
14152
  });
13600
14153
  const restarted = await restartOwnedServer();
13601
- setServerRestarted(restarted);
14154
+ let reloaded = false;
14155
+ try {
14156
+ const response = await client.config.reload.$post();
14157
+ reloaded = response.ok;
14158
+ } catch {}
14159
+ setServerRestarted(restarted || reloaded);
13602
14160
  bumpConfigRefresh();
13603
14161
  setStep("done");
13604
14162
  } catch (error) {
@@ -13625,36 +14183,66 @@ function OnboardingScreen() {
13625
14183
  navigate("/");
13626
14184
  }
13627
14185
  });
13628
- return /* @__PURE__ */ jsxDEV30("box", {
14186
+ const totalSteps = ONBOARDING_STEP_SEQUENCE.length;
14187
+ const currentStepNumber = step === "baseUrl" || step === "saving" ? ONBOARDING_STEP_SEQUENCE.indexOf("model") + 1 : Math.max(1, ONBOARDING_STEP_SEQUENCE.indexOf(step) + 1);
14188
+ return /* @__PURE__ */ jsxDEV31("box", {
13629
14189
  width: "100%",
13630
14190
  height: "100%",
13631
14191
  flexDirection: "column",
13632
14192
  alignItems: "center",
13633
14193
  paddingTop: 2,
13634
- children: /* @__PURE__ */ jsxDEV30("box", {
14194
+ children: /* @__PURE__ */ jsxDEV31("box", {
13635
14195
  width: 72,
13636
14196
  flexDirection: "column",
13637
14197
  gap: 1,
13638
14198
  children: [
13639
- /* @__PURE__ */ jsxDEV30("text", {
14199
+ /* @__PURE__ */ jsxDEV31("text", {
13640
14200
  fg: cliTheme.accent.primary,
13641
- attributes: TextAttributes20.BOLD,
14201
+ attributes: TextAttributes21.BOLD,
13642
14202
  children: "Welcome to Lightcode"
13643
14203
  }, undefined, false, undefined, this),
13644
- /* @__PURE__ */ jsxDEV30("text", {
14204
+ /* @__PURE__ */ jsxDEV31("text", {
13645
14205
  fg: cliTheme.text.secondary,
13646
14206
  children: "One-time setup: pick a model provider and store an API key."
13647
14207
  }, undefined, false, undefined, this),
13648
- /* @__PURE__ */ jsxDEV30("text", {
14208
+ /* @__PURE__ */ jsxDEV31("box", {
14209
+ flexDirection: "row",
14210
+ gap: 1,
14211
+ alignItems: "center",
14212
+ children: [
14213
+ /* @__PURE__ */ jsxDEV31("text", {
14214
+ fg: cliTheme.text.muted,
14215
+ children: [
14216
+ "Step ",
14217
+ currentStepNumber,
14218
+ " of ",
14219
+ totalSteps
14220
+ ]
14221
+ }, undefined, true, undefined, this),
14222
+ /* @__PURE__ */ jsxDEV31("text", {
14223
+ children: [
14224
+ /* @__PURE__ */ jsxDEV31("span", {
14225
+ fg: cliTheme.accent.primary,
14226
+ children: Array.from({ length: currentStepNumber }, () => activeGlyphs.statusDot).join(" ")
14227
+ }, undefined, false, undefined, this),
14228
+ /* @__PURE__ */ jsxDEV31("span", {
14229
+ fg: cliTheme.text.muted,
14230
+ children: Array.from({ length: Math.max(0, totalSteps - currentStepNumber) }, () => ` ${activeGlyphs.bullet}`).join("")
14231
+ }, undefined, false, undefined, this)
14232
+ ]
14233
+ }, undefined, true, undefined, this)
14234
+ ]
14235
+ }, undefined, true, undefined, this),
14236
+ /* @__PURE__ */ jsxDEV31("text", {
13649
14237
  fg: cliTheme.text.muted,
13650
- attributes: TextAttributes20.DIM,
14238
+ attributes: TextAttributes21.DIM,
13651
14239
  children: "Keys are stored in ~/.lightcode/credentials.json (environment variables always take precedence)."
13652
14240
  }, undefined, false, undefined, this),
13653
- errorText ? /* @__PURE__ */ jsxDEV30("text", {
14241
+ errorText ? /* @__PURE__ */ jsxDEV31("text", {
13654
14242
  fg: cliTheme.semantic.error,
13655
14243
  children: errorText
13656
14244
  }, undefined, false, undefined, this) : null,
13657
- step === "provider" ? /* @__PURE__ */ jsxDEV30("box", {
14245
+ step === "provider" ? /* @__PURE__ */ jsxDEV31("box", {
13658
14246
  flexDirection: "column",
13659
14247
  borderStyle: "single",
13660
14248
  borderColor: cliTheme.borders.default,
@@ -13662,53 +14250,53 @@ function OnboardingScreen() {
13662
14250
  paddingX: 1,
13663
14251
  paddingY: 1,
13664
14252
  children: [
13665
- /* @__PURE__ */ jsxDEV30("text", {
14253
+ /* @__PURE__ */ jsxDEV31("text", {
13666
14254
  fg: cliTheme.text.primary,
13667
- attributes: TextAttributes20.BOLD,
14255
+ attributes: TextAttributes21.BOLD,
13668
14256
  children: "Choose a provider"
13669
14257
  }, undefined, false, undefined, this),
13670
- providerChoices.map((choice, index) => /* @__PURE__ */ jsxDEV30("box", {
14258
+ providerChoices.map((choice, index) => /* @__PURE__ */ jsxDEV31("box", {
13671
14259
  flexDirection: "row",
13672
14260
  gap: 1,
13673
14261
  backgroundColor: index === selectedIndex ? cliTheme.overlay.selectedRowBackground : undefined,
13674
14262
  paddingX: 1,
13675
14263
  children: [
13676
- /* @__PURE__ */ jsxDEV30("text", {
14264
+ /* @__PURE__ */ jsxDEV31("text", {
13677
14265
  fg: index === selectedIndex ? cliTheme.accent.primary : cliTheme.text.muted,
13678
14266
  children: index === selectedIndex ? ">" : " "
13679
14267
  }, undefined, false, undefined, this),
13680
- /* @__PURE__ */ jsxDEV30("text", {
14268
+ /* @__PURE__ */ jsxDEV31("text", {
13681
14269
  fg: index === selectedIndex ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
13682
- attributes: index === selectedIndex ? TextAttributes20.BOLD : TextAttributes20.NONE,
14270
+ attributes: index === selectedIndex ? TextAttributes21.BOLD : TextAttributes21.NONE,
13683
14271
  children: choice.label
13684
14272
  }, undefined, false, undefined, this),
13685
- /* @__PURE__ */ jsxDEV30("text", {
14273
+ /* @__PURE__ */ jsxDEV31("text", {
13686
14274
  fg: cliTheme.overlay.description,
13687
14275
  children: choice.description
13688
14276
  }, undefined, false, undefined, this)
13689
14277
  ]
13690
14278
  }, choice.id, true, undefined, this)),
13691
- /* @__PURE__ */ jsxDEV30("text", {
14279
+ /* @__PURE__ */ jsxDEV31("text", {
13692
14280
  fg: cliTheme.text.muted,
13693
- attributes: TextAttributes20.DIM,
14281
+ attributes: TextAttributes21.DIM,
13694
14282
  children: "Up/Down select - Enter continue"
13695
14283
  }, undefined, false, undefined, this)
13696
14284
  ]
13697
14285
  }, undefined, true, undefined, this) : null,
13698
- step === "apiKey" ? /* @__PURE__ */ jsxDEV30("box", {
14286
+ step === "apiKey" ? /* @__PURE__ */ jsxDEV31("box", {
13699
14287
  flexDirection: "column",
13700
14288
  gap: 1,
13701
14289
  children: [
13702
- /* @__PURE__ */ jsxDEV30("text", {
14290
+ /* @__PURE__ */ jsxDEV31("text", {
13703
14291
  fg: cliTheme.text.primary,
13704
14292
  children: `Paste your ${provider.label} API key and press Enter`
13705
14293
  }, undefined, false, undefined, this),
13706
- /* @__PURE__ */ jsxDEV30("text", {
14294
+ /* @__PURE__ */ jsxDEV31("text", {
13707
14295
  fg: cliTheme.text.muted,
13708
- attributes: TextAttributes20.DIM,
14296
+ attributes: TextAttributes21.DIM,
13709
14297
  children: provider.keyHint
13710
14298
  }, undefined, false, undefined, this),
13711
- /* @__PURE__ */ jsxDEV30(ChatTextArea, {
14299
+ /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13712
14300
  placeholder: "API key...",
13713
14301
  onSubmit: (text) => {
13714
14302
  const trimmedKey = text.trim();
@@ -13725,15 +14313,15 @@ function OnboardingScreen() {
13725
14313
  }, undefined, false, undefined, this)
13726
14314
  ]
13727
14315
  }, undefined, true, undefined, this) : null,
13728
- step === "model" ? /* @__PURE__ */ jsxDEV30("box", {
14316
+ step === "model" ? /* @__PURE__ */ jsxDEV31("box", {
13729
14317
  flexDirection: "column",
13730
14318
  gap: 1,
13731
14319
  children: [
13732
- /* @__PURE__ */ jsxDEV30("text", {
14320
+ /* @__PURE__ */ jsxDEV31("text", {
13733
14321
  fg: cliTheme.text.primary,
13734
14322
  children: `Model id for ${provider.label} (e.g. ${provider.id === "openrouter" ? "anthropic/claude-sonnet-4-6" : "your model id"})`
13735
14323
  }, undefined, false, undefined, this),
13736
- /* @__PURE__ */ jsxDEV30(ChatTextArea, {
14324
+ /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13737
14325
  placeholder: "Model id...",
13738
14326
  onSubmit: (text) => {
13739
14327
  const trimmedModel = text.trim();
@@ -13754,15 +14342,15 @@ function OnboardingScreen() {
13754
14342
  }, undefined, false, undefined, this)
13755
14343
  ]
13756
14344
  }, undefined, true, undefined, this) : null,
13757
- step === "baseUrl" ? /* @__PURE__ */ jsxDEV30("box", {
14345
+ step === "baseUrl" ? /* @__PURE__ */ jsxDEV31("box", {
13758
14346
  flexDirection: "column",
13759
14347
  gap: 1,
13760
14348
  children: [
13761
- /* @__PURE__ */ jsxDEV30("text", {
14349
+ /* @__PURE__ */ jsxDEV31("text", {
13762
14350
  fg: cliTheme.text.primary,
13763
14351
  children: "Base URL of the OpenAI-compatible endpoint"
13764
14352
  }, undefined, false, undefined, this),
13765
- /* @__PURE__ */ jsxDEV30(ChatTextArea, {
14353
+ /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13766
14354
  placeholder: "https://...",
13767
14355
  onSubmit: (text) => {
13768
14356
  const trimmedBaseUrl = text.trim();
@@ -13778,24 +14366,24 @@ function OnboardingScreen() {
13778
14366
  }, undefined, false, undefined, this)
13779
14367
  ]
13780
14368
  }, undefined, true, undefined, this) : null,
13781
- step === "saving" ? /* @__PURE__ */ jsxDEV30("text", {
14369
+ step === "saving" ? /* @__PURE__ */ jsxDEV31("text", {
13782
14370
  fg: cliTheme.semantic.info,
13783
14371
  children: "Saving configuration and restarting the server..."
13784
14372
  }, undefined, false, undefined, this) : null,
13785
- step === "done" ? /* @__PURE__ */ jsxDEV30("box", {
14373
+ step === "done" ? /* @__PURE__ */ jsxDEV31("box", {
13786
14374
  flexDirection: "column",
13787
14375
  gap: 1,
13788
14376
  children: [
13789
- /* @__PURE__ */ jsxDEV30("text", {
14377
+ /* @__PURE__ */ jsxDEV31("text", {
13790
14378
  fg: cliTheme.semantic.success,
13791
- attributes: TextAttributes20.BOLD,
14379
+ attributes: TextAttributes21.BOLD,
13792
14380
  children: "Setup complete"
13793
14381
  }, undefined, false, undefined, this),
13794
- /* @__PURE__ */ jsxDEV30("text", {
14382
+ /* @__PURE__ */ jsxDEV31("text", {
13795
14383
  fg: cliTheme.text.secondary,
13796
14384
  children: serverRestarted ? "The local server was restarted with your new configuration." : "Restart the Lightcode server to pick up the new configuration."
13797
14385
  }, undefined, false, undefined, this),
13798
- /* @__PURE__ */ jsxDEV30("text", {
14386
+ /* @__PURE__ */ jsxDEV31("text", {
13799
14387
  fg: cliTheme.text.muted,
13800
14388
  children: "Press Enter to start coding."
13801
14389
  }, undefined, false, undefined, this)
@@ -13805,14 +14393,17 @@ function OnboardingScreen() {
13805
14393
  }, undefined, true, undefined, this)
13806
14394
  }, undefined, false, undefined, this);
13807
14395
  }
13808
- var providerChoices;
14396
+ var ONBOARDING_STEP_SEQUENCE, providerChoices;
13809
14397
  var init_onboarding_screen = __esm(() => {
13810
14398
  init_src2();
13811
14399
  init_chat_text_area();
14400
+ init_client();
13812
14401
  init_server_launcher();
13813
14402
  init_app_state();
13814
14403
  init_cli_theme();
14404
+ init_cli_theme_capabilities();
13815
14405
  init_text_utils();
14406
+ ONBOARDING_STEP_SEQUENCE = ["provider", "apiKey", "model", "done"];
13816
14407
  providerChoices = [
13817
14408
  {
13818
14409
  id: "anthropic",
@@ -13854,11 +14445,11 @@ var init_onboarding_screen = __esm(() => {
13854
14445
  });
13855
14446
 
13856
14447
  // apps/cli/src/screens/session-list-screen.tsx
13857
- import { TextAttributes as TextAttributes21 } from "@opentui/core";
14448
+ import { TextAttributes as TextAttributes22 } from "@opentui/core";
13858
14449
  import { useKeyboard as useKeyboard10 } from "@opentui/react";
13859
- import { useCallback as useCallback8, useEffect as useEffect13, useMemo as useMemo7, useState as useState18 } from "react";
14450
+ import { useCallback as useCallback8, useEffect as useEffect14, useMemo as useMemo7, useState as useState19 } from "react";
13860
14451
  import { useNavigate as useNavigate5 } from "react-router";
13861
- import { jsxDEV as jsxDEV31 } from "@opentui/react/jsx-dev-runtime";
14452
+ import { jsxDEV as jsxDEV32 } from "@opentui/react/jsx-dev-runtime";
13862
14453
  function getSessionLabel(session) {
13863
14454
  return session.title ?? session.latestUserPromptPreview ?? session.id;
13864
14455
  }
@@ -13871,15 +14462,15 @@ function sessionMatchesFilter(session, query) {
13871
14462
  }
13872
14463
  function SessionListScreen() {
13873
14464
  const navigate = useNavigate5();
13874
- const [sessions, setSessions] = useState18([]);
13875
- const [selectedIndex, setSelectedIndex] = useState18(0);
13876
- const [isLoading, setIsLoading] = useState18(true);
13877
- const [errorMessage, setErrorMessage] = useState18(null);
13878
- const [actionMessage, setActionMessage] = useState18(null);
13879
- const [pendingDeleteId, setPendingDeleteId] = useState18(null);
13880
- const [filterQuery, setFilterQuery] = useState18("");
13881
- const [filterOpen, setFilterOpen] = useState18(false);
13882
- const [renamingSessionId, setRenamingSessionId] = useState18(null);
14465
+ const [sessions, setSessions] = useState19([]);
14466
+ const [selectedIndex, setSelectedIndex] = useState19(0);
14467
+ const [isLoading, setIsLoading] = useState19(true);
14468
+ const [errorMessage, setErrorMessage] = useState19(null);
14469
+ const [actionMessage, setActionMessage] = useState19(null);
14470
+ const [pendingDeleteId, setPendingDeleteId] = useState19(null);
14471
+ const [filterQuery, setFilterQuery] = useState19("");
14472
+ const [filterOpen, setFilterOpen] = useState19(false);
14473
+ const [renamingSessionId, setRenamingSessionId] = useState19(null);
13883
14474
  const visibleSessions = useMemo7(() => sessions.filter((session) => sessionMatchesFilter(session, filterQuery)), [filterQuery, sessions]);
13884
14475
  const safeSelectedIndex = Math.min(selectedIndex, Math.max(visibleSessions.length - 1, 0));
13885
14476
  const selectedSession = visibleSessions[safeSelectedIndex] ?? null;
@@ -14036,7 +14627,7 @@ function SessionListScreen() {
14036
14627
  setActionMessage(getErrorMessage(error, "Unable to rename session."));
14037
14628
  }
14038
14629
  }, [loadSessions]);
14039
- useEffect13(() => {
14630
+ useEffect14(() => {
14040
14631
  loadSessions();
14041
14632
  }, [loadSessions]);
14042
14633
  useKeyboard10((keyEvent) => {
@@ -14127,85 +14718,96 @@ function SessionListScreen() {
14127
14718
  }
14128
14719
  return "Enter resume | r rename | f fork | / filter | e export | d delete | u refresh";
14129
14720
  }, [sessions.length]);
14130
- return /* @__PURE__ */ jsxDEV31("box", {
14721
+ return /* @__PURE__ */ jsxDEV32("box", {
14131
14722
  width: "100%",
14132
14723
  height: "100%",
14133
14724
  flexDirection: "column",
14134
14725
  gap: 1,
14135
14726
  children: [
14136
- /* @__PURE__ */ jsxDEV31("box", {
14727
+ /* @__PURE__ */ jsxDEV32("box", {
14137
14728
  flexDirection: "row",
14138
14729
  justifyContent: "space-between",
14139
14730
  paddingX: 1,
14140
14731
  children: [
14141
- /* @__PURE__ */ jsxDEV31("text", {
14732
+ /* @__PURE__ */ jsxDEV32("text", {
14142
14733
  fg: cliTheme.text.primary,
14143
- attributes: TextAttributes21.BOLD,
14734
+ attributes: TextAttributes22.BOLD,
14144
14735
  children: "Sessions"
14145
14736
  }, undefined, false, undefined, this),
14146
- /* @__PURE__ */ jsxDEV31("text", {
14737
+ /* @__PURE__ */ jsxDEV32("text", {
14147
14738
  fg: cliTheme.text.muted,
14148
14739
  children: isLoading ? "loading..." : `${sessions.length} saved`
14149
14740
  }, undefined, false, undefined, this)
14150
14741
  ]
14151
14742
  }, undefined, true, undefined, this),
14152
- errorMessage ? /* @__PURE__ */ jsxDEV31("box", {
14743
+ errorMessage ? /* @__PURE__ */ jsxDEV32("box", {
14153
14744
  paddingX: 1,
14154
- children: /* @__PURE__ */ jsxDEV31("text", {
14745
+ children: /* @__PURE__ */ jsxDEV32("text", {
14155
14746
  fg: cliTheme.semantic.error,
14156
14747
  children: errorMessage
14157
14748
  }, undefined, false, undefined, this)
14158
14749
  }, undefined, false, undefined, this) : null,
14159
- filterQuery && !filterOpen ? /* @__PURE__ */ jsxDEV31("box", {
14750
+ filterQuery && !filterOpen ? /* @__PURE__ */ jsxDEV32("box", {
14160
14751
  paddingX: 1,
14161
- children: /* @__PURE__ */ jsxDEV31("text", {
14752
+ children: /* @__PURE__ */ jsxDEV32("text", {
14162
14753
  fg: cliTheme.accent.primary,
14163
14754
  children: `Filter: "${filterQuery}" (${visibleSessions.length} match${visibleSessions.length === 1 ? "" : "es"}) - Esc clears`
14164
14755
  }, undefined, false, undefined, this)
14165
14756
  }, undefined, false, undefined, this) : null,
14166
- /* @__PURE__ */ jsxDEV31("box", {
14757
+ /* @__PURE__ */ jsxDEV32("box", {
14167
14758
  flexDirection: "column",
14168
14759
  flexGrow: 1,
14169
14760
  children: [
14170
- visibleSessions.length === 0 && !isLoading ? /* @__PURE__ */ jsxDEV31("box", {
14171
- paddingX: 1,
14172
- children: /* @__PURE__ */ jsxDEV31("text", {
14173
- fg: cliTheme.text.muted,
14174
- children: sessions.length === 0 ? "No saved sessions yet." : "No sessions match the filter."
14175
- }, undefined, false, undefined, this)
14176
- }, undefined, false, undefined, this) : null,
14761
+ visibleSessions.length === 0 && !isLoading ? /* @__PURE__ */ jsxDEV32("box", {
14762
+ flexGrow: 1,
14763
+ flexDirection: "column",
14764
+ alignItems: "center",
14765
+ justifyContent: "center",
14766
+ gap: 1,
14767
+ children: [
14768
+ /* @__PURE__ */ jsxDEV32("text", {
14769
+ fg: cliTheme.accent.primary,
14770
+ attributes: TextAttributes22.BOLD,
14771
+ children: sessions.length === 0 ? "\u25C6 No sessions yet" : "No matches"
14772
+ }, undefined, false, undefined, this),
14773
+ /* @__PURE__ */ jsxDEV32("text", {
14774
+ fg: cliTheme.text.muted,
14775
+ children: sessions.length === 0 ? "Go Home and send a message to start your first session." : "No sessions match the filter \u2014 clear it to see all."
14776
+ }, undefined, false, undefined, this)
14777
+ ]
14778
+ }, undefined, true, undefined, this) : null,
14177
14779
  visibleSessions.map((session, index) => {
14178
14780
  const isSelected = index === safeSelectedIndex;
14179
14781
  const rowColors = getOverlayRowColors(isSelected);
14180
14782
  const selectedForDelete = pendingDeleteId === session.id;
14181
- return /* @__PURE__ */ jsxDEV31("box", {
14783
+ return /* @__PURE__ */ jsxDEV32("box", {
14182
14784
  flexDirection: "column",
14183
14785
  paddingX: 1,
14184
14786
  backgroundColor: rowColors.backgroundColor,
14185
14787
  children: [
14186
- /* @__PURE__ */ jsxDEV31("box", {
14788
+ /* @__PURE__ */ jsxDEV32("box", {
14187
14789
  flexDirection: "row",
14188
14790
  justifyContent: "space-between",
14189
14791
  children: [
14190
- /* @__PURE__ */ jsxDEV31("text", {
14792
+ /* @__PURE__ */ jsxDEV32("text", {
14191
14793
  fg: selectedForDelete ? cliTheme.semantic.error : rowColors.primaryTextColor,
14192
- attributes: TextAttributes21.BOLD,
14794
+ attributes: TextAttributes22.BOLD,
14193
14795
  children: [
14194
14796
  index + 1,
14195
14797
  ". ",
14196
14798
  truncateInline2(getSessionLabel(session), 64)
14197
14799
  ]
14198
14800
  }, undefined, true, undefined, this),
14199
- /* @__PURE__ */ jsxDEV31("text", {
14801
+ /* @__PURE__ */ jsxDEV32("text", {
14200
14802
  fg: rowColors.secondaryTextColor,
14201
- attributes: TextAttributes21.DIM,
14803
+ attributes: TextAttributes22.DIM,
14202
14804
  children: `${session.messageCount} msgs - ${session.mode} - ${formatDate2(session.updatedAt)}`
14203
14805
  }, undefined, false, undefined, this)
14204
14806
  ]
14205
14807
  }, undefined, true, undefined, this),
14206
- isSelected && session.latestUserPromptPreview ? /* @__PURE__ */ jsxDEV31("text", {
14808
+ isSelected && session.latestUserPromptPreview ? /* @__PURE__ */ jsxDEV32("text", {
14207
14809
  fg: rowColors.secondaryTextColor,
14208
- attributes: TextAttributes21.DIM,
14810
+ attributes: TextAttributes22.DIM,
14209
14811
  children: truncateInline2(session.latestUserPromptPreview, 110)
14210
14812
  }, undefined, false, undefined, this) : null
14211
14813
  ]
@@ -14213,28 +14815,28 @@ function SessionListScreen() {
14213
14815
  })
14214
14816
  ]
14215
14817
  }, undefined, true, undefined, this),
14216
- actionMessage ? /* @__PURE__ */ jsxDEV31("box", {
14818
+ actionMessage ? /* @__PURE__ */ jsxDEV32("box", {
14217
14819
  paddingX: 1,
14218
- children: /* @__PURE__ */ jsxDEV31("text", {
14820
+ children: /* @__PURE__ */ jsxDEV32("text", {
14219
14821
  fg: cliTheme.semantic.info,
14220
14822
  children: actionMessage
14221
14823
  }, undefined, false, undefined, this)
14222
14824
  }, undefined, false, undefined, this) : null,
14223
- filterOpen ? /* @__PURE__ */ jsxDEV31(ChatTextArea, {
14825
+ filterOpen ? /* @__PURE__ */ jsxDEV32(ChatTextArea, {
14224
14826
  placeholder: "Filter sessions...",
14225
14827
  allowEmpty: true,
14226
14828
  onTextChange: setFilterQuery,
14227
14829
  onSubmit: () => setFilterOpen(false)
14228
14830
  }, undefined, false, undefined, this) : null,
14229
- renamingSessionId !== null ? /* @__PURE__ */ jsxDEV31(ChatTextArea, {
14831
+ renamingSessionId !== null ? /* @__PURE__ */ jsxDEV32(ChatTextArea, {
14230
14832
  placeholder: "New session title...",
14231
14833
  onSubmit: (title) => void renameSession(renamingSessionId, title)
14232
14834
  }, undefined, false, undefined, this) : null,
14233
- /* @__PURE__ */ jsxDEV31("box", {
14835
+ /* @__PURE__ */ jsxDEV32("box", {
14234
14836
  paddingX: 1,
14235
- children: /* @__PURE__ */ jsxDEV31("text", {
14837
+ children: /* @__PURE__ */ jsxDEV32("text", {
14236
14838
  fg: cliTheme.text.muted,
14237
- attributes: TextAttributes21.DIM,
14839
+ attributes: TextAttributes22.DIM,
14238
14840
  children: inputActive ? "Enter confirm | Esc cancel" : footerText
14239
14841
  }, undefined, false, undefined, this)
14240
14842
  }, undefined, false, undefined, this)
@@ -14252,10 +14854,10 @@ var init_session_list_screen = __esm(() => {
14252
14854
  });
14253
14855
 
14254
14856
  // apps/cli/src/hooks/use-config-badge.ts
14255
- import { useEffect as useEffect14, useState as useState19 } from "react";
14857
+ import { useEffect as useEffect15, useState as useState20 } from "react";
14256
14858
  function useConfigBadge(refreshNonce = 0) {
14257
- const [state, setState] = useState19({ status: "loading" });
14258
- useEffect14(() => {
14859
+ const [state, setState] = useState20({ status: "loading" });
14860
+ useEffect15(() => {
14259
14861
  let cancelled = false;
14260
14862
  async function loadConfig() {
14261
14863
  try {
@@ -14296,16 +14898,36 @@ var init_use_config_badge = __esm(() => {
14296
14898
  init_src2();
14297
14899
  });
14298
14900
 
14901
+ // apps/cli/src/ui/components/status-dot.tsx
14902
+ import { jsxDEV as jsxDEV33 } from "@opentui/react/jsx-dev-runtime";
14903
+ function StatusDot({ status }) {
14904
+ return /* @__PURE__ */ jsxDEV33("text", {
14905
+ fg: dotColor[status],
14906
+ children: activeGlyphs.statusDot
14907
+ }, undefined, false, undefined, this);
14908
+ }
14909
+ var dotColor;
14910
+ var init_status_dot = __esm(() => {
14911
+ init_cli_theme();
14912
+ init_cli_theme_capabilities();
14913
+ dotColor = {
14914
+ online: cliTheme.semantic.success,
14915
+ degraded: cliTheme.semantic.warning,
14916
+ offline: cliTheme.semantic.error,
14917
+ loading: cliTheme.text.muted
14918
+ };
14919
+ });
14920
+
14299
14921
  // apps/cli/src/app.tsx
14300
14922
  var exports_app = {};
14301
14923
  __export(exports_app, {
14302
14924
  App: () => App
14303
14925
  });
14304
- import { TextAttributes as TextAttributes22 } from "@opentui/core";
14926
+ import { TextAttributes as TextAttributes23 } from "@opentui/core";
14305
14927
  import { useKeyboard as useKeyboard11, useRenderer as useRenderer2 } from "@opentui/react";
14306
- import { useEffect as useEffect15, useRef as useRef5, useState as useState20 } from "react";
14928
+ import { useEffect as useEffect16, useRef as useRef5, useState as useState21 } from "react";
14307
14929
  import { MemoryRouter, Navigate, Route, Routes, useLocation as useLocation3, useNavigate as useNavigate6 } from "react-router";
14308
- import { jsxDEV as jsxDEV32 } from "@opentui/react/jsx-dev-runtime";
14930
+ import { jsxDEV as jsxDEV34 } from "@opentui/react/jsx-dev-runtime";
14309
14931
  function getCurrentViewLabel(pathname) {
14310
14932
  if (pathname === "/" || pathname === "/home") {
14311
14933
  return "home";
@@ -14359,10 +14981,11 @@ function AppContent() {
14359
14981
  closeSlashMenu,
14360
14982
  requestChatAction,
14361
14983
  configRefreshNonce,
14362
- toggleToolOutputExpansion
14984
+ toggleToolOutputExpansion,
14985
+ toggleReasoningExpansion
14363
14986
  } = useAppState();
14364
- const [helpOpen, setHelpOpen] = useState20(false);
14365
- const [ctrlCNotice, setCtrlCNotice] = useState20(null);
14987
+ const [helpOpen, setHelpOpen] = useState21(false);
14988
+ const [ctrlCNotice, setCtrlCNotice] = useState21(null);
14366
14989
  const ctrlCArmedRef = useRef5(false);
14367
14990
  const ctrlCTimerRef = useRef5(null);
14368
14991
  const showCtrlCNotice = (text) => {
@@ -14393,7 +15016,7 @@ function AppContent() {
14393
15016
  ctrlCArmedRef.current = true;
14394
15017
  showCtrlCNotice("Press Ctrl+C again to exit");
14395
15018
  };
14396
- useEffect15(() => {
15019
+ useEffect16(() => {
14397
15020
  return () => {
14398
15021
  if (ctrlCTimerRef.current) {
14399
15022
  clearTimeout(ctrlCTimerRef.current);
@@ -14431,6 +15054,9 @@ function AppContent() {
14431
15054
  case "system:toggleToolOutput":
14432
15055
  toggleToolOutputExpansion();
14433
15056
  break;
15057
+ case "system:toggleReasoning":
15058
+ toggleReasoningExpansion();
15059
+ break;
14434
15060
  case "system:cancel":
14435
15061
  if (helpOpen) {
14436
15062
  setHelpOpen(false);
@@ -14540,7 +15166,7 @@ function AppContent() {
14540
15166
  });
14541
15167
  const configBadge = useConfigBadge(configRefreshNonce);
14542
15168
  const needsOnboarding = configBadge.status === "available" && configBadge.missingCredentialHints.length > 0;
14543
- useEffect15(() => {
15169
+ useEffect16(() => {
14544
15170
  if (needsOnboarding && location.pathname !== "/onboarding") {
14545
15171
  navigate("/onboarding");
14546
15172
  }
@@ -14549,15 +15175,15 @@ function AppContent() {
14549
15175
  if (slashMenuOpen) {
14550
15176
  return "Slash pages open | Enter Open | Backspace Close | Esc Cancel";
14551
15177
  }
14552
- const backHint = canGoBack ? ` | Esc/${BACK_SHORTCUT_LABEL} Back` : "";
14553
- return "/ Pages | Ctrl+P Cmd | F1 Help | Ctrl+C Quit" + backHint;
15178
+ const backHint = canGoBack ? `Esc/${BACK_SHORTCUT_LABEL} Back | ` : "";
15179
+ return backHint + "/ Pages | Ctrl+P Cmd | F1 Help | Ctrl+C Quit";
14554
15180
  };
14555
- return /* @__PURE__ */ jsxDEV32("box", {
15181
+ return /* @__PURE__ */ jsxDEV34("box", {
14556
15182
  flexDirection: "column",
14557
15183
  flexGrow: 1,
14558
15184
  backgroundColor: cliTheme.surfaces.base,
14559
15185
  children: [
14560
- /* @__PURE__ */ jsxDEV32("box", {
15186
+ /* @__PURE__ */ jsxDEV34("box", {
14561
15187
  flexDirection: "row",
14562
15188
  justifyContent: "space-between",
14563
15189
  paddingLeft: 1,
@@ -14566,144 +15192,148 @@ function AppContent() {
14566
15192
  border: ["bottom"],
14567
15193
  borderColor: cliTheme.borders.default,
14568
15194
  children: [
14569
- /* @__PURE__ */ jsxDEV32("box", {
15195
+ /* @__PURE__ */ jsxDEV34("box", {
14570
15196
  flexDirection: "row",
14571
- gap: 2,
15197
+ gap: 1,
14572
15198
  alignItems: "center",
14573
15199
  children: [
14574
- /* @__PURE__ */ jsxDEV32("text", {
14575
- fg: cliTheme.text.primary,
14576
- attributes: TextAttributes22.BOLD,
15200
+ /* @__PURE__ */ jsxDEV34(StatusDot, {
15201
+ status: configBadge.status === "available" ? "online" : configBadge.status === "loading" ? "loading" : "offline"
15202
+ }, undefined, false, undefined, this),
15203
+ /* @__PURE__ */ jsxDEV34("text", {
15204
+ fg: cliTheme.accent.primary,
15205
+ attributes: TextAttributes23.BOLD,
14577
15206
  children: "Lightcode"
14578
15207
  }, undefined, false, undefined, this),
14579
- configBadge.status === "available" ? /* @__PURE__ */ jsxDEV32("text", {
15208
+ configBadge.status === "available" ? /* @__PURE__ */ jsxDEV34("text", {
14580
15209
  fg: cliTheme.text.secondary,
14581
15210
  children: [
14582
- "| ",
15211
+ "\xB7 ",
14583
15212
  configBadge.provider,
14584
- " | ",
15213
+ " \xB7 ",
14585
15214
  configBadge.model
14586
15215
  ]
14587
- }, undefined, true, undefined, this) : configBadge.status === "loading" ? /* @__PURE__ */ jsxDEV32("text", {
15216
+ }, undefined, true, undefined, this) : configBadge.status === "loading" ? /* @__PURE__ */ jsxDEV34("text", {
14588
15217
  fg: cliTheme.text.muted,
14589
- children: "| loading..."
14590
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV32("text", {
14591
- fg: cliTheme.text.muted,
14592
- children: "| provider unavailable"
15218
+ children: "\xB7 loading\u2026"
15219
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV34("text", {
15220
+ fg: cliTheme.semantic.error,
15221
+ attributes: TextAttributes23.BOLD,
15222
+ children: "\xB7 provider unavailable"
14593
15223
  }, undefined, false, undefined, this)
14594
15224
  ]
14595
15225
  }, undefined, true, undefined, this),
14596
- /* @__PURE__ */ jsxDEV32("text", {
15226
+ /* @__PURE__ */ jsxDEV34("text", {
14597
15227
  fg: cliTheme.text.muted,
14598
15228
  children: currentView
14599
15229
  }, undefined, false, undefined, this)
14600
15230
  ]
14601
15231
  }, undefined, true, undefined, this),
14602
- /* @__PURE__ */ jsxDEV32("box", {
15232
+ /* @__PURE__ */ jsxDEV34("box", {
14603
15233
  flexGrow: 1,
14604
15234
  padding: 1,
14605
15235
  position: "relative",
14606
15236
  children: [
14607
- /* @__PURE__ */ jsxDEV32(Routes, {
15237
+ /* @__PURE__ */ jsxDEV34(Routes, {
14608
15238
  children: [
14609
- /* @__PURE__ */ jsxDEV32(Route, {
15239
+ /* @__PURE__ */ jsxDEV34(Route, {
14610
15240
  path: "/",
14611
- element: /* @__PURE__ */ jsxDEV32(HomeScreen, {}, undefined, false, undefined, this)
15241
+ element: /* @__PURE__ */ jsxDEV34(HomeScreen, {}, undefined, false, undefined, this)
14612
15242
  }, undefined, false, undefined, this),
14613
- /* @__PURE__ */ jsxDEV32(Route, {
15243
+ /* @__PURE__ */ jsxDEV34(Route, {
14614
15244
  path: "/home",
14615
- element: /* @__PURE__ */ jsxDEV32(Navigate, {
15245
+ element: /* @__PURE__ */ jsxDEV34(Navigate, {
14616
15246
  to: "/",
14617
15247
  replace: true
14618
15248
  }, undefined, false, undefined, this)
14619
15249
  }, undefined, false, undefined, this),
14620
- /* @__PURE__ */ jsxDEV32(Route, {
15250
+ /* @__PURE__ */ jsxDEV34(Route, {
14621
15251
  path: "/status",
14622
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15252
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14623
15253
  kind: "status"
14624
15254
  }, undefined, false, undefined, this)
14625
15255
  }, undefined, false, undefined, this),
14626
- /* @__PURE__ */ jsxDEV32(Route, {
15256
+ /* @__PURE__ */ jsxDEV34(Route, {
14627
15257
  path: "/doctor",
14628
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15258
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14629
15259
  kind: "doctor"
14630
15260
  }, undefined, false, undefined, this)
14631
15261
  }, undefined, false, undefined, this),
14632
- /* @__PURE__ */ jsxDEV32(Route, {
15262
+ /* @__PURE__ */ jsxDEV34(Route, {
14633
15263
  path: "/permissions",
14634
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15264
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14635
15265
  kind: "permissions"
14636
15266
  }, undefined, false, undefined, this)
14637
15267
  }, undefined, false, undefined, this),
14638
- /* @__PURE__ */ jsxDEV32(Route, {
15268
+ /* @__PURE__ */ jsxDEV34(Route, {
14639
15269
  path: "/sessions",
14640
- element: /* @__PURE__ */ jsxDEV32(SessionListScreen, {}, undefined, false, undefined, this)
15270
+ element: /* @__PURE__ */ jsxDEV34(SessionListScreen, {}, undefined, false, undefined, this)
14641
15271
  }, undefined, false, undefined, this),
14642
- /* @__PURE__ */ jsxDEV32(Route, {
15272
+ /* @__PURE__ */ jsxDEV34(Route, {
14643
15273
  path: "/sessions/:id",
14644
- element: /* @__PURE__ */ jsxDEV32(ChatScreen, {}, undefined, false, undefined, this)
15274
+ element: /* @__PURE__ */ jsxDEV34(ChatScreen, {}, undefined, false, undefined, this)
14645
15275
  }, undefined, false, undefined, this),
14646
- /* @__PURE__ */ jsxDEV32(Route, {
15276
+ /* @__PURE__ */ jsxDEV34(Route, {
14647
15277
  path: "/tools",
14648
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15278
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14649
15279
  kind: "tools"
14650
15280
  }, undefined, false, undefined, this)
14651
15281
  }, undefined, false, undefined, this),
14652
- /* @__PURE__ */ jsxDEV32(Route, {
15282
+ /* @__PURE__ */ jsxDEV34(Route, {
14653
15283
  path: "/config",
14654
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15284
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14655
15285
  kind: "config"
14656
15286
  }, undefined, false, undefined, this)
14657
15287
  }, undefined, false, undefined, this),
14658
- /* @__PURE__ */ jsxDEV32(Route, {
15288
+ /* @__PURE__ */ jsxDEV34(Route, {
14659
15289
  path: "/model-info",
14660
- element: /* @__PURE__ */ jsxDEV32(ModelScreen, {}, undefined, false, undefined, this)
15290
+ element: /* @__PURE__ */ jsxDEV34(ModelScreen, {}, undefined, false, undefined, this)
14661
15291
  }, undefined, false, undefined, this),
14662
- /* @__PURE__ */ jsxDEV32(Route, {
15292
+ /* @__PURE__ */ jsxDEV34(Route, {
14663
15293
  path: "/model",
14664
- element: /* @__PURE__ */ jsxDEV32(ModelSelectScreen, {}, undefined, false, undefined, this)
15294
+ element: /* @__PURE__ */ jsxDEV34(ModelSelectScreen, {}, undefined, false, undefined, this)
14665
15295
  }, undefined, false, undefined, this),
14666
- /* @__PURE__ */ jsxDEV32(Route, {
15296
+ /* @__PURE__ */ jsxDEV34(Route, {
14667
15297
  path: "/onboarding",
14668
- element: /* @__PURE__ */ jsxDEV32(OnboardingScreen, {}, undefined, false, undefined, this)
15298
+ element: /* @__PURE__ */ jsxDEV34(OnboardingScreen, {}, undefined, false, undefined, this)
14669
15299
  }, undefined, false, undefined, this),
14670
- /* @__PURE__ */ jsxDEV32(Route, {
15300
+ /* @__PURE__ */ jsxDEV34(Route, {
14671
15301
  path: "*",
14672
- element: /* @__PURE__ */ jsxDEV32(Navigate, {
15302
+ element: /* @__PURE__ */ jsxDEV34(Navigate, {
14673
15303
  to: "/",
14674
15304
  replace: true
14675
15305
  }, undefined, false, undefined, this)
14676
15306
  }, undefined, false, undefined, this)
14677
15307
  ]
14678
15308
  }, undefined, true, undefined, this),
14679
- paletteOpen ? /* @__PURE__ */ jsxDEV32(CommandPalette, {
15309
+ paletteOpen ? /* @__PURE__ */ jsxDEV34(CommandPalette, {
14680
15310
  query: paletteQuery,
14681
15311
  setQuery: setPaletteQuery,
14682
15312
  selectedIndex: paletteSelected
14683
15313
  }, undefined, false, undefined, this) : null,
14684
- slashMenuOpen && !inputHostsSlashMenu ? /* @__PURE__ */ jsxDEV32("box", {
15314
+ slashMenuOpen && !inputHostsSlashMenu ? /* @__PURE__ */ jsxDEV34("box", {
14685
15315
  position: "absolute",
14686
15316
  top: 1,
14687
15317
  left: 2,
14688
15318
  right: 2,
14689
15319
  zIndex: 20,
14690
- children: /* @__PURE__ */ jsxDEV32(SlashPageMenu, {
15320
+ children: /* @__PURE__ */ jsxDEV34(SlashPageMenu, {
14691
15321
  query: slashMenuQuery,
14692
15322
  selectedIndex: selectedSlashRouteIndex,
14693
15323
  routes: filteredSlashRoutes
14694
15324
  }, undefined, false, undefined, this)
14695
15325
  }, undefined, false, undefined, this) : null,
14696
- helpOpen ? /* @__PURE__ */ jsxDEV32("box", {
15326
+ helpOpen ? /* @__PURE__ */ jsxDEV34("box", {
14697
15327
  position: "absolute",
14698
15328
  top: 1,
14699
15329
  left: 4,
14700
15330
  right: 4,
14701
15331
  zIndex: 30,
14702
- children: /* @__PURE__ */ jsxDEV32(HelpOverlay, {}, undefined, false, undefined, this)
15332
+ children: /* @__PURE__ */ jsxDEV34(HelpOverlay, {}, undefined, false, undefined, this)
14703
15333
  }, undefined, false, undefined, this) : null
14704
15334
  ]
14705
15335
  }, undefined, true, undefined, this),
14706
- /* @__PURE__ */ jsxDEV32("box", {
15336
+ /* @__PURE__ */ jsxDEV34("box", {
14707
15337
  flexDirection: "row",
14708
15338
  justifyContent: "center",
14709
15339
  gap: 2,
@@ -14712,7 +15342,7 @@ function AppContent() {
14712
15342
  borderStyle: "single",
14713
15343
  border: ["top"],
14714
15344
  borderColor: cliTheme.borders.default,
14715
- children: /* @__PURE__ */ jsxDEV32("text", {
15345
+ children: /* @__PURE__ */ jsxDEV34("text", {
14716
15346
  fg: ctrlCNotice ? cliTheme.semantic.warning : cliTheme.text.muted,
14717
15347
  children: ctrlCNotice ?? getFooterStatus()
14718
15348
  }, undefined, false, undefined, this)
@@ -14721,10 +15351,10 @@ function AppContent() {
14721
15351
  }, undefined, true, undefined, this);
14722
15352
  }
14723
15353
  function App() {
14724
- return /* @__PURE__ */ jsxDEV32(MemoryRouter, {
15354
+ return /* @__PURE__ */ jsxDEV34(MemoryRouter, {
14725
15355
  initialEntries: ["/"],
14726
- children: /* @__PURE__ */ jsxDEV32(AppStateProvider, {
14727
- children: /* @__PURE__ */ jsxDEV32(AppContent, {}, undefined, false, undefined, this)
15356
+ children: /* @__PURE__ */ jsxDEV34(AppStateProvider, {
15357
+ children: /* @__PURE__ */ jsxDEV34(AppContent, {}, undefined, false, undefined, this)
14728
15358
  }, undefined, false, undefined, this)
14729
15359
  }, undefined, false, undefined, this);
14730
15360
  }
@@ -14746,12 +15376,13 @@ var init_app = __esm(() => {
14746
15376
  init_app_state();
14747
15377
  init_use_config_badge();
14748
15378
  init_cli_theme();
15379
+ init_status_dot();
14749
15380
  init_clipboard();
14750
15381
  });
14751
15382
 
14752
15383
  // apps/cli/src/index.tsx
14753
15384
  init_src();
14754
- import { jsxDEV as jsxDEV33 } from "@opentui/react/jsx-dev-runtime";
15385
+ import { jsxDEV as jsxDEV35 } from "@opentui/react/jsx-dev-runtime";
14755
15386
  var cliArgs = Bun.argv.slice(2);
14756
15387
  if (cliArgs.includes("--version") || cliArgs.includes("-v")) {
14757
15388
  console.log(`${productName} v${lightcodeVersion}`);
@@ -14773,10 +15404,19 @@ var renderer = await createCliRenderer({
14773
15404
  });
14774
15405
  if (serverLaunch.ownedProcess) {
14775
15406
  const ownedProcess = serverLaunch.ownedProcess;
14776
- process.on("exit", () => {
15407
+ const killOwnedServer = () => {
14777
15408
  try {
14778
15409
  ownedProcess.kill();
14779
15410
  } catch {}
15411
+ };
15412
+ process.on("exit", killOwnedServer);
15413
+ process.on("SIGINT", () => {
15414
+ killOwnedServer();
15415
+ process.exit(0);
15416
+ });
15417
+ process.on("SIGTERM", () => {
15418
+ killOwnedServer();
15419
+ process.exit(0);
14780
15420
  });
14781
15421
  }
14782
- createRoot(renderer).render(/* @__PURE__ */ jsxDEV33(App2, {}, undefined, false, undefined, this));
15422
+ createRoot(renderer).render(/* @__PURE__ */ jsxDEV35(App2, {}, undefined, false, undefined, this));