@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.
@@ -40,8 +40,8 @@ __export(index_exports, {
40
40
  module.exports = __toCommonJS(index_exports);
41
41
 
42
42
  // src/cli.ts
43
- var import_node_fs6 = require("fs");
44
- var import_node_path8 = require("path");
43
+ var import_node_fs8 = require("fs");
44
+ var import_node_path10 = require("path");
45
45
  var import_node_url = require("url");
46
46
  var import_agent_sdk8 = require("@robota-sdk/agent-sdk");
47
47
  var import_agent_sessions = require("@robota-sdk/agent-sessions");
@@ -98,7 +98,9 @@ function parseCliArgs() {
98
98
  "api-key": { type: "string" },
99
99
  "api-key-env": { type: "string" },
100
100
  "set-current": { type: "boolean", default: false },
101
- "settings-scope": { type: "string" }
101
+ "settings-scope": { type: "string" },
102
+ "check-update": { type: "boolean", default: false },
103
+ "disable-update-check": { type: "boolean", default: false }
102
104
  }
103
105
  });
104
106
  return {
@@ -129,7 +131,9 @@ function parseCliArgs() {
129
131
  apiKey: values["api-key"],
130
132
  apiKeyEnv: values["api-key-env"],
131
133
  setCurrent: values["set-current"] ?? false,
132
- settingsScope: values["settings-scope"]
134
+ settingsScope: values["settings-scope"],
135
+ checkUpdate: values["check-update"] ?? false,
136
+ disableUpdateCheck: values["disable-update-check"] ?? false
133
137
  };
134
138
  }
135
139
 
@@ -193,16 +197,47 @@ var import_node_os = require("os");
193
197
  // src/utils/provider-default-definitions.ts
194
198
  var import_agent_provider_anthropic = require("@robota-sdk/agent-provider-anthropic");
195
199
  var import_agent_provider_gemma = require("@robota-sdk/agent-provider-gemma");
200
+ var import_agent_provider_gemini = require("@robota-sdk/agent-provider-gemini");
196
201
  var import_agent_provider_openai = require("@robota-sdk/agent-provider-openai");
202
+ var import_agent_provider_qwen = require("@robota-sdk/agent-provider-qwen");
197
203
  var DEFAULT_PROVIDER_DEFINITIONS = [
198
204
  (0, import_agent_provider_anthropic.createAnthropicProviderDefinition)(),
199
205
  (0, import_agent_provider_openai.createOpenAIProviderDefinition)(),
200
- (0, import_agent_provider_gemma.createGemmaProviderDefinition)()
206
+ (0, import_agent_provider_gemini.createGeminiProviderDefinition)(),
207
+ (0, import_agent_provider_gemma.createGemmaProviderDefinition)(),
208
+ (0, import_agent_provider_qwen.createQwenProviderDefinition)()
201
209
  ];
202
210
 
203
211
  // src/utils/provider-definition.ts
204
212
  var import_agent_core = require("@robota-sdk/agent-core");
205
213
 
214
+ // src/utils/env-ref.ts
215
+ var ENV_REFERENCE_PREFIX = "$ENV:";
216
+ function isEnvReference(value) {
217
+ return value.startsWith(ENV_REFERENCE_PREFIX);
218
+ }
219
+ function getEnvReferenceName(value) {
220
+ if (!isEnvReference(value)) {
221
+ return void 0;
222
+ }
223
+ const envName = value.slice(ENV_REFERENCE_PREFIX.length).trim();
224
+ return envName.length > 0 ? envName : void 0;
225
+ }
226
+ function resolveEnvReference(value) {
227
+ const envName = getEnvReferenceName(value);
228
+ if (envName === void 0) {
229
+ return value;
230
+ }
231
+ const resolved = process.env[envName];
232
+ return resolved !== void 0 && resolved.length > 0 ? resolved : void 0;
233
+ }
234
+ function hasUsableSecretReference(value) {
235
+ if (value === void 0 || value.length === 0) {
236
+ return false;
237
+ }
238
+ return resolveEnvReference(value) !== void 0;
239
+ }
240
+
206
241
  // src/utils/provider-factory.ts
207
242
  function readProviderSettings(cwd, options = {}) {
208
243
  const merged = readMergedProviderSettings(cwd);
@@ -275,7 +310,8 @@ function resolveActiveProvider(settings, providerOverride, providerDefinitions =
275
310
  model: profile.model,
276
311
  apiKey: profile.apiKey,
277
312
  baseURL: profile.baseURL,
278
- timeout: profile.timeout
313
+ timeout: profile.timeout,
314
+ options: profile.options
279
315
  },
280
316
  providerDefinitions
281
317
  );
@@ -288,7 +324,8 @@ function resolveActiveProvider(settings, providerOverride, providerDefinitions =
288
324
  model: provider.model,
289
325
  apiKey: provider.apiKey,
290
326
  baseURL: provider.baseURL,
291
- timeout: provider.timeout
327
+ timeout: provider.timeout,
328
+ options: provider.options
292
329
  },
293
330
  providerDefinitions
294
331
  );
@@ -301,22 +338,17 @@ function normalizeProviderConfig(settings, providerDefinitions) {
301
338
  if (!model) {
302
339
  throw new Error(`Provider ${settings.name} requires model`);
303
340
  }
341
+ const apiKeyReference = settings.apiKey ?? defaults.apiKey;
342
+ const options = settings.options ?? defaults.options;
304
343
  return {
305
344
  name: settings.name,
306
345
  model,
307
- apiKey: settings.apiKey !== void 0 ? resolveEnvRef(settings.apiKey) : defaults.apiKey,
346
+ apiKey: apiKeyReference !== void 0 ? resolveEnvReference(apiKeyReference) : void 0,
308
347
  baseURL: settings.baseURL ?? defaults.baseURL,
309
- timeout: settings.timeout
348
+ timeout: settings.timeout,
349
+ ...options !== void 0 && { options }
310
350
  };
311
351
  }
312
- function resolveEnvRef(value) {
313
- const envPrefix = "$ENV:";
314
- if (!value.startsWith(envPrefix)) {
315
- return value;
316
- }
317
- const envName = value.slice(envPrefix.length);
318
- return process.env[envName] ?? value;
319
- }
320
352
  function createProviderFromConfig(settings, providerDefinitions) {
321
353
  const definition = (0, import_agent_core.findProviderDefinition)(providerDefinitions, settings.name);
322
354
  if (definition === void 0) {
@@ -371,7 +403,7 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
371
403
  if (!profile) {
372
404
  return false;
373
405
  }
374
- if (profile.apiKey) {
406
+ if (hasUsableSecretReference(profile.apiKey)) {
375
407
  return true;
376
408
  }
377
409
  if (!type) {
@@ -381,7 +413,7 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
381
413
  if (definition === void 0) {
382
414
  return false;
383
415
  }
384
- return definition.requiresApiKey !== true || definition.defaults?.apiKey !== void 0;
416
+ return definition.requiresApiKey !== true || hasUsableSecretReference(definition.defaults?.apiKey);
385
417
  }
386
418
 
387
419
  // src/utils/provider-settings.ts
@@ -411,7 +443,7 @@ function validateProviderProfile(profileName, profile, options = {}) {
411
443
  throw new Error(`Provider profile "${profileName}" is missing model`);
412
444
  }
413
445
  const definition = (0, import_agent_core.findProviderDefinition)(options.providerDefinitions ?? [], profile.type);
414
- if (definition?.requiresApiKey === true && !profile.apiKey && definition.defaults?.apiKey === void 0) {
446
+ if (definition?.requiresApiKey === true && !hasUsableSecretReference(profile.apiKey ?? definition.defaults?.apiKey)) {
415
447
  throw new Error(`Provider profile "${profileName}" is missing apiKey`);
416
448
  }
417
449
  }
@@ -478,6 +510,40 @@ function createProviderSetupFlow(type, providerDefinitions) {
478
510
  values: {}
479
511
  };
480
512
  }
513
+ function formatProviderSetupSelectionPrompt(providerDefinitions) {
514
+ if (providerDefinitions.length === 0) {
515
+ return " No providers are available.";
516
+ }
517
+ const lines = [
518
+ " Select provider:",
519
+ ...providerDefinitions.map(
520
+ (definition, index) => ` ${index + 1}. ${formatProviderSetupChoiceLabel(definition)}`
521
+ ),
522
+ ` Provider [1-${providerDefinitions.length}] (default: 1): `
523
+ ];
524
+ return lines.join("\n");
525
+ }
526
+ function resolveProviderSetupSelection(rawValue, providerDefinitions) {
527
+ const value = rawValue.trim();
528
+ const selectedValue = value.length > 0 ? value : "1";
529
+ const index = parseProviderSelectionIndex(selectedValue);
530
+ if (index !== void 0) {
531
+ const definition2 = providerDefinitions[index];
532
+ if (definition2 !== void 0) {
533
+ return definition2.type;
534
+ }
535
+ throw new Error(
536
+ `Provider selection ${selectedValue} is out of range. Currently supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`
537
+ );
538
+ }
539
+ const definition = (0, import_agent_core.findProviderDefinition)(providerDefinitions, selectedValue);
540
+ if (definition === void 0) {
541
+ throw new Error(
542
+ `Unknown provider: ${selectedValue}. Currently supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`
543
+ );
544
+ }
545
+ return definition.type;
546
+ }
481
547
  function getProviderSetupStep(state) {
482
548
  const step = state.steps[state.stepIndex];
483
549
  if (step === void 0) {
@@ -523,6 +589,16 @@ function formatProviderSetupPromptLabel(step) {
523
589
  const suffix = step.defaultValue !== void 0 ? ` (default: ${step.defaultValue})` : "";
524
590
  return ` ${step.title}${suffix}: `;
525
591
  }
592
+ function formatProviderSetupChoiceLabel(definition) {
593
+ const label = definition.displayName !== void 0 ? `${definition.displayName} (${definition.type})` : definition.type;
594
+ return definition.description !== void 0 ? `${label} - ${definition.description}` : label;
595
+ }
596
+ function parseProviderSelectionIndex(value) {
597
+ if (!/^\d+$/.test(value)) {
598
+ return void 0;
599
+ }
600
+ return Number(value) - 1;
601
+ }
526
602
  function validateProviderSetupValue(step, value) {
527
603
  if (step.required === true && value.length === 0) {
528
604
  return "Required";
@@ -615,15 +691,13 @@ async function ensureConfig(cwd, args, promptInput2, providerDefinitions = DEFAU
615
691
  return;
616
692
  }
617
693
  if (!isInteractiveTerminal()) {
618
- throw new Error(formatMissingProviderConfigMessage());
694
+ throw new Error(formatMissingProviderConfigMessage(providerDefinitions));
619
695
  }
620
696
  await runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions);
621
697
  }
622
698
  async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
623
- const defaultProviderType = providerDefinitions[0]?.type ?? "";
624
- const supportedTypes = providerDefinitions.map((definition) => definition.type).join("/");
625
- const providerChoice = await promptInput2(` Provider (${supportedTypes}, default: ${defaultProviderType}): `) || defaultProviderType;
626
- const type = parseProviderSetupType(providerChoice);
699
+ const providerChoice = await promptInput2(formatProviderSetupSelectionPrompt(providerDefinitions));
700
+ const type = resolveProviderSetupSelection(providerChoice, providerDefinitions);
627
701
  const settingsPath = getSettingsPathForScope(cwd, args.settingsScope);
628
702
  const input = await runProviderSetupPromptFlow(type, promptInput2, providerDefinitions);
629
703
  applyProviderConfiguration(settingsPath, input, {
@@ -640,9 +714,6 @@ async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefi
640
714
 
641
715
  `);
642
716
  }
643
- function parseProviderSetupType(value) {
644
- return value.trim();
645
- }
646
717
  function buildSetupInputFromArgs(args) {
647
718
  const type = args.providerType ?? args.configureProvider;
648
719
  if (!args.configureProvider || !type) {
@@ -671,25 +742,36 @@ function getSettingsCheckPaths(cwd) {
671
742
  function isInteractiveTerminal() {
672
743
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
673
744
  }
674
- function formatMissingProviderConfigMessage() {
745
+ function formatMissingProviderConfigMessage(providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
675
746
  return [
676
747
  "No provider configuration found.",
677
748
  "Run `robota --configure` in an interactive terminal, or configure a provider:",
678
- " robota --configure-provider gemma --type gemma --base-url http://localhost:1234/v1 --model supergemma4-26b-uncensored-v2 --api-key lm-studio --set-current",
679
- " robota --configure-provider openai --type openai --model <openai-compatible-model> --api-key-env OPENAI_API_KEY --set-current"
749
+ `Supported providers: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`,
750
+ ...providerDefinitions.map(formatConfigureProviderExample)
680
751
  ].join("\n");
681
752
  }
753
+ function formatConfigureProviderExample(definition) {
754
+ const flags = [
755
+ `robota --configure-provider ${definition.type}`,
756
+ `--type ${definition.type}`,
757
+ ...definition.defaults?.baseURL !== void 0 ? ["--base-url <url>"] : [],
758
+ "--model <model>",
759
+ ...definition.requiresApiKey === true ? ["--api-key-env <ENV_NAME>"] : [],
760
+ "--set-current"
761
+ ];
762
+ return ` ${flags.join(" ")}`;
763
+ }
682
764
 
683
765
  // src/cli.ts
684
766
  var import_agent_transport_headless = require("@robota-sdk/agent-transport-headless");
685
767
 
686
768
  // src/ui/render.tsx
687
- var import_ink18 = require("ink");
769
+ var import_ink22 = require("ink");
688
770
 
689
771
  // src/ui/App.tsx
690
- var import_react18 = require("react");
691
- var import_ink17 = require("ink");
692
- var import_agent_core7 = require("@robota-sdk/agent-core");
772
+ var import_react19 = require("react");
773
+ var import_ink21 = require("ink");
774
+ var import_agent_core9 = require("@robota-sdk/agent-core");
693
775
 
694
776
  // src/ui/hooks/useInteractiveSession.ts
695
777
  var import_react2 = require("react");
@@ -718,7 +800,9 @@ function toBackgroundTaskViewModel(state, partialText) {
718
800
  errorPreview: trimBackgroundPreview(state.error?.message),
719
801
  startedAt: state.startedAt,
720
802
  lastActivityAt: state.lastActivityAt,
721
- timeoutReason: state.timeoutReason
803
+ timeoutReason: state.timeoutReason,
804
+ exitCode: state.result?.exitCode,
805
+ signalCode: state.result?.signalCode
722
806
  };
723
807
  }
724
808
  function getBackgroundTaskStatusLabel(state) {
@@ -839,6 +923,13 @@ var TuiStateManager = class {
839
923
  this.activeTools = [];
840
924
  this.notify();
841
925
  };
926
+ onContextUpdate = (state) => {
927
+ this.setContextState({
928
+ percentage: state.usedPercentage,
929
+ usedTokens: state.usedTokens,
930
+ maxTokens: state.maxTokens
931
+ });
932
+ };
842
933
  onBackgroundTaskEvent = (event) => {
843
934
  if ("task" in event) {
844
935
  this.upsertBackgroundTask(event.task);
@@ -1013,7 +1104,14 @@ function buildProviderSwitch(providers, profileName) {
1013
1104
  };
1014
1105
  }
1015
1106
  function buildProviderSetup(type, providerDefinitions) {
1016
- if (!type || (0, import_agent_core.findProviderDefinition)(providerDefinitions, type) === void 0) {
1107
+ if (!type) {
1108
+ return {
1109
+ message: "Provider setup requested. Select a provider to continue.",
1110
+ success: true,
1111
+ data: { providerSetup: {} }
1112
+ };
1113
+ }
1114
+ if ((0, import_agent_core.findProviderDefinition)(providerDefinitions, type) === void 0) {
1017
1115
  return {
1018
1116
  message: `Usage: provider add <type>. Supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`,
1019
1117
  success: false
@@ -1101,8 +1199,8 @@ async function routeProviderCommand(cwd, args, interactiveSession, manager, prov
1101
1199
  getEffects(interactiveSession)._pendingProviderProfile = providerSwitch.profile;
1102
1200
  }
1103
1201
  const providerSetup = result.data?.providerSetup;
1104
- if (providerSetup?.type) {
1105
- getEffects(interactiveSession)._pendingProviderSetupType = providerSetup.type;
1202
+ if (providerSetup !== void 0) {
1203
+ getEffects(interactiveSession)._pendingProviderSetup = providerSetup;
1106
1204
  }
1107
1205
  }
1108
1206
  function applySystemCommandResult(result, interactiveSession, manager) {
@@ -1129,6 +1227,11 @@ function applySystemCommandResult(result, interactiveSession, manager) {
1129
1227
  effects._sessionName = data.name;
1130
1228
  return;
1131
1229
  }
1230
+ const statusLinePatch = data?.statuslinePatch;
1231
+ if (isStatusLineSettingsPatch(statusLinePatch)) {
1232
+ effects._statusLinePatch = statusLinePatch;
1233
+ return;
1234
+ }
1132
1235
  const ctx = interactiveSession.getContextState();
1133
1236
  manager.setContextState({
1134
1237
  percentage: ctx.usedPercentage,
@@ -1173,6 +1276,13 @@ function routeTuiCommand(cmd, interactiveSession) {
1173
1276
  function getEffects(interactiveSession) {
1174
1277
  return interactiveSession;
1175
1278
  }
1279
+ function isStatusLineSettingsPatch(value) {
1280
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) {
1281
+ return false;
1282
+ }
1283
+ const candidate = value;
1284
+ return (candidate.enabled === void 0 || typeof candidate.enabled === "boolean") && (candidate.gitBranch === void 0 || typeof candidate.gitBranch === "boolean");
1285
+ }
1176
1286
 
1177
1287
  // src/ui/hooks/useInteractiveSession.ts
1178
1288
  function initializeSession(props, permissionHandler) {
@@ -1235,8 +1345,8 @@ function useInteractiveSession(props) {
1235
1345
  });
1236
1346
  }, []);
1237
1347
  const permissionHandler = (0, import_react2.useCallback)(
1238
- (toolName, toolArgs) => new Promise((resolve) => {
1239
- permissionQueueRef.current.push({ toolName, toolArgs, resolve });
1348
+ (toolName, toolArgs) => new Promise((resolve2) => {
1349
+ permissionQueueRef.current.push({ toolName, toolArgs, resolve: resolve2 });
1240
1350
  processNextPermission();
1241
1351
  }),
1242
1352
  [processNextPermission]
@@ -1261,6 +1371,7 @@ function useInteractiveSession(props) {
1261
1371
  interactiveSession.on("complete", manager.onComplete);
1262
1372
  interactiveSession.on("interrupted", manager.onInterrupted);
1263
1373
  interactiveSession.on("error", manager.onError);
1374
+ interactiveSession.on("context_update", manager.onContextUpdate);
1264
1375
  interactiveSession.on("background_task_event", manager.onBackgroundTaskEvent);
1265
1376
  const initCheck = setInterval(() => {
1266
1377
  try {
@@ -1287,6 +1398,7 @@ function useInteractiveSession(props) {
1287
1398
  interactiveSession.off("complete", manager.onComplete);
1288
1399
  interactiveSession.off("interrupted", manager.onInterrupted);
1289
1400
  interactiveSession.off("error", manager.onError);
1401
+ interactiveSession.off("context_update", manager.onContextUpdate);
1290
1402
  interactiveSession.off("background_task_event", manager.onBackgroundTaskEvent);
1291
1403
  };
1292
1404
  }, [interactiveSession, manager]);
@@ -1449,6 +1561,108 @@ function usePluginCallbacks(cwd) {
1449
1561
  var import_react4 = require("react");
1450
1562
  var import_ink = require("ink");
1451
1563
  var import_agent_core4 = require("@robota-sdk/agent-core");
1564
+
1565
+ // src/utils/provider-setup-interaction.ts
1566
+ function startProviderSetupInteraction(providerDefinitions, type) {
1567
+ if (type === void 0) {
1568
+ const state2 = {
1569
+ mode: "select-provider",
1570
+ providerDefinitions
1571
+ };
1572
+ return { status: "prompt", state: state2, prompt: toProviderSelectionPrompt(providerDefinitions) };
1573
+ }
1574
+ const state = {
1575
+ mode: "setup-fields",
1576
+ providerDefinitions,
1577
+ flow: createProviderSetupFlow(type, providerDefinitions)
1578
+ };
1579
+ return { status: "prompt", state, prompt: toProviderSetupStepPrompt(state.flow) };
1580
+ }
1581
+ function submitProviderSetupInteractionValue(state, value) {
1582
+ if (state.mode === "select-provider") {
1583
+ const nextState2 = {
1584
+ mode: "setup-fields",
1585
+ providerDefinitions: state.providerDefinitions,
1586
+ flow: createProviderSetupFlow(value, state.providerDefinitions)
1587
+ };
1588
+ return {
1589
+ status: "prompt",
1590
+ state: nextState2,
1591
+ prompt: toProviderSetupStepPrompt(nextState2.flow)
1592
+ };
1593
+ }
1594
+ const result = submitProviderSetupValue(state.flow, value);
1595
+ if (result.status === "complete") {
1596
+ return { status: "complete", input: result.input };
1597
+ }
1598
+ const nextState = {
1599
+ ...state,
1600
+ flow: result.state
1601
+ };
1602
+ return { status: "prompt", state: nextState, prompt: toProviderSetupStepPrompt(result.state) };
1603
+ }
1604
+ function toProviderSelectionPrompt(providerDefinitions) {
1605
+ return {
1606
+ kind: "choice",
1607
+ title: "Select provider",
1608
+ options: providerDefinitions.map((definition) => ({
1609
+ value: definition.type,
1610
+ label: formatProviderSetupChoiceLabel(definition)
1611
+ })),
1612
+ maxVisible: 6
1613
+ };
1614
+ }
1615
+ function toProviderSetupStepPrompt(flow) {
1616
+ const step = getProviderSetupStep(flow);
1617
+ return {
1618
+ kind: "text",
1619
+ title: step.title,
1620
+ ...step.defaultValue !== void 0 ? { placeholder: step.defaultValue } : {},
1621
+ ...step.defaultValue !== void 0 ? { allowEmpty: true } : {},
1622
+ ...step.masked !== void 0 ? { masked: step.masked } : {},
1623
+ validate: (value) => validateProviderSetupValue(step, value)
1624
+ };
1625
+ }
1626
+
1627
+ // src/utils/statusline-settings.ts
1628
+ var DEFAULT_STATUS_LINE_SETTINGS = {
1629
+ enabled: true,
1630
+ gitBranch: true
1631
+ };
1632
+ function readStatusLineSettings(settings) {
1633
+ const raw = settings.statusline;
1634
+ if (!isRecord(raw)) {
1635
+ return { ...DEFAULT_STATUS_LINE_SETTINGS };
1636
+ }
1637
+ return {
1638
+ enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_STATUS_LINE_SETTINGS.enabled,
1639
+ gitBranch: typeof raw.gitBranch === "boolean" ? raw.gitBranch : DEFAULT_STATUS_LINE_SETTINGS.gitBranch
1640
+ };
1641
+ }
1642
+ function applyStatusLineSettings(settingsPath, patch) {
1643
+ const settings = readSettings(settingsPath);
1644
+ const next = {
1645
+ ...readStatusLineSettings(settings),
1646
+ ...patch
1647
+ };
1648
+ settings.statusline = next;
1649
+ writeSettings(settingsPath, settings);
1650
+ return next;
1651
+ }
1652
+ function isRecord(value) {
1653
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
1654
+ }
1655
+
1656
+ // src/ui/hooks/statusline-side-effect.ts
1657
+ function applyPendingStatusLinePatch(sideEffects, setStatusLineSettings) {
1658
+ if (!sideEffects._statusLinePatch) return false;
1659
+ const patch = sideEffects._statusLinePatch;
1660
+ delete sideEffects._statusLinePatch;
1661
+ setStatusLineSettings(applyStatusLineSettings(getUserSettingsPath(), patch));
1662
+ return true;
1663
+ }
1664
+
1665
+ // src/ui/hooks/useSideEffects.ts
1452
1666
  var EXIT_DELAY_MS = 500;
1453
1667
  function useSideEffects({
1454
1668
  cwd,
@@ -1456,6 +1670,7 @@ function useSideEffects({
1456
1670
  addEntry,
1457
1671
  baseHandleSubmit,
1458
1672
  setSessionName,
1673
+ setStatusLineSettings,
1459
1674
  providerDefinitions
1460
1675
  }) {
1461
1676
  const { exit } = (0, import_ink.useApp)();
@@ -1463,7 +1678,8 @@ function useSideEffects({
1463
1678
  const pendingModelChangeRef = (0, import_react4.useRef)(null);
1464
1679
  const [pendingProviderProfile, setPendingProviderProfile] = (0, import_react4.useState)(null);
1465
1680
  const pendingProviderProfileRef = (0, import_react4.useRef)(null);
1466
- const [pendingProviderSetupType, setPendingProviderSetupType] = (0, import_react4.useState)(null);
1681
+ const [pendingInteractionPrompt, setPendingInteractionPrompt] = (0, import_react4.useState)(null);
1682
+ const providerSetupInteractionRef = (0, import_react4.useRef)(null);
1467
1683
  const [showPluginTUI, setShowPluginTUI] = (0, import_react4.useState)(false);
1468
1684
  const [showSessionPicker, setShowSessionPicker] = (0, import_react4.useState)(false);
1469
1685
  const requestShutdown = (0, import_react4.useCallback)(
@@ -1506,10 +1722,14 @@ function useSideEffects({
1506
1722
  setPendingProviderProfile(profile);
1507
1723
  return;
1508
1724
  }
1509
- if (sideEffects._pendingProviderSetupType) {
1510
- const type = sideEffects._pendingProviderSetupType;
1511
- delete sideEffects._pendingProviderSetupType;
1512
- setPendingProviderSetupType(type);
1725
+ if (sideEffects._pendingProviderSetup !== void 0) {
1726
+ const setup = sideEffects._pendingProviderSetup;
1727
+ delete sideEffects._pendingProviderSetup;
1728
+ const result = startProviderSetupInteraction(providerDefinitions, setup.type);
1729
+ if (result.status === "prompt") {
1730
+ providerSetupInteractionRef.current = result.state;
1731
+ setPendingInteractionPrompt(result.prompt);
1732
+ }
1513
1733
  return;
1514
1734
  }
1515
1735
  if (sideEffects._resetRequested) {
@@ -1547,8 +1767,17 @@ function useSideEffects({
1547
1767
  setSessionName(name);
1548
1768
  return;
1549
1769
  }
1770
+ if (applyPendingStatusLinePatch(sideEffects, setStatusLineSettings)) return;
1550
1771
  },
1551
- [interactiveSession, baseHandleSubmit, addEntry, requestShutdown, setSessionName]
1772
+ [
1773
+ interactiveSession,
1774
+ baseHandleSubmit,
1775
+ addEntry,
1776
+ requestShutdown,
1777
+ setSessionName,
1778
+ setStatusLineSettings,
1779
+ providerDefinitions
1780
+ ]
1552
1781
  );
1553
1782
  const handleModelConfirm = (0, import_react4.useCallback)(
1554
1783
  (index) => {
@@ -1608,9 +1837,10 @@ function useSideEffects({
1608
1837
  },
1609
1838
  [cwd, addEntry, requestShutdown]
1610
1839
  );
1611
- const handleProviderSetupSubmit = (0, import_react4.useCallback)(
1840
+ const completeProviderSetup = (0, import_react4.useCallback)(
1612
1841
  (input) => {
1613
- setPendingProviderSetupType(null);
1842
+ providerSetupInteractionRef.current = null;
1843
+ setPendingInteractionPrompt(null);
1614
1844
  try {
1615
1845
  const settingsPath = getUserSettingsPath();
1616
1846
  applyProviderConfiguration(settingsPath, input, { providerDefinitions });
@@ -1630,15 +1860,43 @@ function useSideEffects({
1630
1860
  },
1631
1861
  [addEntry, requestShutdown, providerDefinitions]
1632
1862
  );
1633
- const handleProviderSetupCancel = (0, import_react4.useCallback)(() => {
1634
- setPendingProviderSetupType(null);
1863
+ const handleInteractionSubmit = (0, import_react4.useCallback)(
1864
+ (value) => {
1865
+ const state = providerSetupInteractionRef.current;
1866
+ if (state === null) {
1867
+ setPendingInteractionPrompt(null);
1868
+ return;
1869
+ }
1870
+ try {
1871
+ const result = submitProviderSetupInteractionValue(state, value);
1872
+ if (result.status === "complete") {
1873
+ completeProviderSetup(result.input);
1874
+ return;
1875
+ }
1876
+ providerSetupInteractionRef.current = result.state;
1877
+ setPendingInteractionPrompt(result.prompt);
1878
+ } catch (err) {
1879
+ providerSetupInteractionRef.current = null;
1880
+ setPendingInteractionPrompt(null);
1881
+ addEntry(
1882
+ (0, import_agent_core4.messageToHistoryEntry)(
1883
+ (0, import_agent_core4.createSystemMessage)(`Failed: ${err instanceof Error ? err.message : String(err)}`)
1884
+ )
1885
+ );
1886
+ }
1887
+ },
1888
+ [addEntry, completeProviderSetup]
1889
+ );
1890
+ const handleInteractionCancel = (0, import_react4.useCallback)(() => {
1891
+ providerSetupInteractionRef.current = null;
1892
+ setPendingInteractionPrompt(null);
1635
1893
  addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Provider setup cancelled.")));
1636
1894
  }, [addEntry]);
1637
1895
  return {
1638
1896
  handleSubmit,
1639
1897
  pendingModelId,
1640
1898
  pendingProviderProfile,
1641
- pendingProviderSetupType,
1899
+ pendingInteractionPrompt,
1642
1900
  showPluginTUI,
1643
1901
  showSessionPicker,
1644
1902
  setPendingModelId,
@@ -1646,102 +1904,344 @@ function useSideEffects({
1646
1904
  setShowSessionPicker,
1647
1905
  handleModelConfirm,
1648
1906
  handleProviderConfirm,
1649
- handleProviderSetupSubmit,
1650
- handleProviderSetupCancel
1907
+ handleInteractionSubmit,
1908
+ handleInteractionCancel
1651
1909
  };
1652
1910
  }
1653
1911
 
1912
+ // src/ui/hooks/useStatusLineSettings.ts
1913
+ var import_react5 = require("react");
1914
+ function useStatusLineSettings() {
1915
+ return (0, import_react5.useState)(
1916
+ () => readStatusLineSettings(readSettings(getUserSettingsPath()))
1917
+ );
1918
+ }
1919
+
1654
1920
  // src/ui/MessageList.tsx
1655
- var import_react5 = __toESM(require("react"), 1);
1656
- var import_ink3 = require("ink");
1657
- var import_agent_core5 = require("@robota-sdk/agent-core");
1921
+ var import_react6 = __toESM(require("react"), 1);
1922
+ var import_ink5 = require("ink");
1923
+ var import_agent_core6 = require("@robota-sdk/agent-core");
1658
1924
 
1659
1925
  // src/ui/render-markdown.ts
1660
1926
  var import_marked = require("marked");
1661
1927
  var import_marked_terminal = __toESM(require("marked-terminal"), 1);
1662
- import_marked.marked.setOptions({
1663
- renderer: new import_marked_terminal.default()
1664
- });
1665
- function renderMarkdown(md) {
1666
- const result = import_marked.marked.parse(md);
1928
+ var ANSI_RED = "\x1B[31m";
1929
+ var ANSI_GREEN = "\x1B[32m";
1930
+ var ANSI_CYAN = "\x1B[36m";
1931
+ var ANSI_DIM = "\x1B[2m";
1932
+ var ANSI_RESET = "\x1B[0m";
1933
+ var CODE_BLOCK_INDENT = " ";
1934
+ var ZERO_COLOR = "0";
1935
+ var TerminalRendererConstructor = import_marked_terminal.default;
1936
+ function shouldUseColor(option) {
1937
+ if (option !== void 0) {
1938
+ return option;
1939
+ }
1940
+ if (process.env.NO_COLOR || process.env.FORCE_COLOR === ZERO_COLOR) {
1941
+ return false;
1942
+ }
1943
+ if (process.env.FORCE_COLOR) {
1944
+ return true;
1945
+ }
1946
+ return Boolean(process.stdout.isTTY);
1947
+ }
1948
+ function isDiffLanguage(language) {
1949
+ return language?.trim().toLowerCase() === "diff";
1950
+ }
1951
+ function colorizeDiffLine(line, color) {
1952
+ if (!color) {
1953
+ return line;
1954
+ }
1955
+ if (line.startsWith("+")) {
1956
+ return `${ANSI_GREEN}${line}${ANSI_RESET}`;
1957
+ }
1958
+ if (line.startsWith("-")) {
1959
+ return `${ANSI_RED}${line}${ANSI_RESET}`;
1960
+ }
1961
+ if (line.startsWith("@@")) {
1962
+ return `${ANSI_CYAN}${line}${ANSI_RESET}`;
1963
+ }
1964
+ if (line.startsWith("diff ") || line.startsWith("index ")) {
1965
+ return `${ANSI_DIM}${line}${ANSI_RESET}`;
1966
+ }
1967
+ return line;
1968
+ }
1969
+ function renderDiffCodeBlock(code, color) {
1970
+ const body = code.split("\n").map((line) => `${CODE_BLOCK_INDENT}${colorizeDiffLine(line, color)}`).join("\n");
1971
+ return `${body}
1972
+
1973
+ `;
1974
+ }
1975
+ function createTerminalRenderer(color) {
1976
+ const renderer = new TerminalRendererConstructor(void 0, { ignoreIllegals: true });
1977
+ const renderCode = renderer.code.bind(renderer);
1978
+ renderer.code = (code, language, escaped) => {
1979
+ if (isDiffLanguage(language)) {
1980
+ return renderDiffCodeBlock(code, color);
1981
+ }
1982
+ return renderCode(code, language, escaped);
1983
+ };
1984
+ return renderer;
1985
+ }
1986
+ function renderMarkdown(md, options = {}) {
1987
+ const result = import_marked.marked.parse(md, {
1988
+ renderer: createTerminalRenderer(shouldUseColor(options.color))
1989
+ });
1667
1990
  return typeof result === "string" ? result.trimEnd() : md;
1668
1991
  }
1669
1992
 
1670
- // src/ui/DiffBlock.tsx
1993
+ // src/ui/ToolDiffBlock.tsx
1671
1994
  var import_ink2 = require("ink");
1672
- var import_jsx_runtime = require("react/jsx-runtime");
1995
+
1996
+ // src/utils/tool-diff-summary.ts
1673
1997
  var MAX_DIFF_LINES = 12;
1674
1998
  var TRUNCATED_SHOW = 10;
1675
- function DiffBlock({ file, lines }) {
1676
- const truncated = lines.length > MAX_DIFF_LINES;
1677
- const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
1678
- const remaining = lines.length - TRUNCATED_SHOW;
1679
- const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
1680
- const numWidth = String(maxLineNum).length;
1999
+ function buildToolDiffSummary(input) {
2000
+ const visibleLines = input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;
2001
+ const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString().length;
2002
+ const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));
2003
+ const truncated = input.lines.length > MAX_DIFF_LINES;
2004
+ return {
2005
+ file: input.file,
2006
+ markdown: ["```diff", ...body, "```"].join("\n"),
2007
+ truncated,
2008
+ remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0
2009
+ };
2010
+ }
2011
+ function formatDiffLine(line, lineNumberWidth) {
2012
+ if (line.type === "hunk") return line.text;
2013
+ const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, " ");
2014
+ if (line.type === "remove") return `- ${lineNumber} | ${line.text}`;
2015
+ if (line.type === "add") return `+ ${lineNumber} | ${line.text}`;
2016
+ return ` ${lineNumber} | ${line.text}`;
2017
+ }
2018
+ function selectVisibleDiffLines(lines) {
2019
+ const groups = groupByHunk(lines);
2020
+ const visible = [];
2021
+ for (const group of groups) {
2022
+ if (visible.length === 0 && group.length > TRUNCATED_SHOW) {
2023
+ return group.slice(0, TRUNCATED_SHOW);
2024
+ }
2025
+ if (visible.length + group.length > TRUNCATED_SHOW) break;
2026
+ visible.push(...group);
2027
+ }
2028
+ return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);
2029
+ }
2030
+ function groupByHunk(lines) {
2031
+ const groups = [];
2032
+ let current = [];
2033
+ for (const line of lines) {
2034
+ if (line.type === "hunk" && current.length > 0) {
2035
+ groups.push(current);
2036
+ current = [line];
2037
+ continue;
2038
+ }
2039
+ current.push(line);
2040
+ }
2041
+ if (current.length > 0) {
2042
+ groups.push(current);
2043
+ }
2044
+ return groups;
2045
+ }
2046
+
2047
+ // src/ui/ToolDiffBlock.tsx
2048
+ var import_jsx_runtime = require("react/jsx-runtime");
2049
+ function ToolDiffBlock({ file, lines }) {
2050
+ const summary = buildToolDiffSummary({ file, lines });
1681
2051
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Box, { flexDirection: "column", marginLeft: 4, children: [
1682
- file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
2052
+ summary.file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1683
2053
  "\u2502 ",
1684
- file
2054
+ summary.file
1685
2055
  ] }),
1686
- visible.map((line, i) => {
1687
- const lineNum = String(line.lineNumber).padStart(numWidth, " ");
1688
- if (line.type === "context") {
1689
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1690
- "\u2502 ",
1691
- lineNum,
1692
- " ",
1693
- line.text
1694
- ] }, i);
1695
- }
1696
- const prefix = line.type === "remove" ? "-" : "+";
1697
- const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
1698
- const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
1699
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: fgColor, backgroundColor: bgColor, children: [
1700
- "\u2502 ",
1701
- lineNum,
1702
- " ",
1703
- prefix,
1704
- " ",
1705
- line.text
1706
- ] }, i);
1707
- }),
1708
- truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
2056
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink2.Text, { children: renderMarkdown(summary.markdown) }),
2057
+ summary.truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1709
2058
  "\u2502 ... and ",
1710
- remaining,
2059
+ summary.remainingLineCount,
1711
2060
  " more lines"
1712
2061
  ] })
1713
2062
  ] });
1714
2063
  }
1715
2064
 
1716
- // src/ui/MessageList.tsx
2065
+ // src/ui/UsageSummaryEntry.tsx
2066
+ var import_ink3 = require("ink");
2067
+ var import_agent_core5 = require("@robota-sdk/agent-core");
1717
2068
  var import_jsx_runtime2 = require("react/jsx-runtime");
2069
+ var TOKEN_COMPACT_THRESHOLD = 1e3;
2070
+ function UsageSummaryEntry({ entry }) {
2071
+ const usage = entry.data;
2072
+ if (!usage) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
2073
+ const prompt = usage.promptTokens !== void 0 ? formatUsageTokenCount(usage.promptTokens) : "?";
2074
+ const completion = usage.completionTokens !== void 0 ? formatUsageTokenCount(usage.completionTokens) : "?";
2075
+ const total = formatUsageTokenCount(usage.totalTokens);
2076
+ const context = `${Math.round(usage.contextUsedPercentage)}% (${(0, import_agent_core5.formatTokenCount)(
2077
+ usage.contextUsedTokens
2078
+ )}/${(0, import_agent_core5.formatTokenCount)(usage.contextMaxTokens)})`;
2079
+ const costLabel = usage.costStatus === "unknown" ? "cost unknown" : `cost ${usage.costStatus}`;
2080
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", bold: true, children: [
2082
+ "Usage:",
2083
+ " "
2084
+ ] }),
2085
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { dimColor: true, children: [
2086
+ usage.kind,
2087
+ " ",
2088
+ total,
2089
+ " tokens (in ",
2090
+ prompt,
2091
+ " / out ",
2092
+ completion,
2093
+ ") \xB7 Context ",
2094
+ context,
2095
+ " \xB7",
2096
+ " ",
2097
+ costLabel
2098
+ ] })
2099
+ ] }) });
2100
+ }
2101
+ function formatUsageTokenCount(tokens) {
2102
+ return tokens < TOKEN_COMPACT_THRESHOLD ? tokens.toLocaleString() : (0, import_agent_core5.formatTokenCount)(tokens);
2103
+ }
2104
+
2105
+ // src/ui/command-output-summary.ts
2106
+ var MAX_PREVIEW_LINES = 4;
2107
+ var SUCCESS_EXIT_CODE2 = 0;
2108
+ var COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set(["Bash", "BackgroundProcess"]);
2109
+ function formatCommandOutputSummary(tool) {
2110
+ if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return void 0;
2111
+ const parsed = parseToolResultData(tool.toolResultData);
2112
+ const exitCode = getNumberValue(parsed, "exitCode");
2113
+ const successValue = getBooleanValue(parsed, "success");
2114
+ const output = buildOutputText(tool.toolResultData, parsed);
2115
+ const lines = trimTrailingBlankLines(splitOutputLines(output));
2116
+ const previewLines = lines.slice(0, MAX_PREVIEW_LINES);
2117
+ const omittedLineCount = Math.max(0, lines.length - previewLines.length);
2118
+ const isFailed = tool.result === "error" || successValue === false || exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2;
2119
+ return {
2120
+ status: isFailed ? "error" : "success",
2121
+ statusLabel: formatStatusLabel(isFailed, exitCode),
2122
+ previewLines,
2123
+ omittedLineCount,
2124
+ transcriptHint: omittedLineCount > 0 ? `... +${omittedLineCount} lines (full output in session transcript)` : void 0
2125
+ };
2126
+ }
2127
+ function parseToolResultData(value) {
2128
+ try {
2129
+ return JSON.parse(value);
2130
+ } catch {
2131
+ return value;
2132
+ }
2133
+ }
2134
+ function buildOutputText(raw, parsed) {
2135
+ if (!isUniversalObject(parsed)) return raw;
2136
+ const output = getStringValue(parsed, "output");
2137
+ if (output !== void 0) return output;
2138
+ const stdout = getStringValue(parsed, "stdout");
2139
+ const stderr = getStringValue(parsed, "stderr");
2140
+ const error = getStringValue(parsed, "error");
2141
+ const lines = [];
2142
+ if (stdout) lines.push(stdout);
2143
+ if (stderr) lines.push(prefixLines(stderr, "[stderr] "));
2144
+ if (!stdout && !stderr && error) lines.push(error);
2145
+ return lines.join("\n");
2146
+ }
2147
+ function formatStatusLabel(isFailed, exitCode) {
2148
+ if (exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2) return `exit ${exitCode}`;
2149
+ return isFailed ? "error" : "ok";
2150
+ }
2151
+ function splitOutputLines(output) {
2152
+ if (!output) return [];
2153
+ return output.replace(/\r\n/g, "\n").split("\n");
2154
+ }
2155
+ function trimTrailingBlankLines(lines) {
2156
+ let end = lines.length;
2157
+ while (end > 0 && lines[end - 1].trim().length === 0) {
2158
+ end -= 1;
2159
+ }
2160
+ return lines.slice(0, end);
2161
+ }
2162
+ function prefixLines(value, prefix) {
2163
+ return splitOutputLines(value).map((line) => `${prefix}${line}`).join("\n");
2164
+ }
2165
+ function isUniversalObject(value) {
2166
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
2167
+ }
2168
+ function getStringValue(source, key) {
2169
+ if (!isUniversalObject(source)) return void 0;
2170
+ const value = source[key];
2171
+ return typeof value === "string" ? value : void 0;
2172
+ }
2173
+ function getNumberValue(source, key) {
2174
+ if (!isUniversalObject(source)) return void 0;
2175
+ const value = source[key];
2176
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2177
+ }
2178
+ function getBooleanValue(source, key) {
2179
+ if (!isUniversalObject(source)) return void 0;
2180
+ const value = source[key];
2181
+ return typeof value === "boolean" ? value : void 0;
2182
+ }
2183
+
2184
+ // src/ui/ToolCommandOutput.tsx
2185
+ var import_ink4 = require("ink");
2186
+ var import_jsx_runtime3 = require("react/jsx-runtime");
2187
+ function ToolCommandOutput({ tool }) {
2188
+ const summary = formatCommandOutputSummary(tool);
2189
+ if (!summary) return null;
2190
+ if (summary.statusLabel === "ok" && summary.previewLines.length === 0 && !summary.transcriptHint) {
2191
+ return null;
2192
+ }
2193
+ const color = summary.status === "error" ? "red" : "white";
2194
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink4.Box, { flexDirection: "column", marginLeft: 4, children: [
2195
+ summary.statusLabel !== "ok" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color, dimColor: true, children: summary.statusLabel }),
2196
+ summary.previewLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color, dimColor: true, children: line }, `${line}-${index}`)),
2197
+ summary.transcriptHint && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { dimColor: true, children: summary.transcriptHint })
2198
+ ] });
2199
+ }
2200
+
2201
+ // src/ui/MessageList.tsx
2202
+ var import_jsx_runtime4 = require("react/jsx-runtime");
2203
+ function getToolSummaryStatus(tool) {
2204
+ if (formatCommandOutputSummary(tool)?.status === "error") return "\u2717";
2205
+ if (tool.isRunning) return "\u27F3";
2206
+ if (tool.result === "error") return "\u2717";
2207
+ if (tool.result === "denied") return "\u2298";
2208
+ return "\u2713";
2209
+ }
2210
+ function getToolSummaryColor(tool) {
2211
+ if (formatCommandOutputSummary(tool)?.status === "error" || tool.result === "error") return "red";
2212
+ if (tool.isRunning || tool.result === "denied") return "yellow";
2213
+ return "green";
2214
+ }
2215
+ function getToolSummaryLabel(tool) {
2216
+ return `${getToolSummaryStatus(tool)} ${tool.toolName}${tool.firstArg ? `(${tool.firstArg})` : ""}`;
2217
+ }
1718
2218
  function RoleLabel({ role }) {
1719
2219
  switch (role) {
1720
2220
  case "user":
1721
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "green", bold: true, children: [
2221
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", bold: true, children: [
1722
2222
  "You:",
1723
2223
  " "
1724
2224
  ] });
1725
2225
  case "assistant":
1726
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "cyan", bold: true, children: [
2226
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "cyan", bold: true, children: [
1727
2227
  "Robota:",
1728
2228
  " "
1729
2229
  ] });
1730
2230
  case "system":
1731
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "yellow", bold: true, children: [
2231
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "yellow", bold: true, children: [
1732
2232
  "System:",
1733
2233
  " "
1734
2234
  ] });
1735
2235
  case "tool":
1736
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", bold: true, children: [
2236
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1737
2237
  "Tool:",
1738
2238
  " "
1739
2239
  ] });
1740
2240
  }
1741
2241
  }
1742
2242
  function ToolMessage({ message }) {
1743
- if (!(0, import_agent_core5.isToolMessage)(message)) {
1744
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
2243
+ if (!(0, import_agent_core6.isToolMessage)(message)) {
2244
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
1745
2245
  }
1746
2246
  const toolName = message.name;
1747
2247
  const content = message.content;
@@ -1754,45 +2254,45 @@ function ToolMessage({ message }) {
1754
2254
  } catch {
1755
2255
  }
1756
2256
  if (summaries) {
1757
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
1758
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { children: [
1759
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", bold: true, children: [
2257
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
2258
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { children: [
2259
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1760
2260
  "Tool:",
1761
2261
  " "
1762
2262
  ] }),
1763
- toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", dimColor: true, children: [
2263
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", dimColor: true, children: [
1764
2264
  "[",
1765
2265
  toolName,
1766
2266
  "]"
1767
2267
  ] })
1768
2268
  ] }),
1769
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " }),
1770
- summaries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", children: [
1771
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "green", children: [
2269
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
2270
+ summaries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", children: [
2271
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", children: [
1772
2272
  " ",
1773
2273
  "\u2713",
1774
2274
  " ",
1775
2275
  s.line
1776
2276
  ] }),
1777
- s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DiffBlock, { file: s.diffFile, lines: s.diffLines })
2277
+ s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolDiffBlock, { file: s.diffFile, lines: s.diffLines })
1778
2278
  ] }, i))
1779
2279
  ] });
1780
2280
  }
1781
2281
  const lines = content.split("\n").filter((l) => l.trim());
1782
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
1783
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { children: [
1784
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", bold: true, children: [
2282
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
2283
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { children: [
2284
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1785
2285
  "Tool:",
1786
2286
  " "
1787
2287
  ] }),
1788
- toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", dimColor: true, children: [
2288
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", dimColor: true, children: [
1789
2289
  "[",
1790
2290
  toolName,
1791
2291
  "]"
1792
2292
  ] })
1793
2293
  ] }),
1794
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " }),
1795
- lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "green", children: [
2294
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
2295
+ lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", children: [
1796
2296
  " ",
1797
2297
  "\u2713",
1798
2298
  " ",
@@ -1800,30 +2300,48 @@ function ToolMessage({ message }) {
1800
2300
  ] }, i))
1801
2301
  ] });
1802
2302
  }
1803
- var MessageItem = import_react5.default.memo(function MessageItem2({
2303
+ var MessageItem = import_react6.default.memo(function MessageItem2({
1804
2304
  message
1805
2305
  }) {
1806
- if ((0, import_agent_core5.isToolMessage)(message)) {
1807
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
2306
+ if ((0, import_agent_core6.isToolMessage)(message)) {
2307
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolMessage, { message });
1808
2308
  }
1809
2309
  const content = message.content ?? "";
1810
2310
  const isInterrupted = message.state === "interrupted";
1811
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
1812
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
1813
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " }),
1814
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { wrap: "wrap", children: (0, import_agent_core5.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
2311
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
2312
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RoleLabel, { role: message.role }) }),
2313
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
2314
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { wrap: "wrap", children: (0, import_agent_core6.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
1815
2315
  ] });
1816
2316
  });
1817
2317
  function ToolSummaryEntry({ entry }) {
1818
2318
  const data = entry.data;
2319
+ const tools = data?.tools;
1819
2320
  const lines = data?.summary?.split("\n") ?? [];
1820
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
1821
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", bold: true, children: [
2321
+ if (tools && tools.length > 0) {
2322
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
2323
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
2324
+ "Tool:",
2325
+ " "
2326
+ ] }) }),
2327
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
2328
+ tools.map((tool, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", children: [
2329
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: getToolSummaryColor(tool), children: [
2330
+ " ",
2331
+ getToolSummaryLabel(tool)
2332
+ ] }),
2333
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolCommandOutput, { tool }),
2334
+ tool.diffLines && tool.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolDiffBlock, { file: tool.diffFile, lines: tool.diffLines })
2335
+ ] }, i))
2336
+ ] });
2337
+ }
2338
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
2339
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1822
2340
  "Tool:",
1823
2341
  " "
1824
2342
  ] }) }),
1825
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " }),
1826
- lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "green", children: [
2343
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
2344
+ lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", children: [
1827
2345
  " ",
1828
2346
  line
1829
2347
  ] }, i))
@@ -1832,36 +2350,134 @@ function ToolSummaryEntry({ entry }) {
1832
2350
  function EventEntry({ entry }) {
1833
2351
  const eventData = entry.data;
1834
2352
  const eventMessage = typeof eventData?.message === "string" ? eventData.message : typeof eventData?.content === "string" ? eventData.content : entry.type;
1835
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
1836
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "yellow", bold: true, children: [
2353
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
2354
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "yellow", bold: true, children: [
1837
2355
  "System:",
1838
2356
  " "
1839
2357
  ] }) }),
1840
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " }),
1841
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { wrap: "wrap", children: eventMessage }) })
2358
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
2359
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { wrap: "wrap", children: eventMessage }) })
1842
2360
  ] });
1843
2361
  }
1844
2362
  function EntryItem({ entry }) {
1845
2363
  if (entry.category === "chat") {
1846
2364
  const message = entry.data;
1847
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MessageItem, { message });
2365
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageItem, { message });
1848
2366
  }
1849
2367
  if (entry.type === "tool-summary") {
1850
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolSummaryEntry, { entry });
2368
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolSummaryEntry, { entry });
2369
+ }
2370
+ if (entry.type === "usage-summary") {
2371
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UsageSummaryEntry, { entry });
1851
2372
  }
1852
2373
  if (entry.type === "tool-start" || entry.type === "tool-end") {
1853
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
2374
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
1854
2375
  }
1855
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EventEntry, { entry });
2376
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EventEntry, { entry });
1856
2377
  }
1857
2378
  function MessageList({ history }) {
1858
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EntryItem, { entry }, entry.id)) });
2379
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EntryItem, { entry }, entry.id)) });
2380
+ }
2381
+
2382
+ // src/ui/SessionStatusBar.tsx
2383
+ var import_react7 = require("react");
2384
+ var import_agent_core8 = require("@robota-sdk/agent-core");
2385
+
2386
+ // src/utils/git-branch.ts
2387
+ var import_node_fs4 = require("fs");
2388
+ var import_node_path6 = require("path");
2389
+ var DETACHED_HEAD_LENGTH = 7;
2390
+ function resolveGitBranch(cwd) {
2391
+ try {
2392
+ const gitDir = findGitDir(cwd);
2393
+ if (!gitDir) return void 0;
2394
+ const head = (0, import_node_fs4.readFileSync)((0, import_node_path6.join)(gitDir, "HEAD"), "utf8").trim();
2395
+ if (!head) return void 0;
2396
+ if (head.startsWith("ref: ")) {
2397
+ const ref = head.slice("ref: ".length).trim();
2398
+ const branchPrefix = "refs/heads/";
2399
+ return ref.startsWith(branchPrefix) ? ref.slice(branchPrefix.length) : ref;
2400
+ }
2401
+ return head.slice(0, DETACHED_HEAD_LENGTH);
2402
+ } catch {
2403
+ return void 0;
2404
+ }
2405
+ }
2406
+ function findGitDir(start) {
2407
+ let current = (0, import_node_path6.resolve)(start);
2408
+ let parent = (0, import_node_path6.dirname)(current);
2409
+ while (parent !== current) {
2410
+ const candidate = (0, import_node_path6.join)(current, ".git");
2411
+ const resolved = resolveGitMetadata(candidate, current);
2412
+ if (resolved) return resolved;
2413
+ current = parent;
2414
+ parent = (0, import_node_path6.dirname)(current);
2415
+ }
2416
+ const rootCandidate = (0, import_node_path6.join)(current, ".git");
2417
+ return resolveGitMetadata(rootCandidate, current);
2418
+ }
2419
+ function resolveGitMetadata(candidate, repoDir) {
2420
+ if (!(0, import_node_fs4.existsSync)(candidate)) return void 0;
2421
+ const stat = (0, import_node_fs4.lstatSync)(candidate);
2422
+ if (stat.isDirectory()) return candidate;
2423
+ if (!stat.isFile()) return void 0;
2424
+ const content = (0, import_node_fs4.readFileSync)(candidate, "utf8").trim();
2425
+ const prefix = "gitdir:";
2426
+ if (!content.startsWith(prefix)) return void 0;
2427
+ const rawPath = content.slice(prefix.length).trim();
2428
+ return (0, import_node_path6.isAbsolute)(rawPath) ? rawPath : (0, import_node_path6.resolve)(repoDir, rawPath);
1859
2429
  }
1860
2430
 
1861
2431
  // src/ui/StatusBar.tsx
1862
- var import_ink4 = require("ink");
1863
- var import_agent_core6 = require("@robota-sdk/agent-core");
1864
- var import_jsx_runtime3 = require("react/jsx-runtime");
2432
+ var import_ink6 = require("ink");
2433
+ var import_agent_core7 = require("@robota-sdk/agent-core");
2434
+
2435
+ // src/ui/status-activity.ts
2436
+ var NO_ACTIVE_ITEMS = 0;
2437
+ function formatStatusActivity(input) {
2438
+ const base = getPrimaryActivity(input);
2439
+ const segments = input.hasPendingPrompt && base.kind !== "queued" ? ["queued"] : [];
2440
+ const text = [base.label, ...segments].join(" \xB7 ");
2441
+ return { ...base, segments, text };
2442
+ }
2443
+ function getPrimaryActivity(input) {
2444
+ if (input.activeToolCount > NO_ACTIVE_ITEMS) {
2445
+ return {
2446
+ kind: "tools",
2447
+ label: `Tools x${input.activeToolCount}`,
2448
+ color: "cyan"
2449
+ };
2450
+ }
2451
+ if (input.isThinking) {
2452
+ return {
2453
+ kind: "thinking",
2454
+ label: "Thinking",
2455
+ color: "yellow"
2456
+ };
2457
+ }
2458
+ if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {
2459
+ return {
2460
+ kind: "background",
2461
+ label: `Background x${input.activeBackgroundTaskCount}`,
2462
+ color: "cyan"
2463
+ };
2464
+ }
2465
+ if (input.hasPendingPrompt) {
2466
+ return {
2467
+ kind: "queued",
2468
+ label: "Queued",
2469
+ color: "yellow"
2470
+ };
2471
+ }
2472
+ return {
2473
+ kind: "idle",
2474
+ label: "Idle",
2475
+ color: "gray"
2476
+ };
2477
+ }
2478
+
2479
+ // src/ui/StatusBar.tsx
2480
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1865
2481
  var CONTEXT_YELLOW_THRESHOLD = 70;
1866
2482
  var CONTEXT_RED_THRESHOLD = 90;
1867
2483
  function getContextColor(percentage) {
@@ -1869,67 +2485,194 @@ function getContextColor(percentage) {
1869
2485
  if (percentage >= CONTEXT_YELLOW_THRESHOLD) return "yellow";
1870
2486
  return "green";
1871
2487
  }
1872
- function StatusBar({
2488
+ function StatusActivityText({
2489
+ isThinking,
2490
+ activeToolCount,
2491
+ activeBackgroundTaskCount,
2492
+ hasPendingPrompt
2493
+ }) {
2494
+ const activity = formatStatusActivity({
2495
+ isThinking,
2496
+ activeToolCount,
2497
+ activeBackgroundTaskCount,
2498
+ hasPendingPrompt
2499
+ });
2500
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2501
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "cyan", bold: true, children: "Activity:" }),
2502
+ " ",
2503
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: activity.color, bold: activity.kind !== "idle", children: activity.text })
2504
+ ] });
2505
+ }
2506
+ function ContextText({
2507
+ percentage,
2508
+ usedTokens,
2509
+ maxTokens
2510
+ }) {
2511
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { color: getContextColor(percentage), children: [
2512
+ "Context: ",
2513
+ Math.round(percentage),
2514
+ "% (",
2515
+ (0, import_agent_core7.formatTokenCount)(usedTokens),
2516
+ "/",
2517
+ (0, import_agent_core7.formatTokenCount)(maxTokens),
2518
+ ")"
2519
+ ] });
2520
+ }
2521
+ function ModeText({ permissionMode }) {
2522
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2523
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "cyan", bold: true, children: "Mode:" }),
2524
+ " ",
2525
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { children: permissionMode })
2526
+ ] });
2527
+ }
2528
+ function StatusLeft({
1873
2529
  permissionMode,
1874
2530
  modelName,
1875
- sessionId: _sessionId,
1876
- messageCount,
1877
2531
  isThinking,
2532
+ activeToolCount,
2533
+ activeBackgroundTaskCount,
2534
+ hasPendingPrompt,
1878
2535
  contextPercentage,
1879
2536
  contextUsedTokens,
1880
2537
  contextMaxTokens,
1881
- sessionName
2538
+ sessionName,
2539
+ gitBranch,
2540
+ showGitBranch
1882
2541
  }) {
1883
- const contextColor = getContextColor(contextPercentage);
1884
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1885
- import_ink4.Box,
1886
- {
1887
- borderStyle: "single",
1888
- borderColor: "gray",
1889
- paddingLeft: 1,
1890
- paddingRight: 1,
1891
- justifyContent: "space-between",
1892
- children: [
1893
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink4.Text, { children: [
1894
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color: "cyan", bold: true, children: "Mode:" }),
1895
- " ",
1896
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { children: permissionMode }),
1897
- sessionName && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1898
- " | ",
1899
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color: "magenta", children: sessionName })
1900
- ] }),
1901
- " | ",
1902
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { dimColor: true, children: modelName }),
1903
- " | ",
1904
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink4.Text, { color: contextColor, children: [
1905
- "Context: ",
1906
- Math.round(contextPercentage),
1907
- "% (",
1908
- (0, import_agent_core6.formatTokenCount)(contextUsedTokens),
1909
- "/",
1910
- (0, import_agent_core6.formatTokenCount)(contextMaxTokens),
1911
- ")"
1912
- ] })
1913
- ] }),
1914
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink4.Text, { children: [
1915
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color: "yellow", children: "Thinking... " }),
1916
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink4.Text, { dimColor: true, children: [
1917
- "msgs: ",
1918
- messageCount
1919
- ] })
1920
- ] })
2542
+ const shouldShowGitBranch = showGitBranch && gitBranch !== void 0 && gitBranch.length > 0;
2543
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { children: [
2544
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2545
+ StatusActivityText,
2546
+ {
2547
+ isThinking,
2548
+ activeToolCount,
2549
+ activeBackgroundTaskCount,
2550
+ hasPendingPrompt
2551
+ }
2552
+ ),
2553
+ " | ",
2554
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModeText, { permissionMode }),
2555
+ sessionName && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2556
+ " | ",
2557
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "magenta", children: sessionName })
2558
+ ] }),
2559
+ shouldShowGitBranch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2560
+ " | ",
2561
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
2562
+ "git: ",
2563
+ gitBranch
2564
+ ] })
2565
+ ] }),
2566
+ " | ",
2567
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { dimColor: true, children: modelName }),
2568
+ " | ",
2569
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2570
+ ContextText,
2571
+ {
2572
+ percentage: contextPercentage,
2573
+ usedTokens: contextUsedTokens,
2574
+ maxTokens: contextMaxTokens
2575
+ }
2576
+ )
2577
+ ] });
2578
+ }
2579
+ function StatusBar({
2580
+ permissionMode,
2581
+ modelName,
2582
+ sessionId: _sessionId,
2583
+ messageCount,
2584
+ isThinking,
2585
+ activeToolCount = 0,
2586
+ activeBackgroundTaskCount = 0,
2587
+ hasPendingPrompt = false,
2588
+ contextPercentage,
2589
+ contextUsedTokens,
2590
+ contextMaxTokens,
2591
+ sessionName,
2592
+ gitBranch,
2593
+ showGitBranch = true
2594
+ }) {
2595
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2596
+ import_ink6.Box,
2597
+ {
2598
+ borderStyle: "single",
2599
+ borderColor: "gray",
2600
+ paddingLeft: 1,
2601
+ paddingRight: 1,
2602
+ justifyContent: "space-between",
2603
+ children: [
2604
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2605
+ StatusLeft,
2606
+ {
2607
+ permissionMode,
2608
+ modelName,
2609
+ isThinking,
2610
+ activeToolCount,
2611
+ activeBackgroundTaskCount,
2612
+ hasPendingPrompt,
2613
+ contextPercentage,
2614
+ contextUsedTokens,
2615
+ contextMaxTokens,
2616
+ sessionName,
2617
+ gitBranch,
2618
+ showGitBranch
2619
+ }
2620
+ ),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
2622
+ "msgs: ",
2623
+ messageCount
2624
+ ] }) })
1921
2625
  ]
1922
2626
  }
1923
2627
  );
1924
2628
  }
1925
2629
 
2630
+ // src/ui/SessionStatusBar.tsx
2631
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2632
+ function SessionStatusBar({
2633
+ cwd,
2634
+ permissionMode,
2635
+ modelId,
2636
+ sessionId,
2637
+ messageCount,
2638
+ isThinking,
2639
+ activeToolCount,
2640
+ activeBackgroundTaskCount,
2641
+ hasPendingPrompt,
2642
+ contextState,
2643
+ sessionName,
2644
+ settings
2645
+ }) {
2646
+ const gitBranch = (0, import_react7.useMemo)(() => resolveGitBranch(cwd), [cwd]);
2647
+ if (!settings.enabled) return null;
2648
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2649
+ StatusBar,
2650
+ {
2651
+ permissionMode,
2652
+ modelName: modelId ? (0, import_agent_core8.getModelName)(modelId) : "",
2653
+ sessionId,
2654
+ messageCount,
2655
+ isThinking,
2656
+ activeToolCount,
2657
+ activeBackgroundTaskCount,
2658
+ hasPendingPrompt,
2659
+ contextPercentage: contextState.percentage,
2660
+ contextUsedTokens: contextState.usedTokens,
2661
+ contextMaxTokens: contextState.maxTokens,
2662
+ sessionName,
2663
+ gitBranch,
2664
+ showGitBranch: settings.gitBranch
2665
+ }
2666
+ );
2667
+ }
2668
+
1926
2669
  // src/ui/InputArea.tsx
1927
- var import_react9 = require("react");
1928
- var import_ink8 = require("ink");
2670
+ var import_react11 = require("react");
2671
+ var import_ink10 = require("ink");
1929
2672
 
1930
2673
  // src/ui/CjkTextInput.tsx
1931
- var import_react6 = require("react");
1932
- var import_ink5 = require("ink");
2674
+ var import_react8 = require("react");
2675
+ var import_ink7 = require("ink");
1933
2676
  var import_chalk = __toESM(require("chalk"), 1);
1934
2677
 
1935
2678
  // src/ui/flows/cjk-text-input-flow.ts
@@ -1956,10 +2699,20 @@ function applyCjkTextInput(state, input, key, options) {
1956
2699
  if (pasteResult !== void 0) return pasteResult;
1957
2700
  const controlResult = applyControlInput(state, input, key, options);
1958
2701
  if (controlResult !== void 0) return controlResult;
1959
- const cursorResult = applyCursorInput(state, key, options.availableWidth);
2702
+ const cursorResult = applyCursorInput(state, key, options);
1960
2703
  if (cursorResult !== void 0) return cursorResult;
1961
2704
  return insertPrintableInput(state, input);
1962
2705
  }
2706
+ function applyCjkTextPaste(state, text, options) {
2707
+ const normalizedText = text.replace(/\r\n?/g, "\n");
2708
+ if (normalizedText.length === 0) {
2709
+ return { state, effect: { type: "none" } };
2710
+ }
2711
+ if (normalizedText.includes("\n") && options.canPaste) {
2712
+ return { state, effect: { type: "paste", text: normalizedText, cursor: state.cursor } };
2713
+ }
2714
+ return insertPrintableInput(state, normalizedText);
2715
+ }
1963
2716
  function applyPasteBoundaryInput(state, input, options) {
1964
2717
  if (input === PASTE_START || input.startsWith(PASTE_START)) {
1965
2718
  return startBracketedPaste(state, input);
@@ -1984,9 +2737,16 @@ function applyControlInput(state, input, key, options) {
1984
2737
  }
1985
2738
  return void 0;
1986
2739
  }
1987
- function applyCursorInput(state, key, availableWidth) {
2740
+ function applyCursorInput(state, key, options) {
1988
2741
  if (key.upArrow === true || key.downArrow === true) {
1989
- return moveCursorVertically(state, key.upArrow === true ? "up" : "down", availableWidth);
2742
+ if (options.enableVerticalNavigation === false) {
2743
+ return { state, effect: { type: "none" } };
2744
+ }
2745
+ return moveCursorVertically(
2746
+ state,
2747
+ key.upArrow === true ? "up" : "down",
2748
+ options.availableWidth
2749
+ );
1990
2750
  }
1991
2751
  if (key.leftArrow === true) {
1992
2752
  return moveCursorHorizontally(state, "left");
@@ -2049,15 +2809,8 @@ function continueBracketedPaste(state, input, options) {
2049
2809
  };
2050
2810
  }
2051
2811
  const beforeMarker = input.split(PASTE_END)[0] ?? "";
2052
- const text = (state.pasteBuffer + beforeMarker).replace(/\r\n?/g, "\n");
2053
2812
  const nextState = { ...state, isPasting: false, pasteBuffer: "" };
2054
- if (text.length === 0) {
2055
- return { state: nextState, effect: { type: "none" } };
2056
- }
2057
- if (text.includes("\n") && options.canPaste) {
2058
- return { state: nextState, effect: { type: "paste", text, cursor: state.cursor } };
2059
- }
2060
- return insertPrintableInput(nextState, text);
2813
+ return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);
2061
2814
  }
2062
2815
  function moveCursorVertically(state, direction, availableWidth) {
2063
2816
  if (!availableWidth || availableWidth <= 0) {
@@ -2107,7 +2860,7 @@ function insertPrintableInput(state, input) {
2107
2860
  }
2108
2861
 
2109
2862
  // src/ui/CjkTextInput.tsx
2110
- var import_jsx_runtime4 = require("react/jsx-runtime");
2863
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2111
2864
  function CjkTextInput({
2112
2865
  value,
2113
2866
  onChange,
@@ -2117,32 +2870,70 @@ function CjkTextInput({
2117
2870
  focus = true,
2118
2871
  showCursor = true,
2119
2872
  availableWidth,
2120
- cursorHint = null
2873
+ cursorHint = null,
2874
+ enableVerticalNavigation = true
2121
2875
  }) {
2122
- const stateRef = (0, import_react6.useRef)(createCjkTextInputFlowState(value));
2123
- const [, forceRender] = (0, import_react6.useState)(0);
2876
+ const stateRef = (0, import_react8.useRef)(createCjkTextInputFlowState(value));
2877
+ const [, forceRender] = (0, import_react8.useState)(0);
2124
2878
  stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);
2125
- (0, import_ink5.useInput)(
2126
- (input, key) => {
2127
- try {
2128
- const result = applyCjkTextInput(stateRef.current, input, key, {
2129
- availableWidth,
2130
- canPaste: onPaste !== void 0
2131
- });
2132
- stateRef.current = result.state;
2133
- applyCjkTextInputEffect(result.effect, onChange, onSubmit, onPaste, forceRender);
2134
- } catch {
2135
- }
2136
- },
2137
- { isActive: focus }
2138
- );
2139
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: renderWithCursor(
2879
+ useCjkTextInputHandlers({
2880
+ stateRef,
2881
+ onChange,
2882
+ onSubmit,
2883
+ onPaste,
2884
+ availableWidth,
2885
+ focus,
2886
+ enableVerticalNavigation,
2887
+ forceRender
2888
+ });
2889
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { children: renderWithCursor(
2140
2890
  stateRef.current.value,
2141
2891
  stateRef.current.cursor,
2142
2892
  placeholder,
2143
2893
  showCursor && focus
2144
2894
  ) });
2145
2895
  }
2896
+ function useCjkTextInputHandlers(options) {
2897
+ (0, import_ink7.usePaste)(
2898
+ (text) => {
2899
+ applyCjkFlowSafely(
2900
+ options,
2901
+ () => applyCjkTextPaste(options.stateRef.current, text, createFlowOptions(options))
2902
+ );
2903
+ },
2904
+ { isActive: options.focus }
2905
+ );
2906
+ (0, import_ink7.useInput)(
2907
+ (input, key) => {
2908
+ applyCjkFlowSafely(
2909
+ options,
2910
+ () => applyCjkTextInput(options.stateRef.current, input, key, createFlowOptions(options))
2911
+ );
2912
+ },
2913
+ { isActive: options.focus }
2914
+ );
2915
+ }
2916
+ function createFlowOptions(options) {
2917
+ return {
2918
+ availableWidth: options.availableWidth,
2919
+ canPaste: options.onPaste !== void 0,
2920
+ enableVerticalNavigation: options.enableVerticalNavigation
2921
+ };
2922
+ }
2923
+ function applyCjkFlowSafely(options, run) {
2924
+ try {
2925
+ const result = run();
2926
+ options.stateRef.current = result.state;
2927
+ applyCjkTextInputEffect(
2928
+ result.effect,
2929
+ options.onChange,
2930
+ options.onSubmit,
2931
+ options.onPaste,
2932
+ options.forceRender
2933
+ );
2934
+ } catch {
2935
+ }
2936
+ }
2146
2937
  function applyCjkTextInputEffect(effect, onChange, onSubmit, onPaste, forceRender) {
2147
2938
  if (effect.type === "change") {
2148
2939
  onChange(effect.value);
@@ -2177,38 +2968,38 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
2177
2968
  }
2178
2969
 
2179
2970
  // src/ui/WaveText.tsx
2180
- var import_react7 = require("react");
2181
- var import_ink6 = require("ink");
2182
- var import_jsx_runtime5 = require("react/jsx-runtime");
2971
+ var import_react9 = require("react");
2972
+ var import_ink8 = require("ink");
2973
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2183
2974
  var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
2184
2975
  var INTERVAL_MS = 400;
2185
2976
  var CHARS_PER_GROUP = 4;
2186
2977
  function WaveText({ text }) {
2187
- const [tick, setTick] = (0, import_react7.useState)(0);
2188
- (0, import_react7.useEffect)(() => {
2978
+ const [tick, setTick] = (0, import_react9.useState)(0);
2979
+ (0, import_react9.useEffect)(() => {
2189
2980
  const timer = setInterval(() => {
2190
2981
  setTick((prev) => prev + 1);
2191
2982
  }, INTERVAL_MS);
2192
2983
  return () => clearInterval(timer);
2193
2984
  }, []);
2194
2985
  const chars = [...text];
2195
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { children: chars.map((char, i) => {
2986
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: chars.map((char, i) => {
2196
2987
  const group = Math.floor(i / CHARS_PER_GROUP);
2197
2988
  const colorIndex = (tick + group) % WAVE_COLORS.length;
2198
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: WAVE_COLORS[colorIndex], children: char }, i);
2989
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: WAVE_COLORS[colorIndex], children: char }, i);
2199
2990
  }) });
2200
2991
  }
2201
2992
 
2202
2993
  // src/ui/SlashAutocomplete.tsx
2203
- var import_ink7 = require("ink");
2204
- var import_jsx_runtime6 = require("react/jsx-runtime");
2994
+ var import_ink9 = require("ink");
2995
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2205
2996
  var MAX_VISIBLE = 8;
2206
2997
  function CommandRow(props) {
2207
2998
  const { cmd, isSelected, showSlash } = props;
2208
2999
  const indicator = isSelected ? "\u25B8 " : " ";
2209
3000
  const nameColor = isSelected ? "cyan" : void 0;
2210
3001
  const dimmed = !isSelected;
2211
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink7.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink7.Text, { color: nameColor, dimColor: dimmed, children: [
3002
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: nameColor, dimColor: dimmed, children: [
2212
3003
  indicator,
2213
3004
  showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
2214
3005
  ] }) });
@@ -2222,7 +3013,7 @@ function SlashAutocomplete({
2222
3013
  if (!visible || commands.length === 0) return null;
2223
3014
  const scrollOffset = computeScrollOffset(selectedIndex, commands.length);
2224
3015
  const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
2225
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3016
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2226
3017
  CommandRow,
2227
3018
  {
2228
3019
  cmd,
@@ -2246,7 +3037,7 @@ function expandPasteLabels(text, store) {
2246
3037
  }
2247
3038
 
2248
3039
  // src/ui/hooks/useAutocomplete.ts
2249
- var import_react8 = __toESM(require("react"), 1);
3040
+ var import_react10 = __toESM(require("react"), 1);
2250
3041
  function parseSlashInput(value) {
2251
3042
  if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
2252
3043
  const afterSlash = value.slice(1);
@@ -2257,16 +3048,16 @@ function parseSlashInput(value) {
2257
3048
  return { isSlash: true, parentCommand: parent, filter: rest };
2258
3049
  }
2259
3050
  function useAutocomplete(value, registry) {
2260
- const [selectedIndex, setSelectedIndex] = (0, import_react8.useState)(0);
2261
- const [dismissed, setDismissed] = (0, import_react8.useState)(false);
2262
- const prevValueRef = import_react8.default.useRef(value);
3051
+ const [selectedIndex, setSelectedIndex] = (0, import_react10.useState)(0);
3052
+ const [dismissed, setDismissed] = (0, import_react10.useState)(false);
3053
+ const prevValueRef = import_react10.default.useRef(value);
2263
3054
  if (prevValueRef.current !== value) {
2264
3055
  prevValueRef.current = value;
2265
3056
  if (dismissed) setDismissed(false);
2266
3057
  }
2267
3058
  const parsed = parseSlashInput(value);
2268
3059
  const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
2269
- const filteredCommands = (0, import_react8.useMemo)(() => {
3060
+ const filteredCommands = (0, import_react10.useMemo)(() => {
2270
3061
  if (!registry || !parsed.isSlash || dismissed) return [];
2271
3062
  if (isSubcommandMode) {
2272
3063
  const subs = registry.getSubcommands(parsed.parentCommand);
@@ -2314,6 +3105,61 @@ function getPendingPromptInputAction(key) {
2314
3105
  }
2315
3106
  return void 0;
2316
3107
  }
3108
+ function getPromptHistoryInputAction(key) {
3109
+ if (key.upArrow === true) return "previous";
3110
+ if (key.downArrow === true) return "next";
3111
+ return void 0;
3112
+ }
3113
+ function createPromptHistoryNavigationState() {
3114
+ return { selectedIndex: null, draft: "" };
3115
+ }
3116
+ function navigatePromptHistory(value, history, state, action) {
3117
+ if (history.length === 0) {
3118
+ return { value, cursorHint: value.length, state };
3119
+ }
3120
+ if (action === "previous") {
3121
+ const selectedIndex = state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);
3122
+ const nextValue = history[selectedIndex] ?? value;
3123
+ return {
3124
+ value: nextValue,
3125
+ cursorHint: nextValue.length,
3126
+ state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft }
3127
+ };
3128
+ }
3129
+ if (state.selectedIndex === null) {
3130
+ return { value, cursorHint: value.length, state };
3131
+ }
3132
+ if (state.selectedIndex < history.length - 1) {
3133
+ const selectedIndex = state.selectedIndex + 1;
3134
+ const nextValue = history[selectedIndex] ?? value;
3135
+ return {
3136
+ value: nextValue,
3137
+ cursorHint: nextValue.length,
3138
+ state: { ...state, selectedIndex }
3139
+ };
3140
+ }
3141
+ return {
3142
+ value: state.draft,
3143
+ cursorHint: state.draft.length,
3144
+ state: createPromptHistoryNavigationState()
3145
+ };
3146
+ }
3147
+ function appendPromptHistory(history, value) {
3148
+ const prompt = value.trim();
3149
+ if (prompt.length === 0) return [...history];
3150
+ if (history[history.length - 1] === prompt) return [...history];
3151
+ return [...history, prompt];
3152
+ }
3153
+ function extractPromptHistory(entries) {
3154
+ let prompts = [];
3155
+ for (const entry of entries) {
3156
+ if (entry.category !== "chat" || entry.type !== "user") continue;
3157
+ const data = entry.data;
3158
+ if (typeof data?.content !== "string") continue;
3159
+ prompts = appendPromptHistory(prompts, data.content);
3160
+ }
3161
+ return prompts;
3162
+ }
2317
3163
  function moveAutocompleteSelection(selectedIndex, commandCount, direction) {
2318
3164
  if (commandCount === 0) return 0;
2319
3165
  if (direction === "previous") {
@@ -2356,11 +3202,12 @@ function shouldSubmitInput(text) {
2356
3202
  }
2357
3203
 
2358
3204
  // src/ui/InputArea.tsx
2359
- var import_jsx_runtime7 = require("react/jsx-runtime");
3205
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2360
3206
  var BORDER_HORIZONTAL = 2;
2361
3207
  var PADDING_LEFT = 1;
2362
3208
  var PROMPT_WIDTH = 2;
2363
3209
  var INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
3210
+ var DEFAULT_TERMINAL_COLUMNS = 80;
2364
3211
  function InputArea({
2365
3212
  onSubmit,
2366
3213
  onCancelQueue,
@@ -2368,15 +3215,26 @@ function InputArea({
2368
3215
  isAborting,
2369
3216
  pendingPrompt,
2370
3217
  registry,
2371
- sessionName
3218
+ sessionName,
3219
+ history
2372
3220
  }) {
2373
- const [value, setValue] = (0, import_react9.useState)("");
2374
- const [cursorHint, setCursorHint] = (0, import_react9.useState)(null);
2375
- const pasteStore = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
2376
- const { stdout } = (0, import_ink8.useStdout)();
2377
- const terminalColumns = stdout?.columns ?? 80;
3221
+ const [value, setValue] = (0, import_react11.useState)("");
3222
+ const [cursorHint, setCursorHint] = (0, import_react11.useState)(null);
3223
+ const [historyState, setHistoryState] = (0, import_react11.useState)(createPromptHistoryNavigationState);
3224
+ const [localPromptHistory, setLocalPromptHistory] = (0, import_react11.useState)([]);
3225
+ const restoredPromptHistory = (0, import_react11.useMemo)(() => extractPromptHistory(history ?? []), [history]);
3226
+ const promptHistory = (0, import_react11.useMemo)(
3227
+ () => localPromptHistory.reduce(
3228
+ (prompts, prompt) => appendPromptHistory(prompts, prompt),
3229
+ restoredPromptHistory
3230
+ ),
3231
+ [restoredPromptHistory, localPromptHistory]
3232
+ );
3233
+ const pasteStore = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
3234
+ const { columns } = (0, import_ink10.useWindowSize)();
3235
+ const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;
2378
3236
  const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
2379
- const pasteIdRef = (0, import_react9.useRef)(0);
3237
+ const pasteIdRef = (0, import_react11.useRef)(0);
2380
3238
  const {
2381
3239
  showPopup,
2382
3240
  filteredCommands,
@@ -2385,7 +3243,7 @@ function InputArea({
2385
3243
  isSubcommandMode,
2386
3244
  setShowPopup
2387
3245
  } = useAutocomplete(value, registry);
2388
- const handlePaste = (0, import_react9.useCallback)((text, cursorPosition) => {
3246
+ const handlePaste = (0, import_react11.useCallback)((text, cursorPosition) => {
2389
3247
  pasteIdRef.current += 1;
2390
3248
  const id = pasteIdRef.current;
2391
3249
  pasteStore.current.set(id, text);
@@ -2395,7 +3253,21 @@ function InputArea({
2395
3253
  return change.value;
2396
3254
  });
2397
3255
  }, []);
2398
- const tabCompleteCommand = (0, import_react9.useCallback)(
3256
+ const resetHistoryNavigation = (0, import_react11.useCallback)(() => {
3257
+ setHistoryState(createPromptHistoryNavigationState());
3258
+ }, []);
3259
+ const recordPromptHistory = (0, import_react11.useCallback)((prompt) => {
3260
+ setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));
3261
+ }, []);
3262
+ const submitPrompt = (0, import_react11.useCallback)(
3263
+ (prompt) => {
3264
+ recordPromptHistory(prompt);
3265
+ resetHistoryNavigation();
3266
+ onSubmit(prompt);
3267
+ },
3268
+ [onSubmit, recordPromptHistory, resetHistoryNavigation]
3269
+ );
3270
+ const tabCompleteCommand = (0, import_react11.useCallback)(
2399
3271
  (cmd) => {
2400
3272
  const result = resolveTabCompletion(value, cmd);
2401
3273
  if (result.type === "insert") {
@@ -2407,7 +3279,7 @@ function InputArea({
2407
3279
  },
2408
3280
  [value, setSelectedIndex]
2409
3281
  );
2410
- const enterSelectCommand = (0, import_react9.useCallback)(
3282
+ const enterSelectCommand = (0, import_react11.useCallback)(
2411
3283
  (cmd) => {
2412
3284
  const result = resolveEnterCommandSelection(value, cmd);
2413
3285
  if (result.type === "insert") {
@@ -2418,11 +3290,11 @@ function InputArea({
2418
3290
  return;
2419
3291
  }
2420
3292
  setValue("");
2421
- onSubmit(result.value);
3293
+ submitPrompt(result.value);
2422
3294
  },
2423
- [value, onSubmit, setSelectedIndex]
3295
+ [value, submitPrompt, setSelectedIndex]
2424
3296
  );
2425
- const handleSubmit = (0, import_react9.useCallback)(
3297
+ const handleSubmit = (0, import_react11.useCallback)(
2426
3298
  (text) => {
2427
3299
  if (!shouldSubmitInput(text)) return;
2428
3300
  if (showPopup && filteredCommands[selectedIndex]) {
@@ -2433,11 +3305,11 @@ function InputArea({
2433
3305
  setValue("");
2434
3306
  pasteStore.current.clear();
2435
3307
  pasteIdRef.current = 0;
2436
- onSubmit(expanded);
3308
+ submitPrompt(expanded);
2437
3309
  },
2438
- [showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
3310
+ [showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt]
2439
3311
  );
2440
- (0, import_ink8.useInput)(
3312
+ (0, import_ink10.useInput)(
2441
3313
  (_input, key) => {
2442
3314
  if (!showPopup) return;
2443
3315
  const action = getAutocompletePopupAction(key);
@@ -2454,7 +3326,18 @@ function InputArea({
2454
3326
  },
2455
3327
  { isActive: showPopup && !isDisabled }
2456
3328
  );
2457
- (0, import_ink8.useInput)(
3329
+ (0, import_ink10.useInput)(
3330
+ (_input, key) => {
3331
+ const action = getPromptHistoryInputAction(key);
3332
+ if (!action) return;
3333
+ const result = navigatePromptHistory(value, promptHistory, historyState, action);
3334
+ setValue(result.value);
3335
+ setCursorHint(result.cursorHint);
3336
+ setHistoryState(result.state);
3337
+ },
3338
+ { isActive: !showPopup && !isDisabled && !pendingPrompt }
3339
+ );
3340
+ (0, import_ink10.useInput)(
2458
3341
  (_input, key) => {
2459
3342
  if (getPendingPromptInputAction(key) === "cancelQueue" && pendingPrompt) {
2460
3343
  onCancelQueue?.();
@@ -2473,8 +3356,8 @@ function InputArea({
2473
3356
  }
2474
3357
  return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
2475
3358
  })();
2476
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
2477
- showPopup && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3359
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
3360
+ showPopup && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2478
3361
  SlashAutocomplete,
2479
3362
  {
2480
3363
  commands: filteredCommands,
@@ -2483,32 +3366,34 @@ function InputArea({
2483
3366
  isSubcommandMode
2484
3367
  }
2485
3368
  ),
2486
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Text, { color: borderColor, children: [
3369
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color: borderColor, children: [
2487
3370
  topBorder.left,
2488
- topBorder.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Text, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
3371
+ topBorder.label ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
2489
3372
  topBorder.right
2490
3373
  ] }),
2491
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Box, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Text, { color: "cyan", children: [
3374
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color: "cyan", children: [
2492
3375
  " ",
2493
3376
  "Queued: ",
2494
3377
  pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
2495
3378
  " ",
2496
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Text, { dimColor: true, children: "(Backspace to cancel)" })
2497
- ] }) : isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink8.Box, { children: [
2498
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink8.Text, { color: "green", bold: true, children: "> " }),
2499
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3379
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { dimColor: true, children: "(Backspace to cancel)" })
3380
+ ] }) : isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { children: [
3381
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "green", bold: true, children: "> " }),
3382
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2500
3383
  CjkTextInput,
2501
3384
  {
2502
3385
  value,
2503
3386
  onChange: (v) => {
2504
3387
  setValue(v);
3388
+ resetHistoryNavigation();
2505
3389
  setCursorHint(null);
2506
3390
  },
2507
3391
  onSubmit: handleSubmit,
2508
3392
  onPaste: handlePaste,
2509
3393
  placeholder: "Type a message or /help",
2510
3394
  availableWidth,
2511
- cursorHint
3395
+ cursorHint,
3396
+ enableVerticalNavigation: false
2512
3397
  }
2513
3398
  )
2514
3399
  ] }) })
@@ -2516,8 +3401,8 @@ function InputArea({
2516
3401
  }
2517
3402
 
2518
3403
  // src/ui/ConfirmPrompt.tsx
2519
- var import_react10 = require("react");
2520
- var import_ink9 = require("ink");
3404
+ var import_react12 = require("react");
3405
+ var import_ink11 = require("ink");
2521
3406
 
2522
3407
  // src/ui/flows/selection-flow.ts
2523
3408
  function createSelectionFlowState() {
@@ -2620,15 +3505,15 @@ function applyConfirmPromptInput(state, action, optionCount) {
2620
3505
  }
2621
3506
 
2622
3507
  // src/ui/ConfirmPrompt.tsx
2623
- var import_jsx_runtime8 = require("react/jsx-runtime");
3508
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2624
3509
  function ConfirmPrompt({
2625
3510
  message,
2626
3511
  options = ["Yes", "No"],
2627
3512
  onSelect
2628
3513
  }) {
2629
- const [state, setState] = (0, import_react10.useState)(() => createSelectionFlowState());
2630
- const stateRef = (0, import_react10.useRef)(state);
2631
- const applyAction = (0, import_react10.useCallback)(
3514
+ const [state, setState] = (0, import_react12.useState)(() => createSelectionFlowState());
3515
+ const stateRef = (0, import_react12.useRef)(state);
3516
+ const applyAction = (0, import_react12.useCallback)(
2632
3517
  (action) => {
2633
3518
  const result = applyConfirmPromptInput(stateRef.current, action, options.length);
2634
3519
  stateRef.current = result.state;
@@ -2639,16 +3524,16 @@ function ConfirmPrompt({
2639
3524
  },
2640
3525
  [onSelect, options.length]
2641
3526
  );
2642
- (0, import_ink9.useInput)((input, key) => {
3527
+ (0, import_ink11.useInput)((input, key) => {
2643
3528
  const action = getConfirmPromptInputAction(input, key, options.length);
2644
3529
  if (action !== void 0) {
2645
3530
  applyAction(action);
2646
3531
  }
2647
3532
  });
2648
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink9.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2649
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Text, { color: "yellow", children: message }),
2650
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2651
- import_ink9.Text,
3533
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3534
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", children: message }),
3535
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3536
+ import_ink11.Text,
2652
3537
  {
2653
3538
  color: i === state.selectedIndex ? "cyan" : void 0,
2654
3539
  bold: i === state.selectedIndex,
@@ -2658,16 +3543,81 @@ function ConfirmPrompt({
2658
3543
  ]
2659
3544
  }
2660
3545
  ) }, opt)) }),
2661
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
3546
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
2662
3547
  ] });
2663
3548
  }
2664
3549
 
2665
- // src/ui/ProviderSetupPrompt.tsx
2666
- var import_react12 = require("react");
3550
+ // src/ui/InteractivePrompt.tsx
3551
+ var import_ink14 = require("ink");
3552
+
3553
+ // src/ui/ListPicker.tsx
3554
+ var import_react13 = require("react");
3555
+ var import_ink12 = require("ink");
3556
+ var import_jsx_runtime12 = require("react/jsx-runtime");
3557
+ var DEFAULT_MAX_VISIBLE = 3;
3558
+ function ListPicker({
3559
+ items,
3560
+ renderItem,
3561
+ onSelect,
3562
+ onCancel,
3563
+ maxVisible = DEFAULT_MAX_VISIBLE
3564
+ }) {
3565
+ const [state, setState] = (0, import_react13.useState)(() => createSelectionFlowState());
3566
+ const stateRef = (0, import_react13.useRef)(state);
3567
+ const applyAction = (0, import_react13.useCallback)(
3568
+ (action) => {
3569
+ const result = applySelectionInput(stateRef.current, action, {
3570
+ itemCount: items.length,
3571
+ maxVisible
3572
+ });
3573
+ stateRef.current = result.state;
3574
+ setState(result.state);
3575
+ if (result.effect.type === "cancel") {
3576
+ onCancel();
3577
+ } else if (result.effect.type === "select") {
3578
+ const item = items[result.effect.index];
3579
+ if (item !== void 0) {
3580
+ onSelect(item);
3581
+ }
3582
+ }
3583
+ },
3584
+ [items, maxVisible, onCancel, onSelect]
3585
+ );
3586
+ (0, import_ink12.useInput)((_input, key) => {
3587
+ const action = getVerticalSelectionInputAction(key);
3588
+ if (action !== void 0) {
3589
+ applyAction(action);
3590
+ }
3591
+ });
3592
+ if (items.length === 0) {
3593
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Box, {});
3594
+ }
3595
+ const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
3596
+ if (normalizedState !== state) {
3597
+ stateRef.current = normalizedState;
3598
+ }
3599
+ const { selectedIndex, scrollOffset } = normalizedState;
3600
+ const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
3601
+ const hasMore = scrollOffset + maxVisible < items.length;
3602
+ const hasLess = scrollOffset > 0;
3603
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
3604
+ hasLess && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Text, { dimColor: true, children: [
3605
+ " \u2191 ",
3606
+ scrollOffset,
3607
+ " more above"
3608
+ ] }),
3609
+ visibleItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Box, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
3610
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Text, { dimColor: true, children: [
3611
+ " \u2193 ",
3612
+ items.length - scrollOffset - maxVisible,
3613
+ " more below"
3614
+ ] })
3615
+ ] });
3616
+ }
2667
3617
 
2668
3618
  // src/ui/TextPrompt.tsx
2669
- var import_react11 = require("react");
2670
- var import_ink10 = require("ink");
3619
+ var import_react14 = require("react");
3620
+ var import_ink13 = require("ink");
2671
3621
 
2672
3622
  // src/ui/flows/text-prompt-flow.ts
2673
3623
  function createTextPromptFlowState() {
@@ -2726,7 +3676,7 @@ function submitTextPromptValue(state, options) {
2726
3676
  }
2727
3677
 
2728
3678
  // src/ui/TextPrompt.tsx
2729
- var import_jsx_runtime9 = require("react/jsx-runtime");
3679
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2730
3680
  function TextPrompt({
2731
3681
  title,
2732
3682
  placeholder,
@@ -2736,9 +3686,9 @@ function TextPrompt({
2736
3686
  allowEmpty = false,
2737
3687
  masked = false
2738
3688
  }) {
2739
- const [state, setState] = (0, import_react11.useState)(() => createTextPromptFlowState());
2740
- const stateRef = (0, import_react11.useRef)(state);
2741
- const applyAction = (0, import_react11.useCallback)(
3689
+ const [state, setState] = (0, import_react14.useState)(() => createTextPromptFlowState());
3690
+ const stateRef = (0, import_react14.useRef)(state);
3691
+ const applyAction = (0, import_react14.useCallback)(
2742
3692
  (action) => {
2743
3693
  const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });
2744
3694
  stateRef.current = result.state;
@@ -2751,64 +3701,67 @@ function TextPrompt({
2751
3701
  },
2752
3702
  [allowEmpty, validate, onCancel, onSubmit]
2753
3703
  );
2754
- (0, import_ink10.useInput)((input, key) => {
3704
+ (0, import_ink13.useInput)((input, key) => {
2755
3705
  const action = getTextPromptInputAction(input, key);
2756
3706
  if (action !== void 0) {
2757
3707
  applyAction(action);
2758
3708
  }
2759
3709
  });
2760
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink10.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2761
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "yellow", bold: true, children: title }),
2762
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink10.Box, { marginTop: 1, children: [
2763
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "cyan", children: "> " }),
2764
- state.value ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { dimColor: true, children: placeholder }) : null,
2765
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "cyan", children: "\u2588" })
3710
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3711
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "yellow", bold: true, children: title }),
3712
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { marginTop: 1, children: [
3713
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "cyan", children: "> " }),
3714
+ state.value ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: placeholder }) : null,
3715
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "cyan", children: "\u2588" })
2766
3716
  ] }),
2767
- state.error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "red", children: state.error }),
2768
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
3717
+ state.error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "red", children: state.error }),
3718
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
2769
3719
  ] });
2770
3720
  }
2771
3721
 
2772
- // src/ui/ProviderSetupPrompt.tsx
2773
- var import_jsx_runtime10 = require("react/jsx-runtime");
2774
- function ProviderSetupPrompt({
2775
- type,
2776
- providerDefinitions,
3722
+ // src/ui/InteractivePrompt.tsx
3723
+ var import_jsx_runtime14 = require("react/jsx-runtime");
3724
+ function InteractivePrompt({
3725
+ prompt,
2777
3726
  onSubmit,
2778
3727
  onCancel
2779
3728
  }) {
2780
- const [state, setState] = (0, import_react12.useState)(
2781
- () => createProviderSetupFlow(type, providerDefinitions)
2782
- );
2783
- const step = getProviderSetupStep(state);
2784
- const handleStepSubmit = (rawValue) => {
2785
- const result = submitProviderSetupValue(state, rawValue);
2786
- if (result.status === "next") {
2787
- setState(result.state);
2788
- return;
2789
- }
2790
- if (result.status === "complete") {
2791
- onSubmit(result.input);
2792
- }
2793
- };
2794
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2795
- TextPrompt,
2796
- {
2797
- title: step.title,
2798
- placeholder: step.defaultValue,
2799
- allowEmpty: step.defaultValue !== void 0,
2800
- masked: step.masked,
2801
- validate: (value) => validateProviderSetupValue(step, value),
2802
- onSubmit: handleStepSubmit,
2803
- onCancel
2804
- },
2805
- `${type}-${step.key}`
2806
- );
3729
+ if (prompt.kind === "text") {
3730
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3731
+ TextPrompt,
3732
+ {
3733
+ title: prompt.title,
3734
+ placeholder: prompt.placeholder,
3735
+ allowEmpty: prompt.allowEmpty,
3736
+ masked: prompt.masked,
3737
+ validate: prompt.validate,
3738
+ onSubmit,
3739
+ onCancel
3740
+ },
3741
+ `text:${prompt.title}`
3742
+ );
3743
+ }
3744
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
3745
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { bold: true, children: prompt.title }),
3746
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3747
+ ListPicker,
3748
+ {
3749
+ items: [...prompt.options],
3750
+ maxVisible: prompt.maxVisible,
3751
+ renderItem: (option, isSelected) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Text, { color: isSelected ? "cyan" : void 0, children: [
3752
+ isSelected ? "> " : " ",
3753
+ option.label
3754
+ ] }),
3755
+ onSelect: (option) => onSubmit(option.value),
3756
+ onCancel
3757
+ }
3758
+ )
3759
+ ] });
2807
3760
  }
2808
3761
 
2809
3762
  // src/ui/PermissionPrompt.tsx
2810
- var import_react13 = __toESM(require("react"), 1);
2811
- var import_ink11 = require("ink");
3763
+ var import_react15 = __toESM(require("react"), 1);
3764
+ var import_ink15 = require("ink");
2812
3765
 
2813
3766
  // src/ui/flows/permission-prompt-flow.ts
2814
3767
  var PERMISSION_PROMPT_OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
@@ -2859,23 +3812,23 @@ function resolvePermissionIndex(state, index) {
2859
3812
  }
2860
3813
 
2861
3814
  // src/ui/PermissionPrompt.tsx
2862
- var import_jsx_runtime11 = require("react/jsx-runtime");
3815
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2863
3816
  function formatArgs(args) {
2864
3817
  const entries = Object.entries(args);
2865
3818
  if (entries.length === 0) return "(no arguments)";
2866
3819
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
2867
3820
  }
2868
3821
  function PermissionPrompt({ request }) {
2869
- const [state, setState] = import_react13.default.useState(() => createSelectionFlowState());
2870
- const stateRef = import_react13.default.useRef(state);
2871
- const prevRequestRef = import_react13.default.useRef(request);
3822
+ const [state, setState] = import_react15.default.useState(() => createSelectionFlowState());
3823
+ const stateRef = import_react15.default.useRef(state);
3824
+ const prevRequestRef = import_react15.default.useRef(request);
2872
3825
  if (prevRequestRef.current !== request) {
2873
3826
  prevRequestRef.current = request;
2874
3827
  const nextState = createSelectionFlowState();
2875
3828
  stateRef.current = nextState;
2876
3829
  setState(nextState);
2877
3830
  }
2878
- const applyAction = import_react13.default.useCallback(
3831
+ const applyAction = import_react15.default.useCallback(
2879
3832
  (action) => {
2880
3833
  const result = applyPermissionPromptInput(stateRef.current, action);
2881
3834
  stateRef.current = result.state;
@@ -2886,25 +3839,25 @@ function PermissionPrompt({ request }) {
2886
3839
  },
2887
3840
  [request]
2888
3841
  );
2889
- (0, import_ink11.useInput)((input, key) => {
3842
+ (0, import_ink15.useInput)((input, key) => {
2890
3843
  const action = getPermissionPromptInputAction(input, key);
2891
3844
  if (action !== void 0) {
2892
3845
  applyAction(action);
2893
3846
  }
2894
3847
  });
2895
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2896
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
2897
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
3848
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3849
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
3850
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Text, { children: [
2898
3851
  "Tool:",
2899
3852
  " ",
2900
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "cyan", bold: true, children: request.toolName })
3853
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Text, { color: "cyan", bold: true, children: request.toolName })
2901
3854
  ] }),
2902
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
3855
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Text, { dimColor: true, children: [
2903
3856
  " ",
2904
3857
  formatArgs(request.toolArgs)
2905
3858
  ] }),
2906
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2907
- import_ink11.Text,
3859
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Box, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3860
+ import_ink15.Text,
2908
3861
  {
2909
3862
  color: i === state.selectedIndex ? "cyan" : void 0,
2910
3863
  bold: i === state.selectedIndex,
@@ -2914,13 +3867,13 @@ function PermissionPrompt({ request }) {
2914
3867
  ]
2915
3868
  }
2916
3869
  ) }, opt)) }),
2917
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
3870
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
2918
3871
  ] });
2919
3872
  }
2920
3873
 
2921
3874
  // src/ui/StreamingIndicator.tsx
2922
- var import_ink12 = require("ink");
2923
- var import_jsx_runtime12 = require("react/jsx-runtime");
3875
+ var import_ink16 = require("ink");
3876
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2924
3877
  function getToolStyle(t) {
2925
3878
  if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
2926
3879
  if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
@@ -2931,16 +3884,16 @@ function StreamingIndicator({ text, activeTools }) {
2931
3884
  const hasTools = activeTools.length > 0;
2932
3885
  const hasText = text.length > 0;
2933
3886
  if (!hasTools && !hasText) {
2934
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, {});
3887
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, {});
2935
3888
  }
2936
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
2937
- hasTools && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", marginBottom: 1, children: [
2938
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "white", bold: true, children: "Tools:" }),
2939
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: " " }),
3889
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", children: [
3890
+ hasTools && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", marginBottom: 1, children: [
3891
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { color: "white", bold: true, children: "Tools:" }),
3892
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { children: " " }),
2940
3893
  activeTools.map((t, i) => {
2941
3894
  const { color, icon, strikethrough } = getToolStyle(t);
2942
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
2943
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Text, { color, strikethrough, children: [
3895
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", children: [
3896
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Text, { color, strikethrough, children: [
2944
3897
  " ",
2945
3898
  icon,
2946
3899
  " ",
@@ -2949,25 +3902,25 @@ function StreamingIndicator({ text, activeTools }) {
2949
3902
  t.firstArg,
2950
3903
  ")"
2951
3904
  ] }),
2952
- t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DiffBlock, { file: t.diffFile, lines: t.diffLines })
3905
+ t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToolDiffBlock, { file: t.diffFile, lines: t.diffLines })
2953
3906
  ] }, `${t.toolName}-${i}`);
2954
3907
  })
2955
3908
  ] }),
2956
- hasText && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", marginBottom: 1, children: [
2957
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", bold: true, children: "Robota:" }),
2958
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: " " }),
2959
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
3909
+ hasText && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", marginBottom: 1, children: [
3910
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { color: "cyan", bold: true, children: "Robota:" }),
3911
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { children: " " }),
3912
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
2960
3913
  ] })
2961
3914
  ] });
2962
3915
  }
2963
3916
 
2964
3917
  // src/ui/PluginTUI.tsx
2965
- var import_react16 = require("react");
3918
+ var import_react18 = require("react");
2966
3919
 
2967
3920
  // src/ui/MenuSelect.tsx
2968
- var import_react14 = require("react");
2969
- var import_ink13 = require("ink");
2970
- var import_jsx_runtime13 = require("react/jsx-runtime");
3921
+ var import_react16 = require("react");
3922
+ var import_ink17 = require("ink");
3923
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2971
3924
  function MenuSelect({
2972
3925
  title,
2973
3926
  items,
@@ -2976,10 +3929,10 @@ function MenuSelect({
2976
3929
  loading,
2977
3930
  error
2978
3931
  }) {
2979
- const [state, setState] = (0, import_react14.useState)(() => createSelectionFlowState());
2980
- const stateRef = (0, import_react14.useRef)(state);
3932
+ const [state, setState] = (0, import_react16.useState)(() => createSelectionFlowState());
3933
+ const stateRef = (0, import_react16.useRef)(state);
2981
3934
  const isEnabled = !loading && !error;
2982
- const applyAction = (0, import_react14.useCallback)(
3935
+ const applyAction = (0, import_react16.useCallback)(
2983
3936
  (action) => {
2984
3937
  const result = applySelectionInput(stateRef.current, action, {
2985
3938
  itemCount: items.length,
@@ -2998,7 +3951,7 @@ function MenuSelect({
2998
3951
  },
2999
3952
  [isEnabled, items, onBack, onSelect]
3000
3953
  );
3001
- (0, import_ink13.useInput)((input, key) => {
3954
+ (0, import_ink17.useInput)((input, key) => {
3002
3955
  const action = getVerticalSelectionInputAction(key);
3003
3956
  if (action !== void 0) {
3004
3957
  applyAction(action);
@@ -3009,24 +3962,24 @@ function MenuSelect({
3009
3962
  stateRef.current = normalizedState;
3010
3963
  }
3011
3964
  const selected = normalizedState.selectedIndex;
3012
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3013
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "yellow", bold: true, children: title }),
3014
- loading && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: "Loading..." }) }),
3015
- error && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { marginTop: 1, flexDirection: "column", children: [
3016
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "red", children: error }),
3017
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: "Press Esc to go back" })
3965
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
3966
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { color: "yellow", bold: true, children: title }),
3967
+ loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { dimColor: true, children: "Loading..." }) }),
3968
+ error && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Box, { marginTop: 1, flexDirection: "column", children: [
3969
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { color: "red", children: error }),
3970
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { dimColor: true, children: "Press Esc to go back" })
3018
3971
  ] }),
3019
- !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { children: [
3020
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
3972
+ !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Box, { children: [
3973
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
3021
3974
  i === selected ? "> " : " ",
3022
3975
  item.label
3023
3976
  ] }),
3024
- item.hint && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Text, { dimColor: true, children: [
3977
+ item.hint && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Text, { dimColor: true, children: [
3025
3978
  " ",
3026
3979
  item.hint
3027
3980
  ] })
3028
3981
  ] }, item.value)) }),
3029
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
3982
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
3030
3983
  ] });
3031
3984
  }
3032
3985
 
@@ -3123,12 +4076,12 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
3123
4076
  }
3124
4077
 
3125
4078
  // src/ui/hooks/usePluginScreenData.ts
3126
- var import_react15 = require("react");
4079
+ var import_react17 = require("react");
3127
4080
  function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, stackLength) {
3128
- const [items, setItems] = (0, import_react15.useState)([]);
3129
- const [loading, setLoading] = (0, import_react15.useState)(false);
3130
- const [error, setError] = (0, import_react15.useState)();
3131
- (0, import_react15.useEffect)(() => {
4081
+ const [items, setItems] = (0, import_react17.useState)([]);
4082
+ const [loading, setLoading] = (0, import_react17.useState)(false);
4083
+ const [error, setError] = (0, import_react17.useState)();
4084
+ (0, import_react17.useEffect)(() => {
3132
4085
  setItems([]);
3133
4086
  setError(void 0);
3134
4087
  if (screen === "marketplace-list") {
@@ -3183,16 +4136,16 @@ function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, sta
3183
4136
  }
3184
4137
 
3185
4138
  // src/ui/PluginTUI.tsx
3186
- var import_jsx_runtime14 = require("react/jsx-runtime");
4139
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3187
4140
  function PluginTUI({ callbacks, onClose, addMessage }) {
3188
- const [stack, setStack] = (0, import_react16.useState)([{ screen: "main" }]);
3189
- const [confirm, setConfirm] = (0, import_react16.useState)();
3190
- const [refreshCounter, setRefreshCounter] = (0, import_react16.useState)(0);
4141
+ const [stack, setStack] = (0, import_react18.useState)([{ screen: "main" }]);
4142
+ const [confirm, setConfirm] = (0, import_react18.useState)();
4143
+ const [refreshCounter, setRefreshCounter] = (0, import_react18.useState)(0);
3191
4144
  const current = stack[stack.length - 1] ?? { screen: "main" };
3192
- const push = (0, import_react16.useCallback)((state) => {
4145
+ const push = (0, import_react18.useCallback)((state) => {
3193
4146
  setStack((prev) => [...prev, state]);
3194
4147
  }, []);
3195
- const pop = (0, import_react16.useCallback)(() => {
4148
+ const pop = (0, import_react18.useCallback)(() => {
3196
4149
  setStack((prev) => {
3197
4150
  if (prev.length <= 1) {
3198
4151
  onClose();
@@ -3201,7 +4154,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3201
4154
  return prev.slice(0, -1);
3202
4155
  });
3203
4156
  }, [onClose]);
3204
- const popN = (0, import_react16.useCallback)(
4157
+ const popN = (0, import_react18.useCallback)(
3205
4158
  (n) => {
3206
4159
  setStack((prev) => {
3207
4160
  const next = prev.slice(0, Math.max(1, prev.length - n));
@@ -3214,20 +4167,20 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3214
4167
  },
3215
4168
  [onClose]
3216
4169
  );
3217
- const notify = (0, import_react16.useCallback)(
4170
+ const notify = (0, import_react18.useCallback)(
3218
4171
  (content) => {
3219
4172
  addMessage?.({ role: "system", content });
3220
4173
  },
3221
4174
  [addMessage]
3222
4175
  );
3223
- const refresh = (0, import_react16.useCallback)(() => {
4176
+ const refresh = (0, import_react18.useCallback)(() => {
3224
4177
  setRefreshCounter((c) => c + 1);
3225
4178
  }, []);
3226
- const setConfirmNav = (0, import_react16.useCallback)(
4179
+ const setConfirmNav = (0, import_react18.useCallback)(
3227
4180
  (state) => setConfirm(state),
3228
4181
  [setConfirm]
3229
4182
  );
3230
- const pushNav = (0, import_react16.useCallback)(
4183
+ const pushNav = (0, import_react18.useCallback)(
3231
4184
  (state) => push({ screen: state.screen, context: state.context }),
3232
4185
  [push]
3233
4186
  );
@@ -3239,7 +4192,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3239
4192
  refreshCounter,
3240
4193
  stack.length
3241
4194
  );
3242
- const handleSelect = (0, import_react16.useCallback)(
4195
+ const handleSelect = (0, import_react18.useCallback)(
3243
4196
  (value) => {
3244
4197
  const screen2 = current.screen;
3245
4198
  const ctx = current.context;
@@ -3257,7 +4210,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3257
4210
  },
3258
4211
  [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
3259
4212
  );
3260
- const handleTextSubmit = (0, import_react16.useCallback)(
4213
+ const handleTextSubmit = (0, import_react18.useCallback)(
3261
4214
  (value) => {
3262
4215
  if (current.screen === "marketplace-add") {
3263
4216
  callbacks.marketplaceAdd(value).then((name) => {
@@ -3272,7 +4225,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3272
4225
  [current.screen, callbacks, notify, pop]
3273
4226
  );
3274
4227
  if (confirm) {
3275
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4228
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3276
4229
  ConfirmPrompt,
3277
4230
  {
3278
4231
  message: confirm.message,
@@ -3285,7 +4238,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3285
4238
  }
3286
4239
  const screen = current.screen;
3287
4240
  if (screen === "marketplace-add") {
3288
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4241
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3289
4242
  TextPrompt,
3290
4243
  {
3291
4244
  title: "Add Marketplace Source",
@@ -3297,7 +4250,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3297
4250
  );
3298
4251
  }
3299
4252
  if (screen === "marketplace-action") {
3300
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4253
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3301
4254
  MenuSelect,
3302
4255
  {
3303
4256
  title: `Marketplace: ${current.context?.marketplace ?? ""}`,
@@ -3313,7 +4266,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3313
4266
  );
3314
4267
  }
3315
4268
  if (screen === "marketplace-install-scope") {
3316
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4269
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3317
4270
  MenuSelect,
3318
4271
  {
3319
4272
  title: `Install scope for "${current.context?.pluginId ?? ""}"`,
@@ -3328,7 +4281,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3328
4281
  );
3329
4282
  }
3330
4283
  if (screen === "installed-action") {
3331
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4284
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3332
4285
  MenuSelect,
3333
4286
  {
3334
4287
  title: `Plugin: ${current.context?.pluginId ?? ""}`,
@@ -3351,7 +4304,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3351
4304
  { label: "Installed Plugins", value: "installed" }
3352
4305
  ]
3353
4306
  };
3354
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4307
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3355
4308
  MenuSelect,
3356
4309
  {
3357
4310
  title: titleMap[screen] ?? "Plugin Management",
@@ -3366,75 +4319,8 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
3366
4319
  }
3367
4320
 
3368
4321
  // src/ui/SessionPicker.tsx
3369
- var import_ink15 = require("ink");
3370
-
3371
- // src/ui/ListPicker.tsx
3372
- var import_react17 = require("react");
3373
- var import_ink14 = require("ink");
3374
- var import_jsx_runtime15 = require("react/jsx-runtime");
3375
- var DEFAULT_MAX_VISIBLE = 3;
3376
- function ListPicker({
3377
- items,
3378
- renderItem,
3379
- onSelect,
3380
- onCancel,
3381
- maxVisible = DEFAULT_MAX_VISIBLE
3382
- }) {
3383
- const [state, setState] = (0, import_react17.useState)(() => createSelectionFlowState());
3384
- const stateRef = (0, import_react17.useRef)(state);
3385
- const applyAction = (0, import_react17.useCallback)(
3386
- (action) => {
3387
- const result = applySelectionInput(stateRef.current, action, {
3388
- itemCount: items.length,
3389
- maxVisible
3390
- });
3391
- stateRef.current = result.state;
3392
- setState(result.state);
3393
- if (result.effect.type === "cancel") {
3394
- onCancel();
3395
- } else if (result.effect.type === "select") {
3396
- const item = items[result.effect.index];
3397
- if (item !== void 0) {
3398
- onSelect(item);
3399
- }
3400
- }
3401
- },
3402
- [items, maxVisible, onCancel, onSelect]
3403
- );
3404
- (0, import_ink14.useInput)((_input, key) => {
3405
- const action = getVerticalSelectionInputAction(key);
3406
- if (action !== void 0) {
3407
- applyAction(action);
3408
- }
3409
- });
3410
- if (items.length === 0) {
3411
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, {});
3412
- }
3413
- const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
3414
- if (normalizedState !== state) {
3415
- stateRef.current = normalizedState;
3416
- }
3417
- const { selectedIndex, scrollOffset } = normalizedState;
3418
- const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
3419
- const hasMore = scrollOffset + maxVisible < items.length;
3420
- const hasLess = scrollOffset > 0;
3421
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
3422
- hasLess && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
3423
- " \u2191 ",
3424
- scrollOffset,
3425
- " more above"
3426
- ] }),
3427
- visibleItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
3428
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
3429
- " \u2193 ",
3430
- items.length - scrollOffset - maxVisible,
3431
- " more below"
3432
- ] })
3433
- ] });
3434
- }
3435
-
3436
- // src/ui/SessionPicker.tsx
3437
- var import_jsx_runtime16 = require("react/jsx-runtime");
4322
+ var import_ink18 = require("ink");
4323
+ var import_jsx_runtime19 = require("react/jsx-runtime");
3438
4324
  var SESSION_ID_DISPLAY_LENGTH = 8;
3439
4325
  function SessionPicker({
3440
4326
  sessionStore,
@@ -3443,9 +4329,9 @@ function SessionPicker({
3443
4329
  onCancel
3444
4330
  }) {
3445
4331
  const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
3446
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
3447
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
3448
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4332
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4333
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
4334
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3449
4335
  ListPicker,
3450
4336
  {
3451
4337
  items: sessions,
@@ -3456,24 +4342,24 @@ function SessionPicker({
3456
4342
  });
3457
4343
  const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
3458
4344
  const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
3459
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Text, { children: [
4345
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Text, { children: [
3460
4346
  isSelected ? "> " : " ",
3461
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
4347
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
3462
4348
  " ",
3463
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
4349
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
3464
4350
  month: "short",
3465
4351
  day: "numeric",
3466
4352
  hour: "2-digit",
3467
4353
  minute: "2-digit"
3468
4354
  }) }),
3469
4355
  " ",
3470
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Text, { dimColor: true, children: [
4356
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Text, { dimColor: true, children: [
3471
4357
  "msgs: ",
3472
4358
  session.messages.length
3473
4359
  ] }),
3474
- preview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4360
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
3475
4361
  "\n ",
3476
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { color: "gray", children: preview })
4362
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { color: "gray", children: preview })
3477
4363
  ] }) : null
3478
4364
  ] });
3479
4365
  },
@@ -3485,54 +4371,382 @@ function SessionPicker({
3485
4371
  }
3486
4372
 
3487
4373
  // src/ui/BackgroundTaskPanel.tsx
3488
- var import_ink16 = require("ink");
3489
- var import_jsx_runtime17 = require("react/jsx-runtime");
4374
+ var import_ink19 = require("ink");
4375
+
4376
+ // src/ui/background-task-row-format.ts
3490
4377
  var MS_PER_SECOND = 1e3;
3491
4378
  var SECONDS_PER_MINUTE = 60;
3492
4379
  var MINUTES_PER_HOUR = 60;
3493
- function getStatusColor(status) {
3494
- if (status === "completed") return "green";
3495
- if (status === "failed") return "red";
3496
- if (status === "cancelled") return "yellow";
4380
+ var SUCCESS_EXIT_CODE3 = 0;
4381
+ function formatBackgroundTaskRow(task, options = {}) {
4382
+ const row = {
4383
+ connector: options.isLast === false ? "\u251C" : "\u2514",
4384
+ marker: getStatusMarker(task),
4385
+ color: getStatusColor(task),
4386
+ label: getTaskLabel(task),
4387
+ segments: getTaskSegments(task, options.now ?? Date.now()),
4388
+ preview: getTaskPreview(task)
4389
+ };
4390
+ return {
4391
+ ...row,
4392
+ accessibleText: formatAccessibleText(row)
4393
+ };
4394
+ }
4395
+ function getStatusColor(task) {
4396
+ if (isFailedTask(task)) return "red";
4397
+ if (task.status === "completed") return "green";
4398
+ if (task.status === "cancelled") return "yellow";
3497
4399
  return "cyan";
3498
4400
  }
3499
- function getStatusMarker(status) {
3500
- if (status === "queued" || status === "running") return "\u25A1";
4401
+ function getStatusMarker(task) {
4402
+ if (task.status === "queued" || task.status === "running") return "\u25A1";
3501
4403
  return "\u25A0";
3502
4404
  }
4405
+ function getTaskLabel(task) {
4406
+ if (task.kind === "agent") return `${task.label} agent`;
4407
+ if (task.kind === "process") return task.label || "Process";
4408
+ return task.label;
4409
+ }
4410
+ function getTaskSegments(task, now) {
4411
+ const segments = [];
4412
+ if (task.status === "running") {
4413
+ const idle = formatAge(task.lastActivityAt, now);
4414
+ if (idle) segments.push(`idle ${idle}`);
4415
+ }
4416
+ if (task.status === "failed") {
4417
+ segments.push(task.statusLabel === "timed out" ? "timed out" : "failed");
4418
+ }
4419
+ if (task.status === "cancelled") {
4420
+ segments.push("cancelled");
4421
+ }
4422
+ if (task.timeoutReason) {
4423
+ segments.push(task.timeoutReason);
4424
+ }
4425
+ if (task.status === "completed" && task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3) {
4426
+ segments.push(`exit ${task.exitCode}`);
4427
+ }
4428
+ if (task.signalCode) {
4429
+ segments.push(`signal ${task.signalCode}`);
4430
+ }
4431
+ return segments;
4432
+ }
3503
4433
  function getTaskPreview(task) {
3504
- return task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
4434
+ const preview = task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
4435
+ return preview || void 0;
3505
4436
  }
3506
- function formatAge(iso) {
4437
+ function formatAge(iso, now) {
3507
4438
  if (!iso) return void 0;
3508
4439
  const timestamp = Date.parse(iso);
3509
4440
  if (Number.isNaN(timestamp)) return void 0;
3510
- const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / MS_PER_SECOND));
4441
+ const seconds = Math.max(0, Math.floor((now - timestamp) / MS_PER_SECOND));
3511
4442
  if (seconds < SECONDS_PER_MINUTE) return `${seconds}s`;
3512
4443
  const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
3513
4444
  if (minutes < MINUTES_PER_HOUR) return `${minutes}m`;
3514
4445
  return `${Math.floor(minutes / MINUTES_PER_HOUR)}h`;
3515
4446
  }
4447
+ function isFailedTask(task) {
4448
+ return task.status === "failed" || task.status === "completed" && (task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3 || !!task.signalCode);
4449
+ }
4450
+ function formatAccessibleText(row) {
4451
+ const parts = [`${row.connector} ${row.marker} ${row.label}`, ...row.segments];
4452
+ if (row.preview) parts.push(row.preview);
4453
+ return parts.join(" \xB7 ");
4454
+ }
4455
+
4456
+ // src/ui/BackgroundTaskPanel.tsx
4457
+ var import_jsx_runtime20 = require("react/jsx-runtime");
3516
4458
  function BackgroundTaskPanel({ tasks }) {
3517
4459
  if (tasks.length === 0) return null;
3518
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink16.Box, { flexDirection: "column", marginBottom: 1, children: [
3519
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { color: "cyan", bold: true, children: "Background" }),
3520
- tasks.map((task) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink16.Text, { children: [
3521
- "- ",
3522
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { color: getStatusColor(task.status), children: getStatusMarker(task.status) }),
3523
- ` ${task.kind}:${task.label} ${task.id}`,
3524
- task.status === "running" && formatAge(task.lastActivityAt) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { dimColor: true, children: ` idle ${formatAge(task.lastActivityAt)}` }) : null,
3525
- task.timeoutReason ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { dimColor: true, children: ` (${task.timeoutReason})` }) : null,
3526
- getTaskPreview(task) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { dimColor: true, children: ` - ${getTaskPreview(task)}` }) : null
3527
- ] }, task.id))
4460
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Box, { flexDirection: "column", marginBottom: 1, children: [
4461
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { color: "cyan", bold: true, children: "Background work" }),
4462
+ tasks.map((task, index) => {
4463
+ const row = formatBackgroundTaskRow(task, { isLast: index === tasks.length - 1 });
4464
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Text, { children: [
4465
+ `${row.connector} `,
4466
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { color: row.color, children: row.marker }),
4467
+ ` ${row.label}`,
4468
+ row.segments.map((segment, segmentIndex) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { dimColor: true, children: ` \xB7 ${segment}` }, `${segment}-${segmentIndex}`)),
4469
+ row.preview ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
4470
+ ] }, task.id);
4471
+ })
3528
4472
  ] });
3529
4473
  }
3530
4474
 
4475
+ // src/ui/UpdateNotice.tsx
4476
+ var import_ink20 = require("ink");
4477
+ var import_jsx_runtime21 = require("react/jsx-runtime");
4478
+ function UpdateNotice({ message }) {
4479
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Box, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: "yellow", children: message }) });
4480
+ }
4481
+
4482
+ // src/utils/update-check.ts
4483
+ var import_node_fs5 = require("fs");
4484
+ var import_node_path7 = require("path");
4485
+
4486
+ // src/utils/semver-compare.ts
4487
+ function compareSemverVersions(left, right) {
4488
+ const parsedLeft = parseSemver(left);
4489
+ const parsedRight = parseSemver(right);
4490
+ if (parsedLeft === void 0 || parsedRight === void 0) {
4491
+ return Math.sign(left.localeCompare(right));
4492
+ }
4493
+ const coreCompare = compareNumber(parsedLeft.major, parsedRight.major) || compareNumber(parsedLeft.minor, parsedRight.minor) || compareNumber(parsedLeft.patch, parsedRight.patch);
4494
+ if (coreCompare !== 0) {
4495
+ return coreCompare;
4496
+ }
4497
+ return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
4498
+ }
4499
+ function isNewerSemverVersion(candidate, current) {
4500
+ return compareSemverVersions(candidate, current) > 0;
4501
+ }
4502
+ function parseSemver(value) {
4503
+ const normalized = value.trim().replace(/^v/, "").split("+")[0] ?? "";
4504
+ const [core, prereleaseText] = normalized.split("-", 2);
4505
+ const [majorText, minorText, patchText] = core.split(".");
4506
+ const major = parseNumericIdentifier(majorText);
4507
+ const minor = parseNumericIdentifier(minorText);
4508
+ const patch = parseNumericIdentifier(patchText);
4509
+ if (major === void 0 || minor === void 0 || patch === void 0) {
4510
+ return void 0;
4511
+ }
4512
+ return {
4513
+ major,
4514
+ minor,
4515
+ patch,
4516
+ prerelease: prereleaseText ? prereleaseText.split(".") : []
4517
+ };
4518
+ }
4519
+ function parseNumericIdentifier(value) {
4520
+ if (value === void 0 || !/^\d+$/.test(value)) {
4521
+ return void 0;
4522
+ }
4523
+ return Number(value);
4524
+ }
4525
+ function compareNumber(left, right) {
4526
+ return Math.sign(left - right);
4527
+ }
4528
+ function comparePrerelease(left, right) {
4529
+ if (left.length === 0 && right.length === 0) {
4530
+ return 0;
4531
+ }
4532
+ if (left.length === 0) {
4533
+ return 1;
4534
+ }
4535
+ if (right.length === 0) {
4536
+ return -1;
4537
+ }
4538
+ const max = Math.max(left.length, right.length);
4539
+ for (let index = 0; index < max; index += 1) {
4540
+ const leftPart = left[index];
4541
+ const rightPart = right[index];
4542
+ if (leftPart === void 0) {
4543
+ return -1;
4544
+ }
4545
+ if (rightPart === void 0) {
4546
+ return 1;
4547
+ }
4548
+ const partCompare = comparePrereleaseIdentifier(leftPart, rightPart);
4549
+ if (partCompare !== 0) {
4550
+ return partCompare;
4551
+ }
4552
+ }
4553
+ return 0;
4554
+ }
4555
+ function comparePrereleaseIdentifier(left, right) {
4556
+ const leftNumber = parseNumericIdentifier(left);
4557
+ const rightNumber = parseNumericIdentifier(right);
4558
+ if (leftNumber !== void 0 && rightNumber !== void 0) {
4559
+ return compareNumber(leftNumber, rightNumber);
4560
+ }
4561
+ if (leftNumber !== void 0) {
4562
+ return -1;
4563
+ }
4564
+ if (rightNumber !== void 0) {
4565
+ return 1;
4566
+ }
4567
+ return Math.sign(left.localeCompare(right));
4568
+ }
4569
+
4570
+ // src/utils/update-check.ts
4571
+ var CLI_UPDATE_PACKAGE_NAME = "@robota-sdk/agent-cli";
4572
+ var CLI_UPDATE_REGISTRY_URL = "https://registry.npmjs.org";
4573
+ var HOURS_PER_DAY = 24;
4574
+ var MINUTES_PER_HOUR2 = 60;
4575
+ var SECONDS_PER_MINUTE2 = 60;
4576
+ var MS_PER_SECOND2 = 1e3;
4577
+ var CLI_UPDATE_CACHE_TTL_MS = HOURS_PER_DAY * MINUTES_PER_HOUR2 * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
4578
+ var CLI_UPDATE_TIMEOUT_MS = 1500;
4579
+ var DEFAULT_INSTALL_COMMAND = "npm install -g '@robota-sdk/agent-cli@latest'";
4580
+ function getUserUpdateCheckCachePath(home = process.env.HOME ?? process.env.USERPROFILE ?? "/") {
4581
+ return (0, import_node_path7.join)(home, ".robota", "update-check.json");
4582
+ }
4583
+ function readUpdateCheckCache(path) {
4584
+ if (!(0, import_node_fs5.existsSync)(path)) {
4585
+ return void 0;
4586
+ }
4587
+ try {
4588
+ const parsed = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf8"));
4589
+ return parseUpdateCheckCache(parsed);
4590
+ } catch {
4591
+ return void 0;
4592
+ }
4593
+ }
4594
+ function writeUpdateCheckCache(path, cache) {
4595
+ (0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(path), { recursive: true });
4596
+ (0, import_node_fs5.writeFileSync)(path, JSON.stringify(cache, null, 2) + "\n", "utf8");
4597
+ }
4598
+ async function checkForCliUpdate(options) {
4599
+ if (options.disabled === true) {
4600
+ return { status: "skipped", reason: "disabled" };
4601
+ }
4602
+ const packageName = options.packageName ?? CLI_UPDATE_PACKAGE_NAME;
4603
+ const cachePath = options.cachePath ?? getUserUpdateCheckCachePath();
4604
+ const now = options.now ?? /* @__PURE__ */ new Date();
4605
+ const ttlMs = options.ttlMs ?? CLI_UPDATE_CACHE_TTL_MS;
4606
+ if (options.force !== true) {
4607
+ const cached = readUpdateCheckCache(cachePath);
4608
+ if (cached !== void 0 && isFreshCache(cached, now, ttlMs, packageName)) {
4609
+ return resultFromCache(cached, options.currentVersion);
4610
+ }
4611
+ }
4612
+ try {
4613
+ const latestVersion = await fetchLatestVersion({
4614
+ fetchImpl: options.fetchImpl ?? fetch,
4615
+ packageName,
4616
+ registryUrl: options.registryUrl ?? CLI_UPDATE_REGISTRY_URL,
4617
+ timeoutMs: options.timeoutMs ?? CLI_UPDATE_TIMEOUT_MS
4618
+ });
4619
+ tryWriteUpdateCheckCache(cachePath, {
4620
+ packageName,
4621
+ checkedAt: now.toISOString(),
4622
+ currentVersion: options.currentVersion,
4623
+ latestVersion
4624
+ });
4625
+ return resultFromLatestVersion(options.currentVersion, latestVersion);
4626
+ } catch (error) {
4627
+ const errorMessage = error instanceof Error ? error.message : String(error);
4628
+ tryWriteUpdateCheckCache(cachePath, {
4629
+ packageName,
4630
+ checkedAt: now.toISOString(),
4631
+ currentVersion: options.currentVersion,
4632
+ errorMessage
4633
+ });
4634
+ return { status: "error", errorMessage };
4635
+ }
4636
+ }
4637
+ function tryWriteUpdateCheckCache(path, cache) {
4638
+ try {
4639
+ writeUpdateCheckCache(path, cache);
4640
+ } catch {
4641
+ }
4642
+ }
4643
+ async function getStartupCliUpdateNotice(options) {
4644
+ const result = await checkForCliUpdate(options);
4645
+ return result.status === "update_available" ? result.notice : void 0;
4646
+ }
4647
+ function shouldRunStartupCliUpdateCheck(input) {
4648
+ return input.printMode === false && input.disableUpdateCheck === false;
4649
+ }
4650
+ function formatCliUpdateNotice(notice) {
4651
+ return [
4652
+ `Robota update available: ${notice.currentVersion} -> ${notice.latestVersion}.`,
4653
+ `Run ${notice.installCommand}`
4654
+ ].join(" ");
4655
+ }
4656
+ function formatCliUpdateCheckMessage(result) {
4657
+ if (result.status === "update_available") {
4658
+ return formatCliUpdateNotice(result.notice);
4659
+ }
4660
+ if (result.status === "current") {
4661
+ return `Robota is up to date (${result.currentVersion}).`;
4662
+ }
4663
+ if (result.status === "skipped") {
4664
+ return "Robota update check skipped.";
4665
+ }
4666
+ return `Robota update check failed: ${result.errorMessage}`;
4667
+ }
4668
+ function resultFromCache(cache, currentVersion) {
4669
+ if (cache.errorMessage !== void 0) {
4670
+ return { status: "error", errorMessage: cache.errorMessage };
4671
+ }
4672
+ if (cache.latestVersion === void 0) {
4673
+ return { status: "error", errorMessage: "Cached update check has no latest version" };
4674
+ }
4675
+ return resultFromLatestVersion(currentVersion, cache.latestVersion);
4676
+ }
4677
+ function resultFromLatestVersion(currentVersion, latestVersion) {
4678
+ if (isNewerSemverVersion(latestVersion, currentVersion)) {
4679
+ return {
4680
+ status: "update_available",
4681
+ notice: {
4682
+ currentVersion,
4683
+ latestVersion,
4684
+ installCommand: DEFAULT_INSTALL_COMMAND
4685
+ }
4686
+ };
4687
+ }
4688
+ return { status: "current", currentVersion, latestVersion };
4689
+ }
4690
+ function isFreshCache(cache, now, ttlMs, packageName) {
4691
+ if (cache.packageName !== packageName) {
4692
+ return false;
4693
+ }
4694
+ const checkedAt = Date.parse(cache.checkedAt);
4695
+ if (!Number.isFinite(checkedAt)) {
4696
+ return false;
4697
+ }
4698
+ return now.getTime() - checkedAt < ttlMs;
4699
+ }
4700
+ async function fetchLatestVersion(options) {
4701
+ const controller = new AbortController();
4702
+ const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
4703
+ try {
4704
+ const packageUrl = buildPackageMetadataUrl(options.registryUrl, options.packageName);
4705
+ const response = await options.fetchImpl(packageUrl, {
4706
+ headers: { accept: "application/json" },
4707
+ signal: controller.signal
4708
+ });
4709
+ if (!response.ok) {
4710
+ throw new Error(`registry responded with HTTP ${response.status}`);
4711
+ }
4712
+ const metadata = await response.json();
4713
+ const latest = metadata["dist-tags"]?.latest;
4714
+ if (typeof latest !== "string" || latest.trim().length === 0) {
4715
+ throw new Error("registry metadata is missing dist-tags.latest");
4716
+ }
4717
+ return latest;
4718
+ } finally {
4719
+ clearTimeout(timeout);
4720
+ }
4721
+ }
4722
+ function buildPackageMetadataUrl(registryUrl, packageName) {
4723
+ return `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(packageName)}`;
4724
+ }
4725
+ function parseUpdateCheckCache(value) {
4726
+ if (!isJsonObject(value)) {
4727
+ return void 0;
4728
+ }
4729
+ const candidate = value;
4730
+ 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")) {
4731
+ return {
4732
+ packageName: candidate.packageName,
4733
+ checkedAt: candidate.checkedAt,
4734
+ currentVersion: candidate.currentVersion,
4735
+ ...candidate.latestVersion !== void 0 && { latestVersion: candidate.latestVersion },
4736
+ ...candidate.errorMessage !== void 0 && { errorMessage: candidate.errorMessage }
4737
+ };
4738
+ }
4739
+ return void 0;
4740
+ }
4741
+ function isJsonObject(value) {
4742
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4743
+ }
4744
+
3531
4745
  // src/ui/App.tsx
3532
- var import_jsx_runtime18 = require("react/jsx-runtime");
4746
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3533
4747
  function App(props) {
3534
- const [activeSessionId, setActiveSessionId] = (0, import_react18.useState)(props.resumeSessionId);
3535
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4748
+ const [activeSessionId, setActiveSessionId] = (0, import_react19.useState)(props.resumeSessionId);
4749
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3536
4750
  AppInner,
3537
4751
  {
3538
4752
  ...props,
@@ -3544,7 +4758,7 @@ function App(props) {
3544
4758
  }
3545
4759
  function AppInner(props) {
3546
4760
  const cwd = props.cwd;
3547
- const providerDefinitions = props.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
4761
+ const providerDefinitions = props.providerDefinitions ?? [];
3548
4762
  const {
3549
4763
  interactiveSession,
3550
4764
  registry,
@@ -3578,47 +4792,65 @@ function AppInner(props) {
3578
4792
  providerDefinitions
3579
4793
  });
3580
4794
  const pluginCallbacks = usePluginCallbacks(cwd);
3581
- const { exit } = (0, import_ink17.useApp)();
3582
- const [sessionName, setSessionName] = (0, import_react18.useState)(props.sessionName);
4795
+ const { exit } = (0, import_ink21.useApp)();
4796
+ const [sessionName, setSessionName] = (0, import_react19.useState)(props.sessionName);
4797
+ const [updateNotice, setUpdateNotice] = (0, import_react19.useState)();
4798
+ const [statusLineSettings, setStatusLineSettings] = useStatusLineSettings();
4799
+ const activeBackgroundTaskCount = backgroundTasks.filter(
4800
+ (task) => task.status === "queued" || task.status === "running"
4801
+ ).length;
3583
4802
  const {
3584
4803
  handleSubmit,
3585
4804
  pendingModelId,
3586
4805
  pendingProviderProfile,
3587
- pendingProviderSetupType,
4806
+ pendingInteractionPrompt,
3588
4807
  showPluginTUI,
3589
4808
  showSessionPicker,
3590
4809
  setShowPluginTUI,
3591
4810
  setShowSessionPicker,
3592
4811
  handleModelConfirm,
3593
4812
  handleProviderConfirm,
3594
- handleProviderSetupSubmit,
3595
- handleProviderSetupCancel
4813
+ handleInteractionSubmit,
4814
+ handleInteractionCancel
3596
4815
  } = useSideEffects({
3597
4816
  cwd,
3598
4817
  interactiveSession,
3599
4818
  addEntry,
3600
4819
  baseHandleSubmit,
3601
4820
  setSessionName,
4821
+ setStatusLineSettings,
3602
4822
  providerDefinitions
3603
4823
  });
3604
- (0, import_react18.useEffect)(() => {
4824
+ (0, import_react19.useEffect)(() => {
3605
4825
  const name = interactiveSession?.getName?.();
3606
4826
  if (name && !sessionName) setSessionName(name);
3607
4827
  }, [interactiveSession, sessionName]);
3608
- (0, import_react18.useEffect)(() => {
4828
+ (0, import_react19.useEffect)(() => {
4829
+ let isMounted = true;
4830
+ props.startupUpdateNoticePromise?.then((notice) => {
4831
+ if (isMounted && notice !== void 0) {
4832
+ setUpdateNotice(notice);
4833
+ }
4834
+ }).catch(() => {
4835
+ });
4836
+ return () => {
4837
+ isMounted = false;
4838
+ };
4839
+ }, [props.startupUpdateNoticePromise]);
4840
+ (0, import_react19.useEffect)(() => {
3609
4841
  const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
3610
4842
  process.stdout.write(`\x1B]0;${title}\x07`);
3611
4843
  }, [sessionName]);
3612
- (0, import_ink17.useInput)((_input, key) => {
4844
+ (0, import_ink21.useInput)((_input, key) => {
3613
4845
  if (!key.escape || !isThinking) return;
3614
4846
  if (permissionRequest || showPluginTUI || showSessionPicker) return;
3615
4847
  handleAbort();
3616
4848
  });
3617
- (0, import_ink17.useInput)((input, key) => {
4849
+ (0, import_ink21.useInput)((input, key) => {
3618
4850
  if (!key.ctrl || input !== "c" || isShuttingDown) return;
3619
4851
  void handleShutdown("prompt_input_exit").finally(() => exit());
3620
4852
  });
3621
- (0, import_react18.useEffect)(() => {
4853
+ (0, import_react19.useEffect)(() => {
3622
4854
  const onSigterm = () => {
3623
4855
  if (isShuttingDown) return;
3624
4856
  void handleShutdown("other").finally(() => exit());
@@ -3638,59 +4870,59 @@ function AppInner(props) {
3638
4870
  sessionId = session.getSessionId();
3639
4871
  } catch {
3640
4872
  }
3641
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Box, { flexDirection: "column", children: [
3642
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
3643
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { color: "cyan", bold: true, children: `
4873
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", children: [
4874
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
4875
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "cyan", bold: true, children: `
3644
4876
  ____ ___ ____ ___ _____ _
3645
4877
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
3646
4878
  | |_) | | | | _ \\| | | || | / _ \\
3647
4879
  | _ <| |_| | |_) | |_| || |/ ___ \\
3648
4880
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
3649
4881
  ` }),
3650
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Text, { dimColor: true, children: [
4882
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Text, { dimColor: true, children: [
3651
4883
  " v",
3652
4884
  props.version ?? "0.0.0"
3653
4885
  ] })
3654
4886
  ] }),
3655
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
3656
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MessageList, { history }),
3657
- isShuttingDown && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { color: "yellow", children: "Shutting down..." }) }),
3658
- (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StreamingIndicator, { text: streamingText, activeTools }) }),
3659
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(BackgroundTaskPanel, { tasks: backgroundTasks })
4887
+ updateNotice && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(UpdateNotice, { message: formatCliUpdateNotice(updateNotice) }),
4888
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
4889
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageList, { history }),
4890
+ isShuttingDown && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "yellow", children: "Shutting down..." }) }),
4891
+ (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StreamingIndicator, { text: streamingText, activeTools }) }),
4892
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(BackgroundTaskPanel, { tasks: backgroundTasks })
3660
4893
  ] }),
3661
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PermissionPrompt, { request: permissionRequest }),
3662
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4894
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PermissionPrompt, { request: permissionRequest }),
4895
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3663
4896
  ConfirmPrompt,
3664
4897
  {
3665
- message: `Change model to ${(0, import_agent_core7.getModelName)(pendingModelId)}? This will restart the session.`,
4898
+ message: `Change model to ${(0, import_agent_core9.getModelName)(pendingModelId)}? This will restart the session.`,
3666
4899
  onSelect: handleModelConfirm
3667
4900
  }
3668
4901
  ),
3669
- pendingProviderProfile && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4902
+ pendingProviderProfile && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3670
4903
  ConfirmPrompt,
3671
4904
  {
3672
4905
  message: `Change provider to ${pendingProviderProfile}? This will restart the session.`,
3673
4906
  onSelect: handleProviderConfirm
3674
4907
  }
3675
4908
  ),
3676
- pendingProviderSetupType && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3677
- ProviderSetupPrompt,
4909
+ pendingInteractionPrompt && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4910
+ InteractivePrompt,
3678
4911
  {
3679
- type: pendingProviderSetupType,
3680
- providerDefinitions,
3681
- onSubmit: handleProviderSetupSubmit,
3682
- onCancel: handleProviderSetupCancel
4912
+ prompt: pendingInteractionPrompt,
4913
+ onSubmit: handleInteractionSubmit,
4914
+ onCancel: handleInteractionCancel
3683
4915
  }
3684
4916
  ),
3685
- showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4917
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3686
4918
  PluginTUI,
3687
4919
  {
3688
4920
  callbacks: pluginCallbacks,
3689
4921
  onClose: () => setShowPluginTUI(false),
3690
- addMessage: (msg) => addEntry((0, import_agent_core7.messageToHistoryEntry)((0, import_agent_core7.createSystemMessage)(msg.content)))
4922
+ addMessage: (msg) => addEntry((0, import_agent_core9.messageToHistoryEntry)((0, import_agent_core9.createSystemMessage)(msg.content)))
3691
4923
  }
3692
4924
  ),
3693
- showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4925
+ showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3694
4926
  SessionPicker,
3695
4927
  {
3696
4928
  sessionStore: props.sessionStore,
@@ -3701,42 +4933,46 @@ function AppInner(props) {
3701
4933
  },
3702
4934
  onCancel: () => {
3703
4935
  setShowSessionPicker(false);
3704
- addEntry((0, import_agent_core7.messageToHistoryEntry)((0, import_agent_core7.createSystemMessage)("Session resume cancelled.")));
4936
+ addEntry((0, import_agent_core9.messageToHistoryEntry)((0, import_agent_core9.createSystemMessage)("Session resume cancelled.")));
3705
4937
  }
3706
4938
  }
3707
4939
  ),
3708
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3709
- StatusBar,
4940
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4941
+ SessionStatusBar,
3710
4942
  {
4943
+ cwd,
3711
4944
  permissionMode,
3712
- modelName: props.modelId ? (0, import_agent_core7.getModelName)(props.modelId) : "",
4945
+ modelId: props.modelId,
3713
4946
  sessionId,
3714
4947
  messageCount: history.length,
3715
4948
  isThinking,
3716
- contextPercentage: contextState.percentage,
3717
- contextUsedTokens: contextState.usedTokens,
3718
- contextMaxTokens: contextState.maxTokens,
3719
- sessionName
4949
+ activeToolCount: activeTools.length,
4950
+ activeBackgroundTaskCount,
4951
+ hasPendingPrompt: pendingPrompt !== null,
4952
+ contextState,
4953
+ sessionName,
4954
+ settings: statusLineSettings
3720
4955
  }
3721
4956
  ),
3722
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4957
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3723
4958
  InputArea,
3724
4959
  {
3725
4960
  onSubmit: handleSubmit,
3726
4961
  onCancelQueue: handleCancelQueue,
3727
- isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || !!pendingProviderSetupType || isThinking && !!pendingPrompt,
4962
+ isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || pendingInteractionPrompt !== null || isThinking && !!pendingPrompt,
3728
4963
  isAborting,
3729
4964
  pendingPrompt,
3730
4965
  registry,
3731
- sessionName
4966
+ sessionName,
4967
+ history
3732
4968
  }
3733
4969
  ),
3734
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { children: " " })
4970
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { children: " " })
3735
4971
  ] });
3736
4972
  }
3737
4973
 
3738
4974
  // src/ui/render.tsx
3739
- var import_jsx_runtime19 = require("react/jsx-runtime");
4975
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3740
4976
  function renderApp(options) {
3741
4977
  process.on("unhandledRejection", (reason) => {
3742
4978
  process.stderr.write(`
@@ -3747,16 +4983,10 @@ function renderApp(options) {
3747
4983
  `);
3748
4984
  }
3749
4985
  });
3750
- if (process.stdin.isTTY && process.stdout.isTTY) {
3751
- process.stdout.write("\x1B[?2004h");
3752
- }
3753
- const instance = (0, import_ink18.render)(/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(App, { ...options }), {
4986
+ const instance = (0, import_ink22.render)(/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(App, { ...options }), {
3754
4987
  exitOnCtrlC: false
3755
4988
  });
3756
4989
  instance.waitUntilExit().then(() => {
3757
- if (process.stdout.isTTY) {
3758
- process.stdout.write("\x1B[?2004l");
3759
- }
3760
4990
  process.exit(0);
3761
4991
  }).catch((err) => {
3762
4992
  if (err) {
@@ -3806,7 +5036,7 @@ function resolveShell(request) {
3806
5036
  return { command: request.shell ?? "sh", args: ["-c", request.command] };
3807
5037
  }
3808
5038
  function sendInput(child, input) {
3809
- return new Promise((resolve, reject) => {
5039
+ return new Promise((resolve2, reject) => {
3810
5040
  const onError = (error) => {
3811
5041
  child.stdin.off("error", onError);
3812
5042
  reject(error);
@@ -3814,7 +5044,7 @@ function sendInput(child, input) {
3814
5044
  child.stdin.once("error", onError);
3815
5045
  child.stdin.end(input, () => {
3816
5046
  child.stdin.off("error", onError);
3817
- resolve();
5047
+ resolve2();
3818
5048
  });
3819
5049
  });
3820
5050
  }
@@ -3849,7 +5079,7 @@ function startProcessTask(taskId, request, killGraceMs) {
3849
5079
  }
3850
5080
  function createProcessResult(runtime) {
3851
5081
  let settled = false;
3852
- return new Promise((resolve, reject) => {
5082
+ return new Promise((resolve2, reject) => {
3853
5083
  const timeoutTimer = runtime.request.timeoutMs ? setTimeout(() => {
3854
5084
  appendLog(runtime.logs, "system", `timed out after ${runtime.request.timeoutMs}ms`);
3855
5085
  runtime.child.kill("SIGTERM");
@@ -3863,7 +5093,7 @@ function createProcessResult(runtime) {
3863
5093
  if (settled) return;
3864
5094
  settled = true;
3865
5095
  clearTimers();
3866
- resolve({
5096
+ resolve2({
3867
5097
  taskId: runtime.taskId,
3868
5098
  kind: "process",
3869
5099
  output: runtime.capture.getOutput(),
@@ -3938,22 +5168,22 @@ function readProcessLog(runtime, cursor) {
3938
5168
 
3939
5169
  // src/subagents/child-process-subagent-runner.ts
3940
5170
  var import_node_child_process3 = require("child_process");
3941
- var import_node_fs5 = require("fs");
3942
- var import_node_path7 = require("path");
5171
+ var import_node_fs7 = require("fs");
5172
+ var import_node_path9 = require("path");
3943
5173
  var import_agent_sdk7 = require("@robota-sdk/agent-sdk");
3944
5174
 
3945
5175
  // src/subagents/child-process-subagent-runner-result.ts
3946
5176
  var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
3947
5177
 
3948
5178
  // src/subagents/child-process-subagent-ipc.ts
3949
- function isRecord(value) {
5179
+ function isRecord2(value) {
3950
5180
  return typeof value === "object" && value !== null;
3951
5181
  }
3952
5182
  function hasString(value, key) {
3953
5183
  return typeof value[key] === "string";
3954
5184
  }
3955
5185
  function isSubagentWorkerChildMessage(value) {
3956
- if (!isRecord(value) || !hasString(value, "type")) return false;
5186
+ if (!isRecord2(value) || !hasString(value, "type")) return false;
3957
5187
  switch (value.type) {
3958
5188
  case "ready":
3959
5189
  return true;
@@ -4018,7 +5248,7 @@ function extractFirstArg(toolArgs) {
4018
5248
  return typeof firstValue === "object" ? JSON.stringify(firstValue) : String(firstValue);
4019
5249
  }
4020
5250
  function sendWorkerMessage(child, message) {
4021
- return new Promise((resolve, reject) => {
5251
+ return new Promise((resolve2, reject) => {
4022
5252
  if (!child.connected) {
4023
5253
  reject(new import_agent_sdk4.BackgroundTaskError("crash", "Subagent worker IPC channel is closed"));
4024
5254
  return;
@@ -4028,7 +5258,7 @@ function sendWorkerMessage(child, message) {
4028
5258
  reject(error);
4029
5259
  return;
4030
5260
  }
4031
- resolve();
5261
+ resolve2();
4032
5262
  });
4033
5263
  });
4034
5264
  }
@@ -4045,14 +5275,14 @@ async function cancelChildProcess(runtime, reason) {
4045
5275
 
4046
5276
  // src/subagents/child-process-subagent-runner-result.ts
4047
5277
  function createChildProcessSubagentResult(options) {
4048
- return new Promise((resolve, reject) => {
4049
- new ChildProcessSubagentResultController(options, resolve, reject).start();
5278
+ return new Promise((resolve2, reject) => {
5279
+ new ChildProcessSubagentResultController(options, resolve2, reject).start();
4050
5280
  });
4051
5281
  }
4052
5282
  var ChildProcessSubagentResultController = class {
4053
- constructor(options, resolve, reject) {
5283
+ constructor(options, resolve2, reject) {
4054
5284
  this.options = options;
4055
- this.resolve = resolve;
5285
+ this.resolve = resolve2;
4056
5286
  this.reject = reject;
4057
5287
  this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));
4058
5288
  }
@@ -4160,8 +5390,8 @@ function formatEarlyExitMessage(code, signal) {
4160
5390
  // src/subagents/git-worktree-isolation-adapter.ts
4161
5391
  var import_node_child_process2 = require("child_process");
4162
5392
  var import_node_crypto2 = require("crypto");
4163
- var import_node_fs4 = require("fs");
4164
- var import_node_path6 = require("path");
5393
+ var import_node_fs6 = require("fs");
5394
+ var import_node_path8 = require("path");
4165
5395
  var import_agent_sdk6 = require("@robota-sdk/agent-sdk");
4166
5396
  var WORKTREE_DIR = ".robota/worktrees";
4167
5397
  var BRANCH_PREFIX = "robota";
@@ -4181,9 +5411,9 @@ var GitWorktreeIsolationAdapter = class {
4181
5411
  const repoRoot = runGit(request.cwd, ["rev-parse", "--show-toplevel"]).trim();
4182
5412
  const shortId = (0, import_node_crypto2.randomUUID)().slice(0, SHORT_ID_LENGTH);
4183
5413
  const branchName = `${this.branchPrefix}/${request.jobId}-${shortId}`;
4184
- const worktreeRoot = (0, import_node_path6.join)(repoRoot, this.worktreeDir);
4185
- const worktreePath = (0, import_node_path6.join)(worktreeRoot, `${request.jobId}-${shortId}`);
4186
- (0, import_node_fs4.mkdirSync)(worktreeRoot, { recursive: true });
5414
+ const worktreeRoot = (0, import_node_path8.join)(repoRoot, this.worktreeDir);
5415
+ const worktreePath = (0, import_node_path8.join)(worktreeRoot, `${request.jobId}-${shortId}`);
5416
+ (0, import_node_fs6.mkdirSync)(worktreeRoot, { recursive: true });
4187
5417
  runGit(repoRoot, ["worktree", "add", "-b", branchName, worktreePath, "HEAD"]);
4188
5418
  return { repoRoot, worktreePath, branchName };
4189
5419
  }
@@ -4293,7 +5523,7 @@ var ChildProcessSubagentRunner = class {
4293
5523
  }
4294
5524
  resolveTranscriptPath(job) {
4295
5525
  if (!this.logsDir) return void 0;
4296
- return (0, import_node_path7.join)(this.logsDir, job.request.parentSessionId, "subagents", `${job.jobId}.jsonl`);
5526
+ return (0, import_node_path9.join)(this.logsDir, job.request.parentSessionId, "subagents", `${job.jobId}.jsonl`);
4297
5527
  }
4298
5528
  };
4299
5529
  function resolveAgentDefinition(agentType, customRegistry) {
@@ -4319,19 +5549,20 @@ function createProviderProfile(providerConfig, deps, job) {
4319
5549
  model: job.request.model ?? provider.model,
4320
5550
  apiKey: provider.apiKey,
4321
5551
  baseURL: provider.baseURL,
4322
- timeout: provider.timeout
5552
+ timeout: provider.timeout,
5553
+ options: provider.options
4323
5554
  };
4324
5555
  }
4325
5556
  function resolveDefaultWorkerPath() {
4326
5557
  const entryPoint = process.argv[1] ?? "";
4327
- const entryDir = entryPoint ? (0, import_node_path7.dirname)(entryPoint) : process.cwd();
5558
+ const entryDir = entryPoint ? (0, import_node_path9.dirname)(entryPoint) : process.cwd();
4328
5559
  const extension = entryPoint.endsWith(".ts") || entryPoint.endsWith(".tsx") ? ".ts" : ".js";
4329
5560
  const candidates = [
4330
- (0, import_node_path7.join)(entryDir, "subagents", `child-process-subagent-worker${extension}`),
4331
- (0, import_node_path7.join)(entryDir, `child-process-subagent-worker${extension}`)
5561
+ (0, import_node_path9.join)(entryDir, "subagents", `child-process-subagent-worker${extension}`),
5562
+ (0, import_node_path9.join)(entryDir, `child-process-subagent-worker${extension}`)
4332
5563
  ];
4333
5564
  for (const candidate of candidates) {
4334
- if ((0, import_node_fs5.existsSync)(candidate)) {
5565
+ if ((0, import_node_fs7.existsSync)(candidate)) {
4335
5566
  return candidate;
4336
5567
  }
4337
5568
  }
@@ -4348,14 +5579,14 @@ function resolveDefaultExecArgv(workerPath) {
4348
5579
  }
4349
5580
  function readTranscriptLog(jobId, transcriptPath, cursor) {
4350
5581
  const offset = cursor?.offset ?? 0;
4351
- if (!(0, import_node_fs5.existsSync)(transcriptPath)) {
5582
+ if (!(0, import_node_fs7.existsSync)(transcriptPath)) {
4352
5583
  return {
4353
5584
  taskId: jobId,
4354
5585
  cursor,
4355
5586
  lines: []
4356
5587
  };
4357
5588
  }
4358
- const lines = (0, import_node_fs5.readFileSync)(transcriptPath, "utf8").split(/\r?\n/).filter(Boolean);
5589
+ const lines = (0, import_node_fs7.readFileSync)(transcriptPath, "utf8").split(/\r?\n/).filter(Boolean);
4359
5590
  const nextOffset = Math.min(offset + LOG_PAGE_SIZE2, lines.length);
4360
5591
  return {
4361
5592
  taskId: jobId,
@@ -4365,16 +5596,103 @@ function readTranscriptLog(jobId, transcriptPath, cursor) {
4365
5596
  };
4366
5597
  }
4367
5598
 
5599
+ // src/commands/statusline-command-module.ts
5600
+ var USAGE = [
5601
+ "Usage: /statusline on | off | reset | git on | git off",
5602
+ "Fields: model, context, permission mode, message count, session name, thinking state, git branch."
5603
+ ].join("\n");
5604
+ function createStatusLineEntry() {
5605
+ return {
5606
+ name: "statusline",
5607
+ description: "Configure TUI status-line visibility and fields such as model, context, tokens, session, and git branch.",
5608
+ source: "cli",
5609
+ modelInvocable: false,
5610
+ argumentHint: "on | off | reset | git on | git off",
5611
+ subcommands: [
5612
+ { name: "on", description: "Show the status line", source: "cli" },
5613
+ { name: "off", description: "Hide the status line", source: "cli" },
5614
+ { name: "reset", description: "Restore default status-line fields", source: "cli" },
5615
+ { name: "git", description: "Show or hide git branch field", source: "cli" }
5616
+ ]
5617
+ };
5618
+ }
5619
+ function parseStatusLineArgs(args) {
5620
+ const parts = args.trim().toLowerCase().split(/\s+/).filter((part) => part.length > 0);
5621
+ const [first, second] = parts;
5622
+ if (first === "on" && second === void 0) {
5623
+ return { success: true, message: "Status line enabled.", patch: { enabled: true } };
5624
+ }
5625
+ if (first === "off" && second === void 0) {
5626
+ return { success: true, message: "Status line disabled.", patch: { enabled: false } };
5627
+ }
5628
+ if (first === "reset" && second === void 0) {
5629
+ return {
5630
+ success: true,
5631
+ message: "Status line settings reset.",
5632
+ patch: { enabled: true, gitBranch: true }
5633
+ };
5634
+ }
5635
+ if (first === "git" && second === "on" && parts.length === 2) {
5636
+ return {
5637
+ success: true,
5638
+ message: "Status line git branch shown.",
5639
+ patch: { gitBranch: true }
5640
+ };
5641
+ }
5642
+ if (first === "git" && second === "off" && parts.length === 2) {
5643
+ return {
5644
+ success: true,
5645
+ message: "Status line git branch hidden.",
5646
+ patch: { gitBranch: false }
5647
+ };
5648
+ }
5649
+ return { success: false, message: USAGE };
5650
+ }
5651
+ function createStatusLineSystemCommand() {
5652
+ const entry = createStatusLineEntry();
5653
+ return {
5654
+ name: entry.name,
5655
+ description: entry.description,
5656
+ modelInvocable: false,
5657
+ userInvocable: true,
5658
+ argumentHint: entry.argumentHint,
5659
+ execute: (_session, args) => {
5660
+ const action = parseStatusLineArgs(args);
5661
+ if (!action.success) {
5662
+ return { success: false, message: action.message };
5663
+ }
5664
+ return {
5665
+ success: true,
5666
+ message: action.message,
5667
+ data: { statuslinePatch: action.patch }
5668
+ };
5669
+ }
5670
+ };
5671
+ }
5672
+ var StatusLineCommandSource = class {
5673
+ name = "cli-statusline";
5674
+ getCommands() {
5675
+ return [createStatusLineEntry()];
5676
+ }
5677
+ };
5678
+ function createStatusLineCommandModule() {
5679
+ return {
5680
+ name: "cli-statusline",
5681
+ commandSources: [new StatusLineCommandSource()],
5682
+ systemCommands: [createStatusLineSystemCommand()]
5683
+ };
5684
+ }
5685
+
4368
5686
  // src/cli.ts
4369
5687
  var import_meta = {};
4370
5688
  function readVersion() {
4371
5689
  try {
4372
5690
  const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
4373
- const dir = (0, import_node_path8.dirname)(thisFile);
4374
- const candidates = [(0, import_node_path8.join)(dir, "..", "..", "package.json"), (0, import_node_path8.join)(dir, "..", "package.json")];
5691
+ const dir = (0, import_node_path10.dirname)(thisFile);
5692
+ const candidates = [(0, import_node_path10.join)(dir, "..", "..", "package.json"), (0, import_node_path10.join)(dir, "..", "package.json")];
4375
5693
  for (const pkgPath of candidates) {
4376
5694
  try {
4377
- const raw = (0, import_node_fs6.readFileSync)(pkgPath, "utf-8");
5695
+ const raw = (0, import_node_fs8.readFileSync)(pkgPath, "utf-8");
4378
5696
  const pkg = JSON.parse(raw);
4379
5697
  if (pkg.version !== void 0 && pkg.name !== void 0) {
4380
5698
  return pkg.version;
@@ -4388,7 +5706,7 @@ function readVersion() {
4388
5706
  }
4389
5707
  }
4390
5708
  function promptInput(label, masked = false) {
4391
- return new Promise((resolve) => {
5709
+ return new Promise((resolve2) => {
4392
5710
  process.stdout.write(label);
4393
5711
  let input = "";
4394
5712
  const stdin = process.stdin;
@@ -4403,7 +5721,7 @@ function promptInput(label, masked = false) {
4403
5721
  stdin.setRawMode(wasRaw ?? false);
4404
5722
  stdin.pause();
4405
5723
  process.stdout.write("\n");
4406
- resolve(input.trim());
5724
+ resolve2(input.trim());
4407
5725
  return;
4408
5726
  } else if (ch === "\x7F" || ch === "\b") {
4409
5727
  if (input.length > 0) {
@@ -4433,8 +5751,21 @@ function resetConfig() {
4433
5751
  }
4434
5752
  async function startCli(options = {}) {
4435
5753
  const args = parseCliArgs();
5754
+ const version = readVersion();
4436
5755
  if (args.version) {
4437
- process.stdout.write(`robota ${readVersion()}
5756
+ process.stdout.write(`robota ${version}
5757
+ `);
5758
+ return;
5759
+ }
5760
+ if (args.checkUpdate) {
5761
+ const result = await checkForCliUpdate({ currentVersion: version, force: true });
5762
+ const message = formatCliUpdateCheckMessage(result);
5763
+ if (result.status === "error") {
5764
+ process.stderr.write(`${message}
5765
+ `);
5766
+ process.exit(1);
5767
+ }
5768
+ process.stdout.write(`${message}
4438
5769
  `);
4439
5770
  return;
4440
5771
  }
@@ -4444,6 +5775,11 @@ async function startCli(options = {}) {
4444
5775
  }
4445
5776
  const cwd = process.cwd();
4446
5777
  const providerDefinitions = options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
5778
+ const commandModules = [
5779
+ createStatusLineCommandModule(),
5780
+ ...options.commandModules ?? []
5781
+ ];
5782
+ const startupUpdateNoticePromise = shouldRunStartupCliUpdateCheck(args) ? getStartupCliUpdateNotice({ currentVersion: version }) : void 0;
4447
5783
  if (args.configure) {
4448
5784
  await runInteractiveProviderSetup(cwd, args, promptInput, providerDefinitions);
4449
5785
  return;
@@ -4523,7 +5859,7 @@ ${args.jsonSchema}`
4523
5859
  appendSystemPrompt,
4524
5860
  backgroundTaskRunners,
4525
5861
  subagentRunnerFactory,
4526
- commandModules: options.commandModules
5862
+ commandModules
4527
5863
  });
4528
5864
  const transport = (0, import_agent_transport_headless.createHeadlessTransport)({
4529
5865
  outputFormat: args.outputFormat ?? "text",
@@ -4541,15 +5877,16 @@ ${args.jsonSchema}`
4541
5877
  language: args.language,
4542
5878
  permissionMode: args.permissionMode,
4543
5879
  maxTurns: args.maxTurns,
4544
- version: readVersion(),
5880
+ version,
4545
5881
  sessionStore,
4546
5882
  resumeSessionId,
4547
5883
  forkSession: args.forkSession,
4548
5884
  sessionName: args.sessionName,
4549
5885
  backgroundTaskRunners,
4550
5886
  subagentRunnerFactory,
4551
- commandModules: options.commandModules,
4552
- providerDefinitions
5887
+ commandModules,
5888
+ providerDefinitions,
5889
+ startupUpdateNoticePromise
4553
5890
  });
4554
5891
  }
4555
5892
  // Annotate the CommonJS export names for ESM import in node: