@kmugalkhod/lightcode 0.5.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 (3) hide show
  1. package/cli.js +1544 -973
  2. package/package.json +1 -1
  3. package/server.js +388 -249
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.5.0";
127
+ var lightcodeVersion = "0.6.0";
128
128
 
129
129
  // packages/shared/src/index.ts
130
130
  var productName = "Lightcode";
@@ -280,7 +280,8 @@ var init_keymap = __esm(() => {
280
280
  "ctrl+q": { sequence: "ctrl+q", action: "system:quit", label: "Quit", category: "system" },
281
281
  f1: { sequence: "f1", action: "system:help", label: "Help", category: "system" },
282
282
  "ctrl+/": { sequence: "ctrl+/", action: "system:help", label: "Help", category: "system" },
283
- "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" }
284
285
  }
285
286
  };
286
287
  });
@@ -320,7 +321,7 @@ function getOverlayRowColors(selected) {
320
321
  secondaryTextColor: cliTheme.overlay.description
321
322
  };
322
323
  }
323
- var toolStateToneMap, cliTheme, serverStatusColors;
324
+ var toolStateToneMap, cliTheme, space, borderStyleFor, glyphs, serverStatusColors;
324
325
  var init_cli_theme = __esm(() => {
325
326
  toolStateToneMap = {
326
327
  "input-streaming": "muted",
@@ -341,7 +342,7 @@ var init_cli_theme = __esm(() => {
341
342
  borders: {
342
343
  default: "#273241",
343
344
  subtle: "#202A36",
344
- active: "#80D0C7"
345
+ active: "#F2A65A"
345
346
  },
346
347
  text: {
347
348
  primary: "#EEF3F8",
@@ -349,57 +350,66 @@ var init_cli_theme = __esm(() => {
349
350
  muted: "#7A8796"
350
351
  },
351
352
  accent: {
352
- primary: "#80D0C7",
353
- softBackground: "#17333A",
354
- softText: "#BCEDE8"
353
+ primary: "#F2A65A",
354
+ softBackground: "#33260F",
355
+ softText: "#F6C99A"
355
356
  },
356
357
  semantic: {
357
358
  success: "#8BD49C",
358
- warning: "#F1C27D",
359
+ warning: "#E5C07B",
359
360
  error: "#F28B82",
360
361
  info: "#9CBDFD"
361
362
  },
363
+ diff: {
364
+ added: "#8BD49C",
365
+ addedBg: "#13271B",
366
+ removed: "#F28B82",
367
+ removedBg: "#2B1619"
368
+ },
362
369
  messageRoles: {
363
370
  user: {
364
371
  labelColor: "#9CBDFD",
365
372
  borderColor: "#22314A",
366
- backgroundColor: "#111A27"
373
+ backgroundColor: "#111A27",
374
+ glyph: "\u25B8"
367
375
  },
368
376
  assistant: {
369
377
  labelColor: "#8BD49C",
370
378
  borderColor: "#203A2A",
371
- backgroundColor: "#111C18"
379
+ backgroundColor: "#111C18",
380
+ glyph: "\u25C6"
372
381
  },
373
382
  system: {
374
383
  labelColor: "#C3CDD9",
375
384
  borderColor: "#273241",
376
- backgroundColor: "#131820"
385
+ backgroundColor: "#131820",
386
+ glyph: "\u25A0"
377
387
  }
378
388
  },
379
389
  overlay: {
380
390
  surface: "#121820",
381
391
  border: "#273241",
382
- title: "#80D0C7",
383
- selectedRowBackground: "#17333A",
392
+ title: "#F2A65A",
393
+ selectedRowBackground: "#33260F",
384
394
  selectedRowText: "#EEF3F8",
385
395
  inputSurface: "#10151C",
386
396
  inputText: "#EEF3F8",
387
397
  mutedText: "#7A8796",
388
398
  footerText: "#B6C2CF",
389
- selectedBorder: "#80D0C7",
399
+ selectedBorder: "#F2A65A",
390
400
  badgeBackground: "#18212B",
391
401
  badgeText: "#B6C2CF",
392
402
  description: "#9AA8B7",
393
403
  sectionDivider: "#202A36",
394
404
  shortcutHint: "#9AA8B7",
395
405
  headerMuted: "#9AA8B7",
396
- countBadge: "#17333A",
406
+ countBadge: "#33260F",
397
407
  hoverBackground: "#18212B"
398
408
  },
399
409
  input: {
400
410
  container: "#121820",
401
411
  field: "#10151C",
402
- focusedBorder: "#80D0C7",
412
+ focusedBorder: "#F2A65A",
403
413
  blurredBorder: "#273241",
404
414
  placeholder: "#7A8796",
405
415
  text: "#EEF3F8",
@@ -409,12 +419,56 @@ var init_cli_theme = __esm(() => {
409
419
  scroll: {
410
420
  rail: "#0D1117",
411
421
  thumb: "#273241",
412
- thumbActive: "#80D0C7"
422
+ thumbActive: "#F2A65A"
413
423
  },
414
424
  markdown: {
415
425
  tableBorder: "#273241"
416
426
  }
417
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
+ };
418
472
  serverStatusColors = {
419
473
  checking: cliTheme.text.secondary,
420
474
  online: cliTheme.semantic.success,
@@ -434,7 +488,7 @@ function HelpOverlay() {
434
488
  return /* @__PURE__ */ jsxDEV("box", {
435
489
  width: "100%",
436
490
  flexDirection: "column",
437
- borderStyle: "single",
491
+ borderStyle: borderStyleFor.modal,
438
492
  borderColor: cliTheme.accent.primary,
439
493
  backgroundColor: cliTheme.overlay.surface,
440
494
  paddingX: 2,
@@ -494,7 +548,7 @@ var init_help_overlay = __esm(() => {
494
548
  init_cli_theme();
495
549
  screenHints = [
496
550
  { context: "Chat", hint: "Tab/Ctrl+T switch mode | @ attach files | /compact, /undo actions" },
497
- { 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" },
498
552
  { context: "Copy", hint: "Ctrl+Y copy mode (\u2191/\u2193 pick, Enter copy, c code) | /copy [last|code|all]" },
499
553
  { context: "Sessions", hint: "Enter resume | r rename | f fork | / filter | e export (.md) | d delete" }
500
554
  ];
@@ -638,7 +692,7 @@ function CommandPalette({ query, setQuery, selectedIndex }) {
638
692
  minHeight: 20,
639
693
  flexDirection: "column",
640
694
  backgroundColor: cliTheme.overlay.surface,
641
- borderStyle: "single",
695
+ borderStyle: borderStyleFor.modal,
642
696
  border: ["top", "bottom", "left", "right"],
643
697
  borderColor: cliTheme.overlay.border,
644
698
  children: [
@@ -875,7 +929,7 @@ function SlashPageMenu({
875
929
  width: "100%",
876
930
  flexDirection: "column",
877
931
  backgroundColor: cliTheme.overlay.surface,
878
- borderStyle: "single",
932
+ borderStyle: borderStyleFor.modal,
879
933
  borderColor: cliTheme.overlay.border,
880
934
  children: [
881
935
  /* @__PURE__ */ jsxDEV3("box", {
@@ -1618,9 +1672,10 @@ var init_schema = __esm(() => {
1618
1672
 
1619
1673
  // packages/ai/src/edit-file/schema.ts
1620
1674
  import { z as z6 } from "zod";
1621
- 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;
1622
1676
  var init_schema2 = __esm(() => {
1623
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.";
1624
1679
  editFileInputSchema = z6.object({
1625
1680
  path: boundedPathSchema,
1626
1681
  search: z6.string().min(1),
@@ -2493,7 +2548,8 @@ function parseChatStreamError(message) {
2493
2548
  kind: parsed.kind,
2494
2549
  statusCode: typeof parsed.statusCode === "number" ? parsed.statusCode : undefined,
2495
2550
  retryable: parsed.retryable,
2496
- message: parsed.message
2551
+ message: parsed.message,
2552
+ contextLimitTokens: typeof parsed.contextLimitTokens === "number" ? parsed.contextLimitTokens : undefined
2497
2553
  };
2498
2554
  } catch {
2499
2555
  return null;
@@ -2503,6 +2559,15 @@ function describeChatStreamError(info) {
2503
2559
  const status = info.statusCode ? ` (HTTP ${info.statusCode})` : "";
2504
2560
  return `${chatErrorKindLabels[info.kind]}${status}: ${info.message}`;
2505
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
+ }
2506
2571
  var chatStreamErrorPrefix = "LIGHTCODE_ERROR:", maxTransportMessageLength = 500, chatErrorKinds, chatErrorKindLabels;
2507
2572
  var init_chat_error = __esm(() => {
2508
2573
  chatErrorKinds = new Set([
@@ -3184,126 +3249,7 @@ function selectCodingAgentIntentTools({
3184
3249
  if (!normalizedText || casualPromptPattern.test(normalizedText)) {
3185
3250
  return [];
3186
3251
  }
3187
- const selectedTools = [];
3188
- const hasContinuationIntent = includesAny(normalizedText, [
3189
- "continue",
3190
- "proceed",
3191
- "go on",
3192
- "go ahead",
3193
- "keep going",
3194
- "carry on",
3195
- "resume",
3196
- "finish",
3197
- "next step"
3198
- ]);
3199
- const hasCodeIntent = includesAny(normalizedText, [
3200
- "file",
3201
- "folder",
3202
- "code",
3203
- "repo",
3204
- "project",
3205
- "implement",
3206
- "fix",
3207
- "add",
3208
- "build",
3209
- "refactor",
3210
- "change",
3211
- "create",
3212
- "update",
3213
- "edit",
3214
- "delete",
3215
- "run",
3216
- "test",
3217
- "typecheck",
3218
- "debug",
3219
- "error",
3220
- "issue",
3221
- "problem",
3222
- "fails",
3223
- "failed",
3224
- "failing",
3225
- "bug",
3226
- "crash",
3227
- "slow",
3228
- "slowness",
3229
- "not working",
3230
- "epic",
3231
- "ticket"
3232
- ]);
3233
- const hasReadIntent = includesAny(normalizedText, [
3234
- "read",
3235
- "list",
3236
- "find",
3237
- "search",
3238
- "grep",
3239
- "glob",
3240
- "inspect",
3241
- "analyse",
3242
- "analyze",
3243
- "check"
3244
- ]);
3245
- const workspaceReferents = [
3246
- "this",
3247
- "these",
3248
- "that",
3249
- "here",
3250
- "code",
3251
- "codebase",
3252
- "repo",
3253
- "repository",
3254
- "project",
3255
- "file",
3256
- "files",
3257
- "directory",
3258
- "folder",
3259
- "app",
3260
- "application",
3261
- "module",
3262
- "my"
3263
- ];
3264
- const strongReviewIntent = includesAny(normalizedText, [
3265
- "review",
3266
- "audit",
3267
- "walk through",
3268
- "walkthrough",
3269
- "code review"
3270
- ]);
3271
- const softReviewIntent = includesAny(normalizedText, [
3272
- "explain",
3273
- "understand",
3274
- "summarize",
3275
- "summarise",
3276
- "overview",
3277
- "describe",
3278
- "explore",
3279
- "look at"
3280
- ]) && includesAny(normalizedText, workspaceReferents);
3281
- const hasReviewIntent = strongReviewIntent || softReviewIntent;
3282
- const hasWriteIntent = mode === "build" && (hasContinuationIntent || includesAny(normalizedText, [
3283
- "write",
3284
- "edit",
3285
- "change",
3286
- "create",
3287
- "implement",
3288
- "fix",
3289
- "update",
3290
- "delete",
3291
- "remove",
3292
- "rename"
3293
- ]));
3294
- const hasShellIntent = mode === "build" && (hasContinuationIntent || includesAny(normalizedText, [
3295
- "run",
3296
- "command",
3297
- "terminal",
3298
- "shell",
3299
- "bash",
3300
- "powershell",
3301
- "bun",
3302
- "tests",
3303
- "run test",
3304
- "run tests",
3305
- "typecheck"
3306
- ]));
3252
+ const situational = [];
3307
3253
  const hasGitIntent = includesAny(normalizedText, [
3308
3254
  "git",
3309
3255
  "diff",
@@ -3314,95 +3260,64 @@ function selectCodingAgentIntentTools({
3314
3260
  "revision",
3315
3261
  "changes"
3316
3262
  ]);
3317
- const hasTodoIntent = includesAny(normalizedText, [
3318
- "todo",
3319
- "tasks",
3320
- "checklist",
3321
- "steps",
3322
- "epic",
3323
- "ticket"
3324
- ]);
3325
- const hasWebIntent = includesAny(normalizedText, [
3326
- "http://",
3327
- "https://",
3328
- "url",
3329
- "fetch",
3330
- "website",
3331
- "web",
3332
- "online",
3333
- "search internet",
3334
- "latest"
3335
- ]);
3336
- const explicitToolSearch = includesAny(normalizedText, [
3337
- "tool_search",
3338
- "tool search",
3339
- "find tool",
3340
- "available tool"
3341
- ]);
3342
- const hasSkillIntent = includesAny(normalizedText, [
3343
- "skill",
3344
- "skills",
3345
- "load skill"
3346
- ]);
3347
- const hasMcpIntent = includesAny(normalizedText, [
3348
- "mcp",
3349
- "resource",
3350
- "resources",
3351
- "mcp tool",
3352
- "mcp server"
3353
- ]);
3354
- if (hasCodeIntent || hasContinuationIntent || hasReviewIntent || hasReadIntent && !hasGitIntent && !hasWebIntent && !explicitToolSearch) {
3355
- for (const toolName of baseReadTools) {
3356
- pushUniqueTool(selectedTools, toolName);
3357
- }
3358
- }
3359
3263
  if (hasGitIntent) {
3360
3264
  if (includesAny(normalizedText, ["status", "state", "dirty", "working tree"]) || !includesAny(normalizedText, ["diff", "log", "show", "history", "commit"])) {
3361
- pushUniqueTool(selectedTools, "git_status");
3265
+ pushUniqueTool(situational, "git_status");
3362
3266
  }
3363
3267
  if (includesAny(normalizedText, ["diff", "changes", "patch"])) {
3364
- pushUniqueTool(selectedTools, "git_diff");
3268
+ pushUniqueTool(situational, "git_diff");
3365
3269
  }
3366
3270
  if (includesAny(normalizedText, ["log", "history", "commits"])) {
3367
- pushUniqueTool(selectedTools, "git_log");
3271
+ pushUniqueTool(situational, "git_log");
3368
3272
  }
3369
3273
  if (includesAny(normalizedText, ["show", "revision", "commit"])) {
3370
- pushUniqueTool(selectedTools, "git_show");
3274
+ pushUniqueTool(situational, "git_show");
3371
3275
  }
3372
3276
  }
3373
- if (hasTodoIntent && mode === "build") {
3374
- pushUniqueTool(selectedTools, "todo_write");
3277
+ if (mode === "build" && includesAny(normalizedText, ["todo", "tasks", "checklist", "steps", "epic", "ticket"])) {
3278
+ pushUniqueTool(situational, "todo_write");
3375
3279
  }
3376
- if (explicitToolSearch) {
3377
- pushUniqueTool(selectedTools, "tool_search");
3280
+ if (includesAny(normalizedText, ["tool_search", "tool search", "find tool", "available tool"])) {
3281
+ pushUniqueTool(situational, "tool_search");
3378
3282
  }
3379
- if (hasSkillIntent) {
3380
- pushUniqueTool(selectedTools, "skill");
3283
+ if (includesAny(normalizedText, ["skill", "skills", "load skill"])) {
3284
+ pushUniqueTool(situational, "skill");
3381
3285
  }
3382
- if (hasMcpIntent) {
3383
- pushUniqueTool(selectedTools, "list_mcp_resources");
3286
+ if (includesAny(normalizedText, ["mcp", "resource", "resources", "mcp tool", "mcp server"])) {
3287
+ pushUniqueTool(situational, "list_mcp_resources");
3384
3288
  if (includesAny(normalizedText, ["read", "resource", "resources"])) {
3385
- pushUniqueTool(selectedTools, "read_mcp_resource");
3289
+ pushUniqueTool(situational, "read_mcp_resource");
3386
3290
  }
3387
3291
  if (mode === "build" && includesAny(normalizedText, ["call", "execute", "run"])) {
3388
- pushUniqueTool(selectedTools, "call_mcp_tool");
3292
+ pushUniqueTool(situational, "call_mcp_tool");
3389
3293
  }
3390
3294
  }
3391
- if (hasWriteIntent) {
3392
- pushUniqueTool(selectedTools, "write_file");
3393
- 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");
3394
3308
  }
3395
- if (hasShellIntent) {
3396
- pushUniqueTool(selectedTools, "bash");
3309
+ if (mode === "plan" && includesAny(normalizedText, ["question", "ask", "clarify"])) {
3310
+ pushUniqueTool(situational, "request_user_input");
3397
3311
  }
3398
- if (hasWebIntent) {
3399
- pushUniqueTool(selectedTools, "web_fetch");
3400
- pushUniqueTool(selectedTools, "web_search");
3312
+ const ordered = [...baseReadTools, ...situational];
3313
+ if (mode === "build") {
3314
+ ordered.push("write_file", "edit_file", "bash");
3401
3315
  }
3402
- if (mode === "plan" && includesAny(normalizedText, ["question", "ask", "clarify"])) {
3403
- pushUniqueTool(selectedTools, "request_user_input");
3316
+ const selected = [];
3317
+ for (const toolName of ordered) {
3318
+ pushUniqueTool(selected, toolName);
3404
3319
  }
3405
- return selectedTools;
3320
+ return selected.slice(0, maxProviderActiveTools);
3406
3321
  }
3407
3322
  function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
3408
3323
  if (tools.length <= maxTools) {
@@ -3687,17 +3602,33 @@ function normalizeContextOptimizerConfig(config) {
3687
3602
  }
3688
3603
  function resolveContextWindowTokens({
3689
3604
  config,
3690
- 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
3691
3618
  }) {
3692
- 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));
3693
3623
  }
3694
- 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;
3695
3625
  var init_config3 = __esm(() => {
3696
3626
  contextOptimizerConfigSchema = z26.object({
3697
3627
  autoCompact: z26.boolean().optional(),
3698
3628
  compactAtFraction: z26.number().min(0.3).max(0.95).optional(),
3699
3629
  pruneAtFraction: z26.number().min(0.2).max(0.9).optional(),
3700
3630
  preserveRecentMessages: z26.number().int().min(2).max(100).optional(),
3631
+ maxCoverageTokensPerCompaction: z26.number().int().min(2000).max(200000).optional(),
3701
3632
  contextWindowOverride: z26.number().int().min(8000).max(1e7).optional(),
3702
3633
  summaryMaxChars: z26.number().int().min(200).max(50000).optional(),
3703
3634
  aggressivePruneWhenUncached: z26.boolean().optional(),
@@ -3711,6 +3642,7 @@ var init_config3 = __esm(() => {
3711
3642
  compactAtFraction: z26.number().min(0.3).max(0.95),
3712
3643
  pruneAtFraction: z26.number().min(0.2).max(0.9),
3713
3644
  preserveRecentMessages: z26.number().int().min(2).max(100),
3645
+ maxCoverageTokensPerCompaction: z26.number().int().min(2000).max(200000),
3714
3646
  contextWindowOverride: z26.number().int().min(8000).max(1e7).nullable(),
3715
3647
  summaryMaxChars: z26.number().int().min(200).max(50000),
3716
3648
  aggressivePruneWhenUncached: z26.boolean(),
@@ -3723,6 +3655,7 @@ var init_config3 = __esm(() => {
3723
3655
  compactAtFraction: 0.7,
3724
3656
  pruneAtFraction: 0.6,
3725
3657
  preserveRecentMessages: 6,
3658
+ maxCoverageTokensPerCompaction: 12000,
3726
3659
  contextWindowOverride: null,
3727
3660
  summaryMaxChars: 4000,
3728
3661
  aggressivePruneWhenUncached: true,
@@ -3802,10 +3735,61 @@ var init_estimate = __esm(() => {
3802
3735
  totalTokens: z27.number().nonnegative().optional()
3803
3736
  }),
3804
3737
  modelId: z27.string().optional(),
3805
- 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()
3806
3744
  }).loose();
3807
3745
  });
3808
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
+
3809
3793
  // packages/ai/src/context/heuristic-summary.ts
3810
3794
  function truncateInline(text, maxChars) {
3811
3795
  const normalized = text.replace(/\s+/g, " ").trim();
@@ -4155,6 +4139,127 @@ var init_tier1_prune = __esm(() => {
4155
4139
  init_message_parts();
4156
4140
  });
4157
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
+
4158
4263
  // packages/ai/src/context/build-provider-view.ts
4159
4264
  function computeCompactionCutoffIndex(messages, preserveRecentMessages, afterAnchorIndex) {
4160
4265
  const candidate = messages.length - preserveRecentMessages;
@@ -4168,11 +4273,32 @@ function computeCompactionCutoffIndex(messages, preserveRecentMessages, afterAnc
4168
4273
  }
4169
4274
  return afterAnchorIndex;
4170
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
+ }
4171
4292
  function buildProviderView(options) {
4172
4293
  const { messages, contextState, config } = options;
4173
4294
  const contextWindow = resolveContextWindowTokens({
4174
4295
  config,
4175
- modelContextWindow: options.modelContextWindow
4296
+ modelContextWindow: options.modelContextWindow,
4297
+ endpointLimitTokens: options.endpointLimitTokens
4298
+ });
4299
+ const inputBudgetTokens = resolveInputBudgetTokens({
4300
+ contextWindow,
4301
+ reservedOutputTokens: options.reservedOutputTokens
4176
4302
  });
4177
4303
  let anchorResolved = false;
4178
4304
  let afterAnchorIndex = 0;
@@ -4196,7 +4322,8 @@ function buildProviderView(options) {
4196
4322
  let tier1 = null;
4197
4323
  const uncachedActive = options.cacheActive === false && config.aggressivePruneWhenUncached;
4198
4324
  const pruneFraction = uncachedActive ? config.uncachedPruneAtFraction : config.pruneAtFraction;
4199
- 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) {
4200
4327
  tier1 = pruneToolOutputs(providerMessages, {
4201
4328
  preserveRecentMessages: config.preserveRecentMessages,
4202
4329
  minOutputChars: uncachedActive ? config.uncachedPruneMinOutputChars : undefined,
@@ -4211,15 +4338,15 @@ function buildProviderView(options) {
4211
4338
  }
4212
4339
  }
4213
4340
  const compactionCutoffIndex = computeCompactionCutoffIndex(messages, config.preserveRecentMessages, afterAnchorIndex);
4214
- const coveredMessages = messages.slice(afterAnchorIndex, compactionCutoffIndex);
4215
- 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;
4216
4343
  let compactionBlockedReason = null;
4217
4344
  if (overCompactThreshold) {
4218
4345
  if (coveredMessages.length === 0) {
4219
4346
  compactionBlockedReason = "not_enough_messages";
4220
4347
  } else if ((options.pendingInteractionCount ?? 0) > 0) {
4221
4348
  compactionBlockedReason = "pending_interactions";
4222
- } else if (hasUnresolvedToolWork(messages)) {
4349
+ } else if (hasUnresolvedToolWork(coveredMessages)) {
4223
4350
  compactionBlockedReason = "unresolved_tool_work";
4224
4351
  }
4225
4352
  }
@@ -4227,6 +4354,7 @@ function buildProviderView(options) {
4227
4354
  providerMessages,
4228
4355
  estimate,
4229
4356
  contextWindow,
4357
+ inputBudgetTokens,
4230
4358
  needsCompaction: overCompactThreshold && compactionBlockedReason === null,
4231
4359
  compactionBlockedReason,
4232
4360
  anchorResolved,
@@ -4238,6 +4366,7 @@ function buildProviderView(options) {
4238
4366
  var init_build_provider_view = __esm(() => {
4239
4367
  init_config3();
4240
4368
  init_estimate();
4369
+ init_fit_to_budget();
4241
4370
  init_heuristic_summary();
4242
4371
  init_message_parts();
4243
4372
  init_tier1_prune();
@@ -4286,6 +4415,7 @@ var init_context = __esm(() => {
4286
4415
  init_config3();
4287
4416
  init_context_state();
4288
4417
  init_estimate();
4418
+ init_fit_to_budget();
4289
4419
  init_heuristic_summary();
4290
4420
  init_message_parts();
4291
4421
  init_normalize_provider_messages();
@@ -4559,7 +4689,7 @@ var init_lightcode_config = __esm(() => {
4559
4689
  context: defaultContextOptimizerConfig,
4560
4690
  maxOutputTokens: 32768,
4561
4691
  maxSteps: 30,
4562
- maxRetries: 5,
4692
+ maxRetries: 3,
4563
4693
  autoContinue: defaultAutoContinueConfig,
4564
4694
  headroom: defaultHeadroomConfig
4565
4695
  };
@@ -5092,6 +5222,10 @@ async function extractToolCalls(text, knownToolNames) {
5092
5222
  while ((match = DEEPSEEK_WRAPPER_RE.exec(text)) !== null) {
5093
5223
  segments.push({ start: match.index, end: match.index + match[0].length, call: null });
5094
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
+ }
5095
5229
  const covered = (idx) => segments.some((s) => idx >= s.start && idx < s.end);
5096
5230
  const orphanIdx = findOrphanMarker(text, covered);
5097
5231
  if (orphanIdx !== -1) {
@@ -5179,7 +5313,7 @@ async function parseBareTrailingJson(text, knownToolNames) {
5179
5313
  return null;
5180
5314
  return { start: idx, payload };
5181
5315
  }
5182
- 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;
5183
5317
  var init_xml_tool_call_parser = __esm(() => {
5184
5318
  init_tool_call_json_repair();
5185
5319
  TOOL_CALL_OPEN_MARKERS = [
@@ -5206,6 +5340,7 @@ var init_xml_tool_call_parser = __esm(() => {
5206
5340
  deepseek: /<\uFF5Ctool\u2581call\u2581begin\uFF5C>\s*(?:function)?\s*(?:<\uFF5Ctool\u2581sep\uFF5C>)?\s*([\w.-]+)\s*\n?([\s\S]*?)<\uFF5Ctool\u2581call\u2581end\uFF5C>/g
5207
5341
  };
5208
5342
  DEEPSEEK_WRAPPER_RE = /<\uFF5Ctool\u2581calls\u2581(?:begin|end)\uFF5C>/g;
5343
+ FUNCTION_CALLS_WRAPPER_RE = /<\/?function_calls\s*>/gi;
5209
5344
  PARAMETER_TAG_RE = /<parameter\s+name=["']?([\w.-]+)["']?\s*>([\s\S]*?)(?:<\/parameter>|(?=<parameter\s)|$)/gi;
5210
5345
  PARAMETER_EQ_RE = /<parameter=([\w.-]+)>([\s\S]*?)(?=<parameter=|<\/function>|$)/gi;
5211
5346
  });
@@ -6022,52 +6157,6 @@ var init_resolve_within_workspace = __esm(() => {
6022
6157
  WORKSPACE = defaultWorkspaceContext.root;
6023
6158
  });
6024
6159
 
6025
- // packages/ai/src/common/output-utils.ts
6026
- function omissionMarker(omitted) {
6027
- return `
6028
-
6029
- ... [${omitted} characters omitted; re-read a line range with read_file or raise maxChars] ...
6030
-
6031
- `;
6032
- }
6033
- function truncateText(text, maxChars = DEFAULT_TOOL_TEXT_OUTPUT_CHARS) {
6034
- if (text.length <= maxChars) {
6035
- return {
6036
- text,
6037
- truncated: false
6038
- };
6039
- }
6040
- const markerReserve = omissionMarker(text.length).length;
6041
- const usable = Math.max(0, maxChars - markerReserve);
6042
- const headChars = Math.floor(usable * 0.7);
6043
- const tailChars = usable - headChars;
6044
- const head = text.slice(0, headChars);
6045
- const tail = tailChars > 0 ? text.slice(text.length - tailChars) : "";
6046
- const omitted = text.length - head.length - tail.length;
6047
- return {
6048
- text: `${head}${omissionMarker(omitted)}${tail}`,
6049
- truncated: true
6050
- };
6051
- }
6052
- function toSingleLinePreview(value, maxChars = 140) {
6053
- let text;
6054
- if (typeof value === "string") {
6055
- text = value;
6056
- } else {
6057
- try {
6058
- text = JSON.stringify(value);
6059
- } catch {
6060
- text = String(value);
6061
- }
6062
- }
6063
- const normalized = text.replace(/\s+/g, " ").trim();
6064
- if (normalized.length <= maxChars) {
6065
- return normalized;
6066
- }
6067
- return `${normalized.slice(0, maxChars - 3)}...`;
6068
- }
6069
- var init_output_utils = () => {};
6070
-
6071
6160
  // packages/ai/src/bash/runtime.ts
6072
6161
  import { exec } from "child_process";
6073
6162
  import { promisify } from "util";
@@ -6256,24 +6345,129 @@ function createUnifiedDiff(filePath, oldText, newText, { maxChars = DEFAULT_DIFF
6256
6345
  var DEFAULT_DIFF_MAX_CHARS = 6000, DIFF_CONTEXT_LINES = 3;
6257
6346
  var init_unified_diff = () => {};
6258
6347
 
6259
- // packages/ai/src/edit-file/runtime.ts
6260
- import { promises as fs2 } from "fs";
6261
- function countOccurrences(haystack, needle) {
6262
- if (!needle) {
6263
- 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
+ `;
6264
6357
  }
6265
- let count = 0;
6266
- let startIndex = 0;
6267
- while (true) {
6268
- 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);
6269
6381
  if (index === -1) {
6270
6382
  break;
6271
6383
  }
6272
- count += 1;
6273
- startIndex = index + needle.length;
6384
+ ranges.push({ start: index, end: index + search.length });
6385
+ from = index + search.length;
6274
6386
  }
6275
- 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
+ };
6276
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";
6277
6471
  async function executeEditFile(input, workspaceContext = getDefaultWorkspaceContext(), editContext = {}) {
6278
6472
  const parsedInput = editFileInputSchema.parse(input);
6279
6473
  const resolvedPath = resolveWithinWorkspace(parsedInput.path, {
@@ -6281,11 +6475,7 @@ async function executeEditFile(input, workspaceContext = getDefaultWorkspaceCont
6281
6475
  });
6282
6476
  const relativePath = toWorkspaceRelativePath(resolvedPath, workspaceContext);
6283
6477
  const originalContent = await fs2.readFile(resolvedPath, "utf8");
6284
- const replacements = parsedInput.replaceAll ? countOccurrences(originalContent, parsedInput.search) : originalContent.includes(parsedInput.search) ? 1 : 0;
6285
- if (replacements === 0) {
6286
- throw new Error(`No matches found for the provided search text in "${parsedInput.path}".`);
6287
- }
6288
- 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);
6289
6479
  if (editContext.sessionId && editContext.turnKey) {
6290
6480
  await recordFileCheckpoint({
6291
6481
  sessionId: editContext.sessionId,
@@ -6307,6 +6497,7 @@ var init_runtime6 = __esm(() => {
6307
6497
  init_runtime5();
6308
6498
  init_resolve_within_workspace();
6309
6499
  init_unified_diff();
6500
+ init_match();
6310
6501
  init_schema2();
6311
6502
  });
6312
6503
 
@@ -7693,11 +7884,13 @@ __export(exports_runtime, {
7693
7884
  sandboxConfigSchema: () => sandboxConfigSchema,
7694
7885
  safeStringify: () => safeStringify,
7695
7886
  runPluginHooks: () => runPluginHooks,
7887
+ retryReasonLabel: () => retryReasonLabel,
7696
7888
  resolvedHeadroomConfigSchema: () => resolvedHeadroomConfigSchema,
7697
7889
  resolvedContextOptimizerConfigSchema: () => resolvedContextOptimizerConfigSchema,
7698
7890
  resolvedAutoContinueConfigSchema: () => resolvedAutoContinueConfigSchema,
7699
7891
  resolveWithinWorkspace: () => resolveWithinWorkspace,
7700
7892
  resolveMaxOutputTokens: () => resolveMaxOutputTokens,
7893
+ resolveInputBudgetTokens: () => resolveInputBudgetTokens,
7701
7894
  resolveDanglingToolParts: () => resolveDanglingToolParts,
7702
7895
  resolveContextWindowTokens: () => resolveContextWindowTokens,
7703
7896
  resolveCodingPermissionMode: () => resolveCodingPermissionMode,
@@ -7775,6 +7968,7 @@ __export(exports_runtime, {
7775
7968
  getActiveCodingToolsForPolicy: () => getActiveCodingToolsForPolicy,
7776
7969
  formatPermissionDecision: () => formatPermissionDecision,
7777
7970
  formatChatStreamError: () => formatChatStreamError,
7971
+ fitMessagesToBudget: () => fitMessagesToBudget,
7778
7972
  extractProposedPlanBlocks: () => extractProposedPlanBlocks,
7779
7973
  extractLatestProposedPlan: () => extractLatestProposedPlan,
7780
7974
  executeWriteFile: () => executeWriteFile,
@@ -7798,6 +7992,7 @@ __export(exports_runtime, {
7798
7992
  executeBash: () => executeBash,
7799
7993
  evaluateCodingToolPermission: () => evaluateCodingToolPermission,
7800
7994
  estimateTextTokens: () => estimateTextTokens,
7995
+ estimateStructuralTokens: () => estimateStructuralTokens,
7801
7996
  estimateMessageTokens: () => estimateMessageTokens,
7802
7997
  estimateContextTokens: () => estimateContextTokens,
7803
7998
  diagnosticsToolsResponseSchema: () => diagnosticsToolsResponseSchema,
@@ -7864,6 +8059,7 @@ __export(exports_runtime, {
7864
8059
  chatInteractionListQuerySchema: () => chatInteractionListQuerySchema,
7865
8060
  chatInteractionKindSchema: () => chatInteractionKindSchema,
7866
8061
  chatInteractionKindOrder: () => chatInteractionKindOrder,
8062
+ capCoverageToTokenBudget: () => capCoverageToTokenBudget,
7867
8063
  callMcpToolProviderInputSchema: () => callMcpToolProviderInputSchema,
7868
8064
  callMcpToolOutputSchema: () => callMcpToolOutputSchema,
7869
8065
  callMcpToolInputSchema: () => callMcpToolInputSchema,
@@ -7884,6 +8080,7 @@ __export(exports_runtime, {
7884
8080
  ESTIMATED_CHARS_PER_TOKEN: () => ESTIMATED_CHARS_PER_TOKEN,
7885
8081
  DEFAULT_PRUNE_QUANTIZE_USER_TURNS: () => DEFAULT_PRUNE_QUANTIZE_USER_TURNS,
7886
8082
  DEFAULT_PRUNE_MIN_OUTPUT_CHARS: () => DEFAULT_PRUNE_MIN_OUTPUT_CHARS,
8083
+ DEFAULT_INPUT_BUDGET_SAFETY_FRACTION: () => DEFAULT_INPUT_BUDGET_SAFETY_FRACTION,
7887
8084
  DEFAULT_CONTEXT_WINDOW_TOKENS: () => DEFAULT_CONTEXT_WINDOW_TOKENS,
7888
8085
  ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET: () => ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET
7889
8086
  });
@@ -8218,6 +8415,22 @@ import {
8218
8415
  safeValidateUIMessages
8219
8416
  } from "ai";
8220
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
+ }
8221
8434
  function getErrorMessage2(error, fallback) {
8222
8435
  if (error instanceof Error) {
8223
8436
  return error.message;
@@ -8508,6 +8721,7 @@ function useCodingSessionChat({
8508
8721
  const autoContinuesRef = useRef(0);
8509
8722
  const errorRetriesRef = useRef(0);
8510
8723
  const errorRetryTimerRef = useRef(null);
8724
+ const errorEpisodeStartRef = useRef(null);
8511
8725
  const lastDecidedAssistantIdRef = useRef(null);
8512
8726
  const lastProgressMarkerRef = useRef(0);
8513
8727
  const noProgressRetriesRef = useRef(0);
@@ -8518,6 +8732,7 @@ function useCodingSessionChat({
8518
8732
  turnKeyRef.current = `turn-${Date.now()}-${turnCounterRef.current}`;
8519
8733
  autoContinuesRef.current = 0;
8520
8734
  errorRetriesRef.current = 0;
8735
+ errorEpisodeStartRef.current = null;
8521
8736
  lastProgressMarkerRef.current = 0;
8522
8737
  noProgressRetriesRef.current = 0;
8523
8738
  taskStuckRef.current = false;
@@ -8914,6 +9129,7 @@ function useCodingSessionChat({
8914
9129
  useEffect(() => {
8915
9130
  if (status === "ready" && !error) {
8916
9131
  errorRetriesRef.current = 0;
9132
+ errorEpisodeStartRef.current = null;
8917
9133
  }
8918
9134
  }, [error, status]);
8919
9135
  useEffect(() => {
@@ -8951,7 +9167,8 @@ function useCodingSessionChat({
8951
9167
  errorRetriesRef.current += 1;
8952
9168
  setAutoContinueState({
8953
9169
  attempt: errorRetriesRef.current,
8954
- kind: "retry-error"
9170
+ kind: "retry-error",
9171
+ retryReason: "stall"
8955
9172
  });
8956
9173
  (async () => {
8957
9174
  try {
@@ -9026,12 +9243,28 @@ function useCodingSessionChat({
9026
9243
  if (taskStuckRef.current || pendingApprovals.length > 0 || pendingUserPrompts.length > 0 || errorRetryTimerRef.current || stallRecoveryInFlightRef.current) {
9027
9244
  return;
9028
9245
  }
9029
- if (errorRetriesRef.current >= maxErrorRetriesRef.current) {
9030
- return;
9031
- }
9032
9246
  const retryMessages = sanitizeMessagesForRetry(messages);
9247
+ const retryReason = parseChatStreamError(error.message)?.kind ?? "network";
9248
+ if (errorEpisodeStartRef.current === null) {
9249
+ errorEpisodeStartRef.current = Date.now();
9250
+ }
9033
9251
  const attempt = errorRetriesRef.current + 1;
9034
- 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
+ });
9035
9268
  errorRetryTimerRef.current = setTimeout(() => {
9036
9269
  errorRetryTimerRef.current = null;
9037
9270
  if (!registerRetryProgress(messagesRef.current)) {
@@ -9043,7 +9276,7 @@ function useCodingSessionChat({
9043
9276
  return;
9044
9277
  }
9045
9278
  errorRetriesRef.current = attempt;
9046
- setAutoContinueState({ attempt, kind: "retry-error" });
9279
+ setAutoContinueState({ attempt, kind: "retry-error", retryReason });
9047
9280
  setMessages(retryMessages);
9048
9281
  clearError();
9049
9282
  setToolExecutionError(null);
@@ -9261,7 +9494,7 @@ function useCodingSessionChat({
9261
9494
  isSessionIdValid
9262
9495
  };
9263
9496
  }
9264
- 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;
9265
9498
  var init_use_coding_session_chat = __esm(() => {
9266
9499
  init_agent_tools();
9267
9500
  init_chat_error();
@@ -9437,7 +9670,7 @@ function ContextSummaryFrame({ children }) {
9437
9670
  return /* @__PURE__ */ jsxDEV4("box", {
9438
9671
  width: "100%",
9439
9672
  flexDirection: "column",
9440
- borderStyle: "single",
9673
+ borderStyle: borderStyleFor.card,
9441
9674
  borderColor: cliTheme.semantic.info,
9442
9675
  backgroundColor: cliTheme.surfaces.panel,
9443
9676
  paddingX: 1,
@@ -9562,7 +9795,7 @@ function ChatProposedPlanCard({ content }) {
9562
9795
  return /* @__PURE__ */ jsxDEV5("box", {
9563
9796
  width: "100%",
9564
9797
  flexDirection: "column",
9565
- borderStyle: "single",
9798
+ borderStyle: borderStyleFor.card,
9566
9799
  borderColor: cliTheme.borders.active,
9567
9800
  backgroundColor: cliTheme.surfaces.panel,
9568
9801
  paddingX: 1,
@@ -9771,7 +10004,7 @@ function ChatInteractionPopup({
9771
10004
  return /* @__PURE__ */ jsxDEV6("box", {
9772
10005
  width: "100%",
9773
10006
  flexDirection: "column",
9774
- borderStyle: "single",
10007
+ borderStyle: borderStyleFor.modal,
9775
10008
  borderColor: cliTheme.borders.active,
9776
10009
  backgroundColor: cliTheme.surfaces.panel,
9777
10010
  paddingX: 1,
@@ -9895,21 +10128,146 @@ var init_chat_message_error_part = __esm(() => {
9895
10128
  init_cli_theme();
9896
10129
  });
9897
10130
 
9898
- // 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";
9899
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";
9900
10247
  function ChatMessageReasoningPart({ part }) {
10248
+ const { expandedReasoning } = useAppState();
9901
10249
  if (!part.text) {
9902
10250
  return null;
9903
10251
  }
9904
- 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", {
9905
10263
  flexDirection: "column",
9906
10264
  gap: 1,
9907
10265
  children: [
9908
- /* @__PURE__ */ jsxDEV8("text", {
10266
+ /* @__PURE__ */ jsxDEV9("text", {
9909
10267
  fg: cliTheme.semantic.info,
9910
10268
  children: "Thinking:"
9911
10269
  }, undefined, false, undefined, this),
9912
- /* @__PURE__ */ jsxDEV8("text", {
10270
+ /* @__PURE__ */ jsxDEV9("text", {
9913
10271
  fg: cliTheme.text.secondary,
9914
10272
  children: part.text
9915
10273
  }, undefined, false, undefined, this)
@@ -9917,6 +10275,7 @@ function ChatMessageReasoningPart({ part }) {
9917
10275
  }, undefined, true, undefined, this);
9918
10276
  }
9919
10277
  var init_chat_message_reasoning_part = __esm(() => {
10278
+ init_app_state();
9920
10279
  init_cli_theme();
9921
10280
  });
9922
10281
 
@@ -9983,13 +10342,13 @@ var init_code_syntax_style = __esm(() => {
9983
10342
  });
9984
10343
 
9985
10344
  // apps/cli/src/components/chat/chat-message-text-part.tsx
9986
- import { jsxDEV as jsxDEV9 } from "@opentui/react/jsx-dev-runtime";
10345
+ import { jsxDEV as jsxDEV10 } from "@opentui/react/jsx-dev-runtime";
9987
10346
  function renderMarkdown(content, key) {
9988
10347
  const trimmedContent = content.trim();
9989
10348
  if (trimmedContent.length === 0) {
9990
10349
  return null;
9991
10350
  }
9992
- return /* @__PURE__ */ jsxDEV9("markdown", {
10351
+ return /* @__PURE__ */ jsxDEV10("markdown", {
9993
10352
  width: "100%",
9994
10353
  content: trimmedContent,
9995
10354
  syntaxStyle: markdownSyntaxStyle,
@@ -10014,7 +10373,7 @@ function ChatMessageTextPart({ part }) {
10014
10373
  const renderedSegments = segments.map((segment, index) => {
10015
10374
  const key = `${segment.type}:${index}`;
10016
10375
  if (segment.type === "plan") {
10017
- return /* @__PURE__ */ jsxDEV9(ChatProposedPlanCard, {
10376
+ return /* @__PURE__ */ jsxDEV10(ChatProposedPlanCard, {
10018
10377
  content: segment.text
10019
10378
  }, key, false, undefined, this);
10020
10379
  }
@@ -10023,7 +10382,7 @@ function ChatMessageTextPart({ part }) {
10023
10382
  if (renderedSegments.length === 0) {
10024
10383
  return null;
10025
10384
  }
10026
- return /* @__PURE__ */ jsxDEV9("box", {
10385
+ return /* @__PURE__ */ jsxDEV10("box", {
10027
10386
  width: "100%",
10028
10387
  flexDirection: "column",
10029
10388
  gap: 1,
@@ -10038,113 +10397,35 @@ var init_chat_message_text_part = __esm(() => {
10038
10397
  markdownSyntaxStyle = codeSyntaxStyle;
10039
10398
  });
10040
10399
 
10041
- // apps/cli/src/state/app-state.tsx
10042
- import { createContext, useCallback as useCallback2, useContext, useMemo as useMemo3, useState as useState3 } from "react";
10043
- import { jsxDEV as jsxDEV10 } from "@opentui/react/jsx-dev-runtime";
10044
- function AppStateProvider({ children }) {
10045
- const [paletteOpen, setPaletteOpen] = useState3(false);
10046
- const [paletteQuery, setPaletteQuery] = useState3("");
10047
- const [paletteSelected, setPaletteSelected] = useState3(0);
10048
- const [slashMenuOpen, setSlashMenuOpen] = useState3(false);
10049
- const [slashMenuQuery, setSlashMenuQuery] = useState3("/");
10050
- const [slashMenuSelected, setSlashMenuSelected] = useState3(0);
10051
- const openPalette = useCallback2((initialQuery = "") => {
10052
- setPaletteOpen(true);
10053
- setPaletteQuery(initialQuery);
10054
- setPaletteSelected(0);
10055
- setSlashMenuOpen(false);
10056
- }, []);
10057
- const closePalette = useCallback2(() => {
10058
- setPaletteOpen(false);
10059
- setPaletteQuery("");
10060
- setPaletteSelected(0);
10061
- }, []);
10062
- const openSlashMenu = useCallback2(() => {
10063
- setSlashMenuOpen(true);
10064
- setSlashMenuQuery("/");
10065
- setSlashMenuSelected(0);
10066
- setPaletteOpen(false);
10067
- }, []);
10068
- const closeSlashMenu = useCallback2(() => {
10069
- setSlashMenuOpen(false);
10070
- setSlashMenuQuery("/");
10071
- setSlashMenuSelected(0);
10072
- }, []);
10073
- const [requestedChatActionId, setRequestedChatActionId] = useState3(null);
10074
- const requestChatAction = useCallback2((id) => {
10075
- setRequestedChatActionId(id);
10076
- }, []);
10077
- const clearRequestedChatAction = useCallback2(() => {
10078
- setRequestedChatActionId(null);
10079
- }, []);
10080
- const [configRefreshNonce, setConfigRefreshNonce] = useState3(0);
10081
- const bumpConfigRefresh = useCallback2(() => {
10082
- setConfigRefreshNonce((nonce) => nonce + 1);
10083
- }, []);
10084
- const [expandedToolOutput, setExpandedToolOutput] = useState3(false);
10085
- const toggleToolOutputExpansion = useCallback2(() => {
10086
- setExpandedToolOutput((expanded) => !expanded);
10087
- }, []);
10088
- const value = useMemo3(() => ({
10089
- paletteOpen,
10090
- paletteQuery,
10091
- setPaletteQuery,
10092
- paletteSelected,
10093
- setPaletteSelected,
10094
- openPalette,
10095
- closePalette,
10096
- slashMenuOpen,
10097
- slashMenuQuery,
10098
- setSlashMenuQuery,
10099
- slashMenuSelected,
10100
- setSlashMenuSelected,
10101
- openSlashMenu,
10102
- closeSlashMenu,
10103
- requestedChatActionId,
10104
- requestChatAction,
10105
- clearRequestedChatAction,
10106
- configRefreshNonce,
10107
- bumpConfigRefresh,
10108
- expandedToolOutput,
10109
- toggleToolOutputExpansion
10110
- }), [
10111
- paletteOpen,
10112
- paletteQuery,
10113
- paletteSelected,
10114
- openPalette,
10115
- closePalette,
10116
- slashMenuOpen,
10117
- slashMenuQuery,
10118
- slashMenuSelected,
10119
- openSlashMenu,
10120
- closeSlashMenu,
10121
- requestedChatActionId,
10122
- requestChatAction,
10123
- clearRequestedChatAction,
10124
- configRefreshNonce,
10125
- bumpConfigRefresh,
10126
- expandedToolOutput,
10127
- toggleToolOutputExpansion
10128
- ]);
10129
- return /* @__PURE__ */ jsxDEV10(AppStateContext.Provider, {
10130
- value,
10131
- children
10132
- }, undefined, false, undefined, this);
10133
- }
10134
- function useAppState() {
10135
- const context2 = useContext(AppStateContext);
10136
- if (!context2) {
10137
- 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;
10138
10405
  }
10139
- return context2;
10406
+ return /utf-?8/i.test(locale);
10140
10407
  }
10141
- var AppStateContext;
10142
- var init_app_state = __esm(() => {
10143
- 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);
10144
10425
  });
10145
10426
 
10146
10427
  // apps/cli/src/components/chat/chat-diff-card.tsx
10147
- import { TextAttributes as TextAttributes7 } from "@opentui/core";
10428
+ import { TextAttributes as TextAttributes8 } from "@opentui/core";
10148
10429
  import { jsxDEV as jsxDEV11 } from "@opentui/react/jsx-dev-runtime";
10149
10430
  function collapseDiff(diff) {
10150
10431
  const lines = diff.split(`
@@ -10174,7 +10455,7 @@ function ChatDiffCard({ path: path16, diff }) {
10174
10455
  return /* @__PURE__ */ jsxDEV11("box", {
10175
10456
  width: "100%",
10176
10457
  flexDirection: "column",
10177
- borderStyle: "single",
10458
+ borderStyle: borderStyleFor.card,
10178
10459
  borderColor: cliTheme.borders.subtle,
10179
10460
  backgroundColor: cliTheme.surfaces.inset,
10180
10461
  children: [
@@ -10182,7 +10463,7 @@ function ChatDiffCard({ path: path16, diff }) {
10182
10463
  paddingX: 1,
10183
10464
  children: /* @__PURE__ */ jsxDEV11("text", {
10184
10465
  fg: cliTheme.text.secondary,
10185
- attributes: TextAttributes7.BOLD,
10466
+ attributes: TextAttributes8.BOLD,
10186
10467
  children: path16
10187
10468
  }, undefined, false, undefined, this)
10188
10469
  }, undefined, false, undefined, this),
@@ -10193,32 +10474,32 @@ function ChatDiffCard({ path: path16, diff }) {
10193
10474
  filetype: inferFiletype(path16),
10194
10475
  syntaxStyle: codeSyntaxStyle,
10195
10476
  showLineNumbers: true,
10196
- addedBg,
10197
- removedBg,
10477
+ addedBg: cliTheme.diff.addedBg,
10478
+ removedBg: cliTheme.diff.removedBg,
10198
10479
  lineNumberFg: cliTheme.text.muted,
10199
10480
  lineNumberBg: cliTheme.surfaces.inset,
10200
- addedSignColor: cliTheme.semantic.success,
10201
- removedSignColor: cliTheme.semantic.error
10481
+ addedSignColor: cliTheme.diff.added,
10482
+ removedSignColor: cliTheme.diff.removed
10202
10483
  }, undefined, false, undefined, this),
10203
10484
  hiddenLines > 0 || wasPreTruncated ? /* @__PURE__ */ jsxDEV11("box", {
10204
10485
  paddingX: 1,
10205
10486
  children: /* @__PURE__ */ jsxDEV11("text", {
10206
10487
  fg: cliTheme.text.muted,
10207
- attributes: TextAttributes7.DIM,
10488
+ attributes: TextAttributes8.DIM,
10208
10489
  children: hiddenLines > 0 ? `+${hiddenLines} more diff lines` : "diff truncated"
10209
10490
  }, undefined, false, undefined, this)
10210
10491
  }, undefined, false, undefined, this) : null
10211
10492
  ]
10212
10493
  }, undefined, true, undefined, this);
10213
10494
  }
10214
- var COLLAPSED_MAX_LINES = 20, TRUNCATION_MARKER = "... diff truncated ...", addedBg = "#13271B", removedBg = "#2B1619";
10495
+ var COLLAPSED_MAX_LINES = 20, TRUNCATION_MARKER = "... diff truncated ...";
10215
10496
  var init_chat_diff_card = __esm(() => {
10216
10497
  init_cli_theme();
10217
10498
  init_code_syntax_style();
10218
10499
  });
10219
10500
 
10220
10501
  // apps/cli/src/components/chat/chat-message-tool-invocation-part.tsx
10221
- import { TextAttributes as TextAttributes8 } from "@opentui/core";
10502
+ import { TextAttributes as TextAttributes9 } from "@opentui/core";
10222
10503
  import {
10223
10504
  getToolName
10224
10505
  } from "ai";
@@ -10304,6 +10585,78 @@ function getOutputPreviewLines(part, maxLines) {
10304
10585
  }
10305
10586
  return [];
10306
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
+ }
10307
10660
  function ChatMessageToolInvocationPart({
10308
10661
  part,
10309
10662
  pendingApprovalIds
@@ -10313,12 +10666,18 @@ function ChatMessageToolInvocationPart({
10313
10666
  const target = normalizeToolTarget(getToolTarget(part.input));
10314
10667
  const label = target ?? humanizeToolName(toolName);
10315
10668
  const isPendingApproval = part.state === "input-available" && pendingApprovalIds?.has(part.toolCallId);
10316
- 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];
10317
10670
  const stateColor = isPendingApproval ? cliTheme.semantic.warning : getToolToneColor(part.state);
10318
10671
  const line = `${stateView.symbol} ${stateView.verb} [${humanizeToolName(toolName)}] ${target ?? ""}`.trimEnd();
10319
10672
  const fileEditDiff = getFileEditDiff(part);
10320
10673
  const secondaryParams = getSecondaryParams(part.input, target);
10321
- 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;
10322
10681
  return /* @__PURE__ */ jsxDEV12("box", {
10323
10682
  flexDirection: "column",
10324
10683
  children: [
@@ -10328,9 +10687,24 @@ function ChatMessageToolInvocationPart({
10328
10687
  }, undefined, false, undefined, this),
10329
10688
  secondaryParams ? /* @__PURE__ */ jsxDEV12("text", {
10330
10689
  fg: cliTheme.text.muted,
10331
- attributes: TextAttributes8.DIM,
10690
+ attributes: TextAttributes9.DIM,
10332
10691
  children: ` ${secondaryParams}`
10333
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,
10334
10708
  part.state === "output-error" ? /* @__PURE__ */ jsxDEV12("text", {
10335
10709
  fg: cliTheme.text.secondary,
10336
10710
  children: part.errorText
@@ -10342,22 +10716,15 @@ function ChatMessageToolInvocationPart({
10342
10716
  part.approval.reason
10343
10717
  ]
10344
10718
  }, undefined, true, undefined, this) : null,
10345
- outputPreviewLines.length > 0 ? /* @__PURE__ */ jsxDEV12("box", {
10719
+ rawPreviewLines.length > 0 ? /* @__PURE__ */ jsxDEV12("box", {
10346
10720
  flexDirection: "column",
10347
10721
  paddingX: 1,
10348
10722
  backgroundColor: cliTheme.surfaces.inset,
10349
- children: [
10350
- outputPreviewLines.map((previewLine, index) => /* @__PURE__ */ jsxDEV12("text", {
10351
- fg: cliTheme.text.muted,
10352
- children: previewLine || " "
10353
- }, `${part.toolCallId}-preview-${index}`, false, undefined, this)),
10354
- !expandedToolOutput ? /* @__PURE__ */ jsxDEV12("text", {
10355
- fg: cliTheme.text.muted,
10356
- attributes: TextAttributes8.DIM,
10357
- children: "Ctrl+O expand"
10358
- }, undefined, false, undefined, this) : null
10359
- ]
10360
- }, 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,
10361
10728
  fileEditDiff ? /* @__PURE__ */ jsxDEV12(ChatDiffCard, {
10362
10729
  path: fileEditDiff.path,
10363
10730
  diff: fileEditDiff.diff
@@ -10365,10 +10732,11 @@ function ChatMessageToolInvocationPart({
10365
10732
  ]
10366
10733
  }, undefined, true, undefined, this);
10367
10734
  }
10368
- 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;
10369
10736
  var init_chat_message_tool_invocation_part = __esm(() => {
10370
10737
  init_app_state();
10371
10738
  init_cli_theme();
10739
+ init_cli_theme_capabilities();
10372
10740
  init_text_utils();
10373
10741
  init_chat_diff_card();
10374
10742
  previewOutputKeys = [
@@ -10380,19 +10748,19 @@ var init_chat_message_tool_invocation_part = __esm(() => {
10380
10748
  "message"
10381
10749
  ];
10382
10750
  TOOL_STATE_VIEW = {
10383
- "input-streaming": { symbol: ">", verb: "Preparing" },
10384
- "input-available": { symbol: ">", verb: "Running" },
10385
- "approval-requested": { symbol: "!", verb: "Approval needed for" },
10386
- "approval-responded": { symbol: ">", verb: "Approval received for" },
10387
- "output-available": { symbol: "OK", verb: "Completed" },
10388
- "output-error": { symbol: "ERR", verb: "Failed" },
10389
- "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" }
10390
10758
  };
10391
10759
  });
10392
10760
 
10393
10761
  // apps/cli/src/components/chat/chat-message.tsx
10394
10762
  import { isToolUIPart as isToolUIPart3 } from "ai";
10395
- import { TextAttributes as TextAttributes9 } from "@opentui/core";
10763
+ import { TextAttributes as TextAttributes10 } from "@opentui/core";
10396
10764
  import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
10397
10765
  function ChatMessage({ message, pendingApprovalIds }) {
10398
10766
  const roleStyle = getMessageRoleTheme(message.role);
@@ -10428,43 +10796,45 @@ function ChatMessage({ message, pendingApprovalIds }) {
10428
10796
  return /* @__PURE__ */ jsxDEV13("box", {
10429
10797
  width: "100%",
10430
10798
  flexDirection: "column",
10431
- borderStyle: "single",
10432
- borderColor: roleStyle.borderColor,
10433
- backgroundColor: roleStyle.backgroundColor,
10434
- paddingX: 1,
10435
- paddingY: 1,
10436
- gap: 1,
10799
+ gap: space.xs,
10437
10800
  children: [
10438
10801
  /* @__PURE__ */ jsxDEV13("text", {
10439
10802
  fg: roleStyle.labelColor,
10440
- attributes: TextAttributes9.BOLD,
10441
- children: ROLE_LABELS[message.role]
10803
+ attributes: TextAttributes10.BOLD,
10804
+ children: `${ROLE_GLYPHS[message.role]} ${ROLE_LABELS[message.role]}`
10442
10805
  }, undefined, false, undefined, this),
10443
10806
  /* @__PURE__ */ jsxDEV13("box", {
10444
10807
  width: "100%",
10445
10808
  flexDirection: "column",
10446
- gap: 1,
10809
+ gap: space.xs,
10810
+ paddingLeft: space.md,
10447
10811
  children: renderedParts
10448
10812
  }, undefined, false, undefined, this)
10449
10813
  ]
10450
10814
  }, undefined, true, undefined, this);
10451
10815
  }
10452
- var ROLE_LABELS;
10816
+ var ROLE_LABELS, ROLE_GLYPHS;
10453
10817
  var init_chat_message = __esm(() => {
10454
10818
  init_chat_message_error_part();
10455
10819
  init_chat_message_reasoning_part();
10456
10820
  init_chat_message_text_part();
10457
10821
  init_chat_message_tool_invocation_part();
10458
10822
  init_cli_theme();
10823
+ init_cli_theme_capabilities();
10459
10824
  ROLE_LABELS = {
10460
- user: "You",
10461
- assistant: "Assistant",
10462
- 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
10463
10833
  };
10464
10834
  });
10465
10835
 
10466
10836
  // apps/cli/src/components/chat/chat-shell.tsx
10467
- import { TextAttributes as TextAttributes10 } from "@opentui/core";
10837
+ import { TextAttributes as TextAttributes11 } from "@opentui/core";
10468
10838
  import { jsxDEV as jsxDEV14 } from "@opentui/react/jsx-dev-runtime";
10469
10839
  function ChatShell({
10470
10840
  title = "Conversation",
@@ -10490,7 +10860,7 @@ function ChatShell({
10490
10860
  children: [
10491
10861
  /* @__PURE__ */ jsxDEV14("text", {
10492
10862
  fg: cliTheme.text.secondary,
10493
- attributes: TextAttributes10.BOLD,
10863
+ attributes: TextAttributes11.BOLD,
10494
10864
  children: title
10495
10865
  }, undefined, false, undefined, this),
10496
10866
  /* @__PURE__ */ jsxDEV14("text", {
@@ -10517,7 +10887,7 @@ function ChatShell({
10517
10887
  contentOptions: {
10518
10888
  width: "100%",
10519
10889
  flexDirection: "column",
10520
- gap: 1
10890
+ gap: space.md
10521
10891
  },
10522
10892
  scrollbarOptions: {
10523
10893
  trackOptions: {
@@ -10531,10 +10901,29 @@ function ChatShell({
10531
10901
  backgroundColor: cliTheme.scroll.rail
10532
10902
  }
10533
10903
  },
10534
- children: hasMessages ? children : /* @__PURE__ */ jsxDEV14("text", {
10535
- fg: cliTheme.text.muted,
10536
- children: emptyStateLabel
10537
- }, 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)
10538
10927
  }, undefined, false, undefined, this)
10539
10928
  }, undefined, false, undefined, this),
10540
10929
  errorMessage ? /* @__PURE__ */ jsxDEV14(ChatMessageErrorPart, {
@@ -10551,7 +10940,7 @@ var init_chat_shell = __esm(() => {
10551
10940
  });
10552
10941
 
10553
10942
  // apps/cli/src/components/chat/permission-mode-selector.tsx
10554
- import { TextAttributes as TextAttributes11 } from "@opentui/core";
10943
+ import { TextAttributes as TextAttributes12 } from "@opentui/core";
10555
10944
  import { useKeyboard as useKeyboard2 } from "@opentui/react";
10556
10945
  import { useEffect as useEffect2, useState as useState4 } from "react";
10557
10946
  import { jsxDEV as jsxDEV15 } from "@opentui/react/jsx-dev-runtime";
@@ -10603,7 +10992,7 @@ function PermissionModeSelector({
10603
10992
  return /* @__PURE__ */ jsxDEV15("box", {
10604
10993
  width: "100%",
10605
10994
  flexDirection: "column",
10606
- borderStyle: "single",
10995
+ borderStyle: borderStyleFor.modal,
10607
10996
  borderColor: cliTheme.overlay.border,
10608
10997
  backgroundColor: cliTheme.overlay.surface,
10609
10998
  children: [
@@ -10619,7 +11008,7 @@ function PermissionModeSelector({
10619
11008
  children: [
10620
11009
  /* @__PURE__ */ jsxDEV15("text", {
10621
11010
  fg: cliTheme.accent.primary,
10622
- attributes: TextAttributes11.BOLD,
11011
+ attributes: TextAttributes12.BOLD,
10623
11012
  children: "Permission Mode"
10624
11013
  }, undefined, false, undefined, this),
10625
11014
  /* @__PURE__ */ jsxDEV15("text", {
@@ -10652,12 +11041,12 @@ function PermissionModeSelector({
10652
11041
  /* @__PURE__ */ jsxDEV15("text", {
10653
11042
  width: 3,
10654
11043
  fg: isSelected ? cliTheme.accent.primary : cliTheme.text.muted,
10655
- attributes: TextAttributes11.BOLD,
11044
+ attributes: TextAttributes12.BOLD,
10656
11045
  children: isSelected ? "> " : " "
10657
11046
  }, undefined, false, undefined, this),
10658
11047
  /* @__PURE__ */ jsxDEV15("text", {
10659
11048
  fg: isSelected ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
10660
- attributes: isSelected ? TextAttributes11.BOLD : TextAttributes11.NONE,
11049
+ attributes: isSelected ? TextAttributes12.BOLD : TextAttributes12.NONE,
10661
11050
  children: modeOption.label
10662
11051
  }, undefined, false, undefined, this),
10663
11052
  modeOption.mode === currentMode && /* @__PURE__ */ jsxDEV15("text", {
@@ -10679,7 +11068,7 @@ function PermissionModeSelector({
10679
11068
  marginTop: 1,
10680
11069
  children: modeOption.risks.map((risk) => /* @__PURE__ */ jsxDEV15("text", {
10681
11070
  fg: modeOption.mode === "danger-full-access" ? cliTheme.semantic.error : cliTheme.text.muted,
10682
- attributes: TextAttributes11.DIM,
11071
+ attributes: TextAttributes12.DIM,
10683
11072
  children: [
10684
11073
  "\u2022 ",
10685
11074
  risk
@@ -10702,7 +11091,7 @@ function PermissionModeSelector({
10702
11091
  children: [
10703
11092
  /* @__PURE__ */ jsxDEV15("text", {
10704
11093
  fg: cliTheme.overlay.selectedRowText,
10705
- attributes: TextAttributes11.BOLD,
11094
+ attributes: TextAttributes12.BOLD,
10706
11095
  children: [
10707
11096
  "Selected: ",
10708
11097
  currentSelection.label
@@ -10778,7 +11167,7 @@ var init_fuzzy_match = __esm(() => {
10778
11167
  });
10779
11168
 
10780
11169
  // apps/cli/src/components/chat/chat-text-area.tsx
10781
- import { TextAttributes as TextAttributes12 } from "@opentui/core";
11170
+ import { TextAttributes as TextAttributes13 } from "@opentui/core";
10782
11171
  import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2, useState as useState5 } from "react";
10783
11172
  import { jsxDEV as jsxDEV16 } from "@opentui/react/jsx-dev-runtime";
10784
11173
  function getTrailingMentionQuery(text) {
@@ -10795,7 +11184,6 @@ function ChatTextArea({
10795
11184
  containerHeight,
10796
11185
  beforeInput,
10797
11186
  footer,
10798
- modeToggleHint = false,
10799
11187
  slashMenuOpen = false,
10800
11188
  onTextChange,
10801
11189
  mentionCandidates
@@ -10806,7 +11194,6 @@ function ChatTextArea({
10806
11194
  const [mentionQuery, setMentionQuery] = useState5(null);
10807
11195
  const [mentionSelected, setMentionSelected] = useState5(0);
10808
11196
  const isFocused = focused && !disabled;
10809
- const inputHint = modeToggleHint ? "Enter to send | Ctrl+Enter for newline | Tab/Ctrl+T to switch mode" : "Enter to send | Ctrl+Enter for newline";
10810
11197
  const mentionMatches = mentionQuery !== null && mentionCandidates && mentionCandidates.length > 0 ? fuzzyFilter(mentionQuery, mentionCandidates) : [];
10811
11198
  const selectedMentionIndex = Math.min(mentionSelected, Math.max(mentionMatches.length - 1, 0));
10812
11199
  const acceptMention = useCallback3((mentionPath) => {
@@ -10901,12 +11288,12 @@ function ChatTextArea({
10901
11288
  mentionMatches.map((mentionPath, index) => /* @__PURE__ */ jsxDEV16("text", {
10902
11289
  fg: index === selectedMentionIndex ? cliTheme.overlay.selectedRowText : cliTheme.text.secondary,
10903
11290
  bg: index === selectedMentionIndex ? cliTheme.overlay.selectedRowBackground : undefined,
10904
- attributes: index === selectedMentionIndex ? TextAttributes12.BOLD : TextAttributes12.NONE,
11291
+ attributes: index === selectedMentionIndex ? TextAttributes13.BOLD : TextAttributes13.NONE,
10905
11292
  children: `${index === selectedMentionIndex ? "> " : " "}${mentionPath}`
10906
11293
  }, mentionPath, false, undefined, this)),
10907
11294
  /* @__PURE__ */ jsxDEV16("text", {
10908
11295
  fg: cliTheme.overlay.footerText,
10909
- attributes: TextAttributes12.DIM,
11296
+ attributes: TextAttributes13.DIM,
10910
11297
  children: "Tab attach | Up/Down select | Esc dismiss"
10911
11298
  }, undefined, false, undefined, this)
10912
11299
  ]
@@ -10939,10 +11326,6 @@ function ChatTextArea({
10939
11326
  placeholderColor: cliTheme.input.placeholder,
10940
11327
  focused: isFocused
10941
11328
  }, undefined, false, undefined, this),
10942
- /* @__PURE__ */ jsxDEV16("text", {
10943
- fg: cliTheme.input.hint,
10944
- children: inputHint
10945
- }, undefined, false, undefined, this),
10946
11329
  footer
10947
11330
  ]
10948
11331
  }, undefined, true, undefined, this)
@@ -10964,7 +11347,7 @@ var init_chat_text_area = __esm(() => {
10964
11347
  });
10965
11348
 
10966
11349
  // apps/cli/src/components/chat/chat-todo-status-card.tsx
10967
- import { TextAttributes as TextAttributes13 } from "@opentui/core";
11350
+ import { TextAttributes as TextAttributes14 } from "@opentui/core";
10968
11351
  import { jsxDEV as jsxDEV17 } from "@opentui/react/jsx-dev-runtime";
10969
11352
  function getStatusColor(status) {
10970
11353
  if (status === "completed") {
@@ -10986,7 +11369,7 @@ function ChatTodoStatusCard({ todos }) {
10986
11369
  return /* @__PURE__ */ jsxDEV17("box", {
10987
11370
  width: "100%",
10988
11371
  flexDirection: "column",
10989
- borderStyle: "single",
11372
+ borderStyle: borderStyleFor.card,
10990
11373
  borderColor: cliTheme.semantic.info,
10991
11374
  backgroundColor: cliTheme.surfaces.panel,
10992
11375
  paddingX: 1,
@@ -11000,7 +11383,7 @@ function ChatTodoStatusCard({ todos }) {
11000
11383
  children: [
11001
11384
  /* @__PURE__ */ jsxDEV17("text", {
11002
11385
  fg: cliTheme.semantic.info,
11003
- attributes: TextAttributes13.BOLD,
11386
+ attributes: TextAttributes14.BOLD,
11004
11387
  children: "Session Todo"
11005
11388
  }, undefined, false, undefined, this),
11006
11389
  /* @__PURE__ */ jsxDEV17("text", {
@@ -11032,7 +11415,7 @@ function ChatTodoStatusCard({ todos }) {
11032
11415
  }, undefined, false, undefined, this),
11033
11416
  /* @__PURE__ */ jsxDEV17("text", {
11034
11417
  fg: getStatusColor(todo.status),
11035
- attributes: TextAttributes13.DIM,
11418
+ attributes: TextAttributes14.DIM,
11036
11419
  children: todoStatusLabel[todo.status]
11037
11420
  }, undefined, false, undefined, this)
11038
11421
  ]
@@ -11040,7 +11423,7 @@ function ChatTodoStatusCard({ todos }) {
11040
11423
  }, undefined, false, undefined, this),
11041
11424
  todos.length > 6 ? /* @__PURE__ */ jsxDEV17("text", {
11042
11425
  fg: cliTheme.text.muted,
11043
- attributes: TextAttributes13.DIM,
11426
+ attributes: TextAttributes14.DIM,
11044
11427
  children: [
11045
11428
  todos.length - 6,
11046
11429
  " more tasks hidden"
@@ -11062,7 +11445,7 @@ var init_chat_todo_status_card = __esm(() => {
11062
11445
  });
11063
11446
 
11064
11447
  // apps/cli/src/components/chat/chat-tool-approval-card.tsx
11065
- import { TextAttributes as TextAttributes14 } from "@opentui/core";
11448
+ import { TextAttributes as TextAttributes15 } from "@opentui/core";
11066
11449
  import { useKeyboard as useKeyboard3 } from "@opentui/react";
11067
11450
  import { useEffect as useEffect4, useState as useState6 } from "react";
11068
11451
  import { jsxDEV as jsxDEV18 } from "@opentui/react/jsx-dev-runtime";
@@ -11175,7 +11558,7 @@ function ChatToolApprovalCard({
11175
11558
  children: [
11176
11559
  /* @__PURE__ */ jsxDEV18("text", {
11177
11560
  fg: cliTheme.semantic.warning,
11178
- attributes: TextAttributes14.BOLD,
11561
+ attributes: TextAttributes15.BOLD,
11179
11562
  children: "Tool Approval"
11180
11563
  }, undefined, false, undefined, this),
11181
11564
  /* @__PURE__ */ jsxDEV18("text", {
@@ -11198,7 +11581,7 @@ function ChatToolApprovalCard({
11198
11581
  children: [
11199
11582
  /* @__PURE__ */ jsxDEV18("text", {
11200
11583
  fg: rowColors.primaryTextColor,
11201
- attributes: keyboardActive && index === safeSelectedIndex ? TextAttributes14.BOLD : TextAttributes14.NONE,
11584
+ attributes: keyboardActive && index === safeSelectedIndex ? TextAttributes15.BOLD : TextAttributes15.NONE,
11202
11585
  children: [
11203
11586
  keyboardActive && index === safeSelectedIndex ? "> " : " ",
11204
11587
  index + 1,
@@ -11210,7 +11593,7 @@ function ChatToolApprovalCard({
11210
11593
  }, undefined, true, undefined, this),
11211
11594
  /* @__PURE__ */ jsxDEV18("text", {
11212
11595
  fg: rowColors.secondaryTextColor,
11213
- attributes: TextAttributes14.DIM,
11596
+ attributes: TextAttributes15.DIM,
11214
11597
  children: getApprovalDescription(approval)
11215
11598
  }, undefined, false, undefined, this)
11216
11599
  ]
@@ -11224,7 +11607,7 @@ function ChatToolApprovalCard({
11224
11607
  children: [
11225
11608
  /* @__PURE__ */ jsxDEV18("text", {
11226
11609
  fg: cliTheme.text.muted,
11227
- attributes: TextAttributes14.DIM,
11610
+ attributes: TextAttributes15.DIM,
11228
11611
  children: keyboardActive ? "Enter approve | d deny | a approve all | Esc release focus" : "Down focus card | or type approve/deny below"
11229
11612
  }, undefined, false, undefined, this),
11230
11613
  /* @__PURE__ */ jsxDEV18("text", {
@@ -11241,30 +11624,80 @@ var init_chat_tool_approval_card = __esm(() => {
11241
11624
  init_cli_theme();
11242
11625
  });
11243
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
+
11244
11657
  // apps/cli/src/components/chat/loading-timer.tsx
11245
11658
  import { jsxDEV as jsxDEV19 } from "@opentui/react/jsx-dev-runtime";
11246
- 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
+ });
11247
11668
  const hours = Math.floor(elapsedSeconds / 3600);
11248
11669
  const minutes = Math.floor(elapsedSeconds % 3600 / 60);
11249
11670
  const seconds = Math.floor(elapsedSeconds % 60);
11250
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` : "";
11251
11673
  return /* @__PURE__ */ jsxDEV19("box", {
11252
11674
  paddingX: 1,
11253
- children: /* @__PURE__ */ jsxDEV19("text", {
11254
- fg: cliTheme.semantic.info,
11255
- children: [
11256
- "Thinking ",
11257
- formattedTime
11258
- ]
11259
- }, undefined, true, undefined, this)
11260
- }, 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);
11261
11692
  }
11262
11693
  var init_loading_timer = __esm(() => {
11263
11694
  init_cli_theme();
11695
+ init_cli_theme_capabilities();
11696
+ init_use_spinner();
11264
11697
  });
11265
11698
 
11266
11699
  // apps/cli/src/components/chat/copy-mode-overlay.tsx
11267
- import { TextAttributes as TextAttributes15 } from "@opentui/core";
11700
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
11268
11701
  import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
11269
11702
  function previewOf(message) {
11270
11703
  const text = collectMessageText(message).replace(/\s+/g, " ").trim();
@@ -11285,7 +11718,7 @@ function CopyModeOverlay({ messages, selectedIndex }) {
11285
11718
  children: [
11286
11719
  /* @__PURE__ */ jsxDEV20("text", {
11287
11720
  fg: cliTheme.accent.primary,
11288
- attributes: TextAttributes15.BOLD,
11721
+ attributes: TextAttributes16.BOLD,
11289
11722
  children: "Copy mode \u2014 \u2191/\u2193 select \xB7 Enter copy \xB7 c code blocks \xB7 Esc exit"
11290
11723
  }, undefined, false, undefined, this),
11291
11724
  visible.map((message, offset) => {
@@ -11304,7 +11737,7 @@ function CopyModeOverlay({ messages, selectedIndex }) {
11304
11737
  }),
11305
11738
  total > visible.length ? /* @__PURE__ */ jsxDEV20("text", {
11306
11739
  fg: cliTheme.text.muted,
11307
- attributes: TextAttributes15.DIM,
11740
+ attributes: TextAttributes16.DIM,
11308
11741
  children: `${selectedIndex + 1}/${total}`
11309
11742
  }, undefined, false, undefined, this) : null
11310
11743
  ]
@@ -11318,10 +11751,10 @@ var init_copy_mode_overlay = __esm(() => {
11318
11751
  });
11319
11752
 
11320
11753
  // apps/cli/src/hooks/use-auto-continue-config.ts
11321
- import { useEffect as useEffect5, useState as useState7 } from "react";
11754
+ import { useEffect as useEffect6, useState as useState8 } from "react";
11322
11755
  function useAutoContinueConfig() {
11323
- const [config4, setConfig] = useState7(defaultAutoContinueConfig);
11324
- useEffect5(() => {
11756
+ const [config4, setConfig] = useState8(defaultAutoContinueConfig);
11757
+ useEffect6(() => {
11325
11758
  let cancelled = false;
11326
11759
  async function loadConfig() {
11327
11760
  try {
@@ -11348,10 +11781,10 @@ var init_use_auto_continue_config = __esm(() => {
11348
11781
  });
11349
11782
 
11350
11783
  // apps/cli/src/hooks/use-context-window.ts
11351
- import { useEffect as useEffect6, useState as useState8 } from "react";
11784
+ import { useEffect as useEffect7, useState as useState9 } from "react";
11352
11785
  function useContextWindow() {
11353
- const [contextWindow, setContextWindow] = useState8(DEFAULT_CONTEXT_WINDOW_TOKENS);
11354
- useEffect6(() => {
11786
+ const [contextWindow, setContextWindow] = useState9(DEFAULT_CONTEXT_WINDOW_TOKENS);
11787
+ useEffect7(() => {
11355
11788
  let cancelled = false;
11356
11789
  async function loadContextWindow() {
11357
11790
  try {
@@ -11378,12 +11811,12 @@ var init_use_context_window = __esm(() => {
11378
11811
  });
11379
11812
 
11380
11813
  // apps/cli/src/hooks/use-loading-timer.ts
11381
- 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";
11382
11815
  function useLoadingTimer(isActive) {
11383
- const [elapsedSeconds, setElapsedSeconds] = useState9(0);
11816
+ const [elapsedSeconds, setElapsedSeconds] = useState10(0);
11384
11817
  const intervalRef = useRef3(null);
11385
11818
  const startTimeRef = useRef3(null);
11386
- useEffect7(() => {
11819
+ useEffect8(() => {
11387
11820
  if (!isActive) {
11388
11821
  if (intervalRef.current) {
11389
11822
  clearInterval(intervalRef.current);
@@ -11434,19 +11867,33 @@ var init_route_state = __esm(() => {
11434
11867
  });
11435
11868
 
11436
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
+ }
11437
11882
  function formatTokenCount(tokens) {
11438
11883
  return tokens >= 1000 ? `${Math.round(tokens / 1000)}k` : `${tokens}`;
11439
11884
  }
11440
11885
  function estimateContextUsage(messages, contextWindow) {
11441
- const estimate2 = estimateContextTokens(messages);
11442
- 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));
11443
11890
  const level = percentage >= CRITICAL_PERCENTAGE ? "critical" : percentage >= WARNING_PERCENTAGE ? "warning" : "normal";
11444
11891
  return {
11445
11892
  tokens: estimate2.tokens,
11446
11893
  basis: estimate2.basis,
11447
11894
  percentage,
11448
11895
  level,
11449
- displayText: `${formatTokenCount(estimate2.tokens)}/${formatTokenCount(contextWindow)} ${percentage}%`
11896
+ displayText: `${formatTokenCount(estimate2.tokens)}/${formatTokenCount(effectiveWindow)} ${percentage}%`
11450
11897
  };
11451
11898
  }
11452
11899
  var WARNING_PERCENTAGE = 60, CRITICAL_PERCENTAGE = 85;
@@ -11535,11 +11982,35 @@ var init_file_mentions = __esm(() => {
11535
11982
  };
11536
11983
  });
11537
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
+
11538
12009
  // apps/cli/src/screens/chat-screen.tsx
11539
12010
  import { useKeyboard as useKeyboard4, useRenderer } from "@opentui/react";
11540
- 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";
11541
12012
  import { useLocation, useNavigate, useParams } from "react-router";
11542
- import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
12013
+ import { jsxDEV as jsxDEV22 } from "@opentui/react/jsx-dev-runtime";
11543
12014
  function shouldImplementApprovedPlan(response) {
11544
12015
  if (response.selectedValue === "yes") {
11545
12016
  return true;
@@ -11600,14 +12071,14 @@ function ChatScreen() {
11600
12071
  const skipHistoryLoad = locationState.skipHistoryLoad ?? false;
11601
12072
  const sessionId = parsedRouteParams.success ? parsedRouteParams.data.id : "";
11602
12073
  const isSessionIdValid = parsedRouteParams.success;
11603
- const [mode, setMode] = useState10(locationState.mode ?? defaultCodingAgentMode);
11604
- const [permissionMode, setPermissionMode] = useState10(locationState.permissionMode);
11605
- const [planConfirmationMessageId, setPlanConfirmationMessageId] = useState10(null);
11606
- const [handledPlanMessageIds, setHandledPlanMessageIds] = useState10([]);
11607
- const [queuedImplementationInstruction, setQueuedImplementationInstruction] = useState10(null);
11608
- const [contextState, setContextState] = useState10(null);
11609
- const [actionNotice, setActionNotice] = useState10(null);
11610
- 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);
11611
12082
  const slashRoutes = getSlashMenuItems(slashMenuQuery, {
11612
12083
  includeChatActions: true
11613
12084
  });
@@ -11632,12 +12103,12 @@ function ChatScreen() {
11632
12103
  setSlashMenuSelected,
11633
12104
  slashMenuOpen
11634
12105
  ]);
11635
- useEffect8(() => {
12106
+ useEffect9(() => {
11636
12107
  const nextMode = locationState.mode ?? defaultCodingAgentMode;
11637
12108
  setMode((currentMode) => currentMode === nextMode ? currentMode : nextMode);
11638
12109
  setPermissionMode((currentPermissionMode) => currentPermissionMode === locationState.permissionMode ? currentPermissionMode : locationState.permissionMode);
11639
12110
  }, [locationState.mode, locationState.permissionMode, sessionId]);
11640
- useEffect8(() => {
12111
+ useEffect9(() => {
11641
12112
  setPlanConfirmationMessageId(null);
11642
12113
  setHandledPlanMessageIds([]);
11643
12114
  setQueuedImplementationInstruction(null);
@@ -11741,8 +12212,26 @@ function ChatScreen() {
11741
12212
  });
11742
12213
  const elapsedSeconds = useLoadingTimer(isLoading || isStreaming);
11743
12214
  const contextWindow = useContextWindow();
11744
- const [mentionCandidates, setMentionCandidates] = useState10([]);
11745
- 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(() => {
11746
12235
  let cancelled = false;
11747
12236
  async function loadMentionCandidates() {
11748
12237
  try {
@@ -11773,7 +12262,7 @@ function ChatScreen() {
11773
12262
  } catch {}
11774
12263
  }, [sessionId]);
11775
12264
  const wasStreamingRef = useRef4(false);
11776
- useEffect8(() => {
12265
+ useEffect9(() => {
11777
12266
  if (wasStreamingRef.current && !isStreaming) {
11778
12267
  refreshContextState();
11779
12268
  }
@@ -11783,8 +12272,8 @@ function ChatScreen() {
11783
12272
  setActionNotice({ text, tone });
11784
12273
  }, []);
11785
12274
  const renderer = useRenderer();
11786
- const [copyModeOpen, setCopyModeOpen] = useState10(false);
11787
- const [copyModeIndex, setCopyModeIndex] = useState10(0);
12275
+ const [copyModeOpen, setCopyModeOpen] = useState11(false);
12276
+ const [copyModeIndex, setCopyModeIndex] = useState11(0);
11788
12277
  const copyableMessages = useMemo4(() => messages.filter((message) => (message.role === "user" || message.role === "assistant") && collectMessageText(message).trim().length > 0), [messages]);
11789
12278
  const copySelectedMessage = useCallback4(async (kind) => {
11790
12279
  const message = copyableMessages[copyModeIndex];
@@ -11836,7 +12325,7 @@ function ChatScreen() {
11836
12325
  const setPermissionModeToState = useCallback4((newMode) => {
11837
12326
  setPermissionMode(newMode);
11838
12327
  }, []);
11839
- useEffect8(() => {
12328
+ useEffect9(() => {
11840
12329
  if (!requestedChatActionId) {
11841
12330
  return;
11842
12331
  }
@@ -11915,13 +12404,24 @@ function ChatScreen() {
11915
12404
  const contextMeterColor = contextEstimate.level === "critical" ? cliTheme.semantic.error : contextEstimate.level === "warning" ? cliTheme.semantic.warning : cliTheme.text.muted;
11916
12405
  const anchorVisibleInMessages = contextState !== null && messages.some((message) => message.id === contextState.anchorMessageId);
11917
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
+ })();
11918
12418
  const modelHasToolCallXmlLeak = !isLoading && !isStreaming && lastAssistantMessage2 ? hasToolCallXmlLeak(lastAssistantMessage2) : false;
11919
12419
  const activeUserPrompt = pendingUserPrompts[0] ?? null;
11920
12420
  const pendingApprovalIds = useMemo4(() => new Set(pendingApprovals.map((approval) => approval.toolCallId)), [pendingApprovals]);
11921
12421
  const planConfirmationOpen = mode === "plan" && activeUserPrompt === null && planConfirmationMessageId !== null;
11922
12422
  const hasBlockingPopup = activeUserPrompt !== null || planConfirmationOpen;
11923
12423
  const canTypeInChat = !isSessionIdValid ? false : hasBlockingPopup ? false : pendingApprovals.length > 0 ? true : !isLoading;
11924
- useEffect8(() => {
12424
+ useEffect9(() => {
11925
12425
  if (mode !== "plan") {
11926
12426
  setPlanConfirmationMessageId(null);
11927
12427
  return;
@@ -11947,7 +12447,7 @@ function ChatScreen() {
11947
12447
  isStreaming,
11948
12448
  pendingUserPrompts.length
11949
12449
  ]);
11950
- useEffect8(() => {
12450
+ useEffect9(() => {
11951
12451
  if (mode !== "build" || !queuedImplementationInstruction) {
11952
12452
  return;
11953
12453
  }
@@ -12012,113 +12512,110 @@ function ChatScreen() {
12012
12512
  keyEvent.stopPropagation();
12013
12513
  setMode((currentMode) => cycleCodingAgentMode(currentMode));
12014
12514
  });
12015
- return /* @__PURE__ */ jsxDEV21("box", {
12515
+ return /* @__PURE__ */ jsxDEV22("box", {
12016
12516
  width: "100%",
12017
12517
  height: "100%",
12018
- children: /* @__PURE__ */ jsxDEV21(ChatShell, {
12518
+ children: /* @__PURE__ */ jsxDEV22(ChatShell, {
12019
12519
  hasMessages: messages.length > 0 || isStreaming || pendingApprovals.length > 0 || activeUserPrompt !== null || planConfirmationOpen,
12020
12520
  messageCount: messages.length,
12021
12521
  errorMessage,
12022
- inputArea: /* @__PURE__ */ jsxDEV21(ChatTextArea, {
12522
+ inputArea: /* @__PURE__ */ jsxDEV22(ChatTextArea, {
12023
12523
  placeholder: isLoading ? "Waiting for response..." : "Reply... (@ to attach files)",
12024
12524
  focused: canTypeInChat && !copyModeOpen,
12025
12525
  disabled: !canTypeInChat || copyModeOpen,
12026
12526
  slashMenuOpen,
12027
12527
  mentionCandidates,
12028
12528
  onTextChange: syncSlashMenuFromInput,
12029
- beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV21(SlashPageMenu, {
12529
+ beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV22(SlashPageMenu, {
12030
12530
  query: slashMenuQuery,
12031
12531
  selectedIndex: selectedSlashRouteIndex,
12032
12532
  routes: slashRoutes
12033
12533
  }, undefined, false, undefined, this) : null,
12034
- footer: /* @__PURE__ */ jsxDEV21("box", {
12534
+ footer: /* @__PURE__ */ jsxDEV22("box", {
12035
12535
  flexDirection: "row",
12036
12536
  justifyContent: "space-between",
12037
12537
  children: [
12038
- copyModeOpen ? /* @__PURE__ */ jsxDEV21("text", {
12538
+ copyModeOpen ? /* @__PURE__ */ jsxDEV22("text", {
12039
12539
  fg: cliTheme.text.muted,
12040
12540
  children: "Copy mode: \u2191/\u2193 select \xB7 Enter copy \xB7 c code \xB7 Esc exit"
12041
- }, undefined, false, undefined, this) : canRetryRecoverableResponse ? /* @__PURE__ */ jsxDEV21("text", {
12541
+ }, undefined, false, undefined, this) : canRetryRecoverableResponse ? /* @__PURE__ */ jsxDEV22("text", {
12042
12542
  fg: cliTheme.text.muted,
12043
- children: "Type retry or regenerate to try again"
12044
- }, 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", {
12045
12545
  fg: cliTheme.text.muted,
12046
12546
  children: "Use approval card or approve/deny commands"
12047
- }, undefined, false, undefined, this) : hasBlockingPopup ? /* @__PURE__ */ jsxDEV21("text", {
12547
+ }, undefined, false, undefined, this) : hasBlockingPopup ? /* @__PURE__ */ jsxDEV22("text", {
12048
12548
  fg: cliTheme.text.muted,
12049
12549
  children: "Complete the inline prompt to continue"
12050
- }, undefined, false, undefined, this) : slashMenuOpen ? /* @__PURE__ */ jsxDEV21("text", {
12550
+ }, undefined, false, undefined, this) : slashMenuOpen ? /* @__PURE__ */ jsxDEV22("text", {
12051
12551
  fg: cliTheme.text.muted,
12052
12552
  children: "Choose a page or press Esc"
12053
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV21("text", {
12553
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV22("text", {
12054
12554
  fg: cliTheme.text.muted,
12055
- children: "Tab/Ctrl+T mode | Ctrl+Y copy | Ctrl+P commands"
12555
+ children: "Enter send \xB7 Ctrl+Enter newline"
12056
12556
  }, undefined, false, undefined, this),
12057
- /* @__PURE__ */ jsxDEV21("box", {
12557
+ /* @__PURE__ */ jsxDEV22("box", {
12058
12558
  flexDirection: "row",
12059
12559
  gap: 1,
12060
12560
  alignItems: "center",
12061
12561
  children: [
12062
- /* @__PURE__ */ jsxDEV21("text", {
12562
+ /* @__PURE__ */ jsxDEV22("text", {
12063
12563
  fg: contextMeterColor,
12064
- children: contextEstimate.displayText
12564
+ children: truncateInline2(contextEstimate.displayText, 24)
12065
12565
  }, undefined, false, undefined, this),
12066
- /* @__PURE__ */ jsxDEV21("text", {
12566
+ /* @__PURE__ */ jsxDEV22("text", {
12067
12567
  children: [
12068
- /* @__PURE__ */ jsxDEV21("span", {
12568
+ /* @__PURE__ */ jsxDEV22("span", {
12069
12569
  fg: cliTheme.accent.primary,
12070
12570
  children: modeDefinition.label
12071
12571
  }, undefined, false, undefined, this),
12072
- /* @__PURE__ */ jsxDEV21("span", {
12572
+ /* @__PURE__ */ jsxDEV22("span", {
12073
12573
  fg: cliTheme.text.muted,
12074
12574
  children: " mode"
12075
12575
  }, undefined, false, undefined, this)
12076
12576
  ]
12077
12577
  }, undefined, true, undefined, this),
12078
- /* @__PURE__ */ jsxDEV21("text", {
12079
- children: /* @__PURE__ */ jsxDEV21("span", {
12080
- fg: cliTheme.semantic.warning,
12081
- children: "|"
12578
+ /* @__PURE__ */ jsxDEV22("text", {
12579
+ children: /* @__PURE__ */ jsxDEV22("span", {
12580
+ fg: cliTheme.text.muted,
12581
+ children: "\xB7"
12082
12582
  }, undefined, false, undefined, this)
12083
12583
  }, undefined, false, undefined, this),
12084
- /* @__PURE__ */ jsxDEV21("text", {
12085
- children: /* @__PURE__ */ jsxDEV21("span", {
12086
- fg: permissionMode === "danger-full-access" ? cliTheme.semantic.error : permissionMode === "read-only" ? cliTheme.text.muted : cliTheme.text.primary,
12087
- children: permissionMode ?? "default"
12088
- }, undefined, false, undefined, this)
12584
+ /* @__PURE__ */ jsxDEV22(Badge, {
12585
+ tone: permissionMode === "danger-full-access" ? "error" : permissionMode === "read-only" ? "neutral" : "accent",
12586
+ label: permissionMode ?? "default"
12089
12587
  }, undefined, false, undefined, this)
12090
12588
  ]
12091
12589
  }, undefined, true, undefined, this)
12092
12590
  ]
12093
12591
  }, undefined, true, undefined, this),
12094
- modeToggleHint: true,
12095
12592
  onSubmit: submitChatInput
12096
12593
  }, undefined, false, undefined, this),
12097
12594
  children: [
12098
- contextState && !anchorVisibleInMessages ? /* @__PURE__ */ jsxDEV21(ChatContextStateCard, {
12595
+ contextState && !anchorVisibleInMessages ? /* @__PURE__ */ jsxDEV22(ChatContextStateCard, {
12099
12596
  contextState
12100
12597
  }, undefined, false, undefined, this) : null,
12101
- messages.map((message) => /* @__PURE__ */ jsxDEV21(Fragment, {
12598
+ messages.map((message) => /* @__PURE__ */ jsxDEV22(Fragment, {
12102
12599
  children: [
12103
- isAutoContinueMessage(message) ? null : isContextSummaryMessage(message) ? /* @__PURE__ */ jsxDEV21(ChatContextSummaryCard, {
12600
+ isAutoContinueMessage(message) ? null : isContextSummaryMessage(message) ? /* @__PURE__ */ jsxDEV22(ChatContextSummaryCard, {
12104
12601
  message
12105
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV21(ChatMessage, {
12602
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV22(ChatMessage, {
12106
12603
  message,
12107
12604
  pendingApprovalIds
12108
12605
  }, undefined, false, undefined, this),
12109
- contextState && contextState.anchorMessageId === message.id ? /* @__PURE__ */ jsxDEV21(ChatContextStateCard, {
12606
+ contextState && contextState.anchorMessageId === message.id ? /* @__PURE__ */ jsxDEV22(ChatContextStateCard, {
12110
12607
  contextState
12111
12608
  }, undefined, false, undefined, this) : null
12112
12609
  ]
12113
12610
  }, message.id, true, undefined, this)),
12114
- actionNotice ? /* @__PURE__ */ jsxDEV21("box", {
12611
+ actionNotice ? /* @__PURE__ */ jsxDEV22("box", {
12115
12612
  paddingX: 1,
12116
- children: /* @__PURE__ */ jsxDEV21("text", {
12613
+ children: /* @__PURE__ */ jsxDEV22("text", {
12117
12614
  fg: actionNotice.tone === "error" ? cliTheme.semantic.error : cliTheme.semantic.info,
12118
12615
  children: actionNotice.text
12119
12616
  }, undefined, false, undefined, this)
12120
12617
  }, undefined, false, undefined, this) : null,
12121
- modelHasToolCallXmlLeak ? /* @__PURE__ */ jsxDEV21("box", {
12618
+ modelHasToolCallXmlLeak ? /* @__PURE__ */ jsxDEV22("box", {
12122
12619
  paddingX: 1,
12123
12620
  paddingY: 1,
12124
12621
  borderStyle: "single",
@@ -12126,47 +12623,41 @@ function ChatScreen() {
12126
12623
  flexDirection: "column",
12127
12624
  gap: 1,
12128
12625
  children: [
12129
- /* @__PURE__ */ jsxDEV21("text", {
12626
+ /* @__PURE__ */ jsxDEV22("text", {
12130
12627
  fg: cliTheme.semantic.warning,
12131
12628
  children: "Model compatibility issue: this model output raw tool call XML instead of invoking tools."
12132
12629
  }, undefined, false, undefined, this),
12133
- /* @__PURE__ */ jsxDEV21("text", {
12630
+ /* @__PURE__ */ jsxDEV22("text", {
12134
12631
  fg: cliTheme.text.muted,
12135
12632
  children: "Switch to a model with native function calling (Claude, GPT-4, Llama 3) via /model."
12136
12633
  }, undefined, false, undefined, this)
12137
12634
  ]
12138
- }, undefined, true, undefined, this) : autoContinueState?.guardTripped ? /* @__PURE__ */ jsxDEV21("box", {
12635
+ }, undefined, true, undefined, this) : autoContinueState?.guardTripped ? /* @__PURE__ */ jsxDEV22("box", {
12139
12636
  paddingX: 1,
12140
- children: /* @__PURE__ */ jsxDEV21("text", {
12637
+ children: /* @__PURE__ */ jsxDEV22("text", {
12141
12638
  fg: cliTheme.semantic.warning,
12142
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.`
12143
12640
  }, undefined, false, undefined, this)
12144
- }, undefined, false, undefined, this) : autoContinueState ? /* @__PURE__ */ jsxDEV21("box", {
12641
+ }, undefined, false, undefined, this) : autoContinueState ? /* @__PURE__ */ jsxDEV22("box", {
12145
12642
  paddingX: 1,
12146
- children: /* @__PURE__ */ jsxDEV21("text", {
12643
+ children: /* @__PURE__ */ jsxDEV22("text", {
12147
12644
  fg: cliTheme.semantic.info,
12148
- 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})...`
12149
12646
  }, undefined, false, undefined, this)
12150
12647
  }, undefined, false, undefined, this) : null,
12151
- isLoading || isStreaming ? /* @__PURE__ */ jsxDEV21(LoadingTimer, {
12152
- elapsedSeconds
12153
- }, undefined, false, undefined, this) : null,
12154
- isStreaming ? /* @__PURE__ */ jsxDEV21("box", {
12155
- paddingX: 1,
12156
- children: /* @__PURE__ */ jsxDEV21("text", {
12157
- fg: cliTheme.semantic.info,
12158
- children: "Assistant is thinking..."
12159
- }, undefined, false, undefined, this)
12648
+ isLoading || isStreaming ? /* @__PURE__ */ jsxDEV22(LoadingTimer, {
12649
+ elapsedSeconds,
12650
+ outputTokens: liveOutputTokens
12160
12651
  }, undefined, false, undefined, this) : null,
12161
- pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV21(ChatToolApprovalCard, {
12652
+ pendingApprovals.length > 0 ? /* @__PURE__ */ jsxDEV22(ChatToolApprovalCard, {
12162
12653
  approvals: pendingApprovals,
12163
12654
  onResolve: resolveToolApproval,
12164
12655
  onResolveAll: resolveAllToolApprovals
12165
12656
  }, undefined, false, undefined, this) : null,
12166
- todos.length > 0 ? /* @__PURE__ */ jsxDEV21(ChatTodoStatusCard, {
12657
+ todos.length > 0 ? /* @__PURE__ */ jsxDEV22(ChatTodoStatusCard, {
12167
12658
  todos
12168
12659
  }, undefined, false, undefined, this) : null,
12169
- activeUserPrompt ? /* @__PURE__ */ jsxDEV21(ChatInteractionPopup, {
12660
+ activeUserPrompt ? /* @__PURE__ */ jsxDEV22(ChatInteractionPopup, {
12170
12661
  title: activeUserPrompt.header ?? "Question",
12171
12662
  question: activeUserPrompt.question,
12172
12663
  options: activeUserPrompt.options.map((option) => ({
@@ -12197,7 +12688,7 @@ function ChatScreen() {
12197
12688
  });
12198
12689
  }
12199
12690
  }, undefined, false, undefined, this) : null,
12200
- planConfirmationOpen ? /* @__PURE__ */ jsxDEV21(ChatInteractionPopup, {
12691
+ planConfirmationOpen ? /* @__PURE__ */ jsxDEV22(ChatInteractionPopup, {
12201
12692
  title: "Plan review",
12202
12693
  question: "Implement this plan now?",
12203
12694
  options: [
@@ -12245,12 +12736,12 @@ function ChatScreen() {
12245
12736
  sendDirectMessage(defaultPlanRevisionRequest);
12246
12737
  }
12247
12738
  }, undefined, false, undefined, this) : null,
12248
- permissionSelectorOpen ? /* @__PURE__ */ jsxDEV21(PermissionModeSelector, {
12739
+ permissionSelectorOpen ? /* @__PURE__ */ jsxDEV22(PermissionModeSelector, {
12249
12740
  currentMode: permissionMode,
12250
12741
  onSelect: updatePermissionMode,
12251
12742
  onClose: () => setPermissionSelectorOpen(false)
12252
12743
  }, undefined, false, undefined, this) : null,
12253
- copyModeOpen ? /* @__PURE__ */ jsxDEV21(CopyModeOverlay, {
12744
+ copyModeOpen ? /* @__PURE__ */ jsxDEV22(CopyModeOverlay, {
12254
12745
  messages: copyableMessages,
12255
12746
  selectedIndex: Math.min(copyModeIndex, Math.max(copyableMessages.length - 1, 0))
12256
12747
  }, undefined, false, undefined, this) : null
@@ -12288,6 +12779,8 @@ var init_chat_screen = __esm(() => {
12288
12779
  init_chat_context_utils();
12289
12780
  init_file_mentions();
12290
12781
  init_markdown_code();
12782
+ init_text_utils();
12783
+ init_badge();
12291
12784
  planConfirmationAcceptKeywords = [
12292
12785
  "yes",
12293
12786
  "y",
@@ -12315,12 +12808,12 @@ var init_chat_screen = __esm(() => {
12315
12808
  });
12316
12809
 
12317
12810
  // apps/cli/src/screens/diagnostics-screen.tsx
12318
- import { TextAttributes as TextAttributes16 } from "@opentui/core";
12811
+ import { TextAttributes as TextAttributes17 } from "@opentui/core";
12319
12812
  import { useKeyboard as useKeyboard5 } from "@opentui/react";
12320
- 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";
12321
12814
  import { useLocation as useLocation2 } from "react-router";
12322
12815
  import { z as z35 } from "zod";
12323
- 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";
12324
12817
  function getStatusColor2(status) {
12325
12818
  if (status === "ok") {
12326
12819
  return cliTheme.semantic.success;
@@ -12342,16 +12835,16 @@ function InfoRow({
12342
12835
  tone
12343
12836
  }) {
12344
12837
  const valueColor = tone === "muted" || tone === undefined ? cliTheme.text.secondary : getStatusColor2(tone);
12345
- return /* @__PURE__ */ jsxDEV22("box", {
12838
+ return /* @__PURE__ */ jsxDEV23("box", {
12346
12839
  flexDirection: "row",
12347
12840
  gap: 1,
12348
12841
  children: [
12349
- /* @__PURE__ */ jsxDEV22("text", {
12842
+ /* @__PURE__ */ jsxDEV23("text", {
12350
12843
  fg: cliTheme.text.muted,
12351
12844
  width: 24,
12352
12845
  children: label
12353
12846
  }, undefined, false, undefined, this),
12354
- /* @__PURE__ */ jsxDEV22("text", {
12847
+ /* @__PURE__ */ jsxDEV23("text", {
12355
12848
  fg: valueColor,
12356
12849
  children: formatNullable(value)
12357
12850
  }, undefined, false, undefined, this)
@@ -12362,7 +12855,7 @@ function Section({
12362
12855
  title,
12363
12856
  children
12364
12857
  }) {
12365
- return /* @__PURE__ */ jsxDEV22("box", {
12858
+ return /* @__PURE__ */ jsxDEV23("box", {
12366
12859
  flexDirection: "column",
12367
12860
  gap: 1,
12368
12861
  paddingX: 1,
@@ -12371,9 +12864,9 @@ function Section({
12371
12864
  borderColor: cliTheme.borders.subtle,
12372
12865
  backgroundColor: cliTheme.surfaces.panel,
12373
12866
  children: [
12374
- /* @__PURE__ */ jsxDEV22("text", {
12867
+ /* @__PURE__ */ jsxDEV23("text", {
12375
12868
  fg: cliTheme.accent.primary,
12376
- attributes: TextAttributes16.BOLD,
12869
+ attributes: TextAttributes17.BOLD,
12377
12870
  children: title
12378
12871
  }, undefined, false, undefined, this),
12379
12872
  children
@@ -12384,123 +12877,123 @@ function renderStatus(payload, routeState) {
12384
12877
  const parsedRouteState = diagnosticsRouteStateSchema.safeParse(routeState);
12385
12878
  const currentSession = parsedRouteState.success ? parsedRouteState.data : {};
12386
12879
  const latestSession = payload.sessions.latest;
12387
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
12880
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12388
12881
  children: [
12389
- /* @__PURE__ */ jsxDEV22(Section, {
12882
+ /* @__PURE__ */ jsxDEV23(Section, {
12390
12883
  title: "Current Chat",
12391
12884
  children: [
12392
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12885
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12393
12886
  label: "Session",
12394
12887
  value: currentSession.sessionId ?? "not in chat"
12395
12888
  }, undefined, false, undefined, this),
12396
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12889
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12397
12890
  label: "Mode",
12398
12891
  value: currentSession.mode ?? "not set"
12399
12892
  }, undefined, false, undefined, this),
12400
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12893
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12401
12894
  label: "Permission mode",
12402
12895
  value: currentSession.permissionMode ?? "not set"
12403
12896
  }, undefined, false, undefined, this),
12404
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12897
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12405
12898
  label: "CWD",
12406
12899
  value: currentSession.cwd ?? payload.server.cwd
12407
12900
  }, undefined, false, undefined, this),
12408
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12901
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12409
12902
  label: "Messages",
12410
12903
  value: currentSession.messageCount ?? null
12411
12904
  }, undefined, false, undefined, this),
12412
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12905
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12413
12906
  label: "Pending approvals",
12414
12907
  value: currentSession.pendingApprovalCount ?? 0,
12415
12908
  tone: (currentSession.pendingApprovalCount ?? 0) > 0 ? "warn" : "ok"
12416
12909
  }, undefined, false, undefined, this)
12417
12910
  ]
12418
12911
  }, undefined, true, undefined, this),
12419
- /* @__PURE__ */ jsxDEV22(Section, {
12912
+ /* @__PURE__ */ jsxDEV23(Section, {
12420
12913
  title: "Runtime",
12421
12914
  children: [
12422
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12915
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12423
12916
  label: "Server",
12424
12917
  value: payload.server.status,
12425
12918
  tone: payload.server.status
12426
12919
  }, undefined, false, undefined, this),
12427
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12920
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12428
12921
  label: "Provider",
12429
12922
  value: payload.provider.provider,
12430
12923
  tone: payload.provider.status
12431
12924
  }, undefined, false, undefined, this),
12432
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12925
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12433
12926
  label: "Model",
12434
12927
  value: payload.provider.model
12435
12928
  }, undefined, false, undefined, this),
12436
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12929
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12437
12930
  label: "Database",
12438
12931
  value: payload.database.status,
12439
12932
  tone: payload.database.status
12440
12933
  }, undefined, false, undefined, this),
12441
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12934
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12442
12935
  label: "Tool count",
12443
12936
  value: payload.tools.total
12444
12937
  }, undefined, false, undefined, this),
12445
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12938
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12446
12939
  label: "Schema status",
12447
12940
  value: payload.tools.providerSchemaStatus,
12448
12941
  tone: payload.tools.providerSchemaStatus
12449
12942
  }, undefined, false, undefined, this)
12450
12943
  ]
12451
12944
  }, undefined, true, undefined, this),
12452
- /* @__PURE__ */ jsxDEV22(Section, {
12945
+ /* @__PURE__ */ jsxDEV23(Section, {
12453
12946
  title: "Extensions",
12454
12947
  children: [
12455
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12948
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12456
12949
  label: "Skills",
12457
12950
  value: payload.extensions.skills.count
12458
12951
  }, undefined, false, undefined, this),
12459
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12952
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12460
12953
  label: "MCP servers",
12461
12954
  value: payload.extensions.mcp.configuredServers
12462
12955
  }, undefined, false, undefined, this),
12463
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12956
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12464
12957
  label: "MCP running",
12465
12958
  value: payload.extensions.mcp.runningServers
12466
12959
  }, undefined, false, undefined, this),
12467
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12960
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12468
12961
  label: "MCP degraded",
12469
12962
  value: payload.extensions.mcp.degradedServers,
12470
12963
  tone: payload.extensions.mcp.degradedServers > 0 ? "warn" : "ok"
12471
12964
  }, undefined, false, undefined, this),
12472
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12965
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12473
12966
  label: "Plugins",
12474
12967
  value: payload.extensions.plugins.count
12475
12968
  }, undefined, false, undefined, this),
12476
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12969
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12477
12970
  label: "Plugin hooks",
12478
12971
  value: payload.extensions.plugins.hooks
12479
12972
  }, undefined, false, undefined, this)
12480
12973
  ]
12481
12974
  }, undefined, true, undefined, this),
12482
- /* @__PURE__ */ jsxDEV22(Section, {
12975
+ /* @__PURE__ */ jsxDEV23(Section, {
12483
12976
  title: "Latest Saved Session",
12484
- children: latestSession ? /* @__PURE__ */ jsxDEV22(Fragment2, {
12977
+ children: latestSession ? /* @__PURE__ */ jsxDEV23(Fragment2, {
12485
12978
  children: [
12486
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12979
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12487
12980
  label: "Session",
12488
12981
  value: latestSession.id
12489
12982
  }, undefined, false, undefined, this),
12490
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12983
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12491
12984
  label: "Title",
12492
12985
  value: latestSession.title ?? latestSession.latestUserPromptPreview
12493
12986
  }, undefined, false, undefined, this),
12494
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12987
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12495
12988
  label: "Messages",
12496
12989
  value: latestSession.messageCount
12497
12990
  }, undefined, false, undefined, this),
12498
- /* @__PURE__ */ jsxDEV22(InfoRow, {
12991
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12499
12992
  label: "Updated",
12500
12993
  value: formatDate2(latestSession.updatedAt)
12501
12994
  }, undefined, false, undefined, this)
12502
12995
  ]
12503
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV22("text", {
12996
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV23("text", {
12504
12997
  fg: cliTheme.text.muted,
12505
12998
  children: "No saved sessions yet."
12506
12999
  }, undefined, false, undefined, this)
@@ -12509,36 +13002,36 @@ function renderStatus(payload, routeState) {
12509
13002
  }, undefined, true, undefined, this);
12510
13003
  }
12511
13004
  function renderDoctor(payload) {
12512
- return /* @__PURE__ */ jsxDEV22(Section, {
13005
+ return /* @__PURE__ */ jsxDEV23(Section, {
12513
13006
  title: `Overall: ${payload.status}`,
12514
- children: payload.checks.map((check) => /* @__PURE__ */ jsxDEV22("box", {
13007
+ children: payload.checks.map((check) => /* @__PURE__ */ jsxDEV23("box", {
12515
13008
  flexDirection: "column",
12516
13009
  paddingY: 1,
12517
13010
  children: [
12518
- /* @__PURE__ */ jsxDEV22("box", {
13011
+ /* @__PURE__ */ jsxDEV23("box", {
12519
13012
  flexDirection: "row",
12520
13013
  gap: 1,
12521
13014
  children: [
12522
- /* @__PURE__ */ jsxDEV22("text", {
13015
+ /* @__PURE__ */ jsxDEV23("text", {
12523
13016
  fg: getStatusColor2(check.status),
12524
- attributes: TextAttributes16.BOLD,
13017
+ attributes: TextAttributes17.BOLD,
12525
13018
  width: 8,
12526
13019
  children: check.status.toUpperCase()
12527
13020
  }, undefined, false, undefined, this),
12528
- /* @__PURE__ */ jsxDEV22("text", {
13021
+ /* @__PURE__ */ jsxDEV23("text", {
12529
13022
  fg: cliTheme.text.primary,
12530
- attributes: TextAttributes16.BOLD,
13023
+ attributes: TextAttributes17.BOLD,
12531
13024
  children: check.label
12532
13025
  }, undefined, false, undefined, this)
12533
13026
  ]
12534
13027
  }, undefined, true, undefined, this),
12535
- /* @__PURE__ */ jsxDEV22("text", {
13028
+ /* @__PURE__ */ jsxDEV23("text", {
12536
13029
  fg: cliTheme.text.secondary,
12537
13030
  children: check.summary
12538
13031
  }, undefined, false, undefined, this),
12539
- check.details.map((detail) => /* @__PURE__ */ jsxDEV22("text", {
13032
+ check.details.map((detail) => /* @__PURE__ */ jsxDEV23("text", {
12540
13033
  fg: cliTheme.text.muted,
12541
- attributes: TextAttributes16.DIM,
13034
+ attributes: TextAttributes17.DIM,
12542
13035
  children: detail
12543
13036
  }, detail, false, undefined, this))
12544
13037
  ]
@@ -12548,111 +13041,111 @@ function renderDoctor(payload) {
12548
13041
  function renderPermissions(payload, routeState) {
12549
13042
  const parsedRouteState = diagnosticsRouteStateSchema.safeParse(routeState);
12550
13043
  const currentSession = parsedRouteState.success ? parsedRouteState.data : {};
12551
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
13044
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12552
13045
  children: [
12553
- /* @__PURE__ */ jsxDEV22(Section, {
13046
+ /* @__PURE__ */ jsxDEV23(Section, {
12554
13047
  title: "Current Chat",
12555
13048
  children: [
12556
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13049
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12557
13050
  label: "Mode",
12558
13051
  value: currentSession.mode ?? payload.defaultMode
12559
13052
  }, undefined, false, undefined, this),
12560
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13053
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12561
13054
  label: "Permission mode",
12562
13055
  value: currentSession.permissionMode ?? payload.effectivePermissionMode
12563
13056
  }, undefined, false, undefined, this),
12564
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13057
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12565
13058
  label: "Pending approvals",
12566
13059
  value: currentSession.pendingApprovalCount ?? 0,
12567
13060
  tone: (currentSession.pendingApprovalCount ?? 0) > 0 ? "warn" : "ok"
12568
13061
  }, undefined, false, undefined, this),
12569
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13062
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12570
13063
  label: "Persisted pending",
12571
13064
  value: payload.pendingInteractions.total,
12572
13065
  tone: payload.pendingInteractions.total > 0 ? "warn" : "ok"
12573
13066
  }, undefined, false, undefined, this)
12574
13067
  ]
12575
13068
  }, undefined, true, undefined, this),
12576
- /* @__PURE__ */ jsxDEV22(Section, {
13069
+ /* @__PURE__ */ jsxDEV23(Section, {
12577
13070
  title: "Policy",
12578
13071
  children: [
12579
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13072
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12580
13073
  label: "Default mode",
12581
13074
  value: payload.defaultMode
12582
13075
  }, undefined, false, undefined, this),
12583
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13076
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12584
13077
  label: "Configured permission",
12585
13078
  value: payload.configuredPermissionMode
12586
13079
  }, undefined, false, undefined, this),
12587
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13080
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12588
13081
  label: "Effective permission",
12589
13082
  value: payload.effectivePermissionMode
12590
13083
  }, undefined, false, undefined, this),
12591
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13084
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12592
13085
  label: "Allowed tools",
12593
13086
  value: payload.allowedTools ? payload.allowedTools.join(", ") : "all policy-allowed tools"
12594
13087
  }, undefined, false, undefined, this),
12595
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13088
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12596
13089
  label: "Allow rules",
12597
13090
  value: payload.rules.allow?.length ?? 0
12598
13091
  }, undefined, false, undefined, this),
12599
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13092
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12600
13093
  label: "Ask rules",
12601
13094
  value: payload.rules.ask?.length ?? 0
12602
13095
  }, undefined, false, undefined, this),
12603
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13096
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12604
13097
  label: "Deny rules",
12605
13098
  value: payload.rules.deny?.length ?? 0
12606
13099
  }, undefined, false, undefined, this)
12607
13100
  ]
12608
13101
  }, undefined, true, undefined, this),
12609
- /* @__PURE__ */ jsxDEV22(Section, {
13102
+ /* @__PURE__ */ jsxDEV23(Section, {
12610
13103
  title: "Recovery",
12611
13104
  children: [
12612
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13105
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12613
13106
  label: "Tool approvals",
12614
13107
  value: payload.pendingInteractions.toolApprovals
12615
13108
  }, undefined, false, undefined, this),
12616
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13109
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12617
13110
  label: "User prompts",
12618
13111
  value: payload.pendingInteractions.userPrompts
12619
13112
  }, undefined, false, undefined, this),
12620
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13113
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12621
13114
  label: "Stale pending",
12622
13115
  value: payload.pendingInteractions.stale,
12623
13116
  tone: payload.pendingInteractions.stale > 0 ? "warn" : "ok"
12624
13117
  }, undefined, false, undefined, this),
12625
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13118
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12626
13119
  label: "Recoverable",
12627
13120
  value: payload.pendingInteractions.recoverable
12628
13121
  }, undefined, false, undefined, this)
12629
13122
  ]
12630
13123
  }, undefined, true, undefined, this),
12631
- /* @__PURE__ */ jsxDEV22(Section, {
13124
+ /* @__PURE__ */ jsxDEV23(Section, {
12632
13125
  title: "Sandbox",
12633
13126
  children: [
12634
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13127
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12635
13128
  label: "Enabled",
12636
13129
  value: payload.sandbox.enabled ? "yes" : "no"
12637
13130
  }, undefined, false, undefined, this),
12638
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13131
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12639
13132
  label: "Mode",
12640
13133
  value: payload.sandbox.mode
12641
13134
  }, undefined, false, undefined, this),
12642
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13135
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12643
13136
  label: "Supported",
12644
13137
  value: payload.sandbox.supported ? "yes" : "no",
12645
13138
  tone: payload.sandbox.supported ? "ok" : "warn"
12646
13139
  }, undefined, false, undefined, this),
12647
- payload.sandbox.unsupportedReason ? /* @__PURE__ */ jsxDEV22("text", {
13140
+ payload.sandbox.unsupportedReason ? /* @__PURE__ */ jsxDEV23("text", {
12648
13141
  fg: cliTheme.semantic.warning,
12649
13142
  children: payload.sandbox.unsupportedReason
12650
13143
  }, undefined, false, undefined, this) : null
12651
13144
  ]
12652
13145
  }, undefined, true, undefined, this),
12653
- /* @__PURE__ */ jsxDEV22(Section, {
13146
+ /* @__PURE__ */ jsxDEV23(Section, {
12654
13147
  title: "Notes",
12655
- children: payload.notes.map((note) => /* @__PURE__ */ jsxDEV22("text", {
13148
+ children: payload.notes.map((note) => /* @__PURE__ */ jsxDEV23("text", {
12656
13149
  fg: cliTheme.text.muted,
12657
13150
  children: note
12658
13151
  }, note, false, undefined, this))
@@ -12661,62 +13154,62 @@ function renderPermissions(payload, routeState) {
12661
13154
  }, undefined, true, undefined, this);
12662
13155
  }
12663
13156
  function renderTools(payload) {
12664
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
13157
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12665
13158
  children: [
12666
- /* @__PURE__ */ jsxDEV22(Section, {
13159
+ /* @__PURE__ */ jsxDEV23(Section, {
12667
13160
  title: "Summary",
12668
13161
  children: [
12669
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13162
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12670
13163
  label: "Total",
12671
13164
  value: payload.summary.total
12672
13165
  }, undefined, false, undefined, this),
12673
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13166
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12674
13167
  label: "Read-only",
12675
13168
  value: payload.summary.readOnly
12676
13169
  }, undefined, false, undefined, this),
12677
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13170
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12678
13171
  label: "Workspace-write",
12679
13172
  value: payload.summary.workspaceWrite
12680
13173
  }, undefined, false, undefined, this),
12681
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13174
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12682
13175
  label: "Danger-full-access",
12683
13176
  value: payload.summary.dangerFullAccess
12684
13177
  }, undefined, false, undefined, this),
12685
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13178
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12686
13179
  label: "Schema status",
12687
13180
  value: payload.summary.providerSchemaStatus,
12688
13181
  tone: payload.summary.providerSchemaStatus
12689
13182
  }, undefined, false, undefined, this)
12690
13183
  ]
12691
13184
  }, undefined, true, undefined, this),
12692
- /* @__PURE__ */ jsxDEV22(Section, {
13185
+ /* @__PURE__ */ jsxDEV23(Section, {
12693
13186
  title: "Tool Catalog",
12694
- children: payload.tools.map((tool2) => /* @__PURE__ */ jsxDEV22("box", {
13187
+ children: payload.tools.map((tool2) => /* @__PURE__ */ jsxDEV23("box", {
12695
13188
  flexDirection: "column",
12696
13189
  paddingY: 1,
12697
13190
  children: [
12698
- /* @__PURE__ */ jsxDEV22("box", {
13191
+ /* @__PURE__ */ jsxDEV23("box", {
12699
13192
  flexDirection: "row",
12700
13193
  justifyContent: "space-between",
12701
13194
  children: [
12702
- /* @__PURE__ */ jsxDEV22("text", {
13195
+ /* @__PURE__ */ jsxDEV23("text", {
12703
13196
  fg: cliTheme.text.primary,
12704
- attributes: TextAttributes16.BOLD,
13197
+ attributes: TextAttributes17.BOLD,
12705
13198
  children: tool2.name
12706
13199
  }, undefined, false, undefined, this),
12707
- /* @__PURE__ */ jsxDEV22("text", {
13200
+ /* @__PURE__ */ jsxDEV23("text", {
12708
13201
  fg: getStatusColor2(tool2.providerSchemaStatus),
12709
13202
  children: tool2.permissionMode
12710
13203
  }, undefined, false, undefined, this)
12711
13204
  ]
12712
13205
  }, undefined, true, undefined, this),
12713
- /* @__PURE__ */ jsxDEV22("text", {
13206
+ /* @__PURE__ */ jsxDEV23("text", {
12714
13207
  fg: cliTheme.text.secondary,
12715
13208
  children: tool2.description
12716
13209
  }, undefined, false, undefined, this),
12717
- /* @__PURE__ */ jsxDEV22("text", {
13210
+ /* @__PURE__ */ jsxDEV23("text", {
12718
13211
  fg: cliTheme.text.muted,
12719
- attributes: TextAttributes16.DIM,
13212
+ attributes: TextAttributes17.DIM,
12720
13213
  children: [
12721
13214
  "Modes: ",
12722
13215
  tool2.activeInModes.join(", ") || "none",
@@ -12731,51 +13224,51 @@ function renderTools(payload) {
12731
13224
  }, undefined, true, undefined, this);
12732
13225
  }
12733
13226
  function renderConfig(payload) {
12734
- return /* @__PURE__ */ jsxDEV22(Fragment2, {
13227
+ return /* @__PURE__ */ jsxDEV23(Fragment2, {
12735
13228
  children: [
12736
- /* @__PURE__ */ jsxDEV22(Section, {
13229
+ /* @__PURE__ */ jsxDEV23(Section, {
12737
13230
  title: "Effective Config",
12738
13231
  children: [
12739
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13232
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12740
13233
  label: "Provider",
12741
13234
  value: payload.selectedProvider
12742
13235
  }, undefined, false, undefined, this),
12743
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13236
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12744
13237
  label: "Configured model",
12745
13238
  value: payload.configuredModel
12746
13239
  }, undefined, false, undefined, this),
12747
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13240
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12748
13241
  label: "Selected model",
12749
13242
  value: payload.selectedModel
12750
13243
  }, undefined, false, undefined, this),
12751
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13244
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12752
13245
  label: "Base URL",
12753
13246
  value: payload.baseUrl ? "configured" : "default"
12754
13247
  }, undefined, false, undefined, this),
12755
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13248
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12756
13249
  label: "Default mode",
12757
13250
  value: payload.defaultMode
12758
13251
  }, undefined, false, undefined, this),
12759
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13252
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12760
13253
  label: "Permission mode",
12761
13254
  value: payload.permissionMode
12762
13255
  }, undefined, false, undefined, this),
12763
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13256
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12764
13257
  label: "Max output tokens",
12765
13258
  value: payload.maxOutputTokens
12766
13259
  }, undefined, false, undefined, this),
12767
- /* @__PURE__ */ jsxDEV22(InfoRow, {
13260
+ /* @__PURE__ */ jsxDEV23(InfoRow, {
12768
13261
  label: "Max steps",
12769
13262
  value: payload.maxSteps
12770
13263
  }, undefined, false, undefined, this)
12771
13264
  ]
12772
13265
  }, undefined, true, undefined, this),
12773
- /* @__PURE__ */ jsxDEV22(Section, {
13266
+ /* @__PURE__ */ jsxDEV23(Section, {
12774
13267
  title: "Loaded Files",
12775
- children: payload.loadedFiles.map((file) => /* @__PURE__ */ jsxDEV22("box", {
13268
+ children: payload.loadedFiles.map((file) => /* @__PURE__ */ jsxDEV23("box", {
12776
13269
  flexDirection: "column",
12777
13270
  children: [
12778
- /* @__PURE__ */ jsxDEV22("text", {
13271
+ /* @__PURE__ */ jsxDEV23("text", {
12779
13272
  fg: file.loaded ? cliTheme.semantic.success : cliTheme.text.muted,
12780
13273
  children: [
12781
13274
  file.scope,
@@ -12783,17 +13276,17 @@ function renderConfig(payload) {
12783
13276
  file.loaded ? "loaded" : file.exists ? "not loaded" : "missing"
12784
13277
  ]
12785
13278
  }, undefined, true, undefined, this),
12786
- /* @__PURE__ */ jsxDEV22("text", {
13279
+ /* @__PURE__ */ jsxDEV23("text", {
12787
13280
  fg: cliTheme.text.muted,
12788
- attributes: TextAttributes16.DIM,
13281
+ attributes: TextAttributes17.DIM,
12789
13282
  children: file.path
12790
13283
  }, undefined, false, undefined, this)
12791
13284
  ]
12792
13285
  }, `${file.scope}:${file.path}`, true, undefined, this))
12793
13286
  }, undefined, false, undefined, this),
12794
- payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV22(Section, {
13287
+ payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV23(Section, {
12795
13288
  title: "Credential Hints",
12796
- children: payload.missingCredentialHints.map((hint) => /* @__PURE__ */ jsxDEV22("text", {
13289
+ children: payload.missingCredentialHints.map((hint) => /* @__PURE__ */ jsxDEV23("text", {
12797
13290
  fg: cliTheme.semantic.warning,
12798
13291
  children: hint
12799
13292
  }, hint, false, undefined, this))
@@ -12803,9 +13296,9 @@ function renderConfig(payload) {
12803
13296
  }
12804
13297
  function DiagnosticsScreen({ kind }) {
12805
13298
  const location = useLocation2();
12806
- const [payload, setPayload] = useState11(null);
12807
- const [isLoading, setIsLoading] = useState11(true);
12808
- const [errorMessage, setErrorMessage] = useState11(null);
13299
+ const [payload, setPayload] = useState12(null);
13300
+ const [isLoading, setIsLoading] = useState12(true);
13301
+ const [errorMessage, setErrorMessage] = useState12(null);
12809
13302
  const title = pageTitles[kind];
12810
13303
  const loadDiagnostics = useCallback5(async () => {
12811
13304
  setIsLoading(true);
@@ -12838,7 +13331,7 @@ function DiagnosticsScreen({ kind }) {
12838
13331
  setIsLoading(false);
12839
13332
  }
12840
13333
  }, [kind, title]);
12841
- useEffect9(() => {
13334
+ useEffect10(() => {
12842
13335
  loadDiagnostics();
12843
13336
  }, [loadDiagnostics]);
12844
13337
  useKeyboard5((keyEvent) => {
@@ -12867,36 +13360,36 @@ function DiagnosticsScreen({ kind }) {
12867
13360
  }
12868
13361
  return renderConfig(payload);
12869
13362
  }, [kind, location.state, payload]);
12870
- return /* @__PURE__ */ jsxDEV22("box", {
13363
+ return /* @__PURE__ */ jsxDEV23("box", {
12871
13364
  width: "100%",
12872
13365
  height: "100%",
12873
13366
  flexDirection: "column",
12874
13367
  gap: 1,
12875
13368
  children: [
12876
- /* @__PURE__ */ jsxDEV22("box", {
13369
+ /* @__PURE__ */ jsxDEV23("box", {
12877
13370
  flexDirection: "row",
12878
13371
  justifyContent: "space-between",
12879
13372
  paddingX: 1,
12880
13373
  children: [
12881
- /* @__PURE__ */ jsxDEV22("text", {
13374
+ /* @__PURE__ */ jsxDEV23("text", {
12882
13375
  fg: cliTheme.text.primary,
12883
- attributes: TextAttributes16.BOLD,
13376
+ attributes: TextAttributes17.BOLD,
12884
13377
  children: title
12885
13378
  }, undefined, false, undefined, this),
12886
- /* @__PURE__ */ jsxDEV22("text", {
13379
+ /* @__PURE__ */ jsxDEV23("text", {
12887
13380
  fg: cliTheme.text.muted,
12888
13381
  children: isLoading ? "loading..." : "press r to refresh"
12889
13382
  }, undefined, false, undefined, this)
12890
13383
  ]
12891
13384
  }, undefined, true, undefined, this),
12892
- errorMessage ? /* @__PURE__ */ jsxDEV22("box", {
13385
+ errorMessage ? /* @__PURE__ */ jsxDEV23("box", {
12893
13386
  paddingX: 1,
12894
- children: /* @__PURE__ */ jsxDEV22("text", {
13387
+ children: /* @__PURE__ */ jsxDEV23("text", {
12895
13388
  fg: cliTheme.semantic.error,
12896
13389
  children: errorMessage
12897
13390
  }, undefined, false, undefined, this)
12898
13391
  }, undefined, false, undefined, this) : null,
12899
- /* @__PURE__ */ jsxDEV22("scrollbox", {
13392
+ /* @__PURE__ */ jsxDEV23("scrollbox", {
12900
13393
  flexGrow: 1,
12901
13394
  flexDirection: "column",
12902
13395
  gap: 1,
@@ -12929,17 +13422,24 @@ var init_diagnostics_screen = __esm(() => {
12929
13422
  });
12930
13423
 
12931
13424
  // apps/cli/src/components/home-ascii-art.tsx
12932
- import { jsxDEV as jsxDEV23 } from "@opentui/react/jsx-dev-runtime";
13425
+ import { jsxDEV as jsxDEV24 } from "@opentui/react/jsx-dev-runtime";
12933
13426
  function HomeAsciiArt() {
12934
- return /* @__PURE__ */ jsxDEV23("box", {
13427
+ return /* @__PURE__ */ jsxDEV24("box", {
12935
13428
  flexDirection: "column",
12936
13429
  alignItems: "center",
12937
- children: /* @__PURE__ */ jsxDEV23("ascii-font", {
12938
- text: "lightcode",
12939
- font: "block",
12940
- color: logoColors
12941
- }, undefined, false, undefined, this)
12942
- }, 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);
12943
13443
  }
12944
13444
  var logoColors;
12945
13445
  var init_home_ascii_art = __esm(() => {
@@ -12950,9 +13450,9 @@ var init_home_ascii_art = __esm(() => {
12950
13450
 
12951
13451
  // apps/cli/src/components/home-text-area.tsx
12952
13452
  import { useKeyboard as useKeyboard6 } from "@opentui/react";
12953
- import { useState as useState12 } from "react";
13453
+ import { useState as useState13 } from "react";
12954
13454
  import { useNavigate as useNavigate2 } from "react-router";
12955
- import { jsxDEV as jsxDEV24 } from "@opentui/react/jsx-dev-runtime";
13455
+ import { jsxDEV as jsxDEV25 } from "@opentui/react/jsx-dev-runtime";
12956
13456
  function HomeTextArea() {
12957
13457
  const navigate = useNavigate2();
12958
13458
  const {
@@ -12964,9 +13464,9 @@ function HomeTextArea() {
12964
13464
  openSlashMenu,
12965
13465
  closeSlashMenu
12966
13466
  } = useAppState();
12967
- const [sessionCreateError, setSessionCreateError] = useState12(null);
12968
- const [isCreatingSession, setIsCreatingSession] = useState12(false);
12969
- const [mode, setMode] = useState12(defaultCodingAgentMode);
13467
+ const [sessionCreateError, setSessionCreateError] = useState13(null);
13468
+ const [isCreatingSession, setIsCreatingSession] = useState13(false);
13469
+ const [mode, setMode] = useState13(defaultCodingAgentMode);
12970
13470
  const slashRoutes = getSlashPageRoutes(slashMenuQuery);
12971
13471
  const selectedIndex = Math.min(slashMenuSelected, Math.max(slashRoutes.length - 1, 0));
12972
13472
  const modeDefinition = codingAgentModes[mode];
@@ -13030,24 +13530,23 @@ function HomeTextArea() {
13030
13530
  setIsCreatingSession(false);
13031
13531
  }
13032
13532
  }
13033
- return /* @__PURE__ */ jsxDEV24("box", {
13533
+ return /* @__PURE__ */ jsxDEV25("box", {
13034
13534
  width: "66%",
13035
13535
  maxWidth: 104,
13036
13536
  minWidth: 64,
13037
13537
  flexDirection: "column",
13038
13538
  gap: 1,
13039
13539
  children: [
13040
- /* @__PURE__ */ jsxDEV24(ChatTextArea, {
13540
+ /* @__PURE__ */ jsxDEV25(ChatTextArea, {
13041
13541
  containerHeight: 7,
13042
13542
  allowEmpty: true,
13043
13543
  trimOnSubmit: false,
13044
13544
  placeholder: 'Ask anything... "What is the tech stack of this project?"',
13045
13545
  focused: !isCreatingSession,
13046
13546
  disabled: isCreatingSession,
13047
- modeToggleHint: true,
13048
13547
  slashMenuOpen,
13049
13548
  onTextChange: syncSlashMenuFromInput,
13050
- beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV24(SlashPageMenu, {
13549
+ beforeInput: slashMenuOpen ? /* @__PURE__ */ jsxDEV25(SlashPageMenu, {
13051
13550
  query: slashMenuQuery,
13052
13551
  selectedIndex,
13053
13552
  routes: slashRoutes
@@ -13055,21 +13554,21 @@ function HomeTextArea() {
13055
13554
  onSubmit: (text) => {
13056
13555
  createSessionAndNavigate(text);
13057
13556
  },
13058
- footer: /* @__PURE__ */ jsxDEV24("box", {
13557
+ footer: /* @__PURE__ */ jsxDEV25("box", {
13059
13558
  flexDirection: "row",
13060
13559
  justifyContent: "space-between",
13061
13560
  children: [
13062
- /* @__PURE__ */ jsxDEV24("text", {
13561
+ /* @__PURE__ */ jsxDEV25("text", {
13063
13562
  fg: cliTheme.text.muted,
13064
13563
  children: isCreatingSession ? "Creating session..." : "Tab/Ctrl+T switch mode | Ctrl+P commands"
13065
13564
  }, undefined, false, undefined, this),
13066
- /* @__PURE__ */ jsxDEV24("text", {
13565
+ /* @__PURE__ */ jsxDEV25("text", {
13067
13566
  children: [
13068
- /* @__PURE__ */ jsxDEV24("span", {
13567
+ /* @__PURE__ */ jsxDEV25("span", {
13069
13568
  fg: cliTheme.accent.primary,
13070
13569
  children: modeDefinition.label
13071
13570
  }, undefined, false, undefined, this),
13072
- /* @__PURE__ */ jsxDEV24("span", {
13571
+ /* @__PURE__ */ jsxDEV25("span", {
13073
13572
  fg: cliTheme.text.muted,
13074
13573
  children: " mode"
13075
13574
  }, undefined, false, undefined, this)
@@ -13078,9 +13577,9 @@ function HomeTextArea() {
13078
13577
  ]
13079
13578
  }, undefined, true, undefined, this)
13080
13579
  }, undefined, false, undefined, this),
13081
- sessionCreateError ? /* @__PURE__ */ jsxDEV24("box", {
13580
+ sessionCreateError ? /* @__PURE__ */ jsxDEV25("box", {
13082
13581
  paddingX: 1,
13083
- children: /* @__PURE__ */ jsxDEV24("text", {
13582
+ children: /* @__PURE__ */ jsxDEV25("text", {
13084
13583
  fg: cliTheme.semantic.error,
13085
13584
  children: sessionCreateError
13086
13585
  }, undefined, false, undefined, this)
@@ -13100,11 +13599,11 @@ var init_home_text_area = __esm(() => {
13100
13599
  });
13101
13600
 
13102
13601
  // apps/cli/src/components/server-status.tsx
13103
- import { useEffect as useEffect10, useState as useState13 } from "react";
13104
- 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";
13105
13604
  function ServerStatus() {
13106
- const [status, setStatus] = useState13("checking");
13107
- useEffect10(() => {
13605
+ const [status, setStatus] = useState14("checking");
13606
+ useEffect11(() => {
13108
13607
  let cancelled = false;
13109
13608
  async function checkServer2() {
13110
13609
  try {
@@ -13123,7 +13622,7 @@ function ServerStatus() {
13123
13622
  cancelled = true;
13124
13623
  };
13125
13624
  }, []);
13126
- return /* @__PURE__ */ jsxDEV25("text", {
13625
+ return /* @__PURE__ */ jsxDEV26("text", {
13127
13626
  fg: serverStatusColors[status],
13128
13627
  children: [
13129
13628
  "Server: ",
@@ -13137,9 +13636,9 @@ var init_server_status = __esm(() => {
13137
13636
  });
13138
13637
 
13139
13638
  // apps/cli/src/screens/home-screen.tsx
13140
- import { jsxDEV as jsxDEV26 } from "@opentui/react/jsx-dev-runtime";
13639
+ import { jsxDEV as jsxDEV27 } from "@opentui/react/jsx-dev-runtime";
13141
13640
  function HomeScreen() {
13142
- return /* @__PURE__ */ jsxDEV26("box", {
13641
+ return /* @__PURE__ */ jsxDEV27("box", {
13143
13642
  width: "100%",
13144
13643
  height: "100%",
13145
13644
  flexDirection: "column",
@@ -13150,9 +13649,9 @@ function HomeScreen() {
13150
13649
  paddingY: 1,
13151
13650
  backgroundColor: cliTheme.surfaces.base,
13152
13651
  children: [
13153
- /* @__PURE__ */ jsxDEV26(HomeAsciiArt, {}, undefined, false, undefined, this),
13154
- /* @__PURE__ */ jsxDEV26(ServerStatus, {}, undefined, false, undefined, this),
13155
- /* @__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)
13156
13655
  ]
13157
13656
  }, undefined, true, undefined, this);
13158
13657
  }
@@ -13164,14 +13663,14 @@ var init_home_screen = __esm(() => {
13164
13663
  });
13165
13664
 
13166
13665
  // apps/cli/src/screens/model-screen.tsx
13167
- import { TextAttributes as TextAttributes17 } from "@opentui/core";
13666
+ import { TextAttributes as TextAttributes18 } from "@opentui/core";
13168
13667
  import { useKeyboard as useKeyboard7 } from "@opentui/react";
13169
- import { useCallback as useCallback6, useEffect as useEffect11, useState as useState14 } from "react";
13170
- 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";
13171
13670
  function ModelScreen() {
13172
- const [isLoading, setIsLoading] = useState14(true);
13173
- const [payload, setPayload] = useState14(null);
13174
- const [errorMessage, setErrorMessage] = useState14(null);
13671
+ const [isLoading, setIsLoading] = useState15(true);
13672
+ const [payload, setPayload] = useState15(null);
13673
+ const [errorMessage, setErrorMessage] = useState15(null);
13175
13674
  const loadModelConfig = useCallback6(async () => {
13176
13675
  setIsLoading(true);
13177
13676
  try {
@@ -13188,7 +13687,7 @@ function ModelScreen() {
13188
13687
  setIsLoading(false);
13189
13688
  }
13190
13689
  }, []);
13191
- useEffect11(() => {
13690
+ useEffect12(() => {
13192
13691
  loadModelConfig();
13193
13692
  }, [loadModelConfig]);
13194
13693
  useKeyboard7((keyEvent) => {
@@ -13199,42 +13698,42 @@ function ModelScreen() {
13199
13698
  keyEvent.stopPropagation();
13200
13699
  loadModelConfig();
13201
13700
  });
13202
- return /* @__PURE__ */ jsxDEV27("box", {
13701
+ return /* @__PURE__ */ jsxDEV28("box", {
13203
13702
  width: "100%",
13204
13703
  height: "100%",
13205
13704
  flexDirection: "column",
13206
13705
  gap: 1,
13207
13706
  paddingX: 1,
13208
13707
  children: [
13209
- /* @__PURE__ */ jsxDEV27("box", {
13708
+ /* @__PURE__ */ jsxDEV28("box", {
13210
13709
  flexDirection: "row",
13211
13710
  justifyContent: "space-between",
13212
13711
  children: [
13213
- /* @__PURE__ */ jsxDEV27("text", {
13712
+ /* @__PURE__ */ jsxDEV28("text", {
13214
13713
  fg: cliTheme.text.primary,
13215
- attributes: TextAttributes17.BOLD,
13714
+ attributes: TextAttributes18.BOLD,
13216
13715
  children: "Model"
13217
13716
  }, undefined, false, undefined, this),
13218
- /* @__PURE__ */ jsxDEV27("text", {
13717
+ /* @__PURE__ */ jsxDEV28("text", {
13219
13718
  fg: cliTheme.text.muted,
13220
13719
  children: isLoading ? "loading..." : "press r to refresh"
13221
13720
  }, undefined, false, undefined, this)
13222
13721
  ]
13223
13722
  }, undefined, true, undefined, this),
13224
- errorMessage ? /* @__PURE__ */ jsxDEV27("box", {
13225
- children: /* @__PURE__ */ jsxDEV27("text", {
13723
+ errorMessage ? /* @__PURE__ */ jsxDEV28("box", {
13724
+ children: /* @__PURE__ */ jsxDEV28("text", {
13226
13725
  fg: cliTheme.semantic.error,
13227
13726
  children: errorMessage
13228
13727
  }, undefined, false, undefined, this)
13229
- }, undefined, false, undefined, this) : payload ? /* @__PURE__ */ jsxDEV27("scrollbox", {
13728
+ }, undefined, false, undefined, this) : payload ? /* @__PURE__ */ jsxDEV28("scrollbox", {
13230
13729
  flexGrow: 1,
13231
13730
  flexDirection: "column",
13232
13731
  gap: 1,
13233
13732
  children: [
13234
- /* @__PURE__ */ jsxDEV27(ModelInfo, {
13733
+ /* @__PURE__ */ jsxDEV28(ModelInfo, {
13235
13734
  payload
13236
13735
  }, undefined, false, undefined, this),
13237
- payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV27(CredentialHints, {
13736
+ payload.missingCredentialHints.length > 0 ? /* @__PURE__ */ jsxDEV28(CredentialHints, {
13238
13737
  hints: payload.missingCredentialHints
13239
13738
  }, undefined, false, undefined, this) : null
13240
13739
  ]
@@ -13244,7 +13743,7 @@ function ModelScreen() {
13244
13743
  }
13245
13744
  function ModelInfo({ payload }) {
13246
13745
  const baseUrlStatus = payload.baseUrl ? "configured" : "default";
13247
- return /* @__PURE__ */ jsxDEV27("box", {
13746
+ return /* @__PURE__ */ jsxDEV28("box", {
13248
13747
  flexDirection: "column",
13249
13748
  gap: 2,
13250
13749
  paddingY: 1,
@@ -13252,19 +13751,19 @@ function ModelInfo({ payload }) {
13252
13751
  borderColor: cliTheme.borders.subtle,
13253
13752
  backgroundColor: cliTheme.surfaces.panel,
13254
13753
  children: [
13255
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13754
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13256
13755
  label: "Provider",
13257
13756
  value: payload.selectedProvider ?? "not set"
13258
13757
  }, undefined, false, undefined, this),
13259
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13758
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13260
13759
  label: "Model",
13261
13760
  value: payload.selectedModel ?? "not set"
13262
13761
  }, undefined, false, undefined, this),
13263
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13762
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13264
13763
  label: "Base URL",
13265
13764
  value: `${baseUrlStatus}${payload.baseUrl ? ` (${payload.baseUrl})` : ""}`
13266
13765
  }, undefined, false, undefined, this),
13267
- /* @__PURE__ */ jsxDEV27(InfoRow2, {
13766
+ /* @__PURE__ */ jsxDEV28(InfoRow2, {
13268
13767
  label: "Configured model",
13269
13768
  value: payload.configuredModel ?? "not set"
13270
13769
  }, undefined, false, undefined, this)
@@ -13272,16 +13771,16 @@ function ModelInfo({ payload }) {
13272
13771
  }, undefined, true, undefined, this);
13273
13772
  }
13274
13773
  function InfoRow2({ label, value }) {
13275
- return /* @__PURE__ */ jsxDEV27("box", {
13774
+ return /* @__PURE__ */ jsxDEV28("box", {
13276
13775
  flexDirection: "row",
13277
13776
  gap: 2,
13278
13777
  children: [
13279
- /* @__PURE__ */ jsxDEV27("text", {
13778
+ /* @__PURE__ */ jsxDEV28("text", {
13280
13779
  fg: cliTheme.text.muted,
13281
13780
  width: 18,
13282
13781
  children: label
13283
13782
  }, undefined, false, undefined, this),
13284
- /* @__PURE__ */ jsxDEV27("text", {
13783
+ /* @__PURE__ */ jsxDEV28("text", {
13285
13784
  fg: cliTheme.text.primary,
13286
13785
  children: value
13287
13786
  }, undefined, false, undefined, this)
@@ -13289,7 +13788,7 @@ function InfoRow2({ label, value }) {
13289
13788
  }, undefined, true, undefined, this);
13290
13789
  }
13291
13790
  function CredentialHints({ hints }) {
13292
- return /* @__PURE__ */ jsxDEV27("box", {
13791
+ return /* @__PURE__ */ jsxDEV28("box", {
13293
13792
  flexDirection: "column",
13294
13793
  gap: 1,
13295
13794
  paddingY: 1,
@@ -13297,12 +13796,12 @@ function CredentialHints({ hints }) {
13297
13796
  borderColor: cliTheme.borders.subtle,
13298
13797
  backgroundColor: cliTheme.surfaces.panel,
13299
13798
  children: [
13300
- /* @__PURE__ */ jsxDEV27("text", {
13799
+ /* @__PURE__ */ jsxDEV28("text", {
13301
13800
  fg: cliTheme.semantic.warning,
13302
- attributes: TextAttributes17.BOLD,
13801
+ attributes: TextAttributes18.BOLD,
13303
13802
  children: "Missing Credentials"
13304
13803
  }, undefined, false, undefined, this),
13305
- hints.map((hint, index) => /* @__PURE__ */ jsxDEV27("text", {
13804
+ hints.map((hint, index) => /* @__PURE__ */ jsxDEV28("text", {
13306
13805
  fg: cliTheme.text.secondary,
13307
13806
  children: [
13308
13807
  "- ",
@@ -13320,10 +13819,10 @@ var init_model_screen = __esm(() => {
13320
13819
  });
13321
13820
 
13322
13821
  // apps/cli/src/components/chat/model-selector.tsx
13323
- import { TextAttributes as TextAttributes18 } from "@opentui/core";
13822
+ import { TextAttributes as TextAttributes19 } from "@opentui/core";
13324
13823
  import { useKeyboard as useKeyboard8 } from "@opentui/react";
13325
- import { useEffect as useEffect12, useMemo as useMemo6, useState as useState15 } from "react";
13326
- 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";
13327
13826
  function formatContextLength(contextLength) {
13328
13827
  if (!contextLength) {
13329
13828
  return "ctx ?";
@@ -13331,14 +13830,14 @@ function formatContextLength(contextLength) {
13331
13830
  return `ctx ${Math.round(contextLength / 1000)}k`;
13332
13831
  }
13333
13832
  function ModelSelector({ onClose, notify }) {
13334
- const [models, setModels] = useState15([]);
13335
- const [currentModel, setCurrentModel] = useState15(null);
13336
- const [loadError, setLoadError] = useState15(null);
13337
- const [isLoading, setIsLoading] = useState15(true);
13338
- const [isApplying, setIsApplying] = useState15(false);
13339
- const [filter, setFilter] = useState15("");
13340
- const [selectedIndex, setSelectedIndex] = useState15(0);
13341
- 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(() => {
13342
13841
  let cancelled = false;
13343
13842
  async function loadModels() {
13344
13843
  try {
@@ -13380,7 +13879,7 @@ function ModelSelector({ onClose, notify }) {
13380
13879
  }
13381
13880
  return models.filter((model) => model.id.toLowerCase().includes(normalized) || model.name.toLowerCase().includes(normalized));
13382
13881
  }, [filter, models]);
13383
- useEffect12(() => {
13882
+ useEffect13(() => {
13384
13883
  setSelectedIndex((current) => Math.min(current, Math.max(filteredModels.length - 1, 0)));
13385
13884
  }, [filteredModels.length]);
13386
13885
  const applyModel = async (model) => {
@@ -13447,14 +13946,14 @@ function ModelSelector({ onClose, notify }) {
13447
13946
  });
13448
13947
  const windowStart = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisibleRows / 2), filteredModels.length - maxVisibleRows));
13449
13948
  const visibleModels = filteredModels.slice(windowStart, windowStart + maxVisibleRows);
13450
- return /* @__PURE__ */ jsxDEV28("box", {
13949
+ return /* @__PURE__ */ jsxDEV29("box", {
13451
13950
  width: "100%",
13452
13951
  flexDirection: "column",
13453
- borderStyle: "single",
13952
+ borderStyle: borderStyleFor.modal,
13454
13953
  borderColor: cliTheme.overlay.border,
13455
13954
  backgroundColor: cliTheme.overlay.surface,
13456
13955
  children: [
13457
- /* @__PURE__ */ jsxDEV28("box", {
13956
+ /* @__PURE__ */ jsxDEV29("box", {
13458
13957
  width: "100%",
13459
13958
  flexDirection: "row",
13460
13959
  justifyContent: "space-between",
@@ -13464,48 +13963,48 @@ function ModelSelector({ onClose, notify }) {
13464
13963
  border: ["bottom"],
13465
13964
  borderColor: cliTheme.overlay.border,
13466
13965
  children: [
13467
- /* @__PURE__ */ jsxDEV28("text", {
13966
+ /* @__PURE__ */ jsxDEV29("text", {
13468
13967
  fg: cliTheme.accent.primary,
13469
- attributes: TextAttributes18.BOLD,
13968
+ attributes: TextAttributes19.BOLD,
13470
13969
  children: "Switch Model (OpenRouter)"
13471
13970
  }, undefined, false, undefined, this),
13472
- /* @__PURE__ */ jsxDEV28("text", {
13971
+ /* @__PURE__ */ jsxDEV29("text", {
13473
13972
  fg: cliTheme.overlay.footerText,
13474
13973
  children: "type to filter | \u2191/\u2193 select | Enter confirm | Esc cancel"
13475
13974
  }, undefined, false, undefined, this)
13476
13975
  ]
13477
13976
  }, undefined, true, undefined, this),
13478
- /* @__PURE__ */ jsxDEV28("box", {
13977
+ /* @__PURE__ */ jsxDEV29("box", {
13479
13978
  width: "100%",
13480
13979
  paddingX: 2,
13481
13980
  paddingY: 1,
13482
- children: /* @__PURE__ */ jsxDEV28("text", {
13981
+ children: /* @__PURE__ */ jsxDEV29("text", {
13483
13982
  fg: cliTheme.text.primary,
13484
13983
  children: `Filter: ${filter}\u2588`
13485
13984
  }, undefined, false, undefined, this)
13486
13985
  }, undefined, false, undefined, this),
13487
- isLoading ? /* @__PURE__ */ jsxDEV28("box", {
13986
+ isLoading ? /* @__PURE__ */ jsxDEV29("box", {
13488
13987
  paddingX: 2,
13489
13988
  paddingY: 1,
13490
- children: /* @__PURE__ */ jsxDEV28("text", {
13989
+ children: /* @__PURE__ */ jsxDEV29("text", {
13491
13990
  fg: cliTheme.text.muted,
13492
13991
  children: "Loading models..."
13493
13992
  }, undefined, false, undefined, this)
13494
- }, undefined, false, undefined, this) : loadError ? /* @__PURE__ */ jsxDEV28("box", {
13993
+ }, undefined, false, undefined, this) : loadError ? /* @__PURE__ */ jsxDEV29("box", {
13495
13994
  paddingX: 2,
13496
13995
  paddingY: 1,
13497
- children: /* @__PURE__ */ jsxDEV28("text", {
13996
+ children: /* @__PURE__ */ jsxDEV29("text", {
13498
13997
  fg: cliTheme.semantic.error,
13499
13998
  children: loadError
13500
13999
  }, undefined, false, undefined, this)
13501
- }, undefined, false, undefined, this) : filteredModels.length === 0 ? /* @__PURE__ */ jsxDEV28("box", {
14000
+ }, undefined, false, undefined, this) : filteredModels.length === 0 ? /* @__PURE__ */ jsxDEV29("box", {
13502
14001
  paddingX: 2,
13503
14002
  paddingY: 1,
13504
- children: /* @__PURE__ */ jsxDEV28("text", {
14003
+ children: /* @__PURE__ */ jsxDEV29("text", {
13505
14004
  fg: cliTheme.text.muted,
13506
14005
  children: `No models match "${filter}".`
13507
14006
  }, undefined, false, undefined, this)
13508
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV28("box", {
14007
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV29("box", {
13509
14008
  flexDirection: "column",
13510
14009
  paddingY: 1,
13511
14010
  children: [
@@ -13513,29 +14012,29 @@ function ModelSelector({ onClose, notify }) {
13513
14012
  const index = windowStart + visibleIndex;
13514
14013
  const isSelected = index === selectedIndex;
13515
14014
  const rowColors = getOverlayRowColors(isSelected);
13516
- return /* @__PURE__ */ jsxDEV28("box", {
14015
+ return /* @__PURE__ */ jsxDEV29("box", {
13517
14016
  width: "100%",
13518
14017
  flexDirection: "row",
13519
14018
  paddingX: 2,
13520
14019
  backgroundColor: rowColors.backgroundColor,
13521
14020
  children: [
13522
- /* @__PURE__ */ jsxDEV28("text", {
14021
+ /* @__PURE__ */ jsxDEV29("text", {
13523
14022
  width: 2,
13524
14023
  fg: isSelected ? cliTheme.accent.primary : cliTheme.text.muted,
13525
- attributes: TextAttributes18.BOLD,
14024
+ attributes: TextAttributes19.BOLD,
13526
14025
  children: isSelected ? ">" : " "
13527
14026
  }, undefined, false, undefined, this),
13528
- /* @__PURE__ */ jsxDEV28("text", {
14027
+ /* @__PURE__ */ jsxDEV29("text", {
13529
14028
  fg: isSelected ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
13530
- attributes: isSelected ? TextAttributes18.BOLD : TextAttributes18.NONE,
14029
+ attributes: isSelected ? TextAttributes19.BOLD : TextAttributes19.NONE,
13531
14030
  children: model.id
13532
14031
  }, undefined, false, undefined, this),
13533
- /* @__PURE__ */ jsxDEV28("text", {
14032
+ /* @__PURE__ */ jsxDEV29("text", {
13534
14033
  fg: cliTheme.text.muted,
13535
14034
  marginLeft: 2,
13536
14035
  children: `${formatContextLength(model.contextLength)}${model.supportsTools ? " | tools" : ""}${model.supportsReasoning ? " | reasoning" : ""}`
13537
14036
  }, undefined, false, undefined, this),
13538
- model.id === currentModel && /* @__PURE__ */ jsxDEV28("text", {
14037
+ model.id === currentModel && /* @__PURE__ */ jsxDEV29("text", {
13539
14038
  fg: cliTheme.accent.primary,
13540
14039
  marginLeft: 2,
13541
14040
  children: "(current)"
@@ -13543,10 +14042,10 @@ function ModelSelector({ onClose, notify }) {
13543
14042
  ]
13544
14043
  }, model.id, true, undefined, this);
13545
14044
  }),
13546
- /* @__PURE__ */ jsxDEV28("box", {
14045
+ /* @__PURE__ */ jsxDEV29("box", {
13547
14046
  paddingX: 2,
13548
14047
  marginTop: 1,
13549
- children: /* @__PURE__ */ jsxDEV28("text", {
14048
+ children: /* @__PURE__ */ jsxDEV29("text", {
13550
14049
  fg: cliTheme.overlay.footerText,
13551
14050
  children: `${filteredModels.length} model${filteredModels.length === 1 ? "" : "s"}${isApplying ? " | switching..." : ""}`
13552
14051
  }, undefined, false, undefined, this)
@@ -13563,30 +14062,30 @@ var init_model_selector = __esm(() => {
13563
14062
  });
13564
14063
 
13565
14064
  // apps/cli/src/screens/model-select-screen.tsx
13566
- import { TextAttributes as TextAttributes19 } from "@opentui/core";
13567
- import { useState as useState16 } from "react";
14065
+ import { TextAttributes as TextAttributes20 } from "@opentui/core";
14066
+ import { useState as useState17 } from "react";
13568
14067
  import { useNavigate as useNavigate3 } from "react-router";
13569
- import { jsxDEV as jsxDEV29 } from "@opentui/react/jsx-dev-runtime";
14068
+ import { jsxDEV as jsxDEV30 } from "@opentui/react/jsx-dev-runtime";
13570
14069
  function ModelSelectScreen() {
13571
14070
  const navigate = useNavigate3();
13572
- const [notice, setNotice] = useState16(null);
13573
- return /* @__PURE__ */ jsxDEV29("box", {
14071
+ const [notice, setNotice] = useState17(null);
14072
+ return /* @__PURE__ */ jsxDEV30("box", {
13574
14073
  width: "100%",
13575
14074
  height: "100%",
13576
14075
  flexDirection: "column",
13577
14076
  gap: 1,
13578
14077
  paddingX: 1,
13579
14078
  children: [
13580
- /* @__PURE__ */ jsxDEV29("text", {
14079
+ /* @__PURE__ */ jsxDEV30("text", {
13581
14080
  fg: cliTheme.text.primary,
13582
- attributes: TextAttributes19.BOLD,
14081
+ attributes: TextAttributes20.BOLD,
13583
14082
  children: "Switch Model"
13584
14083
  }, undefined, false, undefined, this),
13585
- notice ? /* @__PURE__ */ jsxDEV29("text", {
14084
+ notice ? /* @__PURE__ */ jsxDEV30("text", {
13586
14085
  fg: notice.tone === "error" ? cliTheme.semantic.error : cliTheme.accent.primary,
13587
14086
  children: notice.text
13588
14087
  }, undefined, false, undefined, this) : null,
13589
- /* @__PURE__ */ jsxDEV29(ModelSelector, {
14088
+ /* @__PURE__ */ jsxDEV30(ModelSelector, {
13590
14089
  notify: (text, tone = "info") => setNotice({ text, tone }),
13591
14090
  onClose: () => navigate(-1)
13592
14091
  }, undefined, false, undefined, this)
@@ -13599,14 +14098,14 @@ var init_model_select_screen = __esm(() => {
13599
14098
  });
13600
14099
 
13601
14100
  // apps/cli/src/screens/onboarding-screen.tsx
13602
- import { TextAttributes as TextAttributes20 } from "@opentui/core";
14101
+ import { TextAttributes as TextAttributes21 } from "@opentui/core";
13603
14102
  import { useKeyboard as useKeyboard9 } from "@opentui/react";
13604
14103
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
13605
14104
  import os5 from "os";
13606
14105
  import path17 from "path";
13607
- import { useCallback as useCallback7, useState as useState17 } from "react";
14106
+ import { useCallback as useCallback7, useState as useState18 } from "react";
13608
14107
  import { useNavigate as useNavigate4 } from "react-router";
13609
- import { jsxDEV as jsxDEV30 } from "@opentui/react/jsx-dev-runtime";
14108
+ import { jsxDEV as jsxDEV31 } from "@opentui/react/jsx-dev-runtime";
13610
14109
  function getUserSettingsPath() {
13611
14110
  const configured = process.env.LIGHTCODE_USER_CONFIG?.trim();
13612
14111
  return configured ?? path17.join(os5.homedir(), ".lightcode", "settings.json");
@@ -13630,12 +14129,12 @@ function mergeUserSettings(update) {
13630
14129
  function OnboardingScreen() {
13631
14130
  const navigate = useNavigate4();
13632
14131
  const { bumpConfigRefresh } = useAppState();
13633
- const [step, setStep] = useState17("provider");
13634
- const [selectedIndex, setSelectedIndex] = useState17(0);
13635
- const [apiKey, setApiKey] = useState17("");
13636
- const [model, setModel] = useState17("");
13637
- const [errorText, setErrorText] = useState17(null);
13638
- 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);
13639
14138
  const provider = providerChoices[selectedIndex];
13640
14139
  const completeSetup = useCallback7(async ({
13641
14140
  finalModel,
@@ -13684,36 +14183,66 @@ function OnboardingScreen() {
13684
14183
  navigate("/");
13685
14184
  }
13686
14185
  });
13687
- 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", {
13688
14189
  width: "100%",
13689
14190
  height: "100%",
13690
14191
  flexDirection: "column",
13691
14192
  alignItems: "center",
13692
14193
  paddingTop: 2,
13693
- children: /* @__PURE__ */ jsxDEV30("box", {
14194
+ children: /* @__PURE__ */ jsxDEV31("box", {
13694
14195
  width: 72,
13695
14196
  flexDirection: "column",
13696
14197
  gap: 1,
13697
14198
  children: [
13698
- /* @__PURE__ */ jsxDEV30("text", {
14199
+ /* @__PURE__ */ jsxDEV31("text", {
13699
14200
  fg: cliTheme.accent.primary,
13700
- attributes: TextAttributes20.BOLD,
14201
+ attributes: TextAttributes21.BOLD,
13701
14202
  children: "Welcome to Lightcode"
13702
14203
  }, undefined, false, undefined, this),
13703
- /* @__PURE__ */ jsxDEV30("text", {
14204
+ /* @__PURE__ */ jsxDEV31("text", {
13704
14205
  fg: cliTheme.text.secondary,
13705
14206
  children: "One-time setup: pick a model provider and store an API key."
13706
14207
  }, undefined, false, undefined, this),
13707
- /* @__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", {
13708
14237
  fg: cliTheme.text.muted,
13709
- attributes: TextAttributes20.DIM,
14238
+ attributes: TextAttributes21.DIM,
13710
14239
  children: "Keys are stored in ~/.lightcode/credentials.json (environment variables always take precedence)."
13711
14240
  }, undefined, false, undefined, this),
13712
- errorText ? /* @__PURE__ */ jsxDEV30("text", {
14241
+ errorText ? /* @__PURE__ */ jsxDEV31("text", {
13713
14242
  fg: cliTheme.semantic.error,
13714
14243
  children: errorText
13715
14244
  }, undefined, false, undefined, this) : null,
13716
- step === "provider" ? /* @__PURE__ */ jsxDEV30("box", {
14245
+ step === "provider" ? /* @__PURE__ */ jsxDEV31("box", {
13717
14246
  flexDirection: "column",
13718
14247
  borderStyle: "single",
13719
14248
  borderColor: cliTheme.borders.default,
@@ -13721,53 +14250,53 @@ function OnboardingScreen() {
13721
14250
  paddingX: 1,
13722
14251
  paddingY: 1,
13723
14252
  children: [
13724
- /* @__PURE__ */ jsxDEV30("text", {
14253
+ /* @__PURE__ */ jsxDEV31("text", {
13725
14254
  fg: cliTheme.text.primary,
13726
- attributes: TextAttributes20.BOLD,
14255
+ attributes: TextAttributes21.BOLD,
13727
14256
  children: "Choose a provider"
13728
14257
  }, undefined, false, undefined, this),
13729
- providerChoices.map((choice, index) => /* @__PURE__ */ jsxDEV30("box", {
14258
+ providerChoices.map((choice, index) => /* @__PURE__ */ jsxDEV31("box", {
13730
14259
  flexDirection: "row",
13731
14260
  gap: 1,
13732
14261
  backgroundColor: index === selectedIndex ? cliTheme.overlay.selectedRowBackground : undefined,
13733
14262
  paddingX: 1,
13734
14263
  children: [
13735
- /* @__PURE__ */ jsxDEV30("text", {
14264
+ /* @__PURE__ */ jsxDEV31("text", {
13736
14265
  fg: index === selectedIndex ? cliTheme.accent.primary : cliTheme.text.muted,
13737
14266
  children: index === selectedIndex ? ">" : " "
13738
14267
  }, undefined, false, undefined, this),
13739
- /* @__PURE__ */ jsxDEV30("text", {
14268
+ /* @__PURE__ */ jsxDEV31("text", {
13740
14269
  fg: index === selectedIndex ? cliTheme.overlay.selectedRowText : cliTheme.text.primary,
13741
- attributes: index === selectedIndex ? TextAttributes20.BOLD : TextAttributes20.NONE,
14270
+ attributes: index === selectedIndex ? TextAttributes21.BOLD : TextAttributes21.NONE,
13742
14271
  children: choice.label
13743
14272
  }, undefined, false, undefined, this),
13744
- /* @__PURE__ */ jsxDEV30("text", {
14273
+ /* @__PURE__ */ jsxDEV31("text", {
13745
14274
  fg: cliTheme.overlay.description,
13746
14275
  children: choice.description
13747
14276
  }, undefined, false, undefined, this)
13748
14277
  ]
13749
14278
  }, choice.id, true, undefined, this)),
13750
- /* @__PURE__ */ jsxDEV30("text", {
14279
+ /* @__PURE__ */ jsxDEV31("text", {
13751
14280
  fg: cliTheme.text.muted,
13752
- attributes: TextAttributes20.DIM,
14281
+ attributes: TextAttributes21.DIM,
13753
14282
  children: "Up/Down select - Enter continue"
13754
14283
  }, undefined, false, undefined, this)
13755
14284
  ]
13756
14285
  }, undefined, true, undefined, this) : null,
13757
- step === "apiKey" ? /* @__PURE__ */ jsxDEV30("box", {
14286
+ step === "apiKey" ? /* @__PURE__ */ jsxDEV31("box", {
13758
14287
  flexDirection: "column",
13759
14288
  gap: 1,
13760
14289
  children: [
13761
- /* @__PURE__ */ jsxDEV30("text", {
14290
+ /* @__PURE__ */ jsxDEV31("text", {
13762
14291
  fg: cliTheme.text.primary,
13763
14292
  children: `Paste your ${provider.label} API key and press Enter`
13764
14293
  }, undefined, false, undefined, this),
13765
- /* @__PURE__ */ jsxDEV30("text", {
14294
+ /* @__PURE__ */ jsxDEV31("text", {
13766
14295
  fg: cliTheme.text.muted,
13767
- attributes: TextAttributes20.DIM,
14296
+ attributes: TextAttributes21.DIM,
13768
14297
  children: provider.keyHint
13769
14298
  }, undefined, false, undefined, this),
13770
- /* @__PURE__ */ jsxDEV30(ChatTextArea, {
14299
+ /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13771
14300
  placeholder: "API key...",
13772
14301
  onSubmit: (text) => {
13773
14302
  const trimmedKey = text.trim();
@@ -13784,15 +14313,15 @@ function OnboardingScreen() {
13784
14313
  }, undefined, false, undefined, this)
13785
14314
  ]
13786
14315
  }, undefined, true, undefined, this) : null,
13787
- step === "model" ? /* @__PURE__ */ jsxDEV30("box", {
14316
+ step === "model" ? /* @__PURE__ */ jsxDEV31("box", {
13788
14317
  flexDirection: "column",
13789
14318
  gap: 1,
13790
14319
  children: [
13791
- /* @__PURE__ */ jsxDEV30("text", {
14320
+ /* @__PURE__ */ jsxDEV31("text", {
13792
14321
  fg: cliTheme.text.primary,
13793
14322
  children: `Model id for ${provider.label} (e.g. ${provider.id === "openrouter" ? "anthropic/claude-sonnet-4-6" : "your model id"})`
13794
14323
  }, undefined, false, undefined, this),
13795
- /* @__PURE__ */ jsxDEV30(ChatTextArea, {
14324
+ /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13796
14325
  placeholder: "Model id...",
13797
14326
  onSubmit: (text) => {
13798
14327
  const trimmedModel = text.trim();
@@ -13813,15 +14342,15 @@ function OnboardingScreen() {
13813
14342
  }, undefined, false, undefined, this)
13814
14343
  ]
13815
14344
  }, undefined, true, undefined, this) : null,
13816
- step === "baseUrl" ? /* @__PURE__ */ jsxDEV30("box", {
14345
+ step === "baseUrl" ? /* @__PURE__ */ jsxDEV31("box", {
13817
14346
  flexDirection: "column",
13818
14347
  gap: 1,
13819
14348
  children: [
13820
- /* @__PURE__ */ jsxDEV30("text", {
14349
+ /* @__PURE__ */ jsxDEV31("text", {
13821
14350
  fg: cliTheme.text.primary,
13822
14351
  children: "Base URL of the OpenAI-compatible endpoint"
13823
14352
  }, undefined, false, undefined, this),
13824
- /* @__PURE__ */ jsxDEV30(ChatTextArea, {
14353
+ /* @__PURE__ */ jsxDEV31(ChatTextArea, {
13825
14354
  placeholder: "https://...",
13826
14355
  onSubmit: (text) => {
13827
14356
  const trimmedBaseUrl = text.trim();
@@ -13837,24 +14366,24 @@ function OnboardingScreen() {
13837
14366
  }, undefined, false, undefined, this)
13838
14367
  ]
13839
14368
  }, undefined, true, undefined, this) : null,
13840
- step === "saving" ? /* @__PURE__ */ jsxDEV30("text", {
14369
+ step === "saving" ? /* @__PURE__ */ jsxDEV31("text", {
13841
14370
  fg: cliTheme.semantic.info,
13842
14371
  children: "Saving configuration and restarting the server..."
13843
14372
  }, undefined, false, undefined, this) : null,
13844
- step === "done" ? /* @__PURE__ */ jsxDEV30("box", {
14373
+ step === "done" ? /* @__PURE__ */ jsxDEV31("box", {
13845
14374
  flexDirection: "column",
13846
14375
  gap: 1,
13847
14376
  children: [
13848
- /* @__PURE__ */ jsxDEV30("text", {
14377
+ /* @__PURE__ */ jsxDEV31("text", {
13849
14378
  fg: cliTheme.semantic.success,
13850
- attributes: TextAttributes20.BOLD,
14379
+ attributes: TextAttributes21.BOLD,
13851
14380
  children: "Setup complete"
13852
14381
  }, undefined, false, undefined, this),
13853
- /* @__PURE__ */ jsxDEV30("text", {
14382
+ /* @__PURE__ */ jsxDEV31("text", {
13854
14383
  fg: cliTheme.text.secondary,
13855
14384
  children: serverRestarted ? "The local server was restarted with your new configuration." : "Restart the Lightcode server to pick up the new configuration."
13856
14385
  }, undefined, false, undefined, this),
13857
- /* @__PURE__ */ jsxDEV30("text", {
14386
+ /* @__PURE__ */ jsxDEV31("text", {
13858
14387
  fg: cliTheme.text.muted,
13859
14388
  children: "Press Enter to start coding."
13860
14389
  }, undefined, false, undefined, this)
@@ -13864,7 +14393,7 @@ function OnboardingScreen() {
13864
14393
  }, undefined, true, undefined, this)
13865
14394
  }, undefined, false, undefined, this);
13866
14395
  }
13867
- var providerChoices;
14396
+ var ONBOARDING_STEP_SEQUENCE, providerChoices;
13868
14397
  var init_onboarding_screen = __esm(() => {
13869
14398
  init_src2();
13870
14399
  init_chat_text_area();
@@ -13872,7 +14401,9 @@ var init_onboarding_screen = __esm(() => {
13872
14401
  init_server_launcher();
13873
14402
  init_app_state();
13874
14403
  init_cli_theme();
14404
+ init_cli_theme_capabilities();
13875
14405
  init_text_utils();
14406
+ ONBOARDING_STEP_SEQUENCE = ["provider", "apiKey", "model", "done"];
13876
14407
  providerChoices = [
13877
14408
  {
13878
14409
  id: "anthropic",
@@ -13914,11 +14445,11 @@ var init_onboarding_screen = __esm(() => {
13914
14445
  });
13915
14446
 
13916
14447
  // apps/cli/src/screens/session-list-screen.tsx
13917
- import { TextAttributes as TextAttributes21 } from "@opentui/core";
14448
+ import { TextAttributes as TextAttributes22 } from "@opentui/core";
13918
14449
  import { useKeyboard as useKeyboard10 } from "@opentui/react";
13919
- 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";
13920
14451
  import { useNavigate as useNavigate5 } from "react-router";
13921
- import { jsxDEV as jsxDEV31 } from "@opentui/react/jsx-dev-runtime";
14452
+ import { jsxDEV as jsxDEV32 } from "@opentui/react/jsx-dev-runtime";
13922
14453
  function getSessionLabel(session) {
13923
14454
  return session.title ?? session.latestUserPromptPreview ?? session.id;
13924
14455
  }
@@ -13931,15 +14462,15 @@ function sessionMatchesFilter(session, query) {
13931
14462
  }
13932
14463
  function SessionListScreen() {
13933
14464
  const navigate = useNavigate5();
13934
- const [sessions, setSessions] = useState18([]);
13935
- const [selectedIndex, setSelectedIndex] = useState18(0);
13936
- const [isLoading, setIsLoading] = useState18(true);
13937
- const [errorMessage, setErrorMessage] = useState18(null);
13938
- const [actionMessage, setActionMessage] = useState18(null);
13939
- const [pendingDeleteId, setPendingDeleteId] = useState18(null);
13940
- const [filterQuery, setFilterQuery] = useState18("");
13941
- const [filterOpen, setFilterOpen] = useState18(false);
13942
- 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);
13943
14474
  const visibleSessions = useMemo7(() => sessions.filter((session) => sessionMatchesFilter(session, filterQuery)), [filterQuery, sessions]);
13944
14475
  const safeSelectedIndex = Math.min(selectedIndex, Math.max(visibleSessions.length - 1, 0));
13945
14476
  const selectedSession = visibleSessions[safeSelectedIndex] ?? null;
@@ -14096,7 +14627,7 @@ function SessionListScreen() {
14096
14627
  setActionMessage(getErrorMessage(error, "Unable to rename session."));
14097
14628
  }
14098
14629
  }, [loadSessions]);
14099
- useEffect13(() => {
14630
+ useEffect14(() => {
14100
14631
  loadSessions();
14101
14632
  }, [loadSessions]);
14102
14633
  useKeyboard10((keyEvent) => {
@@ -14187,85 +14718,96 @@ function SessionListScreen() {
14187
14718
  }
14188
14719
  return "Enter resume | r rename | f fork | / filter | e export | d delete | u refresh";
14189
14720
  }, [sessions.length]);
14190
- return /* @__PURE__ */ jsxDEV31("box", {
14721
+ return /* @__PURE__ */ jsxDEV32("box", {
14191
14722
  width: "100%",
14192
14723
  height: "100%",
14193
14724
  flexDirection: "column",
14194
14725
  gap: 1,
14195
14726
  children: [
14196
- /* @__PURE__ */ jsxDEV31("box", {
14727
+ /* @__PURE__ */ jsxDEV32("box", {
14197
14728
  flexDirection: "row",
14198
14729
  justifyContent: "space-between",
14199
14730
  paddingX: 1,
14200
14731
  children: [
14201
- /* @__PURE__ */ jsxDEV31("text", {
14732
+ /* @__PURE__ */ jsxDEV32("text", {
14202
14733
  fg: cliTheme.text.primary,
14203
- attributes: TextAttributes21.BOLD,
14734
+ attributes: TextAttributes22.BOLD,
14204
14735
  children: "Sessions"
14205
14736
  }, undefined, false, undefined, this),
14206
- /* @__PURE__ */ jsxDEV31("text", {
14737
+ /* @__PURE__ */ jsxDEV32("text", {
14207
14738
  fg: cliTheme.text.muted,
14208
14739
  children: isLoading ? "loading..." : `${sessions.length} saved`
14209
14740
  }, undefined, false, undefined, this)
14210
14741
  ]
14211
14742
  }, undefined, true, undefined, this),
14212
- errorMessage ? /* @__PURE__ */ jsxDEV31("box", {
14743
+ errorMessage ? /* @__PURE__ */ jsxDEV32("box", {
14213
14744
  paddingX: 1,
14214
- children: /* @__PURE__ */ jsxDEV31("text", {
14745
+ children: /* @__PURE__ */ jsxDEV32("text", {
14215
14746
  fg: cliTheme.semantic.error,
14216
14747
  children: errorMessage
14217
14748
  }, undefined, false, undefined, this)
14218
14749
  }, undefined, false, undefined, this) : null,
14219
- filterQuery && !filterOpen ? /* @__PURE__ */ jsxDEV31("box", {
14750
+ filterQuery && !filterOpen ? /* @__PURE__ */ jsxDEV32("box", {
14220
14751
  paddingX: 1,
14221
- children: /* @__PURE__ */ jsxDEV31("text", {
14752
+ children: /* @__PURE__ */ jsxDEV32("text", {
14222
14753
  fg: cliTheme.accent.primary,
14223
14754
  children: `Filter: "${filterQuery}" (${visibleSessions.length} match${visibleSessions.length === 1 ? "" : "es"}) - Esc clears`
14224
14755
  }, undefined, false, undefined, this)
14225
14756
  }, undefined, false, undefined, this) : null,
14226
- /* @__PURE__ */ jsxDEV31("box", {
14757
+ /* @__PURE__ */ jsxDEV32("box", {
14227
14758
  flexDirection: "column",
14228
14759
  flexGrow: 1,
14229
14760
  children: [
14230
- visibleSessions.length === 0 && !isLoading ? /* @__PURE__ */ jsxDEV31("box", {
14231
- paddingX: 1,
14232
- children: /* @__PURE__ */ jsxDEV31("text", {
14233
- fg: cliTheme.text.muted,
14234
- children: sessions.length === 0 ? "No saved sessions yet." : "No sessions match the filter."
14235
- }, undefined, false, undefined, this)
14236
- }, 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,
14237
14779
  visibleSessions.map((session, index) => {
14238
14780
  const isSelected = index === safeSelectedIndex;
14239
14781
  const rowColors = getOverlayRowColors(isSelected);
14240
14782
  const selectedForDelete = pendingDeleteId === session.id;
14241
- return /* @__PURE__ */ jsxDEV31("box", {
14783
+ return /* @__PURE__ */ jsxDEV32("box", {
14242
14784
  flexDirection: "column",
14243
14785
  paddingX: 1,
14244
14786
  backgroundColor: rowColors.backgroundColor,
14245
14787
  children: [
14246
- /* @__PURE__ */ jsxDEV31("box", {
14788
+ /* @__PURE__ */ jsxDEV32("box", {
14247
14789
  flexDirection: "row",
14248
14790
  justifyContent: "space-between",
14249
14791
  children: [
14250
- /* @__PURE__ */ jsxDEV31("text", {
14792
+ /* @__PURE__ */ jsxDEV32("text", {
14251
14793
  fg: selectedForDelete ? cliTheme.semantic.error : rowColors.primaryTextColor,
14252
- attributes: TextAttributes21.BOLD,
14794
+ attributes: TextAttributes22.BOLD,
14253
14795
  children: [
14254
14796
  index + 1,
14255
14797
  ". ",
14256
14798
  truncateInline2(getSessionLabel(session), 64)
14257
14799
  ]
14258
14800
  }, undefined, true, undefined, this),
14259
- /* @__PURE__ */ jsxDEV31("text", {
14801
+ /* @__PURE__ */ jsxDEV32("text", {
14260
14802
  fg: rowColors.secondaryTextColor,
14261
- attributes: TextAttributes21.DIM,
14803
+ attributes: TextAttributes22.DIM,
14262
14804
  children: `${session.messageCount} msgs - ${session.mode} - ${formatDate2(session.updatedAt)}`
14263
14805
  }, undefined, false, undefined, this)
14264
14806
  ]
14265
14807
  }, undefined, true, undefined, this),
14266
- isSelected && session.latestUserPromptPreview ? /* @__PURE__ */ jsxDEV31("text", {
14808
+ isSelected && session.latestUserPromptPreview ? /* @__PURE__ */ jsxDEV32("text", {
14267
14809
  fg: rowColors.secondaryTextColor,
14268
- attributes: TextAttributes21.DIM,
14810
+ attributes: TextAttributes22.DIM,
14269
14811
  children: truncateInline2(session.latestUserPromptPreview, 110)
14270
14812
  }, undefined, false, undefined, this) : null
14271
14813
  ]
@@ -14273,28 +14815,28 @@ function SessionListScreen() {
14273
14815
  })
14274
14816
  ]
14275
14817
  }, undefined, true, undefined, this),
14276
- actionMessage ? /* @__PURE__ */ jsxDEV31("box", {
14818
+ actionMessage ? /* @__PURE__ */ jsxDEV32("box", {
14277
14819
  paddingX: 1,
14278
- children: /* @__PURE__ */ jsxDEV31("text", {
14820
+ children: /* @__PURE__ */ jsxDEV32("text", {
14279
14821
  fg: cliTheme.semantic.info,
14280
14822
  children: actionMessage
14281
14823
  }, undefined, false, undefined, this)
14282
14824
  }, undefined, false, undefined, this) : null,
14283
- filterOpen ? /* @__PURE__ */ jsxDEV31(ChatTextArea, {
14825
+ filterOpen ? /* @__PURE__ */ jsxDEV32(ChatTextArea, {
14284
14826
  placeholder: "Filter sessions...",
14285
14827
  allowEmpty: true,
14286
14828
  onTextChange: setFilterQuery,
14287
14829
  onSubmit: () => setFilterOpen(false)
14288
14830
  }, undefined, false, undefined, this) : null,
14289
- renamingSessionId !== null ? /* @__PURE__ */ jsxDEV31(ChatTextArea, {
14831
+ renamingSessionId !== null ? /* @__PURE__ */ jsxDEV32(ChatTextArea, {
14290
14832
  placeholder: "New session title...",
14291
14833
  onSubmit: (title) => void renameSession(renamingSessionId, title)
14292
14834
  }, undefined, false, undefined, this) : null,
14293
- /* @__PURE__ */ jsxDEV31("box", {
14835
+ /* @__PURE__ */ jsxDEV32("box", {
14294
14836
  paddingX: 1,
14295
- children: /* @__PURE__ */ jsxDEV31("text", {
14837
+ children: /* @__PURE__ */ jsxDEV32("text", {
14296
14838
  fg: cliTheme.text.muted,
14297
- attributes: TextAttributes21.DIM,
14839
+ attributes: TextAttributes22.DIM,
14298
14840
  children: inputActive ? "Enter confirm | Esc cancel" : footerText
14299
14841
  }, undefined, false, undefined, this)
14300
14842
  }, undefined, false, undefined, this)
@@ -14312,10 +14854,10 @@ var init_session_list_screen = __esm(() => {
14312
14854
  });
14313
14855
 
14314
14856
  // apps/cli/src/hooks/use-config-badge.ts
14315
- import { useEffect as useEffect14, useState as useState19 } from "react";
14857
+ import { useEffect as useEffect15, useState as useState20 } from "react";
14316
14858
  function useConfigBadge(refreshNonce = 0) {
14317
- const [state, setState] = useState19({ status: "loading" });
14318
- useEffect14(() => {
14859
+ const [state, setState] = useState20({ status: "loading" });
14860
+ useEffect15(() => {
14319
14861
  let cancelled = false;
14320
14862
  async function loadConfig() {
14321
14863
  try {
@@ -14356,16 +14898,36 @@ var init_use_config_badge = __esm(() => {
14356
14898
  init_src2();
14357
14899
  });
14358
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
+
14359
14921
  // apps/cli/src/app.tsx
14360
14922
  var exports_app = {};
14361
14923
  __export(exports_app, {
14362
14924
  App: () => App
14363
14925
  });
14364
- import { TextAttributes as TextAttributes22 } from "@opentui/core";
14926
+ import { TextAttributes as TextAttributes23 } from "@opentui/core";
14365
14927
  import { useKeyboard as useKeyboard11, useRenderer as useRenderer2 } from "@opentui/react";
14366
- 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";
14367
14929
  import { MemoryRouter, Navigate, Route, Routes, useLocation as useLocation3, useNavigate as useNavigate6 } from "react-router";
14368
- import { jsxDEV as jsxDEV32 } from "@opentui/react/jsx-dev-runtime";
14930
+ import { jsxDEV as jsxDEV34 } from "@opentui/react/jsx-dev-runtime";
14369
14931
  function getCurrentViewLabel(pathname) {
14370
14932
  if (pathname === "/" || pathname === "/home") {
14371
14933
  return "home";
@@ -14419,10 +14981,11 @@ function AppContent() {
14419
14981
  closeSlashMenu,
14420
14982
  requestChatAction,
14421
14983
  configRefreshNonce,
14422
- toggleToolOutputExpansion
14984
+ toggleToolOutputExpansion,
14985
+ toggleReasoningExpansion
14423
14986
  } = useAppState();
14424
- const [helpOpen, setHelpOpen] = useState20(false);
14425
- const [ctrlCNotice, setCtrlCNotice] = useState20(null);
14987
+ const [helpOpen, setHelpOpen] = useState21(false);
14988
+ const [ctrlCNotice, setCtrlCNotice] = useState21(null);
14426
14989
  const ctrlCArmedRef = useRef5(false);
14427
14990
  const ctrlCTimerRef = useRef5(null);
14428
14991
  const showCtrlCNotice = (text) => {
@@ -14453,7 +15016,7 @@ function AppContent() {
14453
15016
  ctrlCArmedRef.current = true;
14454
15017
  showCtrlCNotice("Press Ctrl+C again to exit");
14455
15018
  };
14456
- useEffect15(() => {
15019
+ useEffect16(() => {
14457
15020
  return () => {
14458
15021
  if (ctrlCTimerRef.current) {
14459
15022
  clearTimeout(ctrlCTimerRef.current);
@@ -14491,6 +15054,9 @@ function AppContent() {
14491
15054
  case "system:toggleToolOutput":
14492
15055
  toggleToolOutputExpansion();
14493
15056
  break;
15057
+ case "system:toggleReasoning":
15058
+ toggleReasoningExpansion();
15059
+ break;
14494
15060
  case "system:cancel":
14495
15061
  if (helpOpen) {
14496
15062
  setHelpOpen(false);
@@ -14600,7 +15166,7 @@ function AppContent() {
14600
15166
  });
14601
15167
  const configBadge = useConfigBadge(configRefreshNonce);
14602
15168
  const needsOnboarding = configBadge.status === "available" && configBadge.missingCredentialHints.length > 0;
14603
- useEffect15(() => {
15169
+ useEffect16(() => {
14604
15170
  if (needsOnboarding && location.pathname !== "/onboarding") {
14605
15171
  navigate("/onboarding");
14606
15172
  }
@@ -14609,15 +15175,15 @@ function AppContent() {
14609
15175
  if (slashMenuOpen) {
14610
15176
  return "Slash pages open | Enter Open | Backspace Close | Esc Cancel";
14611
15177
  }
14612
- const backHint = canGoBack ? ` | Esc/${BACK_SHORTCUT_LABEL} Back` : "";
14613
- 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";
14614
15180
  };
14615
- return /* @__PURE__ */ jsxDEV32("box", {
15181
+ return /* @__PURE__ */ jsxDEV34("box", {
14616
15182
  flexDirection: "column",
14617
15183
  flexGrow: 1,
14618
15184
  backgroundColor: cliTheme.surfaces.base,
14619
15185
  children: [
14620
- /* @__PURE__ */ jsxDEV32("box", {
15186
+ /* @__PURE__ */ jsxDEV34("box", {
14621
15187
  flexDirection: "row",
14622
15188
  justifyContent: "space-between",
14623
15189
  paddingLeft: 1,
@@ -14626,144 +15192,148 @@ function AppContent() {
14626
15192
  border: ["bottom"],
14627
15193
  borderColor: cliTheme.borders.default,
14628
15194
  children: [
14629
- /* @__PURE__ */ jsxDEV32("box", {
15195
+ /* @__PURE__ */ jsxDEV34("box", {
14630
15196
  flexDirection: "row",
14631
- gap: 2,
15197
+ gap: 1,
14632
15198
  alignItems: "center",
14633
15199
  children: [
14634
- /* @__PURE__ */ jsxDEV32("text", {
14635
- fg: cliTheme.text.primary,
14636
- 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,
14637
15206
  children: "Lightcode"
14638
15207
  }, undefined, false, undefined, this),
14639
- configBadge.status === "available" ? /* @__PURE__ */ jsxDEV32("text", {
15208
+ configBadge.status === "available" ? /* @__PURE__ */ jsxDEV34("text", {
14640
15209
  fg: cliTheme.text.secondary,
14641
15210
  children: [
14642
- "| ",
15211
+ "\xB7 ",
14643
15212
  configBadge.provider,
14644
- " | ",
15213
+ " \xB7 ",
14645
15214
  configBadge.model
14646
15215
  ]
14647
- }, undefined, true, undefined, this) : configBadge.status === "loading" ? /* @__PURE__ */ jsxDEV32("text", {
15216
+ }, undefined, true, undefined, this) : configBadge.status === "loading" ? /* @__PURE__ */ jsxDEV34("text", {
14648
15217
  fg: cliTheme.text.muted,
14649
- children: "| loading..."
14650
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV32("text", {
14651
- fg: cliTheme.text.muted,
14652
- 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"
14653
15223
  }, undefined, false, undefined, this)
14654
15224
  ]
14655
15225
  }, undefined, true, undefined, this),
14656
- /* @__PURE__ */ jsxDEV32("text", {
15226
+ /* @__PURE__ */ jsxDEV34("text", {
14657
15227
  fg: cliTheme.text.muted,
14658
15228
  children: currentView
14659
15229
  }, undefined, false, undefined, this)
14660
15230
  ]
14661
15231
  }, undefined, true, undefined, this),
14662
- /* @__PURE__ */ jsxDEV32("box", {
15232
+ /* @__PURE__ */ jsxDEV34("box", {
14663
15233
  flexGrow: 1,
14664
15234
  padding: 1,
14665
15235
  position: "relative",
14666
15236
  children: [
14667
- /* @__PURE__ */ jsxDEV32(Routes, {
15237
+ /* @__PURE__ */ jsxDEV34(Routes, {
14668
15238
  children: [
14669
- /* @__PURE__ */ jsxDEV32(Route, {
15239
+ /* @__PURE__ */ jsxDEV34(Route, {
14670
15240
  path: "/",
14671
- element: /* @__PURE__ */ jsxDEV32(HomeScreen, {}, undefined, false, undefined, this)
15241
+ element: /* @__PURE__ */ jsxDEV34(HomeScreen, {}, undefined, false, undefined, this)
14672
15242
  }, undefined, false, undefined, this),
14673
- /* @__PURE__ */ jsxDEV32(Route, {
15243
+ /* @__PURE__ */ jsxDEV34(Route, {
14674
15244
  path: "/home",
14675
- element: /* @__PURE__ */ jsxDEV32(Navigate, {
15245
+ element: /* @__PURE__ */ jsxDEV34(Navigate, {
14676
15246
  to: "/",
14677
15247
  replace: true
14678
15248
  }, undefined, false, undefined, this)
14679
15249
  }, undefined, false, undefined, this),
14680
- /* @__PURE__ */ jsxDEV32(Route, {
15250
+ /* @__PURE__ */ jsxDEV34(Route, {
14681
15251
  path: "/status",
14682
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15252
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14683
15253
  kind: "status"
14684
15254
  }, undefined, false, undefined, this)
14685
15255
  }, undefined, false, undefined, this),
14686
- /* @__PURE__ */ jsxDEV32(Route, {
15256
+ /* @__PURE__ */ jsxDEV34(Route, {
14687
15257
  path: "/doctor",
14688
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15258
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14689
15259
  kind: "doctor"
14690
15260
  }, undefined, false, undefined, this)
14691
15261
  }, undefined, false, undefined, this),
14692
- /* @__PURE__ */ jsxDEV32(Route, {
15262
+ /* @__PURE__ */ jsxDEV34(Route, {
14693
15263
  path: "/permissions",
14694
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15264
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14695
15265
  kind: "permissions"
14696
15266
  }, undefined, false, undefined, this)
14697
15267
  }, undefined, false, undefined, this),
14698
- /* @__PURE__ */ jsxDEV32(Route, {
15268
+ /* @__PURE__ */ jsxDEV34(Route, {
14699
15269
  path: "/sessions",
14700
- element: /* @__PURE__ */ jsxDEV32(SessionListScreen, {}, undefined, false, undefined, this)
15270
+ element: /* @__PURE__ */ jsxDEV34(SessionListScreen, {}, undefined, false, undefined, this)
14701
15271
  }, undefined, false, undefined, this),
14702
- /* @__PURE__ */ jsxDEV32(Route, {
15272
+ /* @__PURE__ */ jsxDEV34(Route, {
14703
15273
  path: "/sessions/:id",
14704
- element: /* @__PURE__ */ jsxDEV32(ChatScreen, {}, undefined, false, undefined, this)
15274
+ element: /* @__PURE__ */ jsxDEV34(ChatScreen, {}, undefined, false, undefined, this)
14705
15275
  }, undefined, false, undefined, this),
14706
- /* @__PURE__ */ jsxDEV32(Route, {
15276
+ /* @__PURE__ */ jsxDEV34(Route, {
14707
15277
  path: "/tools",
14708
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15278
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14709
15279
  kind: "tools"
14710
15280
  }, undefined, false, undefined, this)
14711
15281
  }, undefined, false, undefined, this),
14712
- /* @__PURE__ */ jsxDEV32(Route, {
15282
+ /* @__PURE__ */ jsxDEV34(Route, {
14713
15283
  path: "/config",
14714
- element: /* @__PURE__ */ jsxDEV32(DiagnosticsScreen, {
15284
+ element: /* @__PURE__ */ jsxDEV34(DiagnosticsScreen, {
14715
15285
  kind: "config"
14716
15286
  }, undefined, false, undefined, this)
14717
15287
  }, undefined, false, undefined, this),
14718
- /* @__PURE__ */ jsxDEV32(Route, {
15288
+ /* @__PURE__ */ jsxDEV34(Route, {
14719
15289
  path: "/model-info",
14720
- element: /* @__PURE__ */ jsxDEV32(ModelScreen, {}, undefined, false, undefined, this)
15290
+ element: /* @__PURE__ */ jsxDEV34(ModelScreen, {}, undefined, false, undefined, this)
14721
15291
  }, undefined, false, undefined, this),
14722
- /* @__PURE__ */ jsxDEV32(Route, {
15292
+ /* @__PURE__ */ jsxDEV34(Route, {
14723
15293
  path: "/model",
14724
- element: /* @__PURE__ */ jsxDEV32(ModelSelectScreen, {}, undefined, false, undefined, this)
15294
+ element: /* @__PURE__ */ jsxDEV34(ModelSelectScreen, {}, undefined, false, undefined, this)
14725
15295
  }, undefined, false, undefined, this),
14726
- /* @__PURE__ */ jsxDEV32(Route, {
15296
+ /* @__PURE__ */ jsxDEV34(Route, {
14727
15297
  path: "/onboarding",
14728
- element: /* @__PURE__ */ jsxDEV32(OnboardingScreen, {}, undefined, false, undefined, this)
15298
+ element: /* @__PURE__ */ jsxDEV34(OnboardingScreen, {}, undefined, false, undefined, this)
14729
15299
  }, undefined, false, undefined, this),
14730
- /* @__PURE__ */ jsxDEV32(Route, {
15300
+ /* @__PURE__ */ jsxDEV34(Route, {
14731
15301
  path: "*",
14732
- element: /* @__PURE__ */ jsxDEV32(Navigate, {
15302
+ element: /* @__PURE__ */ jsxDEV34(Navigate, {
14733
15303
  to: "/",
14734
15304
  replace: true
14735
15305
  }, undefined, false, undefined, this)
14736
15306
  }, undefined, false, undefined, this)
14737
15307
  ]
14738
15308
  }, undefined, true, undefined, this),
14739
- paletteOpen ? /* @__PURE__ */ jsxDEV32(CommandPalette, {
15309
+ paletteOpen ? /* @__PURE__ */ jsxDEV34(CommandPalette, {
14740
15310
  query: paletteQuery,
14741
15311
  setQuery: setPaletteQuery,
14742
15312
  selectedIndex: paletteSelected
14743
15313
  }, undefined, false, undefined, this) : null,
14744
- slashMenuOpen && !inputHostsSlashMenu ? /* @__PURE__ */ jsxDEV32("box", {
15314
+ slashMenuOpen && !inputHostsSlashMenu ? /* @__PURE__ */ jsxDEV34("box", {
14745
15315
  position: "absolute",
14746
15316
  top: 1,
14747
15317
  left: 2,
14748
15318
  right: 2,
14749
15319
  zIndex: 20,
14750
- children: /* @__PURE__ */ jsxDEV32(SlashPageMenu, {
15320
+ children: /* @__PURE__ */ jsxDEV34(SlashPageMenu, {
14751
15321
  query: slashMenuQuery,
14752
15322
  selectedIndex: selectedSlashRouteIndex,
14753
15323
  routes: filteredSlashRoutes
14754
15324
  }, undefined, false, undefined, this)
14755
15325
  }, undefined, false, undefined, this) : null,
14756
- helpOpen ? /* @__PURE__ */ jsxDEV32("box", {
15326
+ helpOpen ? /* @__PURE__ */ jsxDEV34("box", {
14757
15327
  position: "absolute",
14758
15328
  top: 1,
14759
15329
  left: 4,
14760
15330
  right: 4,
14761
15331
  zIndex: 30,
14762
- children: /* @__PURE__ */ jsxDEV32(HelpOverlay, {}, undefined, false, undefined, this)
15332
+ children: /* @__PURE__ */ jsxDEV34(HelpOverlay, {}, undefined, false, undefined, this)
14763
15333
  }, undefined, false, undefined, this) : null
14764
15334
  ]
14765
15335
  }, undefined, true, undefined, this),
14766
- /* @__PURE__ */ jsxDEV32("box", {
15336
+ /* @__PURE__ */ jsxDEV34("box", {
14767
15337
  flexDirection: "row",
14768
15338
  justifyContent: "center",
14769
15339
  gap: 2,
@@ -14772,7 +15342,7 @@ function AppContent() {
14772
15342
  borderStyle: "single",
14773
15343
  border: ["top"],
14774
15344
  borderColor: cliTheme.borders.default,
14775
- children: /* @__PURE__ */ jsxDEV32("text", {
15345
+ children: /* @__PURE__ */ jsxDEV34("text", {
14776
15346
  fg: ctrlCNotice ? cliTheme.semantic.warning : cliTheme.text.muted,
14777
15347
  children: ctrlCNotice ?? getFooterStatus()
14778
15348
  }, undefined, false, undefined, this)
@@ -14781,10 +15351,10 @@ function AppContent() {
14781
15351
  }, undefined, true, undefined, this);
14782
15352
  }
14783
15353
  function App() {
14784
- return /* @__PURE__ */ jsxDEV32(MemoryRouter, {
15354
+ return /* @__PURE__ */ jsxDEV34(MemoryRouter, {
14785
15355
  initialEntries: ["/"],
14786
- children: /* @__PURE__ */ jsxDEV32(AppStateProvider, {
14787
- children: /* @__PURE__ */ jsxDEV32(AppContent, {}, undefined, false, undefined, this)
15356
+ children: /* @__PURE__ */ jsxDEV34(AppStateProvider, {
15357
+ children: /* @__PURE__ */ jsxDEV34(AppContent, {}, undefined, false, undefined, this)
14788
15358
  }, undefined, false, undefined, this)
14789
15359
  }, undefined, false, undefined, this);
14790
15360
  }
@@ -14806,12 +15376,13 @@ var init_app = __esm(() => {
14806
15376
  init_app_state();
14807
15377
  init_use_config_badge();
14808
15378
  init_cli_theme();
15379
+ init_status_dot();
14809
15380
  init_clipboard();
14810
15381
  });
14811
15382
 
14812
15383
  // apps/cli/src/index.tsx
14813
15384
  init_src();
14814
- import { jsxDEV as jsxDEV33 } from "@opentui/react/jsx-dev-runtime";
15385
+ import { jsxDEV as jsxDEV35 } from "@opentui/react/jsx-dev-runtime";
14815
15386
  var cliArgs = Bun.argv.slice(2);
14816
15387
  if (cliArgs.includes("--version") || cliArgs.includes("-v")) {
14817
15388
  console.log(`${productName} v${lightcodeVersion}`);
@@ -14848,4 +15419,4 @@ if (serverLaunch.ownedProcess) {
14848
15419
  process.exit(0);
14849
15420
  });
14850
15421
  }
14851
- createRoot(renderer).render(/* @__PURE__ */ jsxDEV33(App2, {}, undefined, false, undefined, this));
15422
+ createRoot(renderer).render(/* @__PURE__ */ jsxDEV35(App2, {}, undefined, false, undefined, this));