@robota-sdk/agent-cli 3.0.0-beta.56 → 3.0.0-beta.58

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.
@@ -3,10 +3,11 @@ import {
3
3
  createProviderFromSettings,
4
4
  findProviderDefinition,
5
5
  formatSupportedProviderTypes,
6
+ hasUsableSecretReference,
6
7
  isSubagentWorkerChildMessage,
7
8
  readMergedProviderSettings,
8
9
  readProviderSettings
9
- } from "./chunk-2JAZDNYT.js";
10
+ } from "./chunk-4ZX5RLIX.js";
10
11
 
11
12
  // src/background/managed-shell-process-runner.ts
12
13
  import { spawn } from "child_process";
@@ -48,7 +49,7 @@ function resolveShell(request) {
48
49
  return { command: request.shell ?? "sh", args: ["-c", request.command] };
49
50
  }
50
51
  function sendInput(child, input) {
51
- return new Promise((resolve, reject) => {
52
+ return new Promise((resolve2, reject) => {
52
53
  const onError = (error) => {
53
54
  child.stdin.off("error", onError);
54
55
  reject(error);
@@ -56,7 +57,7 @@ function sendInput(child, input) {
56
57
  child.stdin.once("error", onError);
57
58
  child.stdin.end(input, () => {
58
59
  child.stdin.off("error", onError);
59
- resolve();
60
+ resolve2();
60
61
  });
61
62
  });
62
63
  }
@@ -91,7 +92,7 @@ function startProcessTask(taskId, request, killGraceMs) {
91
92
  }
92
93
  function createProcessResult(runtime) {
93
94
  let settled = false;
94
- return new Promise((resolve, reject) => {
95
+ return new Promise((resolve2, reject) => {
95
96
  const timeoutTimer = runtime.request.timeoutMs ? setTimeout(() => {
96
97
  appendLog(runtime.logs, "system", `timed out after ${runtime.request.timeoutMs}ms`);
97
98
  runtime.child.kill("SIGTERM");
@@ -105,7 +106,7 @@ function createProcessResult(runtime) {
105
106
  if (settled) return;
106
107
  settled = true;
107
108
  clearTimers();
108
- resolve({
109
+ resolve2({
109
110
  taskId: runtime.taskId,
110
111
  kind: "process",
111
112
  output: runtime.capture.getOutput(),
@@ -292,7 +293,7 @@ function extractFirstArg(toolArgs) {
292
293
  return typeof firstValue === "object" ? JSON.stringify(firstValue) : String(firstValue);
293
294
  }
294
295
  function sendWorkerMessage(child, message) {
295
- return new Promise((resolve, reject) => {
296
+ return new Promise((resolve2, reject) => {
296
297
  if (!child.connected) {
297
298
  reject(new BackgroundTaskError3("crash", "Subagent worker IPC channel is closed"));
298
299
  return;
@@ -302,7 +303,7 @@ function sendWorkerMessage(child, message) {
302
303
  reject(error);
303
304
  return;
304
305
  }
305
- resolve();
306
+ resolve2();
306
307
  });
307
308
  });
308
309
  }
@@ -319,14 +320,14 @@ async function cancelChildProcess(runtime, reason) {
319
320
 
320
321
  // src/subagents/child-process-subagent-runner-result.ts
321
322
  function createChildProcessSubagentResult(options) {
322
- return new Promise((resolve, reject) => {
323
- new ChildProcessSubagentResultController(options, resolve, reject).start();
323
+ return new Promise((resolve2, reject) => {
324
+ new ChildProcessSubagentResultController(options, resolve2, reject).start();
324
325
  });
325
326
  }
326
327
  var ChildProcessSubagentResultController = class {
327
- constructor(options, resolve, reject) {
328
+ constructor(options, resolve2, reject) {
328
329
  this.options = options;
329
- this.resolve = resolve;
330
+ this.resolve = resolve2;
330
331
  this.reject = reject;
331
332
  this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));
332
333
  }
@@ -542,7 +543,8 @@ function createProviderProfile(providerConfig, deps, job) {
542
543
  model: job.request.model ?? provider.model,
543
544
  apiKey: provider.apiKey,
544
545
  baseURL: provider.baseURL,
545
- timeout: provider.timeout
546
+ timeout: provider.timeout,
547
+ options: provider.options
546
548
  };
547
549
  }
548
550
  function resolveDefaultWorkerPath() {
@@ -589,8 +591,8 @@ function readTranscriptLog(jobId, transcriptPath, cursor) {
589
591
  }
590
592
 
591
593
  // src/cli.ts
592
- import { readFileSync as readFileSync4 } from "fs";
593
- import { join as join7, dirname as dirname3 } from "path";
594
+ import { readFileSync as readFileSync6 } from "fs";
595
+ import { join as join9, dirname as dirname5 } from "path";
594
596
  import { fileURLToPath } from "url";
595
597
  import { InteractiveSession as InteractiveSession2, projectPaths } from "@robota-sdk/agent-sdk";
596
598
  import { SessionStore } from "@robota-sdk/agent-sessions";
@@ -647,7 +649,9 @@ function parseCliArgs() {
647
649
  "api-key": { type: "string" },
648
650
  "api-key-env": { type: "string" },
649
651
  "set-current": { type: "boolean", default: false },
650
- "settings-scope": { type: "string" }
652
+ "settings-scope": { type: "string" },
653
+ "check-update": { type: "boolean", default: false },
654
+ "disable-update-check": { type: "boolean", default: false }
651
655
  }
652
656
  });
653
657
  return {
@@ -678,7 +682,9 @@ function parseCliArgs() {
678
682
  apiKey: values["api-key"],
679
683
  apiKeyEnv: values["api-key-env"],
680
684
  setCurrent: values["set-current"] ?? false,
681
- settingsScope: values["settings-scope"]
685
+ settingsScope: values["settings-scope"],
686
+ checkUpdate: values["check-update"] ?? false,
687
+ disableUpdateCheck: values["disable-update-check"] ?? false
682
688
  };
683
689
  }
684
690
 
@@ -766,7 +772,7 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
766
772
  if (!profile) {
767
773
  return false;
768
774
  }
769
- if (profile.apiKey) {
775
+ if (hasUsableSecretReference(profile.apiKey)) {
770
776
  return true;
771
777
  }
772
778
  if (!type) {
@@ -776,7 +782,7 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
776
782
  if (definition === void 0) {
777
783
  return false;
778
784
  }
779
- return definition.requiresApiKey !== true || definition.defaults?.apiKey !== void 0;
785
+ return definition.requiresApiKey !== true || hasUsableSecretReference(definition.defaults?.apiKey);
780
786
  }
781
787
 
782
788
  // src/utils/provider-settings.ts
@@ -806,7 +812,7 @@ function validateProviderProfile(profileName, profile, options = {}) {
806
812
  throw new Error(`Provider profile "${profileName}" is missing model`);
807
813
  }
808
814
  const definition = findProviderDefinition(options.providerDefinitions ?? [], profile.type);
809
- if (definition?.requiresApiKey === true && !profile.apiKey && definition.defaults?.apiKey === void 0) {
815
+ if (definition?.requiresApiKey === true && !hasUsableSecretReference(profile.apiKey ?? definition.defaults?.apiKey)) {
810
816
  throw new Error(`Provider profile "${profileName}" is missing apiKey`);
811
817
  }
812
818
  }
@@ -873,6 +879,40 @@ function createProviderSetupFlow(type, providerDefinitions) {
873
879
  values: {}
874
880
  };
875
881
  }
882
+ function formatProviderSetupSelectionPrompt(providerDefinitions) {
883
+ if (providerDefinitions.length === 0) {
884
+ return " No providers are available.";
885
+ }
886
+ const lines = [
887
+ " Select provider:",
888
+ ...providerDefinitions.map(
889
+ (definition, index) => ` ${index + 1}. ${formatProviderSetupChoiceLabel(definition)}`
890
+ ),
891
+ ` Provider [1-${providerDefinitions.length}] (default: 1): `
892
+ ];
893
+ return lines.join("\n");
894
+ }
895
+ function resolveProviderSetupSelection(rawValue, providerDefinitions) {
896
+ const value = rawValue.trim();
897
+ const selectedValue = value.length > 0 ? value : "1";
898
+ const index = parseProviderSelectionIndex(selectedValue);
899
+ if (index !== void 0) {
900
+ const definition2 = providerDefinitions[index];
901
+ if (definition2 !== void 0) {
902
+ return definition2.type;
903
+ }
904
+ throw new Error(
905
+ `Provider selection ${selectedValue} is out of range. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
906
+ );
907
+ }
908
+ const definition = findProviderDefinition(providerDefinitions, selectedValue);
909
+ if (definition === void 0) {
910
+ throw new Error(
911
+ `Unknown provider: ${selectedValue}. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
912
+ );
913
+ }
914
+ return definition.type;
915
+ }
876
916
  function getProviderSetupStep(state) {
877
917
  const step = state.steps[state.stepIndex];
878
918
  if (step === void 0) {
@@ -918,6 +958,16 @@ function formatProviderSetupPromptLabel(step) {
918
958
  const suffix = step.defaultValue !== void 0 ? ` (default: ${step.defaultValue})` : "";
919
959
  return ` ${step.title}${suffix}: `;
920
960
  }
961
+ function formatProviderSetupChoiceLabel(definition) {
962
+ const label = definition.displayName !== void 0 ? `${definition.displayName} (${definition.type})` : definition.type;
963
+ return definition.description !== void 0 ? `${label} - ${definition.description}` : label;
964
+ }
965
+ function parseProviderSelectionIndex(value) {
966
+ if (!/^\d+$/.test(value)) {
967
+ return void 0;
968
+ }
969
+ return Number(value) - 1;
970
+ }
921
971
  function validateProviderSetupValue(step, value) {
922
972
  if (step.required === true && value.length === 0) {
923
973
  return "Required";
@@ -1010,15 +1060,13 @@ async function ensureConfig(cwd, args, promptInput2, providerDefinitions = DEFAU
1010
1060
  return;
1011
1061
  }
1012
1062
  if (!isInteractiveTerminal()) {
1013
- throw new Error(formatMissingProviderConfigMessage());
1063
+ throw new Error(formatMissingProviderConfigMessage(providerDefinitions));
1014
1064
  }
1015
1065
  await runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions);
1016
1066
  }
1017
1067
  async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
1018
- const defaultProviderType = providerDefinitions[0]?.type ?? "";
1019
- const supportedTypes = providerDefinitions.map((definition) => definition.type).join("/");
1020
- const providerChoice = await promptInput2(` Provider (${supportedTypes}, default: ${defaultProviderType}): `) || defaultProviderType;
1021
- const type = parseProviderSetupType(providerChoice);
1068
+ const providerChoice = await promptInput2(formatProviderSetupSelectionPrompt(providerDefinitions));
1069
+ const type = resolveProviderSetupSelection(providerChoice, providerDefinitions);
1022
1070
  const settingsPath = getSettingsPathForScope(cwd, args.settingsScope);
1023
1071
  const input = await runProviderSetupPromptFlow(type, promptInput2, providerDefinitions);
1024
1072
  applyProviderConfiguration(settingsPath, input, {
@@ -1035,9 +1083,6 @@ async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefi
1035
1083
 
1036
1084
  `);
1037
1085
  }
1038
- function parseProviderSetupType(value) {
1039
- return value.trim();
1040
- }
1041
1086
  function buildSetupInputFromArgs(args) {
1042
1087
  const type = args.providerType ?? args.configureProvider;
1043
1088
  if (!args.configureProvider || !type) {
@@ -1066,14 +1111,25 @@ function getSettingsCheckPaths(cwd) {
1066
1111
  function isInteractiveTerminal() {
1067
1112
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
1068
1113
  }
1069
- function formatMissingProviderConfigMessage() {
1114
+ function formatMissingProviderConfigMessage(providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
1070
1115
  return [
1071
1116
  "No provider configuration found.",
1072
1117
  "Run `robota --configure` in an interactive terminal, or configure a provider:",
1073
- " robota --configure-provider gemma --type gemma --base-url http://localhost:1234/v1 --model supergemma4-26b-uncensored-v2 --api-key lm-studio --set-current",
1074
- " robota --configure-provider openai --type openai --model <openai-compatible-model> --api-key-env OPENAI_API_KEY --set-current"
1118
+ `Supported providers: ${formatSupportedProviderTypes(providerDefinitions)}`,
1119
+ ...providerDefinitions.map(formatConfigureProviderExample)
1075
1120
  ].join("\n");
1076
1121
  }
1122
+ function formatConfigureProviderExample(definition) {
1123
+ const flags = [
1124
+ `robota --configure-provider ${definition.type}`,
1125
+ `--type ${definition.type}`,
1126
+ ...definition.defaults?.baseURL !== void 0 ? ["--base-url <url>"] : [],
1127
+ "--model <model>",
1128
+ ...definition.requiresApiKey === true ? ["--api-key-env <ENV_NAME>"] : [],
1129
+ "--set-current"
1130
+ ];
1131
+ return ` ${flags.join(" ")}`;
1132
+ }
1077
1133
 
1078
1134
  // src/cli.ts
1079
1135
  import { createHeadlessTransport } from "@robota-sdk/agent-transport-headless";
@@ -1083,8 +1139,8 @@ import { render } from "ink";
1083
1139
 
1084
1140
  // src/ui/App.tsx
1085
1141
  import { useState as useState14, useEffect as useEffect4 } from "react";
1086
- import { Box as Box14, Text as Text16, useApp as useApp2, useInput as useInput8 } from "ink";
1087
- import { getModelName as getModelName2, createSystemMessage as createSystemMessage4, messageToHistoryEntry as messageToHistoryEntry4 } from "@robota-sdk/agent-core";
1142
+ import { Box as Box18, Text as Text20, useApp as useApp2, useInput as useInput8 } from "ink";
1143
+ import { getModelName as getModelName3, createSystemMessage as createSystemMessage4, messageToHistoryEntry as messageToHistoryEntry4 } from "@robota-sdk/agent-core";
1088
1144
 
1089
1145
  // src/ui/hooks/useInteractiveSession.ts
1090
1146
  import { useState, useRef, useCallback as useCallback2, useEffect } from "react";
@@ -1120,7 +1176,9 @@ function toBackgroundTaskViewModel(state, partialText) {
1120
1176
  errorPreview: trimBackgroundPreview(state.error?.message),
1121
1177
  startedAt: state.startedAt,
1122
1178
  lastActivityAt: state.lastActivityAt,
1123
- timeoutReason: state.timeoutReason
1179
+ timeoutReason: state.timeoutReason,
1180
+ exitCode: state.result?.exitCode,
1181
+ signalCode: state.result?.signalCode
1124
1182
  };
1125
1183
  }
1126
1184
  function getBackgroundTaskStatusLabel(state) {
@@ -1241,6 +1299,13 @@ var TuiStateManager = class {
1241
1299
  this.activeTools = [];
1242
1300
  this.notify();
1243
1301
  };
1302
+ onContextUpdate = (state) => {
1303
+ this.setContextState({
1304
+ percentage: state.usedPercentage,
1305
+ usedTokens: state.usedTokens,
1306
+ maxTokens: state.maxTokens
1307
+ });
1308
+ };
1244
1309
  onBackgroundTaskEvent = (event) => {
1245
1310
  if ("task" in event) {
1246
1311
  this.upsertBackgroundTask(event.task);
@@ -1343,7 +1408,10 @@ var TuiStateManager = class {
1343
1408
  // src/ui/hooks/useSlashRouting.ts
1344
1409
  import { useCallback } from "react";
1345
1410
  import { randomUUID as randomUUID2 } from "crypto";
1346
- import { createSystemMessage, messageToHistoryEntry } from "@robota-sdk/agent-core";
1411
+ import {
1412
+ createSystemMessage,
1413
+ messageToHistoryEntry
1414
+ } from "@robota-sdk/agent-core";
1347
1415
 
1348
1416
  // src/utils/provider-command.ts
1349
1417
  async function handleProviderCommand(cwd, args, deps = {}) {
@@ -1415,7 +1483,14 @@ function buildProviderSwitch(providers, profileName) {
1415
1483
  };
1416
1484
  }
1417
1485
  function buildProviderSetup(type, providerDefinitions) {
1418
- if (!type || findProviderDefinition(providerDefinitions, type) === void 0) {
1486
+ if (!type) {
1487
+ return {
1488
+ message: "Provider setup requested. Select a provider to continue.",
1489
+ success: true,
1490
+ data: { providerSetup: {} }
1491
+ };
1492
+ }
1493
+ if (findProviderDefinition(providerDefinitions, type) === void 0) {
1419
1494
  return {
1420
1495
  message: `Usage: provider add <type>. Supported: ${formatSupportedProviderTypes(providerDefinitions)}`,
1421
1496
  success: false
@@ -1503,8 +1578,8 @@ async function routeProviderCommand(cwd, args, interactiveSession, manager, prov
1503
1578
  getEffects(interactiveSession)._pendingProviderProfile = providerSwitch.profile;
1504
1579
  }
1505
1580
  const providerSetup = result.data?.providerSetup;
1506
- if (providerSetup?.type) {
1507
- getEffects(interactiveSession)._pendingProviderSetupType = providerSetup.type;
1581
+ if (providerSetup !== void 0) {
1582
+ getEffects(interactiveSession)._pendingProviderSetup = providerSetup;
1508
1583
  }
1509
1584
  }
1510
1585
  function applySystemCommandResult(result, interactiveSession, manager) {
@@ -1531,6 +1606,11 @@ function applySystemCommandResult(result, interactiveSession, manager) {
1531
1606
  effects._sessionName = data.name;
1532
1607
  return;
1533
1608
  }
1609
+ const statusLinePatch = data?.statuslinePatch;
1610
+ if (isStatusLineSettingsPatch(statusLinePatch)) {
1611
+ effects._statusLinePatch = statusLinePatch;
1612
+ return;
1613
+ }
1534
1614
  const ctx = interactiveSession.getContextState();
1535
1615
  manager.setContextState({
1536
1616
  percentage: ctx.usedPercentage,
@@ -1575,6 +1655,13 @@ function routeTuiCommand(cmd, interactiveSession) {
1575
1655
  function getEffects(interactiveSession) {
1576
1656
  return interactiveSession;
1577
1657
  }
1658
+ function isStatusLineSettingsPatch(value) {
1659
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) {
1660
+ return false;
1661
+ }
1662
+ const candidate = value;
1663
+ return (candidate.enabled === void 0 || typeof candidate.enabled === "boolean") && (candidate.gitBranch === void 0 || typeof candidate.gitBranch === "boolean");
1664
+ }
1578
1665
 
1579
1666
  // src/ui/hooks/useInteractiveSession.ts
1580
1667
  function initializeSession(props, permissionHandler) {
@@ -1637,8 +1724,8 @@ function useInteractiveSession(props) {
1637
1724
  });
1638
1725
  }, []);
1639
1726
  const permissionHandler = useCallback2(
1640
- (toolName, toolArgs) => new Promise((resolve) => {
1641
- permissionQueueRef.current.push({ toolName, toolArgs, resolve });
1727
+ (toolName, toolArgs) => new Promise((resolve2) => {
1728
+ permissionQueueRef.current.push({ toolName, toolArgs, resolve: resolve2 });
1642
1729
  processNextPermission();
1643
1730
  }),
1644
1731
  [processNextPermission]
@@ -1663,6 +1750,7 @@ function useInteractiveSession(props) {
1663
1750
  interactiveSession.on("complete", manager.onComplete);
1664
1751
  interactiveSession.on("interrupted", manager.onInterrupted);
1665
1752
  interactiveSession.on("error", manager.onError);
1753
+ interactiveSession.on("context_update", manager.onContextUpdate);
1666
1754
  interactiveSession.on("background_task_event", manager.onBackgroundTaskEvent);
1667
1755
  const initCheck = setInterval(() => {
1668
1756
  try {
@@ -1689,6 +1777,7 @@ function useInteractiveSession(props) {
1689
1777
  interactiveSession.off("complete", manager.onComplete);
1690
1778
  interactiveSession.off("interrupted", manager.onInterrupted);
1691
1779
  interactiveSession.off("error", manager.onError);
1780
+ interactiveSession.off("context_update", manager.onContextUpdate);
1692
1781
  interactiveSession.off("background_task_event", manager.onBackgroundTaskEvent);
1693
1782
  };
1694
1783
  }, [interactiveSession, manager]);
@@ -1856,6 +1945,108 @@ function usePluginCallbacks(cwd) {
1856
1945
  import { useState as useState2, useRef as useRef2, useCallback as useCallback3 } from "react";
1857
1946
  import { useApp } from "ink";
1858
1947
  import { createSystemMessage as createSystemMessage3, messageToHistoryEntry as messageToHistoryEntry3, getModelName } from "@robota-sdk/agent-core";
1948
+
1949
+ // src/utils/provider-setup-interaction.ts
1950
+ function startProviderSetupInteraction(providerDefinitions, type) {
1951
+ if (type === void 0) {
1952
+ const state2 = {
1953
+ mode: "select-provider",
1954
+ providerDefinitions
1955
+ };
1956
+ return { status: "prompt", state: state2, prompt: toProviderSelectionPrompt(providerDefinitions) };
1957
+ }
1958
+ const state = {
1959
+ mode: "setup-fields",
1960
+ providerDefinitions,
1961
+ flow: createProviderSetupFlow(type, providerDefinitions)
1962
+ };
1963
+ return { status: "prompt", state, prompt: toProviderSetupStepPrompt(state.flow) };
1964
+ }
1965
+ function submitProviderSetupInteractionValue(state, value) {
1966
+ if (state.mode === "select-provider") {
1967
+ const nextState2 = {
1968
+ mode: "setup-fields",
1969
+ providerDefinitions: state.providerDefinitions,
1970
+ flow: createProviderSetupFlow(value, state.providerDefinitions)
1971
+ };
1972
+ return {
1973
+ status: "prompt",
1974
+ state: nextState2,
1975
+ prompt: toProviderSetupStepPrompt(nextState2.flow)
1976
+ };
1977
+ }
1978
+ const result = submitProviderSetupValue(state.flow, value);
1979
+ if (result.status === "complete") {
1980
+ return { status: "complete", input: result.input };
1981
+ }
1982
+ const nextState = {
1983
+ ...state,
1984
+ flow: result.state
1985
+ };
1986
+ return { status: "prompt", state: nextState, prompt: toProviderSetupStepPrompt(result.state) };
1987
+ }
1988
+ function toProviderSelectionPrompt(providerDefinitions) {
1989
+ return {
1990
+ kind: "choice",
1991
+ title: "Select provider",
1992
+ options: providerDefinitions.map((definition) => ({
1993
+ value: definition.type,
1994
+ label: formatProviderSetupChoiceLabel(definition)
1995
+ })),
1996
+ maxVisible: 6
1997
+ };
1998
+ }
1999
+ function toProviderSetupStepPrompt(flow) {
2000
+ const step = getProviderSetupStep(flow);
2001
+ return {
2002
+ kind: "text",
2003
+ title: step.title,
2004
+ ...step.defaultValue !== void 0 ? { placeholder: step.defaultValue } : {},
2005
+ ...step.defaultValue !== void 0 ? { allowEmpty: true } : {},
2006
+ ...step.masked !== void 0 ? { masked: step.masked } : {},
2007
+ validate: (value) => validateProviderSetupValue(step, value)
2008
+ };
2009
+ }
2010
+
2011
+ // src/utils/statusline-settings.ts
2012
+ var DEFAULT_STATUS_LINE_SETTINGS = {
2013
+ enabled: true,
2014
+ gitBranch: true
2015
+ };
2016
+ function readStatusLineSettings(settings) {
2017
+ const raw = settings.statusline;
2018
+ if (!isRecord(raw)) {
2019
+ return { ...DEFAULT_STATUS_LINE_SETTINGS };
2020
+ }
2021
+ return {
2022
+ enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_STATUS_LINE_SETTINGS.enabled,
2023
+ gitBranch: typeof raw.gitBranch === "boolean" ? raw.gitBranch : DEFAULT_STATUS_LINE_SETTINGS.gitBranch
2024
+ };
2025
+ }
2026
+ function applyStatusLineSettings(settingsPath, patch) {
2027
+ const settings = readSettings(settingsPath);
2028
+ const next = {
2029
+ ...readStatusLineSettings(settings),
2030
+ ...patch
2031
+ };
2032
+ settings.statusline = next;
2033
+ writeSettings(settingsPath, settings);
2034
+ return next;
2035
+ }
2036
+ function isRecord(value) {
2037
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
2038
+ }
2039
+
2040
+ // src/ui/hooks/statusline-side-effect.ts
2041
+ function applyPendingStatusLinePatch(sideEffects, setStatusLineSettings) {
2042
+ if (!sideEffects._statusLinePatch) return false;
2043
+ const patch = sideEffects._statusLinePatch;
2044
+ delete sideEffects._statusLinePatch;
2045
+ setStatusLineSettings(applyStatusLineSettings(getUserSettingsPath(), patch));
2046
+ return true;
2047
+ }
2048
+
2049
+ // src/ui/hooks/useSideEffects.ts
1859
2050
  var EXIT_DELAY_MS = 500;
1860
2051
  function useSideEffects({
1861
2052
  cwd,
@@ -1863,6 +2054,7 @@ function useSideEffects({
1863
2054
  addEntry,
1864
2055
  baseHandleSubmit,
1865
2056
  setSessionName,
2057
+ setStatusLineSettings,
1866
2058
  providerDefinitions
1867
2059
  }) {
1868
2060
  const { exit } = useApp();
@@ -1870,7 +2062,8 @@ function useSideEffects({
1870
2062
  const pendingModelChangeRef = useRef2(null);
1871
2063
  const [pendingProviderProfile, setPendingProviderProfile] = useState2(null);
1872
2064
  const pendingProviderProfileRef = useRef2(null);
1873
- const [pendingProviderSetupType, setPendingProviderSetupType] = useState2(null);
2065
+ const [pendingInteractionPrompt, setPendingInteractionPrompt] = useState2(null);
2066
+ const providerSetupInteractionRef = useRef2(null);
1874
2067
  const [showPluginTUI, setShowPluginTUI] = useState2(false);
1875
2068
  const [showSessionPicker, setShowSessionPicker] = useState2(false);
1876
2069
  const requestShutdown = useCallback3(
@@ -1913,10 +2106,14 @@ function useSideEffects({
1913
2106
  setPendingProviderProfile(profile);
1914
2107
  return;
1915
2108
  }
1916
- if (sideEffects._pendingProviderSetupType) {
1917
- const type = sideEffects._pendingProviderSetupType;
1918
- delete sideEffects._pendingProviderSetupType;
1919
- setPendingProviderSetupType(type);
2109
+ if (sideEffects._pendingProviderSetup !== void 0) {
2110
+ const setup = sideEffects._pendingProviderSetup;
2111
+ delete sideEffects._pendingProviderSetup;
2112
+ const result = startProviderSetupInteraction(providerDefinitions, setup.type);
2113
+ if (result.status === "prompt") {
2114
+ providerSetupInteractionRef.current = result.state;
2115
+ setPendingInteractionPrompt(result.prompt);
2116
+ }
1920
2117
  return;
1921
2118
  }
1922
2119
  if (sideEffects._resetRequested) {
@@ -1954,8 +2151,17 @@ function useSideEffects({
1954
2151
  setSessionName(name);
1955
2152
  return;
1956
2153
  }
2154
+ if (applyPendingStatusLinePatch(sideEffects, setStatusLineSettings)) return;
1957
2155
  },
1958
- [interactiveSession, baseHandleSubmit, addEntry, requestShutdown, setSessionName]
2156
+ [
2157
+ interactiveSession,
2158
+ baseHandleSubmit,
2159
+ addEntry,
2160
+ requestShutdown,
2161
+ setSessionName,
2162
+ setStatusLineSettings,
2163
+ providerDefinitions
2164
+ ]
1959
2165
  );
1960
2166
  const handleModelConfirm = useCallback3(
1961
2167
  (index) => {
@@ -2015,9 +2221,10 @@ function useSideEffects({
2015
2221
  },
2016
2222
  [cwd, addEntry, requestShutdown]
2017
2223
  );
2018
- const handleProviderSetupSubmit = useCallback3(
2224
+ const completeProviderSetup = useCallback3(
2019
2225
  (input) => {
2020
- setPendingProviderSetupType(null);
2226
+ providerSetupInteractionRef.current = null;
2227
+ setPendingInteractionPrompt(null);
2021
2228
  try {
2022
2229
  const settingsPath = getUserSettingsPath();
2023
2230
  applyProviderConfiguration(settingsPath, input, { providerDefinitions });
@@ -2037,15 +2244,43 @@ function useSideEffects({
2037
2244
  },
2038
2245
  [addEntry, requestShutdown, providerDefinitions]
2039
2246
  );
2040
- const handleProviderSetupCancel = useCallback3(() => {
2041
- setPendingProviderSetupType(null);
2247
+ const handleInteractionSubmit = useCallback3(
2248
+ (value) => {
2249
+ const state = providerSetupInteractionRef.current;
2250
+ if (state === null) {
2251
+ setPendingInteractionPrompt(null);
2252
+ return;
2253
+ }
2254
+ try {
2255
+ const result = submitProviderSetupInteractionValue(state, value);
2256
+ if (result.status === "complete") {
2257
+ completeProviderSetup(result.input);
2258
+ return;
2259
+ }
2260
+ providerSetupInteractionRef.current = result.state;
2261
+ setPendingInteractionPrompt(result.prompt);
2262
+ } catch (err) {
2263
+ providerSetupInteractionRef.current = null;
2264
+ setPendingInteractionPrompt(null);
2265
+ addEntry(
2266
+ messageToHistoryEntry3(
2267
+ createSystemMessage3(`Failed: ${err instanceof Error ? err.message : String(err)}`)
2268
+ )
2269
+ );
2270
+ }
2271
+ },
2272
+ [addEntry, completeProviderSetup]
2273
+ );
2274
+ const handleInteractionCancel = useCallback3(() => {
2275
+ providerSetupInteractionRef.current = null;
2276
+ setPendingInteractionPrompt(null);
2042
2277
  addEntry(messageToHistoryEntry3(createSystemMessage3("Provider setup cancelled.")));
2043
2278
  }, [addEntry]);
2044
2279
  return {
2045
2280
  handleSubmit,
2046
2281
  pendingModelId,
2047
2282
  pendingProviderProfile,
2048
- pendingProviderSetupType,
2283
+ pendingInteractionPrompt,
2049
2284
  showPluginTUI,
2050
2285
  showSessionPicker,
2051
2286
  setPendingModelId,
@@ -2053,94 +2288,336 @@ function useSideEffects({
2053
2288
  setShowSessionPicker,
2054
2289
  handleModelConfirm,
2055
2290
  handleProviderConfirm,
2056
- handleProviderSetupSubmit,
2057
- handleProviderSetupCancel
2291
+ handleInteractionSubmit,
2292
+ handleInteractionCancel
2058
2293
  };
2059
2294
  }
2060
2295
 
2296
+ // src/ui/hooks/useStatusLineSettings.ts
2297
+ import { useState as useState3 } from "react";
2298
+ function useStatusLineSettings() {
2299
+ return useState3(
2300
+ () => readStatusLineSettings(readSettings(getUserSettingsPath()))
2301
+ );
2302
+ }
2303
+
2061
2304
  // src/ui/MessageList.tsx
2062
2305
  import React from "react";
2063
- import { Box as Box2, Text as Text2 } from "ink";
2306
+ import { Box as Box4, Text as Text4 } from "ink";
2064
2307
  import { isToolMessage, isAssistantMessage } from "@robota-sdk/agent-core";
2065
2308
 
2066
2309
  // src/ui/render-markdown.ts
2067
2310
  import { marked } from "marked";
2068
2311
  import TerminalRenderer from "marked-terminal";
2069
- marked.setOptions({
2070
- renderer: new TerminalRenderer()
2071
- });
2072
- function renderMarkdown(md) {
2073
- const result = marked.parse(md);
2312
+ var ANSI_RED = "\x1B[31m";
2313
+ var ANSI_GREEN = "\x1B[32m";
2314
+ var ANSI_CYAN = "\x1B[36m";
2315
+ var ANSI_DIM = "\x1B[2m";
2316
+ var ANSI_RESET = "\x1B[0m";
2317
+ var CODE_BLOCK_INDENT = " ";
2318
+ var ZERO_COLOR = "0";
2319
+ var TerminalRendererConstructor = TerminalRenderer;
2320
+ function shouldUseColor(option) {
2321
+ if (option !== void 0) {
2322
+ return option;
2323
+ }
2324
+ if (process.env.NO_COLOR || process.env.FORCE_COLOR === ZERO_COLOR) {
2325
+ return false;
2326
+ }
2327
+ if (process.env.FORCE_COLOR) {
2328
+ return true;
2329
+ }
2330
+ return Boolean(process.stdout.isTTY);
2331
+ }
2332
+ function isDiffLanguage(language) {
2333
+ return language?.trim().toLowerCase() === "diff";
2334
+ }
2335
+ function colorizeDiffLine(line, color) {
2336
+ if (!color) {
2337
+ return line;
2338
+ }
2339
+ if (line.startsWith("+")) {
2340
+ return `${ANSI_GREEN}${line}${ANSI_RESET}`;
2341
+ }
2342
+ if (line.startsWith("-")) {
2343
+ return `${ANSI_RED}${line}${ANSI_RESET}`;
2344
+ }
2345
+ if (line.startsWith("@@")) {
2346
+ return `${ANSI_CYAN}${line}${ANSI_RESET}`;
2347
+ }
2348
+ if (line.startsWith("diff ") || line.startsWith("index ")) {
2349
+ return `${ANSI_DIM}${line}${ANSI_RESET}`;
2350
+ }
2351
+ return line;
2352
+ }
2353
+ function renderDiffCodeBlock(code, color) {
2354
+ const body = code.split("\n").map((line) => `${CODE_BLOCK_INDENT}${colorizeDiffLine(line, color)}`).join("\n");
2355
+ return `${body}
2356
+
2357
+ `;
2358
+ }
2359
+ function createTerminalRenderer(color) {
2360
+ const renderer = new TerminalRendererConstructor(void 0, { ignoreIllegals: true });
2361
+ const renderCode = renderer.code.bind(renderer);
2362
+ renderer.code = (code, language, escaped) => {
2363
+ if (isDiffLanguage(language)) {
2364
+ return renderDiffCodeBlock(code, color);
2365
+ }
2366
+ return renderCode(code, language, escaped);
2367
+ };
2368
+ return renderer;
2369
+ }
2370
+ function renderMarkdown(md, options = {}) {
2371
+ const result = marked.parse(md, {
2372
+ renderer: createTerminalRenderer(shouldUseColor(options.color))
2373
+ });
2074
2374
  return typeof result === "string" ? result.trimEnd() : md;
2075
2375
  }
2076
2376
 
2077
- // src/ui/DiffBlock.tsx
2377
+ // src/ui/ToolDiffBlock.tsx
2078
2378
  import { Box, Text } from "ink";
2079
- import { jsxs } from "react/jsx-runtime";
2379
+
2380
+ // src/utils/tool-diff-summary.ts
2080
2381
  var MAX_DIFF_LINES = 12;
2081
2382
  var TRUNCATED_SHOW = 10;
2082
- function DiffBlock({ file, lines }) {
2083
- const truncated = lines.length > MAX_DIFF_LINES;
2084
- const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
2085
- const remaining = lines.length - TRUNCATED_SHOW;
2086
- const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
2087
- const numWidth = String(maxLineNum).length;
2383
+ function buildToolDiffSummary(input) {
2384
+ const visibleLines = input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;
2385
+ const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString().length;
2386
+ const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));
2387
+ const truncated = input.lines.length > MAX_DIFF_LINES;
2388
+ return {
2389
+ file: input.file,
2390
+ markdown: ["```diff", ...body, "```"].join("\n"),
2391
+ truncated,
2392
+ remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0
2393
+ };
2394
+ }
2395
+ function formatDiffLine(line, lineNumberWidth) {
2396
+ if (line.type === "hunk") return line.text;
2397
+ const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, " ");
2398
+ if (line.type === "remove") return `- ${lineNumber} | ${line.text}`;
2399
+ if (line.type === "add") return `+ ${lineNumber} | ${line.text}`;
2400
+ return ` ${lineNumber} | ${line.text}`;
2401
+ }
2402
+ function selectVisibleDiffLines(lines) {
2403
+ const groups = groupByHunk(lines);
2404
+ const visible = [];
2405
+ for (const group of groups) {
2406
+ if (visible.length === 0 && group.length > TRUNCATED_SHOW) {
2407
+ return group.slice(0, TRUNCATED_SHOW);
2408
+ }
2409
+ if (visible.length + group.length > TRUNCATED_SHOW) break;
2410
+ visible.push(...group);
2411
+ }
2412
+ return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);
2413
+ }
2414
+ function groupByHunk(lines) {
2415
+ const groups = [];
2416
+ let current = [];
2417
+ for (const line of lines) {
2418
+ if (line.type === "hunk" && current.length > 0) {
2419
+ groups.push(current);
2420
+ current = [line];
2421
+ continue;
2422
+ }
2423
+ current.push(line);
2424
+ }
2425
+ if (current.length > 0) {
2426
+ groups.push(current);
2427
+ }
2428
+ return groups;
2429
+ }
2430
+
2431
+ // src/ui/ToolDiffBlock.tsx
2432
+ import { jsx, jsxs } from "react/jsx-runtime";
2433
+ function ToolDiffBlock({ file, lines }) {
2434
+ const summary = buildToolDiffSummary({ file, lines });
2088
2435
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, children: [
2089
- file && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
2436
+ summary.file && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
2090
2437
  "\u2502 ",
2091
- file
2438
+ summary.file
2092
2439
  ] }),
2093
- visible.map((line, i) => {
2094
- const lineNum = String(line.lineNumber).padStart(numWidth, " ");
2095
- if (line.type === "context") {
2096
- return /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
2097
- "\u2502 ",
2098
- lineNum,
2099
- " ",
2100
- line.text
2101
- ] }, i);
2102
- }
2103
- const prefix = line.type === "remove" ? "-" : "+";
2104
- const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
2105
- const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
2106
- return /* @__PURE__ */ jsxs(Text, { color: fgColor, backgroundColor: bgColor, children: [
2107
- "\u2502 ",
2108
- lineNum,
2109
- " ",
2110
- prefix,
2111
- " ",
2112
- line.text
2113
- ] }, i);
2114
- }),
2115
- truncated && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
2440
+ /* @__PURE__ */ jsx(Text, { children: renderMarkdown(summary.markdown) }),
2441
+ summary.truncated && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
2116
2442
  "\u2502 ... and ",
2117
- remaining,
2443
+ summary.remainingLineCount,
2118
2444
  " more lines"
2119
2445
  ] })
2120
2446
  ] });
2121
2447
  }
2122
2448
 
2449
+ // src/ui/UsageSummaryEntry.tsx
2450
+ import { Box as Box2, Text as Text2 } from "ink";
2451
+ import { formatTokenCount } from "@robota-sdk/agent-core";
2452
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2453
+ var TOKEN_COMPACT_THRESHOLD = 1e3;
2454
+ function UsageSummaryEntry({ entry }) {
2455
+ const usage = entry.data;
2456
+ if (!usage) return /* @__PURE__ */ jsx2(Fragment, {});
2457
+ const prompt = usage.promptTokens !== void 0 ? formatUsageTokenCount(usage.promptTokens) : "?";
2458
+ const completion = usage.completionTokens !== void 0 ? formatUsageTokenCount(usage.completionTokens) : "?";
2459
+ const total = formatUsageTokenCount(usage.totalTokens);
2460
+ const context = `${Math.round(usage.contextUsedPercentage)}% (${formatTokenCount(
2461
+ usage.contextUsedTokens
2462
+ )}/${formatTokenCount(usage.contextMaxTokens)})`;
2463
+ const costLabel = usage.costStatus === "unknown" ? "cost unknown" : `cost ${usage.costStatus}`;
2464
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs2(Box2, { children: [
2465
+ /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
2466
+ "Usage:",
2467
+ " "
2468
+ ] }),
2469
+ /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
2470
+ usage.kind,
2471
+ " ",
2472
+ total,
2473
+ " tokens (in ",
2474
+ prompt,
2475
+ " / out ",
2476
+ completion,
2477
+ ") \xB7 Context ",
2478
+ context,
2479
+ " \xB7",
2480
+ " ",
2481
+ costLabel
2482
+ ] })
2483
+ ] }) });
2484
+ }
2485
+ function formatUsageTokenCount(tokens) {
2486
+ return tokens < TOKEN_COMPACT_THRESHOLD ? tokens.toLocaleString() : formatTokenCount(tokens);
2487
+ }
2488
+
2489
+ // src/ui/command-output-summary.ts
2490
+ var MAX_PREVIEW_LINES = 4;
2491
+ var SUCCESS_EXIT_CODE2 = 0;
2492
+ var COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set(["Bash", "BackgroundProcess"]);
2493
+ function formatCommandOutputSummary(tool) {
2494
+ if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return void 0;
2495
+ const parsed = parseToolResultData(tool.toolResultData);
2496
+ const exitCode = getNumberValue(parsed, "exitCode");
2497
+ const successValue = getBooleanValue(parsed, "success");
2498
+ const output = buildOutputText(tool.toolResultData, parsed);
2499
+ const lines = trimTrailingBlankLines(splitOutputLines(output));
2500
+ const previewLines = lines.slice(0, MAX_PREVIEW_LINES);
2501
+ const omittedLineCount = Math.max(0, lines.length - previewLines.length);
2502
+ const isFailed = tool.result === "error" || successValue === false || exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2;
2503
+ return {
2504
+ status: isFailed ? "error" : "success",
2505
+ statusLabel: formatStatusLabel(isFailed, exitCode),
2506
+ previewLines,
2507
+ omittedLineCount,
2508
+ transcriptHint: omittedLineCount > 0 ? `... +${omittedLineCount} lines (full output in session transcript)` : void 0
2509
+ };
2510
+ }
2511
+ function parseToolResultData(value) {
2512
+ try {
2513
+ return JSON.parse(value);
2514
+ } catch {
2515
+ return value;
2516
+ }
2517
+ }
2518
+ function buildOutputText(raw, parsed) {
2519
+ if (!isUniversalObject(parsed)) return raw;
2520
+ const output = getStringValue(parsed, "output");
2521
+ if (output !== void 0) return output;
2522
+ const stdout = getStringValue(parsed, "stdout");
2523
+ const stderr = getStringValue(parsed, "stderr");
2524
+ const error = getStringValue(parsed, "error");
2525
+ const lines = [];
2526
+ if (stdout) lines.push(stdout);
2527
+ if (stderr) lines.push(prefixLines(stderr, "[stderr] "));
2528
+ if (!stdout && !stderr && error) lines.push(error);
2529
+ return lines.join("\n");
2530
+ }
2531
+ function formatStatusLabel(isFailed, exitCode) {
2532
+ if (exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2) return `exit ${exitCode}`;
2533
+ return isFailed ? "error" : "ok";
2534
+ }
2535
+ function splitOutputLines(output) {
2536
+ if (!output) return [];
2537
+ return output.replace(/\r\n/g, "\n").split("\n");
2538
+ }
2539
+ function trimTrailingBlankLines(lines) {
2540
+ let end = lines.length;
2541
+ while (end > 0 && lines[end - 1].trim().length === 0) {
2542
+ end -= 1;
2543
+ }
2544
+ return lines.slice(0, end);
2545
+ }
2546
+ function prefixLines(value, prefix) {
2547
+ return splitOutputLines(value).map((line) => `${prefix}${line}`).join("\n");
2548
+ }
2549
+ function isUniversalObject(value) {
2550
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
2551
+ }
2552
+ function getStringValue(source, key) {
2553
+ if (!isUniversalObject(source)) return void 0;
2554
+ const value = source[key];
2555
+ return typeof value === "string" ? value : void 0;
2556
+ }
2557
+ function getNumberValue(source, key) {
2558
+ if (!isUniversalObject(source)) return void 0;
2559
+ const value = source[key];
2560
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2561
+ }
2562
+ function getBooleanValue(source, key) {
2563
+ if (!isUniversalObject(source)) return void 0;
2564
+ const value = source[key];
2565
+ return typeof value === "boolean" ? value : void 0;
2566
+ }
2567
+
2568
+ // src/ui/ToolCommandOutput.tsx
2569
+ import { Box as Box3, Text as Text3 } from "ink";
2570
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2571
+ function ToolCommandOutput({ tool }) {
2572
+ const summary = formatCommandOutputSummary(tool);
2573
+ if (!summary) return null;
2574
+ if (summary.statusLabel === "ok" && summary.previewLines.length === 0 && !summary.transcriptHint) {
2575
+ return null;
2576
+ }
2577
+ const color = summary.status === "error" ? "red" : "white";
2578
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 4, children: [
2579
+ summary.statusLabel !== "ok" && /* @__PURE__ */ jsx3(Text3, { color, dimColor: true, children: summary.statusLabel }),
2580
+ summary.previewLines.map((line, index) => /* @__PURE__ */ jsx3(Text3, { color, dimColor: true, children: line }, `${line}-${index}`)),
2581
+ summary.transcriptHint && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: summary.transcriptHint })
2582
+ ] });
2583
+ }
2584
+
2123
2585
  // src/ui/MessageList.tsx
2124
- import { Fragment, jsx, jsxs as jsxs2 } from "react/jsx-runtime";
2586
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2587
+ function getToolSummaryStatus(tool) {
2588
+ if (formatCommandOutputSummary(tool)?.status === "error") return "\u2717";
2589
+ if (tool.isRunning) return "\u27F3";
2590
+ if (tool.result === "error") return "\u2717";
2591
+ if (tool.result === "denied") return "\u2298";
2592
+ return "\u2713";
2593
+ }
2594
+ function getToolSummaryColor(tool) {
2595
+ if (formatCommandOutputSummary(tool)?.status === "error" || tool.result === "error") return "red";
2596
+ if (tool.isRunning || tool.result === "denied") return "yellow";
2597
+ return "green";
2598
+ }
2599
+ function getToolSummaryLabel(tool) {
2600
+ return `${getToolSummaryStatus(tool)} ${tool.toolName}${tool.firstArg ? `(${tool.firstArg})` : ""}`;
2601
+ }
2125
2602
  function RoleLabel({ role }) {
2126
2603
  switch (role) {
2127
2604
  case "user":
2128
- return /* @__PURE__ */ jsxs2(Text2, { color: "green", bold: true, children: [
2605
+ return /* @__PURE__ */ jsxs4(Text4, { color: "green", bold: true, children: [
2129
2606
  "You:",
2130
2607
  " "
2131
2608
  ] });
2132
2609
  case "assistant":
2133
- return /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
2610
+ return /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
2134
2611
  "Robota:",
2135
2612
  " "
2136
2613
  ] });
2137
2614
  case "system":
2138
- return /* @__PURE__ */ jsxs2(Text2, { color: "yellow", bold: true, children: [
2615
+ return /* @__PURE__ */ jsxs4(Text4, { color: "yellow", bold: true, children: [
2139
2616
  "System:",
2140
2617
  " "
2141
2618
  ] });
2142
2619
  case "tool":
2143
- return /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
2620
+ return /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
2144
2621
  "Tool:",
2145
2622
  " "
2146
2623
  ] });
@@ -2148,7 +2625,7 @@ function RoleLabel({ role }) {
2148
2625
  }
2149
2626
  function ToolMessage({ message }) {
2150
2627
  if (!isToolMessage(message)) {
2151
- return /* @__PURE__ */ jsx(Fragment, {});
2628
+ return /* @__PURE__ */ jsx4(Fragment2, {});
2152
2629
  }
2153
2630
  const toolName = message.name;
2154
2631
  const content = message.content;
@@ -2161,45 +2638,45 @@ function ToolMessage({ message }) {
2161
2638
  } catch {
2162
2639
  }
2163
2640
  if (summaries) {
2164
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2165
- /* @__PURE__ */ jsxs2(Box2, { children: [
2166
- /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
2641
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
2642
+ /* @__PURE__ */ jsxs4(Box4, { children: [
2643
+ /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
2167
2644
  "Tool:",
2168
2645
  " "
2169
2646
  ] }),
2170
- toolName && /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
2647
+ toolName && /* @__PURE__ */ jsxs4(Text4, { color: "white", dimColor: true, children: [
2171
2648
  "[",
2172
2649
  toolName,
2173
2650
  "]"
2174
2651
  ] })
2175
2652
  ] }),
2176
- /* @__PURE__ */ jsx(Text2, { children: " " }),
2177
- summaries.map((s, i) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
2178
- /* @__PURE__ */ jsxs2(Text2, { color: "green", children: [
2653
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2654
+ summaries.map((s, i) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
2655
+ /* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
2179
2656
  " ",
2180
2657
  "\u2713",
2181
2658
  " ",
2182
2659
  s.line
2183
2660
  ] }),
2184
- s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ jsx(DiffBlock, { file: s.diffFile, lines: s.diffLines })
2661
+ s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ jsx4(ToolDiffBlock, { file: s.diffFile, lines: s.diffLines })
2185
2662
  ] }, i))
2186
2663
  ] });
2187
2664
  }
2188
2665
  const lines = content.split("\n").filter((l) => l.trim());
2189
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2190
- /* @__PURE__ */ jsxs2(Box2, { children: [
2191
- /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
2666
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
2667
+ /* @__PURE__ */ jsxs4(Box4, { children: [
2668
+ /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
2192
2669
  "Tool:",
2193
2670
  " "
2194
2671
  ] }),
2195
- toolName && /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
2672
+ toolName && /* @__PURE__ */ jsxs4(Text4, { color: "white", dimColor: true, children: [
2196
2673
  "[",
2197
2674
  toolName,
2198
2675
  "]"
2199
2676
  ] })
2200
2677
  ] }),
2201
- /* @__PURE__ */ jsx(Text2, { children: " " }),
2202
- lines.map((line, i) => /* @__PURE__ */ jsxs2(Text2, { color: "green", children: [
2678
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2679
+ lines.map((line, i) => /* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
2203
2680
  " ",
2204
2681
  "\u2713",
2205
2682
  " ",
@@ -2211,26 +2688,44 @@ var MessageItem = React.memo(function MessageItem2({
2211
2688
  message
2212
2689
  }) {
2213
2690
  if (isToolMessage(message)) {
2214
- return /* @__PURE__ */ jsx(ToolMessage, { message });
2691
+ return /* @__PURE__ */ jsx4(ToolMessage, { message });
2215
2692
  }
2216
2693
  const content = message.content ?? "";
2217
2694
  const isInterrupted = message.state === "interrupted";
2218
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2219
- /* @__PURE__ */ jsx(Box2, { children: /* @__PURE__ */ jsx(RoleLabel, { role: message.role }) }),
2220
- /* @__PURE__ */ jsx(Text2, { children: " " }),
2221
- /* @__PURE__ */ jsx(Box2, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text2, { wrap: "wrap", children: isAssistantMessage(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
2695
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
2696
+ /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(RoleLabel, { role: message.role }) }),
2697
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2698
+ /* @__PURE__ */ jsx4(Box4, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { wrap: "wrap", children: isAssistantMessage(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
2222
2699
  ] });
2223
2700
  });
2224
2701
  function ToolSummaryEntry({ entry }) {
2225
2702
  const data = entry.data;
2703
+ const tools = data?.tools;
2226
2704
  const lines = data?.summary?.split("\n") ?? [];
2227
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2228
- /* @__PURE__ */ jsx(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
2705
+ if (tools && tools.length > 0) {
2706
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
2707
+ /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
2708
+ "Tool:",
2709
+ " "
2710
+ ] }) }),
2711
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2712
+ tools.map((tool, i) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
2713
+ /* @__PURE__ */ jsxs4(Text4, { color: getToolSummaryColor(tool), children: [
2714
+ " ",
2715
+ getToolSummaryLabel(tool)
2716
+ ] }),
2717
+ /* @__PURE__ */ jsx4(ToolCommandOutput, { tool }),
2718
+ tool.diffLines && tool.diffLines.length > 0 && /* @__PURE__ */ jsx4(ToolDiffBlock, { file: tool.diffFile, lines: tool.diffLines })
2719
+ ] }, i))
2720
+ ] });
2721
+ }
2722
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
2723
+ /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
2229
2724
  "Tool:",
2230
2725
  " "
2231
2726
  ] }) }),
2232
- /* @__PURE__ */ jsx(Text2, { children: " " }),
2233
- lines.map((line, i) => /* @__PURE__ */ jsxs2(Text2, { color: "green", children: [
2727
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2728
+ lines.map((line, i) => /* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
2234
2729
  " ",
2235
2730
  line
2236
2731
  ] }, i))
@@ -2239,36 +2734,134 @@ function ToolSummaryEntry({ entry }) {
2239
2734
  function EventEntry({ entry }) {
2240
2735
  const eventData = entry.data;
2241
2736
  const eventMessage = typeof eventData?.message === "string" ? eventData.message : typeof eventData?.content === "string" ? eventData.content : entry.type;
2242
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2243
- /* @__PURE__ */ jsx(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "yellow", bold: true, children: [
2737
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
2738
+ /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "yellow", bold: true, children: [
2244
2739
  "System:",
2245
2740
  " "
2246
2741
  ] }) }),
2247
- /* @__PURE__ */ jsx(Text2, { children: " " }),
2248
- /* @__PURE__ */ jsx(Box2, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text2, { wrap: "wrap", children: eventMessage }) })
2742
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2743
+ /* @__PURE__ */ jsx4(Box4, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { wrap: "wrap", children: eventMessage }) })
2249
2744
  ] });
2250
2745
  }
2251
2746
  function EntryItem({ entry }) {
2252
2747
  if (entry.category === "chat") {
2253
2748
  const message = entry.data;
2254
- return /* @__PURE__ */ jsx(MessageItem, { message });
2749
+ return /* @__PURE__ */ jsx4(MessageItem, { message });
2255
2750
  }
2256
2751
  if (entry.type === "tool-summary") {
2257
- return /* @__PURE__ */ jsx(ToolSummaryEntry, { entry });
2752
+ return /* @__PURE__ */ jsx4(ToolSummaryEntry, { entry });
2753
+ }
2754
+ if (entry.type === "usage-summary") {
2755
+ return /* @__PURE__ */ jsx4(UsageSummaryEntry, { entry });
2258
2756
  }
2259
2757
  if (entry.type === "tool-start" || entry.type === "tool-end") {
2260
- return /* @__PURE__ */ jsx(Fragment, {});
2758
+ return /* @__PURE__ */ jsx4(Fragment2, {});
2261
2759
  }
2262
- return /* @__PURE__ */ jsx(EventEntry, { entry });
2760
+ return /* @__PURE__ */ jsx4(EventEntry, { entry });
2263
2761
  }
2264
2762
  function MessageList({ history }) {
2265
- return /* @__PURE__ */ jsx(Box2, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ jsx(EntryItem, { entry }, entry.id)) });
2763
+ return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ jsx4(EntryItem, { entry }, entry.id)) });
2764
+ }
2765
+
2766
+ // src/ui/SessionStatusBar.tsx
2767
+ import { useMemo as useMemo2 } from "react";
2768
+ import { getModelName as getModelName2 } from "@robota-sdk/agent-core";
2769
+
2770
+ // src/utils/git-branch.ts
2771
+ import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync4 } from "fs";
2772
+ import { dirname as dirname3, isAbsolute, join as join7, resolve } from "path";
2773
+ var DETACHED_HEAD_LENGTH = 7;
2774
+ function resolveGitBranch(cwd) {
2775
+ try {
2776
+ const gitDir = findGitDir(cwd);
2777
+ if (!gitDir) return void 0;
2778
+ const head = readFileSync4(join7(gitDir, "HEAD"), "utf8").trim();
2779
+ if (!head) return void 0;
2780
+ if (head.startsWith("ref: ")) {
2781
+ const ref = head.slice("ref: ".length).trim();
2782
+ const branchPrefix = "refs/heads/";
2783
+ return ref.startsWith(branchPrefix) ? ref.slice(branchPrefix.length) : ref;
2784
+ }
2785
+ return head.slice(0, DETACHED_HEAD_LENGTH);
2786
+ } catch {
2787
+ return void 0;
2788
+ }
2789
+ }
2790
+ function findGitDir(start) {
2791
+ let current = resolve(start);
2792
+ let parent = dirname3(current);
2793
+ while (parent !== current) {
2794
+ const candidate = join7(current, ".git");
2795
+ const resolved = resolveGitMetadata(candidate, current);
2796
+ if (resolved) return resolved;
2797
+ current = parent;
2798
+ parent = dirname3(current);
2799
+ }
2800
+ const rootCandidate = join7(current, ".git");
2801
+ return resolveGitMetadata(rootCandidate, current);
2802
+ }
2803
+ function resolveGitMetadata(candidate, repoDir) {
2804
+ if (!existsSync4(candidate)) return void 0;
2805
+ const stat = lstatSync(candidate);
2806
+ if (stat.isDirectory()) return candidate;
2807
+ if (!stat.isFile()) return void 0;
2808
+ const content = readFileSync4(candidate, "utf8").trim();
2809
+ const prefix = "gitdir:";
2810
+ if (!content.startsWith(prefix)) return void 0;
2811
+ const rawPath = content.slice(prefix.length).trim();
2812
+ return isAbsolute(rawPath) ? rawPath : resolve(repoDir, rawPath);
2266
2813
  }
2267
2814
 
2268
2815
  // src/ui/StatusBar.tsx
2269
- import { Box as Box3, Text as Text3 } from "ink";
2270
- import { formatTokenCount } from "@robota-sdk/agent-core";
2271
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
2816
+ import { Box as Box5, Text as Text5 } from "ink";
2817
+ import { formatTokenCount as formatTokenCount2 } from "@robota-sdk/agent-core";
2818
+
2819
+ // src/ui/status-activity.ts
2820
+ var NO_ACTIVE_ITEMS = 0;
2821
+ function formatStatusActivity(input) {
2822
+ const base = getPrimaryActivity(input);
2823
+ const segments = input.hasPendingPrompt && base.kind !== "queued" ? ["queued"] : [];
2824
+ const text = [base.label, ...segments].join(" \xB7 ");
2825
+ return { ...base, segments, text };
2826
+ }
2827
+ function getPrimaryActivity(input) {
2828
+ if (input.activeToolCount > NO_ACTIVE_ITEMS) {
2829
+ return {
2830
+ kind: "tools",
2831
+ label: `Tools x${input.activeToolCount}`,
2832
+ color: "cyan"
2833
+ };
2834
+ }
2835
+ if (input.isThinking) {
2836
+ return {
2837
+ kind: "thinking",
2838
+ label: "Thinking",
2839
+ color: "yellow"
2840
+ };
2841
+ }
2842
+ if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {
2843
+ return {
2844
+ kind: "background",
2845
+ label: `Background x${input.activeBackgroundTaskCount}`,
2846
+ color: "cyan"
2847
+ };
2848
+ }
2849
+ if (input.hasPendingPrompt) {
2850
+ return {
2851
+ kind: "queued",
2852
+ label: "Queued",
2853
+ color: "yellow"
2854
+ };
2855
+ }
2856
+ return {
2857
+ kind: "idle",
2858
+ label: "Idle",
2859
+ color: "gray"
2860
+ };
2861
+ }
2862
+
2863
+ // src/ui/StatusBar.tsx
2864
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2272
2865
  var CONTEXT_YELLOW_THRESHOLD = 70;
2273
2866
  var CONTEXT_RED_THRESHOLD = 90;
2274
2867
  function getContextColor(percentage) {
@@ -2276,20 +2869,115 @@ function getContextColor(percentage) {
2276
2869
  if (percentage >= CONTEXT_YELLOW_THRESHOLD) return "yellow";
2277
2870
  return "green";
2278
2871
  }
2872
+ function StatusActivityText({
2873
+ isThinking,
2874
+ activeToolCount,
2875
+ activeBackgroundTaskCount,
2876
+ hasPendingPrompt
2877
+ }) {
2878
+ const activity = formatStatusActivity({
2879
+ isThinking,
2880
+ activeToolCount,
2881
+ activeBackgroundTaskCount,
2882
+ hasPendingPrompt
2883
+ });
2884
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
2885
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "Activity:" }),
2886
+ " ",
2887
+ /* @__PURE__ */ jsx5(Text5, { color: activity.color, bold: activity.kind !== "idle", children: activity.text })
2888
+ ] });
2889
+ }
2890
+ function ContextText({
2891
+ percentage,
2892
+ usedTokens,
2893
+ maxTokens
2894
+ }) {
2895
+ return /* @__PURE__ */ jsxs5(Text5, { color: getContextColor(percentage), children: [
2896
+ "Context: ",
2897
+ Math.round(percentage),
2898
+ "% (",
2899
+ formatTokenCount2(usedTokens),
2900
+ "/",
2901
+ formatTokenCount2(maxTokens),
2902
+ ")"
2903
+ ] });
2904
+ }
2905
+ function ModeText({ permissionMode }) {
2906
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
2907
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "Mode:" }),
2908
+ " ",
2909
+ /* @__PURE__ */ jsx5(Text5, { children: permissionMode })
2910
+ ] });
2911
+ }
2912
+ function StatusLeft({
2913
+ permissionMode,
2914
+ modelName,
2915
+ isThinking,
2916
+ activeToolCount,
2917
+ activeBackgroundTaskCount,
2918
+ hasPendingPrompt,
2919
+ contextPercentage,
2920
+ contextUsedTokens,
2921
+ contextMaxTokens,
2922
+ sessionName,
2923
+ gitBranch,
2924
+ showGitBranch
2925
+ }) {
2926
+ const shouldShowGitBranch = showGitBranch && gitBranch !== void 0 && gitBranch.length > 0;
2927
+ return /* @__PURE__ */ jsxs5(Text5, { children: [
2928
+ /* @__PURE__ */ jsx5(
2929
+ StatusActivityText,
2930
+ {
2931
+ isThinking,
2932
+ activeToolCount,
2933
+ activeBackgroundTaskCount,
2934
+ hasPendingPrompt
2935
+ }
2936
+ ),
2937
+ " | ",
2938
+ /* @__PURE__ */ jsx5(ModeText, { permissionMode }),
2939
+ sessionName && /* @__PURE__ */ jsxs5(Fragment3, { children: [
2940
+ " | ",
2941
+ /* @__PURE__ */ jsx5(Text5, { color: "magenta", children: sessionName })
2942
+ ] }),
2943
+ shouldShowGitBranch && /* @__PURE__ */ jsxs5(Fragment3, { children: [
2944
+ " | ",
2945
+ /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
2946
+ "git: ",
2947
+ gitBranch
2948
+ ] })
2949
+ ] }),
2950
+ " | ",
2951
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: modelName }),
2952
+ " | ",
2953
+ /* @__PURE__ */ jsx5(
2954
+ ContextText,
2955
+ {
2956
+ percentage: contextPercentage,
2957
+ usedTokens: contextUsedTokens,
2958
+ maxTokens: contextMaxTokens
2959
+ }
2960
+ )
2961
+ ] });
2962
+ }
2279
2963
  function StatusBar({
2280
2964
  permissionMode,
2281
2965
  modelName,
2282
2966
  sessionId: _sessionId,
2283
2967
  messageCount,
2284
2968
  isThinking,
2969
+ activeToolCount = 0,
2970
+ activeBackgroundTaskCount = 0,
2971
+ hasPendingPrompt = false,
2285
2972
  contextPercentage,
2286
2973
  contextUsedTokens,
2287
2974
  contextMaxTokens,
2288
- sessionName
2975
+ sessionName,
2976
+ gitBranch,
2977
+ showGitBranch = true
2289
2978
  }) {
2290
- const contextColor = getContextColor(contextPercentage);
2291
- return /* @__PURE__ */ jsxs3(
2292
- Box3,
2979
+ return /* @__PURE__ */ jsxs5(
2980
+ Box5,
2293
2981
  {
2294
2982
  borderStyle: "single",
2295
2983
  borderColor: "gray",
@@ -2297,46 +2985,78 @@ function StatusBar({
2297
2985
  paddingRight: 1,
2298
2986
  justifyContent: "space-between",
2299
2987
  children: [
2300
- /* @__PURE__ */ jsxs3(Text3, { children: [
2301
- /* @__PURE__ */ jsx2(Text3, { color: "cyan", bold: true, children: "Mode:" }),
2302
- " ",
2303
- /* @__PURE__ */ jsx2(Text3, { children: permissionMode }),
2304
- sessionName && /* @__PURE__ */ jsxs3(Fragment2, { children: [
2305
- " | ",
2306
- /* @__PURE__ */ jsx2(Text3, { color: "magenta", children: sessionName })
2307
- ] }),
2308
- " | ",
2309
- /* @__PURE__ */ jsx2(Text3, { dimColor: true, children: modelName }),
2310
- " | ",
2311
- /* @__PURE__ */ jsxs3(Text3, { color: contextColor, children: [
2312
- "Context: ",
2313
- Math.round(contextPercentage),
2314
- "% (",
2315
- formatTokenCount(contextUsedTokens),
2316
- "/",
2317
- formatTokenCount(contextMaxTokens),
2318
- ")"
2319
- ] })
2320
- ] }),
2321
- /* @__PURE__ */ jsxs3(Text3, { children: [
2322
- isThinking && /* @__PURE__ */ jsx2(Text3, { color: "yellow", children: "Thinking... " }),
2323
- /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
2324
- "msgs: ",
2325
- messageCount
2326
- ] })
2327
- ] })
2988
+ /* @__PURE__ */ jsx5(
2989
+ StatusLeft,
2990
+ {
2991
+ permissionMode,
2992
+ modelName,
2993
+ isThinking,
2994
+ activeToolCount,
2995
+ activeBackgroundTaskCount,
2996
+ hasPendingPrompt,
2997
+ contextPercentage,
2998
+ contextUsedTokens,
2999
+ contextMaxTokens,
3000
+ sessionName,
3001
+ gitBranch,
3002
+ showGitBranch
3003
+ }
3004
+ ),
3005
+ /* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
3006
+ "msgs: ",
3007
+ messageCount
3008
+ ] }) })
2328
3009
  ]
2329
3010
  }
2330
3011
  );
2331
3012
  }
2332
3013
 
3014
+ // src/ui/SessionStatusBar.tsx
3015
+ import { jsx as jsx6 } from "react/jsx-runtime";
3016
+ function SessionStatusBar({
3017
+ cwd,
3018
+ permissionMode,
3019
+ modelId,
3020
+ sessionId,
3021
+ messageCount,
3022
+ isThinking,
3023
+ activeToolCount,
3024
+ activeBackgroundTaskCount,
3025
+ hasPendingPrompt,
3026
+ contextState,
3027
+ sessionName,
3028
+ settings
3029
+ }) {
3030
+ const gitBranch = useMemo2(() => resolveGitBranch(cwd), [cwd]);
3031
+ if (!settings.enabled) return null;
3032
+ return /* @__PURE__ */ jsx6(
3033
+ StatusBar,
3034
+ {
3035
+ permissionMode,
3036
+ modelName: modelId ? getModelName2(modelId) : "",
3037
+ sessionId,
3038
+ messageCount,
3039
+ isThinking,
3040
+ activeToolCount,
3041
+ activeBackgroundTaskCount,
3042
+ hasPendingPrompt,
3043
+ contextPercentage: contextState.percentage,
3044
+ contextUsedTokens: contextState.usedTokens,
3045
+ contextMaxTokens: contextState.maxTokens,
3046
+ sessionName,
3047
+ gitBranch,
3048
+ showGitBranch: settings.gitBranch
3049
+ }
3050
+ );
3051
+ }
3052
+
2333
3053
  // src/ui/InputArea.tsx
2334
- import { useState as useState6, useCallback as useCallback4, useRef as useRef4 } from "react";
2335
- import { Box as Box5, Text as Text7, useInput as useInput2, useStdout } from "ink";
3054
+ import { useState as useState7, useCallback as useCallback4, useRef as useRef4, useMemo as useMemo4 } from "react";
3055
+ import { Box as Box7, Text as Text9, useInput as useInput2, useWindowSize } from "ink";
2336
3056
 
2337
3057
  // src/ui/CjkTextInput.tsx
2338
- import { useRef as useRef3, useState as useState3 } from "react";
2339
- import { Text as Text4, useInput } from "ink";
3058
+ import { useRef as useRef3, useState as useState4 } from "react";
3059
+ import { Text as Text6, useInput, usePaste } from "ink";
2340
3060
  import chalk from "chalk";
2341
3061
 
2342
3062
  // src/ui/flows/cjk-text-input-flow.ts
@@ -2363,10 +3083,20 @@ function applyCjkTextInput(state, input, key, options) {
2363
3083
  if (pasteResult !== void 0) return pasteResult;
2364
3084
  const controlResult = applyControlInput(state, input, key, options);
2365
3085
  if (controlResult !== void 0) return controlResult;
2366
- const cursorResult = applyCursorInput(state, key, options.availableWidth);
3086
+ const cursorResult = applyCursorInput(state, key, options);
2367
3087
  if (cursorResult !== void 0) return cursorResult;
2368
3088
  return insertPrintableInput(state, input);
2369
3089
  }
3090
+ function applyCjkTextPaste(state, text, options) {
3091
+ const normalizedText = text.replace(/\r\n?/g, "\n");
3092
+ if (normalizedText.length === 0) {
3093
+ return { state, effect: { type: "none" } };
3094
+ }
3095
+ if (normalizedText.includes("\n") && options.canPaste) {
3096
+ return { state, effect: { type: "paste", text: normalizedText, cursor: state.cursor } };
3097
+ }
3098
+ return insertPrintableInput(state, normalizedText);
3099
+ }
2370
3100
  function applyPasteBoundaryInput(state, input, options) {
2371
3101
  if (input === PASTE_START || input.startsWith(PASTE_START)) {
2372
3102
  return startBracketedPaste(state, input);
@@ -2391,9 +3121,16 @@ function applyControlInput(state, input, key, options) {
2391
3121
  }
2392
3122
  return void 0;
2393
3123
  }
2394
- function applyCursorInput(state, key, availableWidth) {
3124
+ function applyCursorInput(state, key, options) {
2395
3125
  if (key.upArrow === true || key.downArrow === true) {
2396
- return moveCursorVertically(state, key.upArrow === true ? "up" : "down", availableWidth);
3126
+ if (options.enableVerticalNavigation === false) {
3127
+ return { state, effect: { type: "none" } };
3128
+ }
3129
+ return moveCursorVertically(
3130
+ state,
3131
+ key.upArrow === true ? "up" : "down",
3132
+ options.availableWidth
3133
+ );
2397
3134
  }
2398
3135
  if (key.leftArrow === true) {
2399
3136
  return moveCursorHorizontally(state, "left");
@@ -2456,15 +3193,8 @@ function continueBracketedPaste(state, input, options) {
2456
3193
  };
2457
3194
  }
2458
3195
  const beforeMarker = input.split(PASTE_END)[0] ?? "";
2459
- const text = (state.pasteBuffer + beforeMarker).replace(/\r\n?/g, "\n");
2460
3196
  const nextState = { ...state, isPasting: false, pasteBuffer: "" };
2461
- if (text.length === 0) {
2462
- return { state: nextState, effect: { type: "none" } };
2463
- }
2464
- if (text.includes("\n") && options.canPaste) {
2465
- return { state: nextState, effect: { type: "paste", text, cursor: state.cursor } };
2466
- }
2467
- return insertPrintableInput(nextState, text);
3197
+ return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);
2468
3198
  }
2469
3199
  function moveCursorVertically(state, direction, availableWidth) {
2470
3200
  if (!availableWidth || availableWidth <= 0) {
@@ -2514,7 +3244,7 @@ function insertPrintableInput(state, input) {
2514
3244
  }
2515
3245
 
2516
3246
  // src/ui/CjkTextInput.tsx
2517
- import { jsx as jsx3 } from "react/jsx-runtime";
3247
+ import { jsx as jsx7 } from "react/jsx-runtime";
2518
3248
  function CjkTextInput({
2519
3249
  value,
2520
3250
  onChange,
@@ -2524,41 +3254,79 @@ function CjkTextInput({
2524
3254
  focus = true,
2525
3255
  showCursor = true,
2526
3256
  availableWidth,
2527
- cursorHint = null
3257
+ cursorHint = null,
3258
+ enableVerticalNavigation = true
2528
3259
  }) {
2529
3260
  const stateRef = useRef3(createCjkTextInputFlowState(value));
2530
- const [, forceRender] = useState3(0);
3261
+ const [, forceRender] = useState4(0);
2531
3262
  stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);
2532
- useInput(
2533
- (input, key) => {
2534
- try {
2535
- const result = applyCjkTextInput(stateRef.current, input, key, {
2536
- availableWidth,
2537
- canPaste: onPaste !== void 0
2538
- });
2539
- stateRef.current = result.state;
2540
- applyCjkTextInputEffect(result.effect, onChange, onSubmit, onPaste, forceRender);
2541
- } catch {
2542
- }
2543
- },
2544
- { isActive: focus }
2545
- );
2546
- return /* @__PURE__ */ jsx3(Text4, { children: renderWithCursor(
3263
+ useCjkTextInputHandlers({
3264
+ stateRef,
3265
+ onChange,
3266
+ onSubmit,
3267
+ onPaste,
3268
+ availableWidth,
3269
+ focus,
3270
+ enableVerticalNavigation,
3271
+ forceRender
3272
+ });
3273
+ return /* @__PURE__ */ jsx7(Text6, { children: renderWithCursor(
2547
3274
  stateRef.current.value,
2548
3275
  stateRef.current.cursor,
2549
3276
  placeholder,
2550
3277
  showCursor && focus
2551
3278
  ) });
2552
3279
  }
2553
- function applyCjkTextInputEffect(effect, onChange, onSubmit, onPaste, forceRender) {
2554
- if (effect.type === "change") {
2555
- onChange(effect.value);
2556
- } else if (effect.type === "submit") {
2557
- onSubmit?.(effect.value);
2558
- } else if (effect.type === "paste") {
2559
- onPaste?.(effect.text, effect.cursor);
2560
- } else if (effect.type === "render") {
2561
- forceRender((n) => n + 1);
3280
+ function useCjkTextInputHandlers(options) {
3281
+ usePaste(
3282
+ (text) => {
3283
+ applyCjkFlowSafely(
3284
+ options,
3285
+ () => applyCjkTextPaste(options.stateRef.current, text, createFlowOptions(options))
3286
+ );
3287
+ },
3288
+ { isActive: options.focus }
3289
+ );
3290
+ useInput(
3291
+ (input, key) => {
3292
+ applyCjkFlowSafely(
3293
+ options,
3294
+ () => applyCjkTextInput(options.stateRef.current, input, key, createFlowOptions(options))
3295
+ );
3296
+ },
3297
+ { isActive: options.focus }
3298
+ );
3299
+ }
3300
+ function createFlowOptions(options) {
3301
+ return {
3302
+ availableWidth: options.availableWidth,
3303
+ canPaste: options.onPaste !== void 0,
3304
+ enableVerticalNavigation: options.enableVerticalNavigation
3305
+ };
3306
+ }
3307
+ function applyCjkFlowSafely(options, run) {
3308
+ try {
3309
+ const result = run();
3310
+ options.stateRef.current = result.state;
3311
+ applyCjkTextInputEffect(
3312
+ result.effect,
3313
+ options.onChange,
3314
+ options.onSubmit,
3315
+ options.onPaste,
3316
+ options.forceRender
3317
+ );
3318
+ } catch {
3319
+ }
3320
+ }
3321
+ function applyCjkTextInputEffect(effect, onChange, onSubmit, onPaste, forceRender) {
3322
+ if (effect.type === "change") {
3323
+ onChange(effect.value);
3324
+ } else if (effect.type === "submit") {
3325
+ onSubmit?.(effect.value);
3326
+ } else if (effect.type === "paste") {
3327
+ onPaste?.(effect.text, effect.cursor);
3328
+ } else if (effect.type === "render") {
3329
+ forceRender((n) => n + 1);
2562
3330
  }
2563
3331
  }
2564
3332
  function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
@@ -2584,14 +3352,14 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
2584
3352
  }
2585
3353
 
2586
3354
  // src/ui/WaveText.tsx
2587
- import { useState as useState4, useEffect as useEffect2 } from "react";
2588
- import { Text as Text5 } from "ink";
2589
- import { jsx as jsx4 } from "react/jsx-runtime";
3355
+ import { useState as useState5, useEffect as useEffect2 } from "react";
3356
+ import { Text as Text7 } from "ink";
3357
+ import { jsx as jsx8 } from "react/jsx-runtime";
2590
3358
  var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
2591
3359
  var INTERVAL_MS = 400;
2592
3360
  var CHARS_PER_GROUP = 4;
2593
3361
  function WaveText({ text }) {
2594
- const [tick, setTick] = useState4(0);
3362
+ const [tick, setTick] = useState5(0);
2595
3363
  useEffect2(() => {
2596
3364
  const timer = setInterval(() => {
2597
3365
  setTick((prev) => prev + 1);
@@ -2599,23 +3367,23 @@ function WaveText({ text }) {
2599
3367
  return () => clearInterval(timer);
2600
3368
  }, []);
2601
3369
  const chars = [...text];
2602
- return /* @__PURE__ */ jsx4(Text5, { children: chars.map((char, i) => {
3370
+ return /* @__PURE__ */ jsx8(Text7, { children: chars.map((char, i) => {
2603
3371
  const group = Math.floor(i / CHARS_PER_GROUP);
2604
3372
  const colorIndex = (tick + group) % WAVE_COLORS.length;
2605
- return /* @__PURE__ */ jsx4(Text5, { color: WAVE_COLORS[colorIndex], children: char }, i);
3373
+ return /* @__PURE__ */ jsx8(Text7, { color: WAVE_COLORS[colorIndex], children: char }, i);
2606
3374
  }) });
2607
3375
  }
2608
3376
 
2609
3377
  // src/ui/SlashAutocomplete.tsx
2610
- import { Box as Box4, Text as Text6 } from "ink";
2611
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
3378
+ import { Box as Box6, Text as Text8 } from "ink";
3379
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2612
3380
  var MAX_VISIBLE = 8;
2613
3381
  function CommandRow(props) {
2614
3382
  const { cmd, isSelected, showSlash } = props;
2615
3383
  const indicator = isSelected ? "\u25B8 " : " ";
2616
3384
  const nameColor = isSelected ? "cyan" : void 0;
2617
3385
  const dimmed = !isSelected;
2618
- return /* @__PURE__ */ jsx5(Box4, { children: /* @__PURE__ */ jsxs4(Text6, { color: nameColor, dimColor: dimmed, children: [
3386
+ return /* @__PURE__ */ jsx9(Box6, { children: /* @__PURE__ */ jsxs6(Text8, { color: nameColor, dimColor: dimmed, children: [
2619
3387
  indicator,
2620
3388
  showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
2621
3389
  ] }) });
@@ -2629,7 +3397,7 @@ function SlashAutocomplete({
2629
3397
  if (!visible || commands.length === 0) return null;
2630
3398
  const scrollOffset = computeScrollOffset(selectedIndex, commands.length);
2631
3399
  const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
2632
- return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ jsx5(
3400
+ return /* @__PURE__ */ jsx9(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ jsx9(
2633
3401
  CommandRow,
2634
3402
  {
2635
3403
  cmd,
@@ -2653,7 +3421,7 @@ function expandPasteLabels(text, store) {
2653
3421
  }
2654
3422
 
2655
3423
  // src/ui/hooks/useAutocomplete.ts
2656
- import React4, { useState as useState5, useMemo as useMemo2 } from "react";
3424
+ import React5, { useState as useState6, useMemo as useMemo3 } from "react";
2657
3425
  function parseSlashInput(value) {
2658
3426
  if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
2659
3427
  const afterSlash = value.slice(1);
@@ -2664,16 +3432,16 @@ function parseSlashInput(value) {
2664
3432
  return { isSlash: true, parentCommand: parent, filter: rest };
2665
3433
  }
2666
3434
  function useAutocomplete(value, registry) {
2667
- const [selectedIndex, setSelectedIndex] = useState5(0);
2668
- const [dismissed, setDismissed] = useState5(false);
2669
- const prevValueRef = React4.useRef(value);
3435
+ const [selectedIndex, setSelectedIndex] = useState6(0);
3436
+ const [dismissed, setDismissed] = useState6(false);
3437
+ const prevValueRef = React5.useRef(value);
2670
3438
  if (prevValueRef.current !== value) {
2671
3439
  prevValueRef.current = value;
2672
3440
  if (dismissed) setDismissed(false);
2673
3441
  }
2674
3442
  const parsed = parseSlashInput(value);
2675
3443
  const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
2676
- const filteredCommands = useMemo2(() => {
3444
+ const filteredCommands = useMemo3(() => {
2677
3445
  if (!registry || !parsed.isSlash || dismissed) return [];
2678
3446
  if (isSubcommandMode) {
2679
3447
  const subs = registry.getSubcommands(parsed.parentCommand);
@@ -2721,6 +3489,61 @@ function getPendingPromptInputAction(key) {
2721
3489
  }
2722
3490
  return void 0;
2723
3491
  }
3492
+ function getPromptHistoryInputAction(key) {
3493
+ if (key.upArrow === true) return "previous";
3494
+ if (key.downArrow === true) return "next";
3495
+ return void 0;
3496
+ }
3497
+ function createPromptHistoryNavigationState() {
3498
+ return { selectedIndex: null, draft: "" };
3499
+ }
3500
+ function navigatePromptHistory(value, history, state, action) {
3501
+ if (history.length === 0) {
3502
+ return { value, cursorHint: value.length, state };
3503
+ }
3504
+ if (action === "previous") {
3505
+ const selectedIndex = state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);
3506
+ const nextValue = history[selectedIndex] ?? value;
3507
+ return {
3508
+ value: nextValue,
3509
+ cursorHint: nextValue.length,
3510
+ state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft }
3511
+ };
3512
+ }
3513
+ if (state.selectedIndex === null) {
3514
+ return { value, cursorHint: value.length, state };
3515
+ }
3516
+ if (state.selectedIndex < history.length - 1) {
3517
+ const selectedIndex = state.selectedIndex + 1;
3518
+ const nextValue = history[selectedIndex] ?? value;
3519
+ return {
3520
+ value: nextValue,
3521
+ cursorHint: nextValue.length,
3522
+ state: { ...state, selectedIndex }
3523
+ };
3524
+ }
3525
+ return {
3526
+ value: state.draft,
3527
+ cursorHint: state.draft.length,
3528
+ state: createPromptHistoryNavigationState()
3529
+ };
3530
+ }
3531
+ function appendPromptHistory(history, value) {
3532
+ const prompt = value.trim();
3533
+ if (prompt.length === 0) return [...history];
3534
+ if (history[history.length - 1] === prompt) return [...history];
3535
+ return [...history, prompt];
3536
+ }
3537
+ function extractPromptHistory(entries) {
3538
+ let prompts = [];
3539
+ for (const entry of entries) {
3540
+ if (entry.category !== "chat" || entry.type !== "user") continue;
3541
+ const data = entry.data;
3542
+ if (typeof data?.content !== "string") continue;
3543
+ prompts = appendPromptHistory(prompts, data.content);
3544
+ }
3545
+ return prompts;
3546
+ }
2724
3547
  function moveAutocompleteSelection(selectedIndex, commandCount, direction) {
2725
3548
  if (commandCount === 0) return 0;
2726
3549
  if (direction === "previous") {
@@ -2763,11 +3586,12 @@ function shouldSubmitInput(text) {
2763
3586
  }
2764
3587
 
2765
3588
  // src/ui/InputArea.tsx
2766
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3589
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2767
3590
  var BORDER_HORIZONTAL = 2;
2768
3591
  var PADDING_LEFT = 1;
2769
3592
  var PROMPT_WIDTH = 2;
2770
3593
  var INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
3594
+ var DEFAULT_TERMINAL_COLUMNS = 80;
2771
3595
  function InputArea({
2772
3596
  onSubmit,
2773
3597
  onCancelQueue,
@@ -2775,13 +3599,24 @@ function InputArea({
2775
3599
  isAborting,
2776
3600
  pendingPrompt,
2777
3601
  registry,
2778
- sessionName
3602
+ sessionName,
3603
+ history
2779
3604
  }) {
2780
- const [value, setValue] = useState6("");
2781
- const [cursorHint, setCursorHint] = useState6(null);
3605
+ const [value, setValue] = useState7("");
3606
+ const [cursorHint, setCursorHint] = useState7(null);
3607
+ const [historyState, setHistoryState] = useState7(createPromptHistoryNavigationState);
3608
+ const [localPromptHistory, setLocalPromptHistory] = useState7([]);
3609
+ const restoredPromptHistory = useMemo4(() => extractPromptHistory(history ?? []), [history]);
3610
+ const promptHistory = useMemo4(
3611
+ () => localPromptHistory.reduce(
3612
+ (prompts, prompt) => appendPromptHistory(prompts, prompt),
3613
+ restoredPromptHistory
3614
+ ),
3615
+ [restoredPromptHistory, localPromptHistory]
3616
+ );
2782
3617
  const pasteStore = useRef4(/* @__PURE__ */ new Map());
2783
- const { stdout } = useStdout();
2784
- const terminalColumns = stdout?.columns ?? 80;
3618
+ const { columns } = useWindowSize();
3619
+ const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;
2785
3620
  const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
2786
3621
  const pasteIdRef = useRef4(0);
2787
3622
  const {
@@ -2802,6 +3637,20 @@ function InputArea({
2802
3637
  return change.value;
2803
3638
  });
2804
3639
  }, []);
3640
+ const resetHistoryNavigation = useCallback4(() => {
3641
+ setHistoryState(createPromptHistoryNavigationState());
3642
+ }, []);
3643
+ const recordPromptHistory = useCallback4((prompt) => {
3644
+ setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));
3645
+ }, []);
3646
+ const submitPrompt = useCallback4(
3647
+ (prompt) => {
3648
+ recordPromptHistory(prompt);
3649
+ resetHistoryNavigation();
3650
+ onSubmit(prompt);
3651
+ },
3652
+ [onSubmit, recordPromptHistory, resetHistoryNavigation]
3653
+ );
2805
3654
  const tabCompleteCommand = useCallback4(
2806
3655
  (cmd) => {
2807
3656
  const result = resolveTabCompletion(value, cmd);
@@ -2825,9 +3674,9 @@ function InputArea({
2825
3674
  return;
2826
3675
  }
2827
3676
  setValue("");
2828
- onSubmit(result.value);
3677
+ submitPrompt(result.value);
2829
3678
  },
2830
- [value, onSubmit, setSelectedIndex]
3679
+ [value, submitPrompt, setSelectedIndex]
2831
3680
  );
2832
3681
  const handleSubmit = useCallback4(
2833
3682
  (text) => {
@@ -2840,9 +3689,9 @@ function InputArea({
2840
3689
  setValue("");
2841
3690
  pasteStore.current.clear();
2842
3691
  pasteIdRef.current = 0;
2843
- onSubmit(expanded);
3692
+ submitPrompt(expanded);
2844
3693
  },
2845
- [showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
3694
+ [showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt]
2846
3695
  );
2847
3696
  useInput2(
2848
3697
  (_input, key) => {
@@ -2861,6 +3710,17 @@ function InputArea({
2861
3710
  },
2862
3711
  { isActive: showPopup && !isDisabled }
2863
3712
  );
3713
+ useInput2(
3714
+ (_input, key) => {
3715
+ const action = getPromptHistoryInputAction(key);
3716
+ if (!action) return;
3717
+ const result = navigatePromptHistory(value, promptHistory, historyState, action);
3718
+ setValue(result.value);
3719
+ setCursorHint(result.cursorHint);
3720
+ setHistoryState(result.state);
3721
+ },
3722
+ { isActive: !showPopup && !isDisabled && !pendingPrompt }
3723
+ );
2864
3724
  useInput2(
2865
3725
  (_input, key) => {
2866
3726
  if (getPendingPromptInputAction(key) === "cancelQueue" && pendingPrompt) {
@@ -2880,8 +3740,8 @@ function InputArea({
2880
3740
  }
2881
3741
  return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
2882
3742
  })();
2883
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
2884
- showPopup && /* @__PURE__ */ jsx6(
3743
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
3744
+ showPopup && /* @__PURE__ */ jsx10(
2885
3745
  SlashAutocomplete,
2886
3746
  {
2887
3747
  commands: filteredCommands,
@@ -2890,32 +3750,34 @@ function InputArea({
2890
3750
  isSubcommandMode
2891
3751
  }
2892
3752
  ),
2893
- /* @__PURE__ */ jsxs5(Text7, { color: borderColor, children: [
3753
+ /* @__PURE__ */ jsxs7(Text9, { color: borderColor, children: [
2894
3754
  topBorder.left,
2895
- topBorder.label ? /* @__PURE__ */ jsx6(Text7, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
3755
+ topBorder.label ? /* @__PURE__ */ jsx10(Text9, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
2896
3756
  topBorder.right
2897
3757
  ] }),
2898
- /* @__PURE__ */ jsx6(Box5, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ jsx6(Text7, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ jsxs5(Text7, { color: "cyan", children: [
3758
+ /* @__PURE__ */ jsx10(Box7, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ jsx10(Text9, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ jsxs7(Text9, { color: "cyan", children: [
2899
3759
  " ",
2900
3760
  "Queued: ",
2901
3761
  pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
2902
3762
  " ",
2903
- /* @__PURE__ */ jsx6(Text7, { dimColor: true, children: "(Backspace to cancel)" })
2904
- ] }) : isDisabled ? /* @__PURE__ */ jsx6(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ jsxs5(Box5, { children: [
2905
- /* @__PURE__ */ jsx6(Text7, { color: "green", bold: true, children: "> " }),
2906
- /* @__PURE__ */ jsx6(
3763
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "(Backspace to cancel)" })
3764
+ ] }) : isDisabled ? /* @__PURE__ */ jsx10(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ jsxs7(Box7, { children: [
3765
+ /* @__PURE__ */ jsx10(Text9, { color: "green", bold: true, children: "> " }),
3766
+ /* @__PURE__ */ jsx10(
2907
3767
  CjkTextInput,
2908
3768
  {
2909
3769
  value,
2910
3770
  onChange: (v) => {
2911
3771
  setValue(v);
3772
+ resetHistoryNavigation();
2912
3773
  setCursorHint(null);
2913
3774
  },
2914
3775
  onSubmit: handleSubmit,
2915
3776
  onPaste: handlePaste,
2916
3777
  placeholder: "Type a message or /help",
2917
3778
  availableWidth,
2918
- cursorHint
3779
+ cursorHint,
3780
+ enableVerticalNavigation: false
2919
3781
  }
2920
3782
  )
2921
3783
  ] }) })
@@ -2923,8 +3785,8 @@ function InputArea({
2923
3785
  }
2924
3786
 
2925
3787
  // src/ui/ConfirmPrompt.tsx
2926
- import { useState as useState7, useRef as useRef5, useCallback as useCallback5 } from "react";
2927
- import { Box as Box6, Text as Text8, useInput as useInput3 } from "ink";
3788
+ import { useState as useState8, useRef as useRef5, useCallback as useCallback5 } from "react";
3789
+ import { Box as Box8, Text as Text10, useInput as useInput3 } from "ink";
2928
3790
 
2929
3791
  // src/ui/flows/selection-flow.ts
2930
3792
  function createSelectionFlowState() {
@@ -3027,13 +3889,13 @@ function applyConfirmPromptInput(state, action, optionCount) {
3027
3889
  }
3028
3890
 
3029
3891
  // src/ui/ConfirmPrompt.tsx
3030
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3892
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3031
3893
  function ConfirmPrompt({
3032
3894
  message,
3033
3895
  options = ["Yes", "No"],
3034
3896
  onSelect
3035
3897
  }) {
3036
- const [state, setState] = useState7(() => createSelectionFlowState());
3898
+ const [state, setState] = useState8(() => createSelectionFlowState());
3037
3899
  const stateRef = useRef5(state);
3038
3900
  const applyAction = useCallback5(
3039
3901
  (action) => {
@@ -3052,10 +3914,10 @@ function ConfirmPrompt({
3052
3914
  applyAction(action);
3053
3915
  }
3054
3916
  });
3055
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3056
- /* @__PURE__ */ jsx7(Text8, { color: "yellow", children: message }),
3057
- /* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsx7(Box6, { marginRight: 2, children: /* @__PURE__ */ jsxs6(
3058
- Text8,
3917
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3918
+ /* @__PURE__ */ jsx11(Text10, { color: "yellow", children: message }),
3919
+ /* @__PURE__ */ jsx11(Box8, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsx11(Box8, { marginRight: 2, children: /* @__PURE__ */ jsxs8(
3920
+ Text10,
3059
3921
  {
3060
3922
  color: i === state.selectedIndex ? "cyan" : void 0,
3061
3923
  bold: i === state.selectedIndex,
@@ -3065,16 +3927,81 @@ function ConfirmPrompt({
3065
3927
  ]
3066
3928
  }
3067
3929
  ) }, opt)) }),
3068
- /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
3930
+ /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
3069
3931
  ] });
3070
3932
  }
3071
3933
 
3072
- // src/ui/ProviderSetupPrompt.tsx
3073
- import { useState as useState9 } from "react";
3934
+ // src/ui/InteractivePrompt.tsx
3935
+ import { Box as Box11, Text as Text13 } from "ink";
3936
+
3937
+ // src/ui/ListPicker.tsx
3938
+ import { useState as useState9, useRef as useRef6, useCallback as useCallback6 } from "react";
3939
+ import { Box as Box9, Text as Text11, useInput as useInput4 } from "ink";
3940
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3941
+ var DEFAULT_MAX_VISIBLE = 3;
3942
+ function ListPicker({
3943
+ items,
3944
+ renderItem,
3945
+ onSelect,
3946
+ onCancel,
3947
+ maxVisible = DEFAULT_MAX_VISIBLE
3948
+ }) {
3949
+ const [state, setState] = useState9(() => createSelectionFlowState());
3950
+ const stateRef = useRef6(state);
3951
+ const applyAction = useCallback6(
3952
+ (action) => {
3953
+ const result = applySelectionInput(stateRef.current, action, {
3954
+ itemCount: items.length,
3955
+ maxVisible
3956
+ });
3957
+ stateRef.current = result.state;
3958
+ setState(result.state);
3959
+ if (result.effect.type === "cancel") {
3960
+ onCancel();
3961
+ } else if (result.effect.type === "select") {
3962
+ const item = items[result.effect.index];
3963
+ if (item !== void 0) {
3964
+ onSelect(item);
3965
+ }
3966
+ }
3967
+ },
3968
+ [items, maxVisible, onCancel, onSelect]
3969
+ );
3970
+ useInput4((_input, key) => {
3971
+ const action = getVerticalSelectionInputAction(key);
3972
+ if (action !== void 0) {
3973
+ applyAction(action);
3974
+ }
3975
+ });
3976
+ if (items.length === 0) {
3977
+ return /* @__PURE__ */ jsx12(Box9, {});
3978
+ }
3979
+ const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
3980
+ if (normalizedState !== state) {
3981
+ stateRef.current = normalizedState;
3982
+ }
3983
+ const { selectedIndex, scrollOffset } = normalizedState;
3984
+ const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
3985
+ const hasMore = scrollOffset + maxVisible < items.length;
3986
+ const hasLess = scrollOffset > 0;
3987
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3988
+ hasLess && /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3989
+ " \u2191 ",
3990
+ scrollOffset,
3991
+ " more above"
3992
+ ] }),
3993
+ visibleItems.map((item, index) => /* @__PURE__ */ jsx12(Box9, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
3994
+ hasMore && /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
3995
+ " \u2193 ",
3996
+ items.length - scrollOffset - maxVisible,
3997
+ " more below"
3998
+ ] })
3999
+ ] });
4000
+ }
3074
4001
 
3075
4002
  // src/ui/TextPrompt.tsx
3076
- import { useState as useState8, useRef as useRef6, useCallback as useCallback6 } from "react";
3077
- import { Box as Box7, Text as Text9, useInput as useInput4 } from "ink";
4003
+ import { useState as useState10, useRef as useRef7, useCallback as useCallback7 } from "react";
4004
+ import { Box as Box10, Text as Text12, useInput as useInput5 } from "ink";
3078
4005
 
3079
4006
  // src/ui/flows/text-prompt-flow.ts
3080
4007
  function createTextPromptFlowState() {
@@ -3133,7 +4060,7 @@ function submitTextPromptValue(state, options) {
3133
4060
  }
3134
4061
 
3135
4062
  // src/ui/TextPrompt.tsx
3136
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4063
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
3137
4064
  function TextPrompt({
3138
4065
  title,
3139
4066
  placeholder,
@@ -3143,9 +4070,9 @@ function TextPrompt({
3143
4070
  allowEmpty = false,
3144
4071
  masked = false
3145
4072
  }) {
3146
- const [state, setState] = useState8(() => createTextPromptFlowState());
3147
- const stateRef = useRef6(state);
3148
- const applyAction = useCallback6(
4073
+ const [state, setState] = useState10(() => createTextPromptFlowState());
4074
+ const stateRef = useRef7(state);
4075
+ const applyAction = useCallback7(
3149
4076
  (action) => {
3150
4077
  const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });
3151
4078
  stateRef.current = result.state;
@@ -3158,64 +4085,67 @@ function TextPrompt({
3158
4085
  },
3159
4086
  [allowEmpty, validate, onCancel, onSubmit]
3160
4087
  );
3161
- useInput4((input, key) => {
4088
+ useInput5((input, key) => {
3162
4089
  const action = getTextPromptInputAction(input, key);
3163
4090
  if (action !== void 0) {
3164
4091
  applyAction(action);
3165
4092
  }
3166
4093
  });
3167
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3168
- /* @__PURE__ */ jsx8(Text9, { color: "yellow", bold: true, children: title }),
3169
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
3170
- /* @__PURE__ */ jsx8(Text9, { color: "cyan", children: "> " }),
3171
- state.value ? /* @__PURE__ */ jsx8(Text9, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ jsx8(Text9, { dimColor: true, children: placeholder }) : null,
3172
- /* @__PURE__ */ jsx8(Text9, { color: "cyan", children: "\u2588" })
4094
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4095
+ /* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: title }),
4096
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
4097
+ /* @__PURE__ */ jsx13(Text12, { color: "cyan", children: "> " }),
4098
+ state.value ? /* @__PURE__ */ jsx13(Text12, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: placeholder }) : null,
4099
+ /* @__PURE__ */ jsx13(Text12, { color: "cyan", children: "\u2588" })
3173
4100
  ] }),
3174
- state.error && /* @__PURE__ */ jsx8(Text9, { color: "red", children: state.error }),
3175
- /* @__PURE__ */ jsx8(Text9, { dimColor: true, children: " Enter Submit Esc Cancel" })
4101
+ state.error && /* @__PURE__ */ jsx13(Text12, { color: "red", children: state.error }),
4102
+ /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " Enter Submit Esc Cancel" })
3176
4103
  ] });
3177
4104
  }
3178
4105
 
3179
- // src/ui/ProviderSetupPrompt.tsx
3180
- import { jsx as jsx9 } from "react/jsx-runtime";
3181
- function ProviderSetupPrompt({
3182
- type,
3183
- providerDefinitions,
4106
+ // src/ui/InteractivePrompt.tsx
4107
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4108
+ function InteractivePrompt({
4109
+ prompt,
3184
4110
  onSubmit,
3185
4111
  onCancel
3186
4112
  }) {
3187
- const [state, setState] = useState9(
3188
- () => createProviderSetupFlow(type, providerDefinitions)
3189
- );
3190
- const step = getProviderSetupStep(state);
3191
- const handleStepSubmit = (rawValue) => {
3192
- const result = submitProviderSetupValue(state, rawValue);
3193
- if (result.status === "next") {
3194
- setState(result.state);
3195
- return;
3196
- }
3197
- if (result.status === "complete") {
3198
- onSubmit(result.input);
3199
- }
3200
- };
3201
- return /* @__PURE__ */ jsx9(
3202
- TextPrompt,
3203
- {
3204
- title: step.title,
3205
- placeholder: step.defaultValue,
3206
- allowEmpty: step.defaultValue !== void 0,
3207
- masked: step.masked,
3208
- validate: (value) => validateProviderSetupValue(step, value),
3209
- onSubmit: handleStepSubmit,
3210
- onCancel
3211
- },
3212
- `${type}-${step.key}`
3213
- );
4113
+ if (prompt.kind === "text") {
4114
+ return /* @__PURE__ */ jsx14(
4115
+ TextPrompt,
4116
+ {
4117
+ title: prompt.title,
4118
+ placeholder: prompt.placeholder,
4119
+ allowEmpty: prompt.allowEmpty,
4120
+ masked: prompt.masked,
4121
+ validate: prompt.validate,
4122
+ onSubmit,
4123
+ onCancel
4124
+ },
4125
+ `text:${prompt.title}`
4126
+ );
4127
+ }
4128
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
4129
+ /* @__PURE__ */ jsx14(Text13, { bold: true, children: prompt.title }),
4130
+ /* @__PURE__ */ jsx14(
4131
+ ListPicker,
4132
+ {
4133
+ items: [...prompt.options],
4134
+ maxVisible: prompt.maxVisible,
4135
+ renderItem: (option, isSelected) => /* @__PURE__ */ jsxs11(Text13, { color: isSelected ? "cyan" : void 0, children: [
4136
+ isSelected ? "> " : " ",
4137
+ option.label
4138
+ ] }),
4139
+ onSelect: (option) => onSubmit(option.value),
4140
+ onCancel
4141
+ }
4142
+ )
4143
+ ] });
3214
4144
  }
3215
4145
 
3216
4146
  // src/ui/PermissionPrompt.tsx
3217
- import React9 from "react";
3218
- import { Box as Box8, Text as Text10, useInput as useInput5 } from "ink";
4147
+ import React10 from "react";
4148
+ import { Box as Box12, Text as Text14, useInput as useInput6 } from "ink";
3219
4149
 
3220
4150
  // src/ui/flows/permission-prompt-flow.ts
3221
4151
  var PERMISSION_PROMPT_OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
@@ -3266,23 +4196,23 @@ function resolvePermissionIndex(state, index) {
3266
4196
  }
3267
4197
 
3268
4198
  // src/ui/PermissionPrompt.tsx
3269
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4199
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
3270
4200
  function formatArgs(args) {
3271
4201
  const entries = Object.entries(args);
3272
4202
  if (entries.length === 0) return "(no arguments)";
3273
4203
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
3274
4204
  }
3275
4205
  function PermissionPrompt({ request }) {
3276
- const [state, setState] = React9.useState(() => createSelectionFlowState());
3277
- const stateRef = React9.useRef(state);
3278
- const prevRequestRef = React9.useRef(request);
4206
+ const [state, setState] = React10.useState(() => createSelectionFlowState());
4207
+ const stateRef = React10.useRef(state);
4208
+ const prevRequestRef = React10.useRef(request);
3279
4209
  if (prevRequestRef.current !== request) {
3280
4210
  prevRequestRef.current = request;
3281
4211
  const nextState = createSelectionFlowState();
3282
4212
  stateRef.current = nextState;
3283
4213
  setState(nextState);
3284
4214
  }
3285
- const applyAction = React9.useCallback(
4215
+ const applyAction = React10.useCallback(
3286
4216
  (action) => {
3287
4217
  const result = applyPermissionPromptInput(stateRef.current, action);
3288
4218
  stateRef.current = result.state;
@@ -3293,25 +4223,25 @@ function PermissionPrompt({ request }) {
3293
4223
  },
3294
4224
  [request]
3295
4225
  );
3296
- useInput5((input, key) => {
4226
+ useInput6((input, key) => {
3297
4227
  const action = getPermissionPromptInputAction(input, key);
3298
4228
  if (action !== void 0) {
3299
4229
  applyAction(action);
3300
4230
  }
3301
4231
  });
3302
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3303
- /* @__PURE__ */ jsx10(Text10, { color: "yellow", bold: true, children: "[Permission Required]" }),
3304
- /* @__PURE__ */ jsxs8(Text10, { children: [
4232
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4233
+ /* @__PURE__ */ jsx15(Text14, { color: "yellow", bold: true, children: "[Permission Required]" }),
4234
+ /* @__PURE__ */ jsxs12(Text14, { children: [
3305
4235
  "Tool:",
3306
4236
  " ",
3307
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: request.toolName })
4237
+ /* @__PURE__ */ jsx15(Text14, { color: "cyan", bold: true, children: request.toolName })
3308
4238
  ] }),
3309
- /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
4239
+ /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
3310
4240
  " ",
3311
4241
  formatArgs(request.toolArgs)
3312
4242
  ] }),
3313
- /* @__PURE__ */ jsx10(Box8, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ jsx10(Box8, { marginRight: 2, children: /* @__PURE__ */ jsxs8(
3314
- Text10,
4243
+ /* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ jsx15(Box12, { marginRight: 2, children: /* @__PURE__ */ jsxs12(
4244
+ Text14,
3315
4245
  {
3316
4246
  color: i === state.selectedIndex ? "cyan" : void 0,
3317
4247
  bold: i === state.selectedIndex,
@@ -3321,13 +4251,13 @@ function PermissionPrompt({ request }) {
3321
4251
  ]
3322
4252
  }
3323
4253
  ) }, opt)) }),
3324
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " left/right to select, Enter to confirm" })
4254
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: " left/right to select, Enter to confirm" })
3325
4255
  ] });
3326
4256
  }
3327
4257
 
3328
4258
  // src/ui/StreamingIndicator.tsx
3329
- import { Box as Box9, Text as Text11 } from "ink";
3330
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
4259
+ import { Box as Box13, Text as Text15 } from "ink";
4260
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
3331
4261
  function getToolStyle(t) {
3332
4262
  if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
3333
4263
  if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
@@ -3338,16 +4268,16 @@ function StreamingIndicator({ text, activeTools }) {
3338
4268
  const hasTools = activeTools.length > 0;
3339
4269
  const hasText = text.length > 0;
3340
4270
  if (!hasTools && !hasText) {
3341
- return /* @__PURE__ */ jsx11(Fragment3, {});
4271
+ return /* @__PURE__ */ jsx16(Fragment4, {});
3342
4272
  }
3343
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3344
- hasTools && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
3345
- /* @__PURE__ */ jsx11(Text11, { color: "white", bold: true, children: "Tools:" }),
3346
- /* @__PURE__ */ jsx11(Text11, { children: " " }),
4273
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4274
+ hasTools && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
4275
+ /* @__PURE__ */ jsx16(Text15, { color: "white", bold: true, children: "Tools:" }),
4276
+ /* @__PURE__ */ jsx16(Text15, { children: " " }),
3347
4277
  activeTools.map((t, i) => {
3348
4278
  const { color, icon, strikethrough } = getToolStyle(t);
3349
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3350
- /* @__PURE__ */ jsxs9(Text11, { color, strikethrough, children: [
4279
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4280
+ /* @__PURE__ */ jsxs13(Text15, { color, strikethrough, children: [
3351
4281
  " ",
3352
4282
  icon,
3353
4283
  " ",
@@ -3356,25 +4286,25 @@ function StreamingIndicator({ text, activeTools }) {
3356
4286
  t.firstArg,
3357
4287
  ")"
3358
4288
  ] }),
3359
- t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ jsx11(DiffBlock, { file: t.diffFile, lines: t.diffLines })
4289
+ t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ jsx16(ToolDiffBlock, { file: t.diffFile, lines: t.diffLines })
3360
4290
  ] }, `${t.toolName}-${i}`);
3361
4291
  })
3362
4292
  ] }),
3363
- hasText && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
3364
- /* @__PURE__ */ jsx11(Text11, { color: "cyan", bold: true, children: "Robota:" }),
3365
- /* @__PURE__ */ jsx11(Text11, { children: " " }),
3366
- /* @__PURE__ */ jsx11(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsx11(Text11, { wrap: "wrap", children: renderMarkdown(text) }) })
4293
+ hasText && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
4294
+ /* @__PURE__ */ jsx16(Text15, { color: "cyan", bold: true, children: "Robota:" }),
4295
+ /* @__PURE__ */ jsx16(Text15, { children: " " }),
4296
+ /* @__PURE__ */ jsx16(Box13, { marginLeft: 2, children: /* @__PURE__ */ jsx16(Text15, { wrap: "wrap", children: renderMarkdown(text) }) })
3367
4297
  ] })
3368
4298
  ] });
3369
4299
  }
3370
4300
 
3371
4301
  // src/ui/PluginTUI.tsx
3372
- import { useState as useState12, useCallback as useCallback8 } from "react";
4302
+ import { useState as useState13, useCallback as useCallback9 } from "react";
3373
4303
 
3374
4304
  // src/ui/MenuSelect.tsx
3375
- import { useState as useState10, useCallback as useCallback7, useRef as useRef7 } from "react";
3376
- import { Box as Box10, Text as Text12, useInput as useInput6 } from "ink";
3377
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
4305
+ import { useState as useState11, useCallback as useCallback8, useRef as useRef8 } from "react";
4306
+ import { Box as Box14, Text as Text16, useInput as useInput7 } from "ink";
4307
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
3378
4308
  function MenuSelect({
3379
4309
  title,
3380
4310
  items,
@@ -3383,10 +4313,10 @@ function MenuSelect({
3383
4313
  loading,
3384
4314
  error
3385
4315
  }) {
3386
- const [state, setState] = useState10(() => createSelectionFlowState());
3387
- const stateRef = useRef7(state);
4316
+ const [state, setState] = useState11(() => createSelectionFlowState());
4317
+ const stateRef = useRef8(state);
3388
4318
  const isEnabled = !loading && !error;
3389
- const applyAction = useCallback7(
4319
+ const applyAction = useCallback8(
3390
4320
  (action) => {
3391
4321
  const result = applySelectionInput(stateRef.current, action, {
3392
4322
  itemCount: items.length,
@@ -3405,7 +4335,7 @@ function MenuSelect({
3405
4335
  },
3406
4336
  [isEnabled, items, onBack, onSelect]
3407
4337
  );
3408
- useInput6((input, key) => {
4338
+ useInput7((input, key) => {
3409
4339
  const action = getVerticalSelectionInputAction(key);
3410
4340
  if (action !== void 0) {
3411
4341
  applyAction(action);
@@ -3416,24 +4346,24 @@ function MenuSelect({
3416
4346
  stateRef.current = normalizedState;
3417
4347
  }
3418
4348
  const selected = normalizedState.selectedIndex;
3419
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3420
- /* @__PURE__ */ jsx12(Text12, { color: "yellow", bold: true, children: title }),
3421
- loading && /* @__PURE__ */ jsx12(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Loading..." }) }),
3422
- error && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
3423
- /* @__PURE__ */ jsx12(Text12, { color: "red", children: error }),
3424
- /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Press Esc to go back" })
4349
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
4350
+ /* @__PURE__ */ jsx17(Text16, { color: "yellow", bold: true, children: title }),
4351
+ loading && /* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: "Loading..." }) }),
4352
+ error && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
4353
+ /* @__PURE__ */ jsx17(Text16, { color: "red", children: error }),
4354
+ /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: "Press Esc to go back" })
3425
4355
  ] }),
3426
- !loading && !error && /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
3427
- /* @__PURE__ */ jsxs10(Text12, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
4356
+ !loading && !error && /* @__PURE__ */ jsx17(Box14, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ jsxs14(Box14, { children: [
4357
+ /* @__PURE__ */ jsxs14(Text16, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
3428
4358
  i === selected ? "> " : " ",
3429
4359
  item.label
3430
4360
  ] }),
3431
- item.hint && /* @__PURE__ */ jsxs10(Text12, { dimColor: true, children: [
4361
+ item.hint && /* @__PURE__ */ jsxs14(Text16, { dimColor: true, children: [
3432
4362
  " ",
3433
4363
  item.hint
3434
4364
  ] })
3435
4365
  ] }, item.value)) }),
3436
- /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
4366
+ /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
3437
4367
  ] });
3438
4368
  }
3439
4369
 
@@ -3530,11 +4460,11 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
3530
4460
  }
3531
4461
 
3532
4462
  // src/ui/hooks/usePluginScreenData.ts
3533
- import { useState as useState11, useEffect as useEffect3 } from "react";
4463
+ import { useState as useState12, useEffect as useEffect3 } from "react";
3534
4464
  function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, stackLength) {
3535
- const [items, setItems] = useState11([]);
3536
- const [loading, setLoading] = useState11(false);
3537
- const [error, setError] = useState11();
4465
+ const [items, setItems] = useState12([]);
4466
+ const [loading, setLoading] = useState12(false);
4467
+ const [error, setError] = useState12();
3538
4468
  useEffect3(() => {
3539
4469
  setItems([]);
3540
4470
  setError(void 0);
@@ -3590,16 +4520,16 @@ function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, sta
3590
4520
  }
3591
4521
 
3592
4522
  // src/ui/PluginTUI.tsx
3593
- import { jsx as jsx13 } from "react/jsx-runtime";
4523
+ import { jsx as jsx18 } from "react/jsx-runtime";
3594
4524
  function PluginTUI({ callbacks, onClose, addMessage }) {
3595
- const [stack, setStack] = useState12([{ screen: "main" }]);
3596
- const [confirm, setConfirm] = useState12();
3597
- const [refreshCounter, setRefreshCounter] = useState12(0);
4525
+ const [stack, setStack] = useState13([{ screen: "main" }]);
4526
+ const [confirm, setConfirm] = useState13();
4527
+ const [refreshCounter, setRefreshCounter] = useState13(0);
3598
4528
  const current = stack[stack.length - 1] ?? { screen: "main" };
3599
- const push = useCallback8((state) => {
4529
+ const push = useCallback9((state) => {
3600
4530
  setStack((prev) => [...prev, state]);
3601
4531
  }, []);
3602
- const pop = useCallback8(() => {
4532
+ const pop = useCallback9(() => {
3603
4533
  setStack((prev) => {
3604
4534
  if (prev.length <= 1) {
3605
4535
  onClose();
@@ -3608,7 +4538,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3608
4538
  return prev.slice(0, -1);
3609
4539
  });
3610
4540
  }, [onClose]);
3611
- const popN = useCallback8(
4541
+ const popN = useCallback9(
3612
4542
  (n) => {
3613
4543
  setStack((prev) => {
3614
4544
  const next = prev.slice(0, Math.max(1, prev.length - n));
@@ -3621,20 +4551,20 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3621
4551
  },
3622
4552
  [onClose]
3623
4553
  );
3624
- const notify = useCallback8(
4554
+ const notify = useCallback9(
3625
4555
  (content) => {
3626
4556
  addMessage?.({ role: "system", content });
3627
4557
  },
3628
4558
  [addMessage]
3629
4559
  );
3630
- const refresh = useCallback8(() => {
4560
+ const refresh = useCallback9(() => {
3631
4561
  setRefreshCounter((c) => c + 1);
3632
4562
  }, []);
3633
- const setConfirmNav = useCallback8(
4563
+ const setConfirmNav = useCallback9(
3634
4564
  (state) => setConfirm(state),
3635
4565
  [setConfirm]
3636
4566
  );
3637
- const pushNav = useCallback8(
4567
+ const pushNav = useCallback9(
3638
4568
  (state) => push({ screen: state.screen, context: state.context }),
3639
4569
  [push]
3640
4570
  );
@@ -3646,7 +4576,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3646
4576
  refreshCounter,
3647
4577
  stack.length
3648
4578
  );
3649
- const handleSelect = useCallback8(
4579
+ const handleSelect = useCallback9(
3650
4580
  (value) => {
3651
4581
  const screen2 = current.screen;
3652
4582
  const ctx = current.context;
@@ -3664,7 +4594,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3664
4594
  },
3665
4595
  [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
3666
4596
  );
3667
- const handleTextSubmit = useCallback8(
4597
+ const handleTextSubmit = useCallback9(
3668
4598
  (value) => {
3669
4599
  if (current.screen === "marketplace-add") {
3670
4600
  callbacks.marketplaceAdd(value).then((name) => {
@@ -3679,7 +4609,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3679
4609
  [current.screen, callbacks, notify, pop]
3680
4610
  );
3681
4611
  if (confirm) {
3682
- return /* @__PURE__ */ jsx13(
4612
+ return /* @__PURE__ */ jsx18(
3683
4613
  ConfirmPrompt,
3684
4614
  {
3685
4615
  message: confirm.message,
@@ -3692,7 +4622,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3692
4622
  }
3693
4623
  const screen = current.screen;
3694
4624
  if (screen === "marketplace-add") {
3695
- return /* @__PURE__ */ jsx13(
4625
+ return /* @__PURE__ */ jsx18(
3696
4626
  TextPrompt,
3697
4627
  {
3698
4628
  title: "Add Marketplace Source",
@@ -3704,7 +4634,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3704
4634
  );
3705
4635
  }
3706
4636
  if (screen === "marketplace-action") {
3707
- return /* @__PURE__ */ jsx13(
4637
+ return /* @__PURE__ */ jsx18(
3708
4638
  MenuSelect,
3709
4639
  {
3710
4640
  title: `Marketplace: ${current.context?.marketplace ?? ""}`,
@@ -3720,7 +4650,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3720
4650
  );
3721
4651
  }
3722
4652
  if (screen === "marketplace-install-scope") {
3723
- return /* @__PURE__ */ jsx13(
4653
+ return /* @__PURE__ */ jsx18(
3724
4654
  MenuSelect,
3725
4655
  {
3726
4656
  title: `Install scope for "${current.context?.pluginId ?? ""}"`,
@@ -3735,7 +4665,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3735
4665
  );
3736
4666
  }
3737
4667
  if (screen === "installed-action") {
3738
- return /* @__PURE__ */ jsx13(
4668
+ return /* @__PURE__ */ jsx18(
3739
4669
  MenuSelect,
3740
4670
  {
3741
4671
  title: `Plugin: ${current.context?.pluginId ?? ""}`,
@@ -3758,7 +4688,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3758
4688
  { label: "Installed Plugins", value: "installed" }
3759
4689
  ]
3760
4690
  };
3761
- return /* @__PURE__ */ jsx13(
4691
+ return /* @__PURE__ */ jsx18(
3762
4692
  MenuSelect,
3763
4693
  {
3764
4694
  title: titleMap[screen] ?? "Plugin Management",
@@ -3773,75 +4703,8 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3773
4703
  }
3774
4704
 
3775
4705
  // src/ui/SessionPicker.tsx
3776
- import { Box as Box12, Text as Text14 } from "ink";
3777
-
3778
- // src/ui/ListPicker.tsx
3779
- import { useState as useState13, useRef as useRef8, useCallback as useCallback9 } from "react";
3780
- import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
3781
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
3782
- var DEFAULT_MAX_VISIBLE = 3;
3783
- function ListPicker({
3784
- items,
3785
- renderItem,
3786
- onSelect,
3787
- onCancel,
3788
- maxVisible = DEFAULT_MAX_VISIBLE
3789
- }) {
3790
- const [state, setState] = useState13(() => createSelectionFlowState());
3791
- const stateRef = useRef8(state);
3792
- const applyAction = useCallback9(
3793
- (action) => {
3794
- const result = applySelectionInput(stateRef.current, action, {
3795
- itemCount: items.length,
3796
- maxVisible
3797
- });
3798
- stateRef.current = result.state;
3799
- setState(result.state);
3800
- if (result.effect.type === "cancel") {
3801
- onCancel();
3802
- } else if (result.effect.type === "select") {
3803
- const item = items[result.effect.index];
3804
- if (item !== void 0) {
3805
- onSelect(item);
3806
- }
3807
- }
3808
- },
3809
- [items, maxVisible, onCancel, onSelect]
3810
- );
3811
- useInput7((_input, key) => {
3812
- const action = getVerticalSelectionInputAction(key);
3813
- if (action !== void 0) {
3814
- applyAction(action);
3815
- }
3816
- });
3817
- if (items.length === 0) {
3818
- return /* @__PURE__ */ jsx14(Box11, {});
3819
- }
3820
- const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
3821
- if (normalizedState !== state) {
3822
- stateRef.current = normalizedState;
3823
- }
3824
- const { selectedIndex, scrollOffset } = normalizedState;
3825
- const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
3826
- const hasMore = scrollOffset + maxVisible < items.length;
3827
- const hasLess = scrollOffset > 0;
3828
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
3829
- hasLess && /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
3830
- " \u2191 ",
3831
- scrollOffset,
3832
- " more above"
3833
- ] }),
3834
- visibleItems.map((item, index) => /* @__PURE__ */ jsx14(Box11, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
3835
- hasMore && /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
3836
- " \u2193 ",
3837
- items.length - scrollOffset - maxVisible,
3838
- " more below"
3839
- ] })
3840
- ] });
3841
- }
3842
-
3843
- // src/ui/SessionPicker.tsx
3844
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
4706
+ import { Box as Box15, Text as Text17 } from "ink";
4707
+ import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
3845
4708
  var SESSION_ID_DISPLAY_LENGTH = 8;
3846
4709
  function SessionPicker({
3847
4710
  sessionStore,
@@ -3850,9 +4713,9 @@ function SessionPicker({
3850
4713
  onCancel
3851
4714
  }) {
3852
4715
  const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
3853
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
3854
- /* @__PURE__ */ jsx15(Text14, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
3855
- /* @__PURE__ */ jsx15(
4716
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4717
+ /* @__PURE__ */ jsx19(Text17, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
4718
+ /* @__PURE__ */ jsx19(
3856
4719
  ListPicker,
3857
4720
  {
3858
4721
  items: sessions,
@@ -3863,24 +4726,24 @@ function SessionPicker({
3863
4726
  });
3864
4727
  const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
3865
4728
  const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
3866
- return /* @__PURE__ */ jsxs12(Text14, { children: [
4729
+ return /* @__PURE__ */ jsxs15(Text17, { children: [
3867
4730
  isSelected ? "> " : " ",
3868
- /* @__PURE__ */ jsx15(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
4731
+ /* @__PURE__ */ jsx19(Text17, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
3869
4732
  " ",
3870
- /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
4733
+ /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
3871
4734
  month: "short",
3872
4735
  day: "numeric",
3873
4736
  hour: "2-digit",
3874
4737
  minute: "2-digit"
3875
4738
  }) }),
3876
4739
  " ",
3877
- /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
4740
+ /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
3878
4741
  "msgs: ",
3879
4742
  session.messages.length
3880
4743
  ] }),
3881
- preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
4744
+ preview ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
3882
4745
  "\n ",
3883
- /* @__PURE__ */ jsx15(Text14, { color: "gray", children: preview })
4746
+ /* @__PURE__ */ jsx19(Text17, { color: "gray", children: preview })
3884
4747
  ] }) : null
3885
4748
  ] });
3886
4749
  },
@@ -3892,54 +4755,382 @@ function SessionPicker({
3892
4755
  }
3893
4756
 
3894
4757
  // src/ui/BackgroundTaskPanel.tsx
3895
- import { Box as Box13, Text as Text15 } from "ink";
3896
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
4758
+ import { Box as Box16, Text as Text18 } from "ink";
4759
+
4760
+ // src/ui/background-task-row-format.ts
3897
4761
  var MS_PER_SECOND = 1e3;
3898
4762
  var SECONDS_PER_MINUTE = 60;
3899
4763
  var MINUTES_PER_HOUR = 60;
3900
- function getStatusColor(status) {
3901
- if (status === "completed") return "green";
3902
- if (status === "failed") return "red";
3903
- if (status === "cancelled") return "yellow";
4764
+ var SUCCESS_EXIT_CODE3 = 0;
4765
+ function formatBackgroundTaskRow(task, options = {}) {
4766
+ const row = {
4767
+ connector: options.isLast === false ? "\u251C" : "\u2514",
4768
+ marker: getStatusMarker(task),
4769
+ color: getStatusColor(task),
4770
+ label: getTaskLabel(task),
4771
+ segments: getTaskSegments(task, options.now ?? Date.now()),
4772
+ preview: getTaskPreview(task)
4773
+ };
4774
+ return {
4775
+ ...row,
4776
+ accessibleText: formatAccessibleText(row)
4777
+ };
4778
+ }
4779
+ function getStatusColor(task) {
4780
+ if (isFailedTask(task)) return "red";
4781
+ if (task.status === "completed") return "green";
4782
+ if (task.status === "cancelled") return "yellow";
3904
4783
  return "cyan";
3905
4784
  }
3906
- function getStatusMarker(status) {
3907
- if (status === "queued" || status === "running") return "\u25A1";
4785
+ function getStatusMarker(task) {
4786
+ if (task.status === "queued" || task.status === "running") return "\u25A1";
3908
4787
  return "\u25A0";
3909
4788
  }
4789
+ function getTaskLabel(task) {
4790
+ if (task.kind === "agent") return `${task.label} agent`;
4791
+ if (task.kind === "process") return task.label || "Process";
4792
+ return task.label;
4793
+ }
4794
+ function getTaskSegments(task, now) {
4795
+ const segments = [];
4796
+ if (task.status === "running") {
4797
+ const idle = formatAge(task.lastActivityAt, now);
4798
+ if (idle) segments.push(`idle ${idle}`);
4799
+ }
4800
+ if (task.status === "failed") {
4801
+ segments.push(task.statusLabel === "timed out" ? "timed out" : "failed");
4802
+ }
4803
+ if (task.status === "cancelled") {
4804
+ segments.push("cancelled");
4805
+ }
4806
+ if (task.timeoutReason) {
4807
+ segments.push(task.timeoutReason);
4808
+ }
4809
+ if (task.status === "completed" && task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3) {
4810
+ segments.push(`exit ${task.exitCode}`);
4811
+ }
4812
+ if (task.signalCode) {
4813
+ segments.push(`signal ${task.signalCode}`);
4814
+ }
4815
+ return segments;
4816
+ }
3910
4817
  function getTaskPreview(task) {
3911
- return task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
4818
+ const preview = task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
4819
+ return preview || void 0;
3912
4820
  }
3913
- function formatAge(iso) {
4821
+ function formatAge(iso, now) {
3914
4822
  if (!iso) return void 0;
3915
4823
  const timestamp = Date.parse(iso);
3916
4824
  if (Number.isNaN(timestamp)) return void 0;
3917
- const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / MS_PER_SECOND));
4825
+ const seconds = Math.max(0, Math.floor((now - timestamp) / MS_PER_SECOND));
3918
4826
  if (seconds < SECONDS_PER_MINUTE) return `${seconds}s`;
3919
4827
  const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
3920
4828
  if (minutes < MINUTES_PER_HOUR) return `${minutes}m`;
3921
4829
  return `${Math.floor(minutes / MINUTES_PER_HOUR)}h`;
3922
4830
  }
4831
+ function isFailedTask(task) {
4832
+ return task.status === "failed" || task.status === "completed" && (task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3 || !!task.signalCode);
4833
+ }
4834
+ function formatAccessibleText(row) {
4835
+ const parts = [`${row.connector} ${row.marker} ${row.label}`, ...row.segments];
4836
+ if (row.preview) parts.push(row.preview);
4837
+ return parts.join(" \xB7 ");
4838
+ }
4839
+
4840
+ // src/ui/BackgroundTaskPanel.tsx
4841
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
3923
4842
  function BackgroundTaskPanel({ tasks }) {
3924
4843
  if (tasks.length === 0) return null;
3925
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
3926
- /* @__PURE__ */ jsx16(Text15, { color: "cyan", bold: true, children: "Background" }),
3927
- tasks.map((task) => /* @__PURE__ */ jsxs13(Text15, { children: [
3928
- "- ",
3929
- /* @__PURE__ */ jsx16(Text15, { color: getStatusColor(task.status), children: getStatusMarker(task.status) }),
3930
- ` ${task.kind}:${task.label} ${task.id}`,
3931
- task.status === "running" && formatAge(task.lastActivityAt) ? /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: ` idle ${formatAge(task.lastActivityAt)}` }) : null,
3932
- task.timeoutReason ? /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: ` (${task.timeoutReason})` }) : null,
3933
- getTaskPreview(task) ? /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: ` - ${getTaskPreview(task)}` }) : null
3934
- ] }, task.id))
4844
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginBottom: 1, children: [
4845
+ /* @__PURE__ */ jsx20(Text18, { color: "cyan", bold: true, children: "Background work" }),
4846
+ tasks.map((task, index) => {
4847
+ const row = formatBackgroundTaskRow(task, { isLast: index === tasks.length - 1 });
4848
+ return /* @__PURE__ */ jsxs16(Text18, { children: [
4849
+ `${row.connector} `,
4850
+ /* @__PURE__ */ jsx20(Text18, { color: row.color, children: row.marker }),
4851
+ ` ${row.label}`,
4852
+ row.segments.map((segment, segmentIndex) => /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: ` \xB7 ${segment}` }, `${segment}-${segmentIndex}`)),
4853
+ row.preview ? /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
4854
+ ] }, task.id);
4855
+ })
3935
4856
  ] });
3936
4857
  }
3937
4858
 
4859
+ // src/ui/UpdateNotice.tsx
4860
+ import { Box as Box17, Text as Text19 } from "ink";
4861
+ import { jsx as jsx21 } from "react/jsx-runtime";
4862
+ function UpdateNotice({ message }) {
4863
+ return /* @__PURE__ */ jsx21(Box17, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx21(Text19, { color: "yellow", children: message }) });
4864
+ }
4865
+
4866
+ // src/utils/update-check.ts
4867
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
4868
+ import { dirname as dirname4, join as join8 } from "path";
4869
+
4870
+ // src/utils/semver-compare.ts
4871
+ function compareSemverVersions(left, right) {
4872
+ const parsedLeft = parseSemver(left);
4873
+ const parsedRight = parseSemver(right);
4874
+ if (parsedLeft === void 0 || parsedRight === void 0) {
4875
+ return Math.sign(left.localeCompare(right));
4876
+ }
4877
+ const coreCompare = compareNumber(parsedLeft.major, parsedRight.major) || compareNumber(parsedLeft.minor, parsedRight.minor) || compareNumber(parsedLeft.patch, parsedRight.patch);
4878
+ if (coreCompare !== 0) {
4879
+ return coreCompare;
4880
+ }
4881
+ return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
4882
+ }
4883
+ function isNewerSemverVersion(candidate, current) {
4884
+ return compareSemverVersions(candidate, current) > 0;
4885
+ }
4886
+ function parseSemver(value) {
4887
+ const normalized = value.trim().replace(/^v/, "").split("+")[0] ?? "";
4888
+ const [core, prereleaseText] = normalized.split("-", 2);
4889
+ const [majorText, minorText, patchText] = core.split(".");
4890
+ const major = parseNumericIdentifier(majorText);
4891
+ const minor = parseNumericIdentifier(minorText);
4892
+ const patch = parseNumericIdentifier(patchText);
4893
+ if (major === void 0 || minor === void 0 || patch === void 0) {
4894
+ return void 0;
4895
+ }
4896
+ return {
4897
+ major,
4898
+ minor,
4899
+ patch,
4900
+ prerelease: prereleaseText ? prereleaseText.split(".") : []
4901
+ };
4902
+ }
4903
+ function parseNumericIdentifier(value) {
4904
+ if (value === void 0 || !/^\d+$/.test(value)) {
4905
+ return void 0;
4906
+ }
4907
+ return Number(value);
4908
+ }
4909
+ function compareNumber(left, right) {
4910
+ return Math.sign(left - right);
4911
+ }
4912
+ function comparePrerelease(left, right) {
4913
+ if (left.length === 0 && right.length === 0) {
4914
+ return 0;
4915
+ }
4916
+ if (left.length === 0) {
4917
+ return 1;
4918
+ }
4919
+ if (right.length === 0) {
4920
+ return -1;
4921
+ }
4922
+ const max = Math.max(left.length, right.length);
4923
+ for (let index = 0; index < max; index += 1) {
4924
+ const leftPart = left[index];
4925
+ const rightPart = right[index];
4926
+ if (leftPart === void 0) {
4927
+ return -1;
4928
+ }
4929
+ if (rightPart === void 0) {
4930
+ return 1;
4931
+ }
4932
+ const partCompare = comparePrereleaseIdentifier(leftPart, rightPart);
4933
+ if (partCompare !== 0) {
4934
+ return partCompare;
4935
+ }
4936
+ }
4937
+ return 0;
4938
+ }
4939
+ function comparePrereleaseIdentifier(left, right) {
4940
+ const leftNumber = parseNumericIdentifier(left);
4941
+ const rightNumber = parseNumericIdentifier(right);
4942
+ if (leftNumber !== void 0 && rightNumber !== void 0) {
4943
+ return compareNumber(leftNumber, rightNumber);
4944
+ }
4945
+ if (leftNumber !== void 0) {
4946
+ return -1;
4947
+ }
4948
+ if (rightNumber !== void 0) {
4949
+ return 1;
4950
+ }
4951
+ return Math.sign(left.localeCompare(right));
4952
+ }
4953
+
4954
+ // src/utils/update-check.ts
4955
+ var CLI_UPDATE_PACKAGE_NAME = "@robota-sdk/agent-cli";
4956
+ var CLI_UPDATE_REGISTRY_URL = "https://registry.npmjs.org";
4957
+ var HOURS_PER_DAY = 24;
4958
+ var MINUTES_PER_HOUR2 = 60;
4959
+ var SECONDS_PER_MINUTE2 = 60;
4960
+ var MS_PER_SECOND2 = 1e3;
4961
+ var CLI_UPDATE_CACHE_TTL_MS = HOURS_PER_DAY * MINUTES_PER_HOUR2 * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
4962
+ var CLI_UPDATE_TIMEOUT_MS = 1500;
4963
+ var DEFAULT_INSTALL_COMMAND = "npm install -g '@robota-sdk/agent-cli@latest'";
4964
+ function getUserUpdateCheckCachePath(home = process.env.HOME ?? process.env.USERPROFILE ?? "/") {
4965
+ return join8(home, ".robota", "update-check.json");
4966
+ }
4967
+ function readUpdateCheckCache(path) {
4968
+ if (!existsSync5(path)) {
4969
+ return void 0;
4970
+ }
4971
+ try {
4972
+ const parsed = JSON.parse(readFileSync5(path, "utf8"));
4973
+ return parseUpdateCheckCache(parsed);
4974
+ } catch {
4975
+ return void 0;
4976
+ }
4977
+ }
4978
+ function writeUpdateCheckCache(path, cache) {
4979
+ mkdirSync3(dirname4(path), { recursive: true });
4980
+ writeFileSync2(path, JSON.stringify(cache, null, 2) + "\n", "utf8");
4981
+ }
4982
+ async function checkForCliUpdate(options) {
4983
+ if (options.disabled === true) {
4984
+ return { status: "skipped", reason: "disabled" };
4985
+ }
4986
+ const packageName = options.packageName ?? CLI_UPDATE_PACKAGE_NAME;
4987
+ const cachePath = options.cachePath ?? getUserUpdateCheckCachePath();
4988
+ const now = options.now ?? /* @__PURE__ */ new Date();
4989
+ const ttlMs = options.ttlMs ?? CLI_UPDATE_CACHE_TTL_MS;
4990
+ if (options.force !== true) {
4991
+ const cached = readUpdateCheckCache(cachePath);
4992
+ if (cached !== void 0 && isFreshCache(cached, now, ttlMs, packageName)) {
4993
+ return resultFromCache(cached, options.currentVersion);
4994
+ }
4995
+ }
4996
+ try {
4997
+ const latestVersion = await fetchLatestVersion({
4998
+ fetchImpl: options.fetchImpl ?? fetch,
4999
+ packageName,
5000
+ registryUrl: options.registryUrl ?? CLI_UPDATE_REGISTRY_URL,
5001
+ timeoutMs: options.timeoutMs ?? CLI_UPDATE_TIMEOUT_MS
5002
+ });
5003
+ tryWriteUpdateCheckCache(cachePath, {
5004
+ packageName,
5005
+ checkedAt: now.toISOString(),
5006
+ currentVersion: options.currentVersion,
5007
+ latestVersion
5008
+ });
5009
+ return resultFromLatestVersion(options.currentVersion, latestVersion);
5010
+ } catch (error) {
5011
+ const errorMessage = error instanceof Error ? error.message : String(error);
5012
+ tryWriteUpdateCheckCache(cachePath, {
5013
+ packageName,
5014
+ checkedAt: now.toISOString(),
5015
+ currentVersion: options.currentVersion,
5016
+ errorMessage
5017
+ });
5018
+ return { status: "error", errorMessage };
5019
+ }
5020
+ }
5021
+ function tryWriteUpdateCheckCache(path, cache) {
5022
+ try {
5023
+ writeUpdateCheckCache(path, cache);
5024
+ } catch {
5025
+ }
5026
+ }
5027
+ async function getStartupCliUpdateNotice(options) {
5028
+ const result = await checkForCliUpdate(options);
5029
+ return result.status === "update_available" ? result.notice : void 0;
5030
+ }
5031
+ function shouldRunStartupCliUpdateCheck(input) {
5032
+ return input.printMode === false && input.disableUpdateCheck === false;
5033
+ }
5034
+ function formatCliUpdateNotice(notice) {
5035
+ return [
5036
+ `Robota update available: ${notice.currentVersion} -> ${notice.latestVersion}.`,
5037
+ `Run ${notice.installCommand}`
5038
+ ].join(" ");
5039
+ }
5040
+ function formatCliUpdateCheckMessage(result) {
5041
+ if (result.status === "update_available") {
5042
+ return formatCliUpdateNotice(result.notice);
5043
+ }
5044
+ if (result.status === "current") {
5045
+ return `Robota is up to date (${result.currentVersion}).`;
5046
+ }
5047
+ if (result.status === "skipped") {
5048
+ return "Robota update check skipped.";
5049
+ }
5050
+ return `Robota update check failed: ${result.errorMessage}`;
5051
+ }
5052
+ function resultFromCache(cache, currentVersion) {
5053
+ if (cache.errorMessage !== void 0) {
5054
+ return { status: "error", errorMessage: cache.errorMessage };
5055
+ }
5056
+ if (cache.latestVersion === void 0) {
5057
+ return { status: "error", errorMessage: "Cached update check has no latest version" };
5058
+ }
5059
+ return resultFromLatestVersion(currentVersion, cache.latestVersion);
5060
+ }
5061
+ function resultFromLatestVersion(currentVersion, latestVersion) {
5062
+ if (isNewerSemverVersion(latestVersion, currentVersion)) {
5063
+ return {
5064
+ status: "update_available",
5065
+ notice: {
5066
+ currentVersion,
5067
+ latestVersion,
5068
+ installCommand: DEFAULT_INSTALL_COMMAND
5069
+ }
5070
+ };
5071
+ }
5072
+ return { status: "current", currentVersion, latestVersion };
5073
+ }
5074
+ function isFreshCache(cache, now, ttlMs, packageName) {
5075
+ if (cache.packageName !== packageName) {
5076
+ return false;
5077
+ }
5078
+ const checkedAt = Date.parse(cache.checkedAt);
5079
+ if (!Number.isFinite(checkedAt)) {
5080
+ return false;
5081
+ }
5082
+ return now.getTime() - checkedAt < ttlMs;
5083
+ }
5084
+ async function fetchLatestVersion(options) {
5085
+ const controller = new AbortController();
5086
+ const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
5087
+ try {
5088
+ const packageUrl = buildPackageMetadataUrl(options.registryUrl, options.packageName);
5089
+ const response = await options.fetchImpl(packageUrl, {
5090
+ headers: { accept: "application/json" },
5091
+ signal: controller.signal
5092
+ });
5093
+ if (!response.ok) {
5094
+ throw new Error(`registry responded with HTTP ${response.status}`);
5095
+ }
5096
+ const metadata = await response.json();
5097
+ const latest = metadata["dist-tags"]?.latest;
5098
+ if (typeof latest !== "string" || latest.trim().length === 0) {
5099
+ throw new Error("registry metadata is missing dist-tags.latest");
5100
+ }
5101
+ return latest;
5102
+ } finally {
5103
+ clearTimeout(timeout);
5104
+ }
5105
+ }
5106
+ function buildPackageMetadataUrl(registryUrl, packageName) {
5107
+ return `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(packageName)}`;
5108
+ }
5109
+ function parseUpdateCheckCache(value) {
5110
+ if (!isJsonObject(value)) {
5111
+ return void 0;
5112
+ }
5113
+ const candidate = value;
5114
+ if (typeof candidate.packageName === "string" && typeof candidate.checkedAt === "string" && typeof candidate.currentVersion === "string" && (candidate.latestVersion === void 0 || typeof candidate.latestVersion === "string") && (candidate.errorMessage === void 0 || typeof candidate.errorMessage === "string")) {
5115
+ return {
5116
+ packageName: candidate.packageName,
5117
+ checkedAt: candidate.checkedAt,
5118
+ currentVersion: candidate.currentVersion,
5119
+ ...candidate.latestVersion !== void 0 && { latestVersion: candidate.latestVersion },
5120
+ ...candidate.errorMessage !== void 0 && { errorMessage: candidate.errorMessage }
5121
+ };
5122
+ }
5123
+ return void 0;
5124
+ }
5125
+ function isJsonObject(value) {
5126
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5127
+ }
5128
+
3938
5129
  // src/ui/App.tsx
3939
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
5130
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
3940
5131
  function App(props) {
3941
5132
  const [activeSessionId, setActiveSessionId] = useState14(props.resumeSessionId);
3942
- return /* @__PURE__ */ jsx17(
5133
+ return /* @__PURE__ */ jsx22(
3943
5134
  AppInner,
3944
5135
  {
3945
5136
  ...props,
@@ -3951,7 +5142,7 @@ function App(props) {
3951
5142
  }
3952
5143
  function AppInner(props) {
3953
5144
  const cwd = props.cwd;
3954
- const providerDefinitions = props.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
5145
+ const providerDefinitions = props.providerDefinitions ?? [];
3955
5146
  const {
3956
5147
  interactiveSession,
3957
5148
  registry,
@@ -3987,31 +5178,49 @@ function AppInner(props) {
3987
5178
  const pluginCallbacks = usePluginCallbacks(cwd);
3988
5179
  const { exit } = useApp2();
3989
5180
  const [sessionName, setSessionName] = useState14(props.sessionName);
5181
+ const [updateNotice, setUpdateNotice] = useState14();
5182
+ const [statusLineSettings, setStatusLineSettings] = useStatusLineSettings();
5183
+ const activeBackgroundTaskCount = backgroundTasks.filter(
5184
+ (task) => task.status === "queued" || task.status === "running"
5185
+ ).length;
3990
5186
  const {
3991
5187
  handleSubmit,
3992
5188
  pendingModelId,
3993
5189
  pendingProviderProfile,
3994
- pendingProviderSetupType,
5190
+ pendingInteractionPrompt,
3995
5191
  showPluginTUI,
3996
5192
  showSessionPicker,
3997
5193
  setShowPluginTUI,
3998
5194
  setShowSessionPicker,
3999
5195
  handleModelConfirm,
4000
5196
  handleProviderConfirm,
4001
- handleProviderSetupSubmit,
4002
- handleProviderSetupCancel
5197
+ handleInteractionSubmit,
5198
+ handleInteractionCancel
4003
5199
  } = useSideEffects({
4004
5200
  cwd,
4005
5201
  interactiveSession,
4006
5202
  addEntry,
4007
5203
  baseHandleSubmit,
4008
5204
  setSessionName,
5205
+ setStatusLineSettings,
4009
5206
  providerDefinitions
4010
5207
  });
4011
5208
  useEffect4(() => {
4012
5209
  const name = interactiveSession?.getName?.();
4013
5210
  if (name && !sessionName) setSessionName(name);
4014
5211
  }, [interactiveSession, sessionName]);
5212
+ useEffect4(() => {
5213
+ let isMounted = true;
5214
+ props.startupUpdateNoticePromise?.then((notice) => {
5215
+ if (isMounted && notice !== void 0) {
5216
+ setUpdateNotice(notice);
5217
+ }
5218
+ }).catch(() => {
5219
+ });
5220
+ return () => {
5221
+ isMounted = false;
5222
+ };
5223
+ }, [props.startupUpdateNoticePromise]);
4015
5224
  useEffect4(() => {
4016
5225
  const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
4017
5226
  process.stdout.write(`\x1B]0;${title}\x07`);
@@ -4045,51 +5254,51 @@ function AppInner(props) {
4045
5254
  sessionId = session.getSessionId();
4046
5255
  } catch {
4047
5256
  }
4048
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
4049
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4050
- /* @__PURE__ */ jsx17(Text16, { color: "cyan", bold: true, children: `
5257
+ return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", children: [
5258
+ /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
5259
+ /* @__PURE__ */ jsx22(Text20, { color: "cyan", bold: true, children: `
4051
5260
  ____ ___ ____ ___ _____ _
4052
5261
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
4053
5262
  | |_) | | | | _ \\| | | || | / _ \\
4054
5263
  | _ <| |_| | |_) | |_| || |/ ___ \\
4055
5264
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
4056
5265
  ` }),
4057
- /* @__PURE__ */ jsxs14(Text16, { dimColor: true, children: [
5266
+ /* @__PURE__ */ jsxs17(Text20, { dimColor: true, children: [
4058
5267
  " v",
4059
5268
  props.version ?? "0.0.0"
4060
5269
  ] })
4061
5270
  ] }),
4062
- /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
4063
- /* @__PURE__ */ jsx17(MessageList, { history }),
4064
- isShuttingDown && /* @__PURE__ */ jsx17(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { color: "yellow", children: "Shutting down..." }) }),
4065
- (isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx17(Box14, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx17(StreamingIndicator, { text: streamingText, activeTools }) }),
4066
- /* @__PURE__ */ jsx17(BackgroundTaskPanel, { tasks: backgroundTasks })
5271
+ updateNotice && /* @__PURE__ */ jsx22(UpdateNotice, { message: formatCliUpdateNotice(updateNotice) }),
5272
+ /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
5273
+ /* @__PURE__ */ jsx22(MessageList, { history }),
5274
+ isShuttingDown && /* @__PURE__ */ jsx22(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text20, { color: "yellow", children: "Shutting down..." }) }),
5275
+ (isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx22(Box18, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx22(StreamingIndicator, { text: streamingText, activeTools }) }),
5276
+ /* @__PURE__ */ jsx22(BackgroundTaskPanel, { tasks: backgroundTasks })
4067
5277
  ] }),
4068
- permissionRequest && /* @__PURE__ */ jsx17(PermissionPrompt, { request: permissionRequest }),
4069
- pendingModelId && /* @__PURE__ */ jsx17(
5278
+ permissionRequest && /* @__PURE__ */ jsx22(PermissionPrompt, { request: permissionRequest }),
5279
+ pendingModelId && /* @__PURE__ */ jsx22(
4070
5280
  ConfirmPrompt,
4071
5281
  {
4072
- message: `Change model to ${getModelName2(pendingModelId)}? This will restart the session.`,
5282
+ message: `Change model to ${getModelName3(pendingModelId)}? This will restart the session.`,
4073
5283
  onSelect: handleModelConfirm
4074
5284
  }
4075
5285
  ),
4076
- pendingProviderProfile && /* @__PURE__ */ jsx17(
5286
+ pendingProviderProfile && /* @__PURE__ */ jsx22(
4077
5287
  ConfirmPrompt,
4078
5288
  {
4079
5289
  message: `Change provider to ${pendingProviderProfile}? This will restart the session.`,
4080
5290
  onSelect: handleProviderConfirm
4081
5291
  }
4082
5292
  ),
4083
- pendingProviderSetupType && /* @__PURE__ */ jsx17(
4084
- ProviderSetupPrompt,
5293
+ pendingInteractionPrompt && /* @__PURE__ */ jsx22(
5294
+ InteractivePrompt,
4085
5295
  {
4086
- type: pendingProviderSetupType,
4087
- providerDefinitions,
4088
- onSubmit: handleProviderSetupSubmit,
4089
- onCancel: handleProviderSetupCancel
5296
+ prompt: pendingInteractionPrompt,
5297
+ onSubmit: handleInteractionSubmit,
5298
+ onCancel: handleInteractionCancel
4090
5299
  }
4091
5300
  ),
4092
- showPluginTUI && /* @__PURE__ */ jsx17(
5301
+ showPluginTUI && /* @__PURE__ */ jsx22(
4093
5302
  PluginTUI,
4094
5303
  {
4095
5304
  callbacks: pluginCallbacks,
@@ -4097,7 +5306,7 @@ function AppInner(props) {
4097
5306
  addMessage: (msg) => addEntry(messageToHistoryEntry4(createSystemMessage4(msg.content)))
4098
5307
  }
4099
5308
  ),
4100
- showSessionPicker && /* @__PURE__ */ jsx17(
5309
+ showSessionPicker && /* @__PURE__ */ jsx22(
4101
5310
  SessionPicker,
4102
5311
  {
4103
5312
  sessionStore: props.sessionStore,
@@ -4112,38 +5321,42 @@ function AppInner(props) {
4112
5321
  }
4113
5322
  }
4114
5323
  ),
4115
- /* @__PURE__ */ jsx17(
4116
- StatusBar,
5324
+ /* @__PURE__ */ jsx22(
5325
+ SessionStatusBar,
4117
5326
  {
5327
+ cwd,
4118
5328
  permissionMode,
4119
- modelName: props.modelId ? getModelName2(props.modelId) : "",
5329
+ modelId: props.modelId,
4120
5330
  sessionId,
4121
5331
  messageCount: history.length,
4122
5332
  isThinking,
4123
- contextPercentage: contextState.percentage,
4124
- contextUsedTokens: contextState.usedTokens,
4125
- contextMaxTokens: contextState.maxTokens,
4126
- sessionName
5333
+ activeToolCount: activeTools.length,
5334
+ activeBackgroundTaskCount,
5335
+ hasPendingPrompt: pendingPrompt !== null,
5336
+ contextState,
5337
+ sessionName,
5338
+ settings: statusLineSettings
4127
5339
  }
4128
5340
  ),
4129
- /* @__PURE__ */ jsx17(
5341
+ /* @__PURE__ */ jsx22(
4130
5342
  InputArea,
4131
5343
  {
4132
5344
  onSubmit: handleSubmit,
4133
5345
  onCancelQueue: handleCancelQueue,
4134
- isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || !!pendingProviderSetupType || isThinking && !!pendingPrompt,
5346
+ isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || pendingInteractionPrompt !== null || isThinking && !!pendingPrompt,
4135
5347
  isAborting,
4136
5348
  pendingPrompt,
4137
5349
  registry,
4138
- sessionName
5350
+ sessionName,
5351
+ history
4139
5352
  }
4140
5353
  ),
4141
- /* @__PURE__ */ jsx17(Text16, { children: " " })
5354
+ /* @__PURE__ */ jsx22(Text20, { children: " " })
4142
5355
  ] });
4143
5356
  }
4144
5357
 
4145
5358
  // src/ui/render.tsx
4146
- import { jsx as jsx18 } from "react/jsx-runtime";
5359
+ import { jsx as jsx23 } from "react/jsx-runtime";
4147
5360
  function renderApp(options) {
4148
5361
  process.on("unhandledRejection", (reason) => {
4149
5362
  process.stderr.write(`
@@ -4154,16 +5367,10 @@ function renderApp(options) {
4154
5367
  `);
4155
5368
  }
4156
5369
  });
4157
- if (process.stdin.isTTY && process.stdout.isTTY) {
4158
- process.stdout.write("\x1B[?2004h");
4159
- }
4160
- const instance = render(/* @__PURE__ */ jsx18(App, { ...options }), {
5370
+ const instance = render(/* @__PURE__ */ jsx23(App, { ...options }), {
4161
5371
  exitOnCtrlC: false
4162
5372
  });
4163
5373
  instance.waitUntilExit().then(() => {
4164
- if (process.stdout.isTTY) {
4165
- process.stdout.write("\x1B[?2004l");
4166
- }
4167
5374
  process.exit(0);
4168
5375
  }).catch((err) => {
4169
5376
  if (err) {
@@ -4175,15 +5382,102 @@ function renderApp(options) {
4175
5382
  });
4176
5383
  }
4177
5384
 
5385
+ // src/commands/statusline-command-module.ts
5386
+ var USAGE = [
5387
+ "Usage: /statusline on | off | reset | git on | git off",
5388
+ "Fields: model, context, permission mode, message count, session name, thinking state, git branch."
5389
+ ].join("\n");
5390
+ function createStatusLineEntry() {
5391
+ return {
5392
+ name: "statusline",
5393
+ description: "Configure TUI status-line visibility and fields such as model, context, tokens, session, and git branch.",
5394
+ source: "cli",
5395
+ modelInvocable: false,
5396
+ argumentHint: "on | off | reset | git on | git off",
5397
+ subcommands: [
5398
+ { name: "on", description: "Show the status line", source: "cli" },
5399
+ { name: "off", description: "Hide the status line", source: "cli" },
5400
+ { name: "reset", description: "Restore default status-line fields", source: "cli" },
5401
+ { name: "git", description: "Show or hide git branch field", source: "cli" }
5402
+ ]
5403
+ };
5404
+ }
5405
+ function parseStatusLineArgs(args) {
5406
+ const parts = args.trim().toLowerCase().split(/\s+/).filter((part) => part.length > 0);
5407
+ const [first, second] = parts;
5408
+ if (first === "on" && second === void 0) {
5409
+ return { success: true, message: "Status line enabled.", patch: { enabled: true } };
5410
+ }
5411
+ if (first === "off" && second === void 0) {
5412
+ return { success: true, message: "Status line disabled.", patch: { enabled: false } };
5413
+ }
5414
+ if (first === "reset" && second === void 0) {
5415
+ return {
5416
+ success: true,
5417
+ message: "Status line settings reset.",
5418
+ patch: { enabled: true, gitBranch: true }
5419
+ };
5420
+ }
5421
+ if (first === "git" && second === "on" && parts.length === 2) {
5422
+ return {
5423
+ success: true,
5424
+ message: "Status line git branch shown.",
5425
+ patch: { gitBranch: true }
5426
+ };
5427
+ }
5428
+ if (first === "git" && second === "off" && parts.length === 2) {
5429
+ return {
5430
+ success: true,
5431
+ message: "Status line git branch hidden.",
5432
+ patch: { gitBranch: false }
5433
+ };
5434
+ }
5435
+ return { success: false, message: USAGE };
5436
+ }
5437
+ function createStatusLineSystemCommand() {
5438
+ const entry = createStatusLineEntry();
5439
+ return {
5440
+ name: entry.name,
5441
+ description: entry.description,
5442
+ modelInvocable: false,
5443
+ userInvocable: true,
5444
+ argumentHint: entry.argumentHint,
5445
+ execute: (_session, args) => {
5446
+ const action = parseStatusLineArgs(args);
5447
+ if (!action.success) {
5448
+ return { success: false, message: action.message };
5449
+ }
5450
+ return {
5451
+ success: true,
5452
+ message: action.message,
5453
+ data: { statuslinePatch: action.patch }
5454
+ };
5455
+ }
5456
+ };
5457
+ }
5458
+ var StatusLineCommandSource = class {
5459
+ name = "cli-statusline";
5460
+ getCommands() {
5461
+ return [createStatusLineEntry()];
5462
+ }
5463
+ };
5464
+ function createStatusLineCommandModule() {
5465
+ return {
5466
+ name: "cli-statusline",
5467
+ commandSources: [new StatusLineCommandSource()],
5468
+ systemCommands: [createStatusLineSystemCommand()]
5469
+ };
5470
+ }
5471
+
4178
5472
  // src/cli.ts
4179
5473
  function readVersion() {
4180
5474
  try {
4181
5475
  const thisFile = fileURLToPath(import.meta.url);
4182
- const dir = dirname3(thisFile);
4183
- const candidates = [join7(dir, "..", "..", "package.json"), join7(dir, "..", "package.json")];
5476
+ const dir = dirname5(thisFile);
5477
+ const candidates = [join9(dir, "..", "..", "package.json"), join9(dir, "..", "package.json")];
4184
5478
  for (const pkgPath of candidates) {
4185
5479
  try {
4186
- const raw = readFileSync4(pkgPath, "utf-8");
5480
+ const raw = readFileSync6(pkgPath, "utf-8");
4187
5481
  const pkg = JSON.parse(raw);
4188
5482
  if (pkg.version !== void 0 && pkg.name !== void 0) {
4189
5483
  return pkg.version;
@@ -4197,7 +5491,7 @@ function readVersion() {
4197
5491
  }
4198
5492
  }
4199
5493
  function promptInput(label, masked = false) {
4200
- return new Promise((resolve) => {
5494
+ return new Promise((resolve2) => {
4201
5495
  process.stdout.write(label);
4202
5496
  let input = "";
4203
5497
  const stdin = process.stdin;
@@ -4212,7 +5506,7 @@ function promptInput(label, masked = false) {
4212
5506
  stdin.setRawMode(wasRaw ?? false);
4213
5507
  stdin.pause();
4214
5508
  process.stdout.write("\n");
4215
- resolve(input.trim());
5509
+ resolve2(input.trim());
4216
5510
  return;
4217
5511
  } else if (ch === "\x7F" || ch === "\b") {
4218
5512
  if (input.length > 0) {
@@ -4242,8 +5536,21 @@ function resetConfig() {
4242
5536
  }
4243
5537
  async function startCli(options = {}) {
4244
5538
  const args = parseCliArgs();
5539
+ const version = readVersion();
4245
5540
  if (args.version) {
4246
- process.stdout.write(`robota ${readVersion()}
5541
+ process.stdout.write(`robota ${version}
5542
+ `);
5543
+ return;
5544
+ }
5545
+ if (args.checkUpdate) {
5546
+ const result = await checkForCliUpdate({ currentVersion: version, force: true });
5547
+ const message = formatCliUpdateCheckMessage(result);
5548
+ if (result.status === "error") {
5549
+ process.stderr.write(`${message}
5550
+ `);
5551
+ process.exit(1);
5552
+ }
5553
+ process.stdout.write(`${message}
4247
5554
  `);
4248
5555
  return;
4249
5556
  }
@@ -4253,6 +5560,11 @@ async function startCli(options = {}) {
4253
5560
  }
4254
5561
  const cwd = process.cwd();
4255
5562
  const providerDefinitions = options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
5563
+ const commandModules = [
5564
+ createStatusLineCommandModule(),
5565
+ ...options.commandModules ?? []
5566
+ ];
5567
+ const startupUpdateNoticePromise = shouldRunStartupCliUpdateCheck(args) ? getStartupCliUpdateNotice({ currentVersion: version }) : void 0;
4256
5568
  if (args.configure) {
4257
5569
  await runInteractiveProviderSetup(cwd, args, promptInput, providerDefinitions);
4258
5570
  return;
@@ -4332,7 +5644,7 @@ ${args.jsonSchema}`
4332
5644
  appendSystemPrompt,
4333
5645
  backgroundTaskRunners,
4334
5646
  subagentRunnerFactory,
4335
- commandModules: options.commandModules
5647
+ commandModules
4336
5648
  });
4337
5649
  const transport = createHeadlessTransport({
4338
5650
  outputFormat: args.outputFormat ?? "text",
@@ -4350,15 +5662,16 @@ ${args.jsonSchema}`
4350
5662
  language: args.language,
4351
5663
  permissionMode: args.permissionMode,
4352
5664
  maxTurns: args.maxTurns,
4353
- version: readVersion(),
5665
+ version,
4354
5666
  sessionStore,
4355
5667
  resumeSessionId,
4356
5668
  forkSession: args.forkSession,
4357
5669
  sessionName: args.sessionName,
4358
5670
  backgroundTaskRunners,
4359
5671
  subagentRunnerFactory,
4360
- commandModules: options.commandModules,
4361
- providerDefinitions
5672
+ commandModules,
5673
+ providerDefinitions,
5674
+ startupUpdateNoticePromise
4362
5675
  });
4363
5676
  }
4364
5677