bravecode-cli 0.1.10 → 0.1.12

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.
package/dist/cli.mjs CHANGED
@@ -37370,70 +37370,87 @@ ${instructions}` : instructions;
37370
37370
  payload.temperature = params.temperature;
37371
37371
  }
37372
37372
  const parser = new TextToolCallParser(() => this.nextToolCallId());
37373
- try {
37374
- const response = await fetch(this.coderoUrl, {
37375
- method: "POST",
37376
- headers: {
37377
- "Content-Type": "application/json",
37378
- ...this.coderoApiKey ? { Authorization: `Bearer ${this.coderoApiKey}` } : {}
37379
- },
37380
- body: JSON.stringify(payload),
37381
- signal: params.signal
37382
- });
37383
- if (!response.ok) {
37384
- throw new Error(`BraveCode API error: ${response.status}`);
37385
- }
37386
- const reader = response.body?.getReader();
37387
- if (!reader) {
37388
- throw new Error("No response body");
37389
- }
37390
- const decoder = new TextDecoder();
37391
- let lineBuffer = "";
37392
- let sawDone = false;
37393
- while (!sawDone) {
37394
- if (params.signal?.aborted) break;
37395
- const { done, value } = await reader.read();
37396
- if (done) break;
37397
- lineBuffer += decoder.decode(value, { stream: true });
37398
- const lines = lineBuffer.split("\n");
37399
- lineBuffer = lines.pop() || "";
37400
- for (const line of lines) {
37401
- const trimmed = line.trim();
37402
- if (!trimmed) continue;
37403
- let delta = null;
37404
- try {
37405
- delta = JSON.parse(trimmed);
37406
- } catch {
37407
- delta = null;
37408
- }
37409
- const rawText = delta?.delta || delta?.chunk || "";
37410
- if (rawText) {
37411
- const { text: text2, toolCalls } = parser.push(rawText);
37412
- if (text2) {
37413
- yield { type: "text", text: text2 };
37373
+ const maxRetries = 2;
37374
+ let lastError = null;
37375
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
37376
+ try {
37377
+ const controller = new AbortController();
37378
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
37379
+ if (params.signal) {
37380
+ params.signal.addEventListener("abort", () => controller.abort());
37381
+ }
37382
+ const response = await fetch(this.coderoUrl, {
37383
+ method: "POST",
37384
+ headers: {
37385
+ "Content-Type": "application/json",
37386
+ "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
37387
+ ...this.coderoApiKey ? { Authorization: `Bearer ${this.coderoApiKey}` } : {}
37388
+ },
37389
+ body: JSON.stringify(payload),
37390
+ signal: controller.signal
37391
+ });
37392
+ clearTimeout(timeoutId);
37393
+ if (!response.ok) {
37394
+ throw new Error(`BraveCode API error: ${response.status}`);
37395
+ }
37396
+ const reader = response.body?.getReader();
37397
+ if (!reader) {
37398
+ throw new Error("No response body");
37399
+ }
37400
+ const decoder = new TextDecoder();
37401
+ let lineBuffer = "";
37402
+ let sawDone = false;
37403
+ while (!sawDone) {
37404
+ if (params.signal?.aborted) break;
37405
+ const { done, value } = await reader.read();
37406
+ if (done) break;
37407
+ lineBuffer += decoder.decode(value, { stream: true });
37408
+ const lines = lineBuffer.split("\n");
37409
+ lineBuffer = lines.pop() || "";
37410
+ for (const line of lines) {
37411
+ const trimmed = line.trim();
37412
+ if (!trimmed) continue;
37413
+ let delta = null;
37414
+ try {
37415
+ delta = JSON.parse(trimmed);
37416
+ } catch {
37417
+ delta = null;
37414
37418
  }
37415
- for (const call of toolCalls) {
37416
- yield { type: "tool_call", toolCall: call };
37419
+ const rawText = delta?.delta || delta?.chunk || "";
37420
+ if (rawText) {
37421
+ const { text: text2, toolCalls } = parser.push(rawText);
37422
+ if (text2) {
37423
+ yield { type: "text", text: text2 };
37424
+ }
37425
+ for (const call of toolCalls) {
37426
+ yield { type: "tool_call", toolCall: call };
37427
+ }
37428
+ }
37429
+ if (delta && (delta.done || delta.type === "done")) {
37430
+ sawDone = true;
37431
+ break;
37417
37432
  }
37418
- }
37419
- if (delta && (delta.done || delta.type === "done")) {
37420
- sawDone = true;
37421
- break;
37422
37433
  }
37423
37434
  }
37435
+ const tail = parser.end();
37436
+ if (tail.text) {
37437
+ yield { type: "text", text: tail.text };
37438
+ }
37439
+ for (const call of tail.toolCalls) {
37440
+ yield { type: "tool_call", toolCall: call };
37441
+ }
37442
+ yield { type: "finish", finishReason: "stop" };
37443
+ return;
37444
+ } catch (error40) {
37445
+ lastError = error40;
37446
+ console.error(`BraveCode stream error (attempt ${attempt + 1}/${maxRetries + 1}):`, error40.message);
37447
+ if (attempt < maxRetries) {
37448
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3 * (attempt + 1)));
37449
+ continue;
37450
+ }
37424
37451
  }
37425
- const tail = parser.end();
37426
- if (tail.text) {
37427
- yield { type: "text", text: tail.text };
37428
- }
37429
- for (const call of tail.toolCalls) {
37430
- yield { type: "tool_call", toolCall: call };
37431
- }
37432
- yield { type: "finish", finishReason: "stop" };
37433
- } catch (error40) {
37434
- console.error("BraveCode stream error:", error40);
37435
- throw error40;
37436
37452
  }
37453
+ throw lastError || new Error("BraveCode API: All connection attempts failed");
37437
37454
  }
37438
37455
  async coderoChat(params) {
37439
37456
  let text2 = "";
@@ -38030,7 +38047,7 @@ var APP_VERSION;
38030
38047
  var init_version = __esm({
38031
38048
  "src/version.ts"() {
38032
38049
  "use strict";
38033
- APP_VERSION = "0.1.10";
38050
+ APP_VERSION = "0.1.12";
38034
38051
  }
38035
38052
  });
38036
38053
 
@@ -41560,55 +41577,36 @@ var init_toast = __esm({
41560
41577
  }
41561
41578
  });
41562
41579
 
41563
- // src/core/tui/components/header-bar.tsx
41580
+ // src/core/tui/components/logo.tsx
41564
41581
  import "react";
41565
41582
  import { Box, Text } from "ink";
41566
- import { jsx as jsx6, jsxs } from "react/jsx-runtime";
41567
- function HeaderBar() {
41568
- const { colors } = useTheme();
41569
- const { currentAgent, currentModel, isRunning } = useAgent();
41570
- const { session } = useSession();
41571
- const width = process.stdout.columns || 80;
41572
- const separatorWidth = width - 2;
41573
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
41574
- /* @__PURE__ */ jsx6(Text, { color: colors.border, children: "\u2500".repeat(separatorWidth) }),
41575
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
41576
- /* @__PURE__ */ jsxs(Box, { children: [
41577
- /* @__PURE__ */ jsx6(Text, { bold: true, color: colors.primary, children: "BraveCode" }),
41578
- /* @__PURE__ */ jsx6(Text, { color: colors.textMuted, children: " \xB7 " }),
41579
- /* @__PURE__ */ jsx6(Text, { color: colors.textSecondary, children: currentAgent.name })
41580
- ] }),
41581
- /* @__PURE__ */ jsxs(Box, { children: [
41582
- isRunning && /* @__PURE__ */ jsx6(Text, { color: colors.warning, children: "\u25CF " }),
41583
- /* @__PURE__ */ jsx6(Text, { color: colors.textMuted, children: "model " }),
41584
- /* @__PURE__ */ jsx6(Text, { color: colors.accent, children: currentModel })
41585
- ] })
41586
- ] }),
41587
- session && /* @__PURE__ */ jsxs(Box, { children: [
41588
- /* @__PURE__ */ jsx6(Text, { color: colors.textMuted, children: "session " }),
41589
- /* @__PURE__ */ jsx6(Text, { color: colors.textSecondary, children: session.title }),
41590
- /* @__PURE__ */ jsx6(Text, { color: colors.textMuted, children: " \xB7 " }),
41591
- /* @__PURE__ */ jsxs(Text, { color: colors.textMuted, children: [
41592
- session.messages.length,
41593
- " messages"
41594
- ] })
41595
- ] }),
41596
- /* @__PURE__ */ jsx6(Text, { color: colors.border, children: "\u2500".repeat(separatorWidth) })
41597
- ] });
41583
+ import { jsx as jsx6 } from "react/jsx-runtime";
41584
+ function Logo({ width = 80 }) {
41585
+ const logo = [
41586
+ "\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557",
41587
+ "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551",
41588
+ "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551",
41589
+ "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551",
41590
+ "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D",
41591
+ "\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D "
41592
+ ];
41593
+ const maxLineWidth = Math.max(...logo.map((line) => line.length));
41594
+ const centeredLines = logo.map((line) => {
41595
+ const padding = Math.max(0, Math.floor((width - line.length) / 2));
41596
+ return " ".repeat(padding) + line;
41597
+ });
41598
+ return /* @__PURE__ */ jsx6(Box, { flexDirection: "column", alignItems: "center", children: centeredLines.map((line, i) => /* @__PURE__ */ jsx6(Text, { color: "gray", children: line }, i)) });
41598
41599
  }
41599
- var init_header_bar = __esm({
41600
- "src/core/tui/components/header-bar.tsx"() {
41600
+ var init_logo = __esm({
41601
+ "src/core/tui/components/logo.tsx"() {
41601
41602
  "use strict";
41602
- init_theme();
41603
- init_agent2();
41604
- init_session();
41605
41603
  }
41606
41604
  });
41607
41605
 
41608
41606
  // src/core/tui/components/message.tsx
41609
41607
  import "react";
41610
41608
  import { Box as Box2, Text as Text2 } from "ink";
41611
- import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
41609
+ import { jsx as jsx7, jsxs } from "react/jsx-runtime";
41612
41610
  function ToolCallDisplay({ toolCall }) {
41613
41611
  const { colors } = useTheme();
41614
41612
  const statusIcon = {
@@ -41638,17 +41636,17 @@ function ToolCallDisplay({ toolCall }) {
41638
41636
  };
41639
41637
  const label = toolLabels[toolCall.name]?.past || toolCall.name;
41640
41638
  const argStr = getToolArgs(toolCall);
41641
- return /* @__PURE__ */ jsxs2(Box2, { marginLeft: 2, children: [
41642
- /* @__PURE__ */ jsxs2(Text2, { color: statusColor, children: [
41639
+ return /* @__PURE__ */ jsxs(Box2, { marginLeft: 2, children: [
41640
+ /* @__PURE__ */ jsxs(Text2, { color: statusColor, children: [
41643
41641
  statusIcon,
41644
41642
  " "
41645
41643
  ] }),
41646
41644
  /* @__PURE__ */ jsx7(Text2, { bold: true, color: colors.text, children: label }),
41647
- argStr && /* @__PURE__ */ jsxs2(Text2, { color: colors.textMuted, children: [
41645
+ argStr && /* @__PURE__ */ jsxs(Text2, { color: colors.textMuted, children: [
41648
41646
  " ",
41649
41647
  argStr
41650
41648
  ] }),
41651
- toolCall.output && toolCall.status === "completed" && /* @__PURE__ */ jsxs2(Text2, { color: colors.textMuted, children: [
41649
+ toolCall.output && toolCall.status === "completed" && /* @__PURE__ */ jsxs(Text2, { color: colors.textMuted, children: [
41652
41650
  " \u2514 ",
41653
41651
  truncate(toolCall.output.split("\n")[0], 60)
41654
41652
  ] })
@@ -41714,19 +41712,19 @@ function Message({ message, showTimestamp = false }) {
41714
41712
  }
41715
41713
  };
41716
41714
  const config2 = roleConfig[message.role];
41717
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
41718
- /* @__PURE__ */ jsxs2(Box2, { children: [
41719
- /* @__PURE__ */ jsxs2(Text2, { bold: true, color: config2.color, children: [
41715
+ return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", marginBottom: 1, children: [
41716
+ /* @__PURE__ */ jsxs(Box2, { children: [
41717
+ /* @__PURE__ */ jsxs(Text2, { bold: true, color: config2.color, children: [
41720
41718
  config2.prefix,
41721
41719
  " ",
41722
41720
  config2.label
41723
41721
  ] }),
41724
- message.agent && /* @__PURE__ */ jsxs2(Text2, { color: colors.textMuted, children: [
41722
+ message.agent && /* @__PURE__ */ jsxs(Text2, { color: colors.textMuted, children: [
41725
41723
  " (",
41726
41724
  message.agent,
41727
41725
  ")"
41728
41726
  ] }),
41729
- showTimestamp && /* @__PURE__ */ jsxs2(Text2, { color: colors.textMuted, children: [
41727
+ showTimestamp && /* @__PURE__ */ jsxs(Text2, { color: colors.textMuted, children: [
41730
41728
  " ",
41731
41729
  formatTime(message.timestamp)
41732
41730
  ] })
@@ -41745,7 +41743,7 @@ var init_message = __esm({
41745
41743
  // src/core/tui/components/message-container.tsx
41746
41744
  import { useRef, useEffect as useEffect2 } from "react";
41747
41745
  import { Box as Box3, Text as Text3 } from "ink";
41748
- import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
41746
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
41749
41747
  function MessageContainer() {
41750
41748
  const { colors } = useTheme();
41751
41749
  const { messages } = useSession();
@@ -41755,7 +41753,7 @@ function MessageContainer() {
41755
41753
  }
41756
41754
  }, [messages.length]);
41757
41755
  if (messages.length === 0) {
41758
- return /* @__PURE__ */ jsxs3(
41756
+ return /* @__PURE__ */ jsxs2(
41759
41757
  Box3,
41760
41758
  {
41761
41759
  flexDirection: "column",
@@ -41799,9 +41797,9 @@ var init_message_container = __esm({
41799
41797
  });
41800
41798
 
41801
41799
  // src/core/tui/components/input-editor.tsx
41802
- import { useState as useState6, useRef as useRef2, useEffect as useEffect3 } from "react";
41800
+ import { useState as useState6 } from "react";
41803
41801
  import { Box as Box4, Text as Text4, useInput, useApp } from "ink";
41804
- import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
41802
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
41805
41803
  function InputEditor({ onSubmit, placeholder = "Type a message...", disabled = false }) {
41806
41804
  const { colors } = useTheme();
41807
41805
  const { isRunning } = useAgent();
@@ -41810,11 +41808,6 @@ function InputEditor({ onSubmit, placeholder = "Type a message...", disabled = f
41810
41808
  const [cursorPos, setCursorPos] = useState6(0);
41811
41809
  const [history, setHistory] = useState6([]);
41812
41810
  const [historyIndex, setHistoryIndex] = useState6(-1);
41813
- const [isMultiline, setIsMultiline] = useState6(false);
41814
- const inputRef = useRef2("");
41815
- useEffect3(() => {
41816
- inputRef.current = value;
41817
- }, [value]);
41818
41811
  useInput((input, key) => {
41819
41812
  if (disabled || isRunning) return;
41820
41813
  if (key.return) {
@@ -41822,7 +41815,6 @@ function InputEditor({ onSubmit, placeholder = "Type a message...", disabled = f
41822
41815
  const newValue = value.slice(0, cursorPos) + "\n" + value.slice(cursorPos);
41823
41816
  setValue(newValue);
41824
41817
  setCursorPos(cursorPos + 1);
41825
- setIsMultiline(true);
41826
41818
  return;
41827
41819
  }
41828
41820
  if (value.trim()) {
@@ -41831,7 +41823,6 @@ function InputEditor({ onSubmit, placeholder = "Type a message...", disabled = f
41831
41823
  setValue("");
41832
41824
  setCursorPos(0);
41833
41825
  setHistoryIndex(-1);
41834
- setIsMultiline(false);
41835
41826
  }
41836
41827
  return;
41837
41828
  }
@@ -41924,10 +41915,6 @@ function InputEditor({ onSubmit, placeholder = "Type a message...", disabled = f
41924
41915
  }
41925
41916
  return;
41926
41917
  }
41927
- if (key.ctrl && input === "l") {
41928
- process.stdout.write("\x1B[2J\x1B[H");
41929
- return;
41930
- }
41931
41918
  if (input.length === 1 && !key.ctrl && !key.meta) {
41932
41919
  const newValue = value.slice(0, cursorPos) + input + value.slice(cursorPos);
41933
41920
  setValue(newValue);
@@ -41938,32 +41925,22 @@ function InputEditor({ onSubmit, placeholder = "Type a message...", disabled = f
41938
41925
  if (disabled || isRunning) {
41939
41926
  return /* @__PURE__ */ jsx9(Box4, { children: /* @__PURE__ */ jsx9(Text4, { color: colors.textMuted, children: isRunning ? "Thinking..." : "Disabled" }) });
41940
41927
  }
41941
- if (!value && !isMultiline) {
41928
+ if (!value) {
41942
41929
  return /* @__PURE__ */ jsx9(Box4, { children: /* @__PURE__ */ jsx9(Text4, { color: colors.textMuted, children: placeholder }) });
41943
41930
  }
41944
41931
  const before = value.slice(0, cursorPos);
41945
41932
  const cursor = value[cursorPos] || " ";
41946
41933
  const after = value.slice(cursorPos + 1);
41947
- return /* @__PURE__ */ jsx9(Box4, { flexDirection: "column", children: /* @__PURE__ */ jsxs4(Box4, { children: [
41934
+ return /* @__PURE__ */ jsxs3(Box4, { children: [
41948
41935
  /* @__PURE__ */ jsx9(Text4, { children: before }),
41949
41936
  /* @__PURE__ */ jsx9(Text4, { inverse: true, children: cursor }),
41950
41937
  /* @__PURE__ */ jsx9(Text4, { children: after })
41951
- ] }) });
41938
+ ] });
41952
41939
  };
41953
- return /* @__PURE__ */ jsx9(
41954
- Box4,
41955
- {
41956
- borderStyle: "round",
41957
- borderColor: colors.borderFocused,
41958
- paddingX: 1,
41959
- paddingY: 0,
41960
- flexDirection: "column",
41961
- children: /* @__PURE__ */ jsxs4(Box4, { children: [
41962
- /* @__PURE__ */ jsx9(Text4, { bold: true, color: colors.primary, children: "\u203A " }),
41963
- renderInput()
41964
- ] })
41965
- }
41966
- );
41940
+ return /* @__PURE__ */ jsxs3(Box4, { children: [
41941
+ /* @__PURE__ */ jsx9(Text4, { bold: true, color: colors.primary, children: "\u203A " }),
41942
+ renderInput()
41943
+ ] });
41967
41944
  }
41968
41945
  var init_input_editor = __esm({
41969
41946
  "src/core/tui/components/input-editor.tsx"() {
@@ -41976,18 +41953,21 @@ var init_input_editor = __esm({
41976
41953
  // src/core/tui/components/status-bar.tsx
41977
41954
  import "react";
41978
41955
  import { Box as Box5, Text as Text5 } from "ink";
41979
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
41956
+ import { Fragment, jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
41980
41957
  function StatusBar() {
41981
41958
  const { colors } = useTheme();
41982
41959
  const { currentAgent, isRunning } = useAgent();
41983
41960
  const width = process.stdout.columns || 80;
41984
- return /* @__PURE__ */ jsxs5(Box5, { justifyContent: "space-between", children: [
41985
- /* @__PURE__ */ jsx10(Box5, { children: /* @__PURE__ */ jsxs5(Text5, { color: colors.textMuted, children: [
41986
- "v",
41987
- APP_VERSION
41988
- ] }) }),
41989
- /* @__PURE__ */ jsx10(Box5, { children: isRunning ? /* @__PURE__ */ jsx10(Text5, { color: colors.warning, children: "\u25CF Running" }) : /* @__PURE__ */ jsx10(Text5, { color: colors.textMuted, children: "Ready" }) }),
41990
- /* @__PURE__ */ jsx10(Box5, { children: /* @__PURE__ */ jsx10(Text5, { color: colors.textMuted, children: "/help for commands" }) })
41961
+ const cwd = process.cwd().replace(process.env.HOME || "", "~");
41962
+ return /* @__PURE__ */ jsxs4(Box5, { justifyContent: "space-between", width, children: [
41963
+ /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
41964
+ /* @__PURE__ */ jsx10(Text5, { color: colors.textMuted, children: cwd }),
41965
+ isRunning && /* @__PURE__ */ jsxs4(Fragment, { children: [
41966
+ /* @__PURE__ */ jsx10(Text5, { color: colors.textMuted, children: "\xB7" }),
41967
+ /* @__PURE__ */ jsx10(Text5, { color: colors.warning, children: "\u25CF running" })
41968
+ ] })
41969
+ ] }),
41970
+ /* @__PURE__ */ jsx10(Text5, { color: colors.textMuted, children: APP_VERSION })
41991
41971
  ] });
41992
41972
  }
41993
41973
  var init_status_bar = __esm({
@@ -42002,14 +41982,14 @@ var init_status_bar = __esm({
42002
41982
  // src/core/tui/components/toast.tsx
42003
41983
  import "react";
42004
41984
  import { Box as Box6, Text as Text6 } from "ink";
42005
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
41985
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
42006
41986
  function ToastContainer() {
42007
41987
  const { colors } = useTheme();
42008
41988
  const { toasts } = useToast();
42009
41989
  if (toasts.length === 0) {
42010
41990
  return null;
42011
41991
  }
42012
- return /* @__PURE__ */ jsx11(Box6, { flexDirection: "column", position: "absolute", bottom: 2, right: 2, children: toasts.map((toast) => /* @__PURE__ */ jsxs6(
41992
+ return /* @__PURE__ */ jsx11(Box6, { flexDirection: "column", position: "absolute", bottom: 2, right: 2, children: toasts.map((toast) => /* @__PURE__ */ jsxs5(
42013
41993
  Box6,
42014
41994
  {
42015
41995
  borderStyle: "round",
@@ -42017,7 +41997,7 @@ function ToastContainer() {
42017
41997
  paddingX: 1,
42018
41998
  marginBottom: 1,
42019
41999
  children: [
42020
- /* @__PURE__ */ jsxs6(Text6, { color: colors[variantColors[toast.variant]], children: [
42000
+ /* @__PURE__ */ jsxs5(Text6, { color: colors[variantColors[toast.variant]], children: [
42021
42001
  variantIcons[toast.variant],
42022
42002
  " "
42023
42003
  ] }),
@@ -42051,110 +42031,39 @@ var init_toast2 = __esm({
42051
42031
  // src/core/tui/components/command-palette.tsx
42052
42032
  import { useState as useState7 } from "react";
42053
42033
  import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
42054
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
42055
- function CommandPalette() {
42034
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
42035
+ var init_command_palette = __esm({
42036
+ "src/core/tui/components/command-palette.tsx"() {
42037
+ "use strict";
42038
+ init_theme();
42039
+ init_dialog();
42040
+ init_agent2();
42041
+ init_session();
42042
+ init_toast();
42043
+ }
42044
+ });
42045
+
42046
+ // src/core/tui/components/model-selector.tsx
42047
+ import { useState as useState8 } from "react";
42048
+ import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
42049
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
42050
+ function ModelSelector() {
42056
42051
  const { colors } = useTheme();
42052
+ const { currentModel, setModel } = useAgent();
42057
42053
  const { closeDialog } = useDialog();
42058
- const { setAgent, setModel, availableAgents } = useAgent();
42059
- const { createSession, clearMessages } = useSession();
42060
42054
  const toast = useToast();
42061
- const [query, setQuery] = useState7("");
42062
- const [selectedIndex, setSelectedIndex] = useState7(0);
42063
- const commands = [
42064
- // Session commands
42065
- {
42066
- name: "new",
42067
- label: "New Session",
42068
- description: "Start a new conversation",
42069
- category: "Session",
42070
- action: () => {
42071
- createSession();
42072
- toast.success("New session created");
42073
- closeDialog();
42074
- }
42075
- },
42076
- {
42077
- name: "clear",
42078
- label: "Clear Messages",
42079
- description: "Clear current conversation",
42080
- category: "Session",
42081
- action: () => {
42082
- clearMessages();
42083
- toast.success("Messages cleared");
42084
- closeDialog();
42085
- }
42086
- },
42087
- // Agent commands
42088
- ...availableAgents.map((agent) => ({
42089
- name: `agent:${agent.id}`,
42090
- label: `Switch to ${agent.name}`,
42091
- description: agent.description,
42092
- category: "Agent",
42093
- action: () => {
42094
- setAgent(agent.id);
42095
- toast.success(`Switched to ${agent.name}`);
42096
- closeDialog();
42097
- }
42098
- })),
42099
- // Model commands
42100
- {
42101
- name: "model:codestral",
42102
- label: "Switch to Codestral",
42103
- description: "Code generation model",
42104
- category: "Model",
42105
- action: () => {
42106
- setModel("codestral-latest");
42107
- toast.success("Switched to Codestral");
42108
- closeDialog();
42109
- }
42110
- },
42111
- {
42112
- name: "model:deepseek-r1",
42113
- label: "Switch to DeepSeek R1",
42114
- description: "Reasoning model",
42115
- category: "Model",
42116
- action: () => {
42117
- setModel("DeepSeek-R1");
42118
- toast.success("Switched to DeepSeek R1");
42119
- closeDialog();
42120
- }
42121
- },
42122
- {
42123
- name: "model:mistral",
42124
- label: "Switch to Mistral Large",
42125
- description: "General purpose model",
42126
- category: "Model",
42127
- action: () => {
42128
- setModel("mistral-large-2512");
42129
- toast.success("Switched to Mistral Large");
42130
- closeDialog();
42131
- }
42132
- },
42133
- // System commands
42134
- {
42135
- name: "help",
42136
- label: "Help",
42137
- description: "Show help information",
42138
- category: "System",
42139
- action: () => {
42140
- toast.info("Help: Use arrow keys to navigate, Enter to select, Esc to close");
42141
- closeDialog();
42142
- }
42143
- },
42144
- {
42145
- name: "exit",
42146
- label: "Exit",
42147
- description: "Quit the application",
42148
- category: "System",
42149
- action: () => {
42150
- process.exit(0);
42151
- }
42152
- }
42153
- ];
42154
- const filteredCommands = commands.filter(
42155
- (cmd) => cmd.label.toLowerCase().includes(query.toLowerCase()) || cmd.description.toLowerCase().includes(query.toLowerCase())
42055
+ const [selectedIndex, setSelectedIndex] = useState8(0);
42056
+ const [filter3, setFilter] = useState8("");
42057
+ const models = models_default.models.map((m) => ({
42058
+ id: m.id,
42059
+ name: m.name,
42060
+ provider: m.provider,
42061
+ tags: m.tags
42062
+ }));
42063
+ const filteredModels = models.filter(
42064
+ (m) => m.tags.includes("free") && (m.name.toLowerCase().includes(filter3.toLowerCase()) || m.id.toLowerCase().includes(filter3.toLowerCase()))
42156
42065
  );
42157
- useInput2((input, key) => {
42066
+ useInput3((input, key) => {
42158
42067
  if (key.escape) {
42159
42068
  closeDialog();
42160
42069
  return;
@@ -42164,75 +42073,84 @@ function CommandPalette() {
42164
42073
  return;
42165
42074
  }
42166
42075
  if (key.downArrow) {
42167
- setSelectedIndex((prev) => Math.min(filteredCommands.length - 1, prev + 1));
42076
+ setSelectedIndex((prev) => Math.min(filteredModels.length - 1, prev + 1));
42168
42077
  return;
42169
42078
  }
42170
42079
  if (key.return) {
42171
- if (filteredCommands[selectedIndex]) {
42172
- filteredCommands[selectedIndex].action();
42080
+ if (filteredModels[selectedIndex]) {
42081
+ const selected = filteredModels[selectedIndex];
42082
+ setModel(selected.id);
42083
+ toast.success(`Switched to ${selected.name}`);
42084
+ closeDialog();
42173
42085
  }
42174
42086
  return;
42175
42087
  }
42176
42088
  if (!key.ctrl && !key.meta && input.length === 1) {
42177
- setQuery((prev) => prev + input);
42089
+ setFilter((prev) => prev + input);
42178
42090
  setSelectedIndex(0);
42179
42091
  }
42180
42092
  if (key.backspace) {
42181
- setQuery((prev) => prev.slice(0, -1));
42093
+ setFilter((prev) => prev.slice(0, -1));
42182
42094
  setSelectedIndex(0);
42183
42095
  }
42184
42096
  });
42097
+ const renderModel = (model, index) => {
42098
+ const isSelected = model.id === currentModel;
42099
+ const isHighlighted = index === selectedIndex;
42100
+ return /* @__PURE__ */ jsxs7(Box8, { children: [
42101
+ /* @__PURE__ */ jsxs7(
42102
+ Text8,
42103
+ {
42104
+ bold: isHighlighted,
42105
+ color: isSelected ? colors.primary : isHighlighted ? colors.accent : colors.text,
42106
+ children: [
42107
+ isSelected ? "\u25CF " : "\u25CB ",
42108
+ model.name
42109
+ ]
42110
+ }
42111
+ ),
42112
+ /* @__PURE__ */ jsxs7(Text8, { color: colors.textMuted, children: [
42113
+ " (",
42114
+ model.provider,
42115
+ ")"
42116
+ ] }),
42117
+ model.tags.includes("free") && /* @__PURE__ */ jsx13(Text8, { color: colors.success, children: " free" })
42118
+ ] }, model.id);
42119
+ };
42185
42120
  return /* @__PURE__ */ jsxs7(
42186
- Box7,
42121
+ Box8,
42187
42122
  {
42188
42123
  flexDirection: "column",
42189
42124
  borderStyle: "double",
42190
42125
  borderColor: colors.borderFocused,
42126
+ width: 60,
42191
42127
  position: "absolute",
42192
42128
  top: "50%",
42193
42129
  left: "50%",
42194
- width: "60%",
42195
42130
  children: [
42196
- /* @__PURE__ */ jsx12(Box7, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text7, { bold: true, color: colors.primary, children: "Command Palette" }) }),
42197
- /* @__PURE__ */ jsxs7(Box7, { paddingX: 1, paddingY: 0, children: [
42198
- /* @__PURE__ */ jsx12(Text7, { color: colors.textMuted, children: "Search: " }),
42199
- /* @__PURE__ */ jsx12(Text7, { children: query }),
42200
- /* @__PURE__ */ jsx12(Text7, { inverse: true, children: " " })
42131
+ /* @__PURE__ */ jsx13(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx13(Text8, { bold: true, color: colors.primary, children: "Select Model" }) }),
42132
+ /* @__PURE__ */ jsxs7(Box8, { paddingX: 1, children: [
42133
+ /* @__PURE__ */ jsx13(Text8, { color: colors.textMuted, children: "Search: " }),
42134
+ /* @__PURE__ */ jsx13(Text8, { children: filter3 }),
42135
+ /* @__PURE__ */ jsx13(Text8, { inverse: true, children: " " })
42201
42136
  ] }),
42202
- /* @__PURE__ */ jsx12(Box7, { flexDirection: "column", paddingX: 1, maxHeight: 15, children: filteredCommands.length === 0 ? /* @__PURE__ */ jsx12(Text7, { color: colors.textMuted, children: "No matching commands" }) : filteredCommands.map((cmd, index) => /* @__PURE__ */ jsxs7(
42203
- Box7,
42204
- {
42205
- backgroundColor: index === selectedIndex ? colors.backgroundHighlight : void 0,
42206
- children: [
42207
- /* @__PURE__ */ jsx12(
42208
- Text7,
42209
- {
42210
- bold: index === selectedIndex,
42211
- color: index === selectedIndex ? colors.primary : colors.text,
42212
- children: cmd.label
42213
- }
42214
- ),
42215
- /* @__PURE__ */ jsxs7(Text7, { color: colors.textMuted, children: [
42216
- " - ",
42217
- cmd.description
42218
- ] })
42219
- ]
42220
- },
42221
- cmd.name
42222
- )) }),
42223
- /* @__PURE__ */ jsx12(Box7, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text7, { color: colors.textMuted, children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc close" }) })
42137
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", paddingX: 1, maxHeight: 15, children: [
42138
+ /* @__PURE__ */ jsx13(Text8, { color: colors.textMuted, children: "Available Models (free):" }),
42139
+ filteredModels.map((model, index) => renderModel(model, index))
42140
+ ] }),
42141
+ /* @__PURE__ */ jsx13(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx13(Text8, { color: colors.textMuted, children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc close" }) })
42224
42142
  ]
42225
42143
  }
42226
42144
  );
42227
42145
  }
42228
- var init_command_palette = __esm({
42229
- "src/core/tui/components/command-palette.tsx"() {
42146
+ var init_model_selector = __esm({
42147
+ "src/core/tui/components/model-selector.tsx"() {
42230
42148
  "use strict";
42231
42149
  init_theme();
42232
- init_dialog();
42233
42150
  init_agent2();
42234
- init_session();
42151
+ init_dialog();
42235
42152
  init_toast();
42153
+ init_models();
42236
42154
  }
42237
42155
  });
42238
42156
 
@@ -42333,20 +42251,27 @@ var init_use_agent = __esm({
42333
42251
 
42334
42252
  // src/core/tui/app.tsx
42335
42253
  import "react";
42336
- import { Box as Box8, useInput as useInput3, useApp as useApp2 } from "ink";
42337
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
42254
+ import { Box as Box9, Text as Text9, useInput as useInput4, useApp as useApp2 } from "ink";
42255
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
42338
42256
  function App() {
42339
42257
  const { colors } = useTheme();
42340
42258
  const { messages } = useSession();
42341
- const { isRunning } = useAgent();
42342
- const { isOpen, openDialog, closeDialog } = useDialog();
42259
+ const { currentAgent, currentModel, isRunning, setAgent } = useAgent();
42260
+ const { isOpen, openDialog, closeDialog, dialog } = useDialog();
42343
42261
  const { sendMessage, cancelCurrent } = useAgent2();
42344
42262
  const { exit } = useApp2();
42345
- useInput3((input, key) => {
42263
+ useInput4((input, key) => {
42346
42264
  if (key.ctrl && input === "p") {
42347
42265
  openDialog("model");
42348
42266
  return;
42349
42267
  }
42268
+ if (key.tab) {
42269
+ const agents = ["build", "plan", "debug", "testing", "security"];
42270
+ const currentIndex = agents.indexOf(currentAgent.id);
42271
+ const nextIndex = (currentIndex + 1) % agents.length;
42272
+ setAgent(agents[nextIndex]);
42273
+ return;
42274
+ }
42350
42275
  if (key.ctrl && input === "c") {
42351
42276
  if (isRunning) {
42352
42277
  cancelCurrent();
@@ -42392,29 +42317,51 @@ function App() {
42392
42317
  }
42393
42318
  await sendMessage(value);
42394
42319
  };
42320
+ const hasMessages = messages.length > 0;
42395
42321
  return /* @__PURE__ */ jsxs8(
42396
- Box8,
42322
+ Box9,
42397
42323
  {
42398
42324
  flexDirection: "column",
42399
42325
  width: "100%",
42400
42326
  height: "100%",
42401
42327
  backgroundColor: colors.background,
42402
42328
  children: [
42403
- /* @__PURE__ */ jsx13(HeaderBar, {}),
42404
- /* @__PURE__ */ jsx13(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: /* @__PURE__ */ jsx13(MessageContainer, {}) }),
42405
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
42406
- /* @__PURE__ */ jsx13(
42407
- InputEditor,
42408
- {
42409
- onSubmit: handleSubmit,
42410
- disabled: isOpen,
42411
- placeholder: isRunning ? "Press Ctrl+C to cancel..." : "Type a message... (Ctrl+P for commands)"
42412
- }
42413
- ),
42414
- /* @__PURE__ */ jsx13(StatusBar, {})
42415
- ] }),
42416
- isOpen && /* @__PURE__ */ jsx13(CommandPalette, {}),
42417
- /* @__PURE__ */ jsx13(ToastContainer, {})
42329
+ /* @__PURE__ */ jsx14(Box9, { flexDirection: "column", flexGrow: 1, justifyContent: hasMessages ? "flex-start" : "center", children: hasMessages ? /* @__PURE__ */ jsx14(MessageContainer, {}) : (
42330
+ /* Welcome screen - centered layout like OpenCode */
42331
+ /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", alignItems: "center", gap: 1, children: [
42332
+ /* @__PURE__ */ jsx14(Logo, { width: 80 }),
42333
+ /* @__PURE__ */ jsx14(
42334
+ Box9,
42335
+ {
42336
+ flexDirection: "column",
42337
+ width: 60,
42338
+ borderStyle: "round",
42339
+ borderColor: colors.border,
42340
+ paddingX: 2,
42341
+ paddingY: 1,
42342
+ children: /* @__PURE__ */ jsx14(
42343
+ InputEditor,
42344
+ {
42345
+ onSubmit: handleSubmit,
42346
+ placeholder: 'Ask anything... "What is the tech stack of this project?"'
42347
+ }
42348
+ )
42349
+ }
42350
+ ),
42351
+ /* @__PURE__ */ jsxs8(Box9, { gap: 1, children: [
42352
+ /* @__PURE__ */ jsx14(Text9, { bold: true, color: colors.primary, children: currentAgent.name }),
42353
+ /* @__PURE__ */ jsx14(Text9, { color: colors.textMuted, children: "\xB7" }),
42354
+ /* @__PURE__ */ jsx14(Text9, { color: colors.textSecondary, children: currentModel })
42355
+ ] }),
42356
+ /* @__PURE__ */ jsxs8(Box9, { gap: 2, children: [
42357
+ /* @__PURE__ */ jsx14(Text9, { color: colors.textMuted, children: "tab agents" }),
42358
+ /* @__PURE__ */ jsx14(Text9, { color: colors.textMuted, children: "ctrl+p commands" })
42359
+ ] })
42360
+ ] })
42361
+ ) }),
42362
+ /* @__PURE__ */ jsx14(StatusBar, {}),
42363
+ isOpen && dialog.type === "model" && /* @__PURE__ */ jsx14(ModelSelector, {}),
42364
+ /* @__PURE__ */ jsx14(ToastContainer, {})
42418
42365
  ]
42419
42366
  }
42420
42367
  );
@@ -42426,12 +42373,13 @@ var init_app = __esm({
42426
42373
  init_session();
42427
42374
  init_agent2();
42428
42375
  init_dialog();
42429
- init_header_bar();
42376
+ init_logo();
42430
42377
  init_message_container();
42431
42378
  init_input_editor();
42432
42379
  init_status_bar();
42433
42380
  init_toast2();
42434
42381
  init_command_palette();
42382
+ init_model_selector();
42435
42383
  init_use_agent();
42436
42384
  }
42437
42385
  });
@@ -42443,7 +42391,7 @@ __export(tui_exports, {
42443
42391
  });
42444
42392
  import "react";
42445
42393
  import { render } from "ink";
42446
- import { jsx as jsx14 } from "react/jsx-runtime";
42394
+ import { jsx as jsx15 } from "react/jsx-runtime";
42447
42395
  var TUILauncher;
42448
42396
  var init_tui = __esm({
42449
42397
  "src/core/tui/index.tsx"() {
@@ -42461,12 +42409,12 @@ var init_tui = __esm({
42461
42409
  }
42462
42410
  async start() {
42463
42411
  const { waitUntilExit } = render(
42464
- /* @__PURE__ */ jsx14(ThemeProvider, { children: /* @__PURE__ */ jsx14(SessionProvider, { children: /* @__PURE__ */ jsx14(
42412
+ /* @__PURE__ */ jsx15(ThemeProvider, { children: /* @__PURE__ */ jsx15(SessionProvider, { children: /* @__PURE__ */ jsx15(
42465
42413
  AgentProvider,
42466
42414
  {
42467
42415
  initialAgent: this.options.agent || "build",
42468
42416
  initialModel: this.options.model,
42469
- children: /* @__PURE__ */ jsx14(DialogProvider, { children: /* @__PURE__ */ jsx14(ToastProvider, { children: /* @__PURE__ */ jsx14(App, {}) }) })
42417
+ children: /* @__PURE__ */ jsx15(DialogProvider, { children: /* @__PURE__ */ jsx15(ToastProvider, { children: /* @__PURE__ */ jsx15(App, {}) }) })
42470
42418
  }
42471
42419
  ) }) })
42472
42420
  );