darkfoo-code 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +191 -138
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -18,18 +18,30 @@ var init_theme = __esm({
18
18
  "src/utils/theme.ts"() {
19
19
  "use strict";
20
20
  theme = {
21
- cyan: "#5eead4",
22
- cyanDim: "#3ab5a0",
23
- pink: "#f472b6",
24
- pinkDim: "#c2588e",
25
- purple: "#a78bfa",
21
+ cyan: "#3b82f6",
22
+ // bright blue (primary)
23
+ cyanDim: "#2563eb",
24
+ // deeper blue
25
+ pink: "#ea580c",
26
+ // burnt orange (accent)
27
+ pinkDim: "#c2410c",
28
+ // deep burnt orange
29
+ purple: "#7c6ddb",
30
+ // blue-violet (midpoint)
26
31
  green: "#4ade80",
27
- yellow: "#fbbf24",
32
+ // success green
33
+ yellow: "#f59e0b",
34
+ // warm amber
28
35
  red: "#ef4444",
36
+ // errors
29
37
  text: "#e2e8f0",
38
+ // body text
30
39
  dim: "#7e8ea6",
40
+ // secondary
31
41
  surface: "#111827",
42
+ // component backgrounds
32
43
  bg: "#0c1021"
44
+ // terminal background
33
45
  };
34
46
  }
35
47
  });
@@ -2056,37 +2068,16 @@ function App({ model, systemPromptOverride, maxTurns, initialMessages, initialSe
2056
2068
  }
2057
2069
 
2058
2070
  // src/repl.tsx
2059
- import { useState as useState2, useCallback as useCallback2, useEffect, useRef } from "react";
2060
- import { Box as Box7, Text as Text7, useApp, useInput as useInput2 } from "ink";
2071
+ import { useState as useState2, useCallback as useCallback2, useEffect, useRef, useMemo } from "react";
2072
+ import { Box as Box6, Text as Text6, useApp, useInput as useInput2 } from "ink";
2061
2073
  import { nanoid as nanoid6 } from "nanoid";
2062
2074
 
2063
2075
  // src/components/Banner.tsx
2064
2076
  init_theme();
2065
- import { memo as memo2 } from "react";
2066
- import { Box as Box2, Text as Text2 } from "ink";
2067
-
2068
- // src/components/Mascot.tsx
2069
2077
  import { memo } from "react";
2070
2078
  import { Box, Text } from "ink";
2071
2079
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
2072
- var MOOD_COLORS = {
2073
- idle: "#5eead4",
2074
- thinking: "#a78bfa",
2075
- working: "#fbbf24",
2076
- success: "#4ade80",
2077
- error: "#f472b6"
2078
- };
2079
- var Mascot = memo(function Mascot2({ mood = "idle" }) {
2080
- const color = MOOD_COLORS[mood];
2081
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", children: [
2082
- /* @__PURE__ */ jsx2(Text, { color, children: " \u259F\u2588\u2588\u2599" }),
2083
- /* @__PURE__ */ jsx2(Text, { color, children: " \u2590\u2588\u2588\u2588\u2588\u258C" }),
2084
- /* @__PURE__ */ jsx2(Text, { color, children: " \u259C\u2588\u2588\u259B" })
2085
- ] });
2086
- });
2087
-
2088
- // src/components/Banner.tsx
2089
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2080
+ var version = "0.4.1";
2090
2081
  var LOGO_LINES = [
2091
2082
  " \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
2092
2083
  " \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\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557",
@@ -2097,14 +2088,21 @@ var LOGO_LINES = [
2097
2088
  ];
2098
2089
  var SUBTITLE = " -- C O D E --";
2099
2090
  var GRADIENT = [
2100
- "#5eead4",
2101
- "#6ee0c8",
2102
- "#82c8d0",
2103
- "#96b0d8",
2104
- "#a78bfa",
2105
- "#c47ee8",
2106
- "#f472b6"
2091
+ "#3b82f6",
2092
+ "#5a72e0",
2093
+ "#7c6ddb",
2094
+ "#9a5ec0",
2095
+ "#b8509a",
2096
+ "#d46030",
2097
+ "#ea580c"
2107
2098
  ];
2099
+ var MOOD_COLORS = {
2100
+ idle: "#3b82f6",
2101
+ thinking: "#7c6ddb",
2102
+ working: "#f59e0b",
2103
+ success: "#4ade80",
2104
+ error: "#ea580c"
2105
+ };
2108
2106
  function lineColor(index) {
2109
2107
  const t = index / (LOGO_LINES.length - 1);
2110
2108
  return GRADIENT[Math.round(t * (GRADIENT.length - 1))];
@@ -2115,37 +2113,51 @@ function gradientBar(width) {
2115
2113
  color: GRADIENT[Math.round(i / (width - 1) * (GRADIENT.length - 1))]
2116
2114
  }));
2117
2115
  }
2118
- var Banner = memo2(function Banner2({ model, cwd, providerName, providerOnline, mood }) {
2116
+ var Banner = memo(function Banner2({ model, cwd, providerName, providerOnline, mood = "idle" }) {
2119
2117
  const bar = gradientBar(60);
2120
2118
  const statusTag = providerOnline ? "[connected]" : "[offline]";
2121
2119
  const statusColor = providerOnline ? theme.green ?? "#4ade80" : theme.pink ?? "#f472b6";
2122
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
2123
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", alignItems: "center", children: [
2124
- LOGO_LINES.map((line, i) => /* @__PURE__ */ jsx3(Text2, { color: lineColor(i), bold: true, children: line }, i)),
2125
- /* @__PURE__ */ jsx3(Text2, { color: theme.purple ?? "#a78bfa", bold: true, children: SUBTITLE })
2120
+ const mascotColor = MOOD_COLORS[mood] ?? MOOD_COLORS.idle;
2121
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
2122
+ /* @__PURE__ */ jsxs(Box, { children: [
2123
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
2124
+ LOGO_LINES.map((line, i) => /* @__PURE__ */ jsx2(Text, { color: lineColor(i), bold: true, children: line }, i)),
2125
+ /* @__PURE__ */ jsx2(Text, { color: theme.purple ?? "#a78bfa", bold: true, children: SUBTITLE }),
2126
+ /* @__PURE__ */ jsxs(Text, { color: theme.dim ?? "#7e8ea6", children: [
2127
+ " v",
2128
+ version
2129
+ ] })
2130
+ ] }),
2131
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", justifyContent: "flex-end", children: [
2132
+ /* @__PURE__ */ jsx2(Text, { color: mascotColor, children: " \u2584\u2588\u2588\u2584" }),
2133
+ /* @__PURE__ */ jsx2(Text, { color: mascotColor, children: " \u2590\u2588\u25E6\u25E6\u2588\u258C" }),
2134
+ /* @__PURE__ */ jsx2(Text, { color: mascotColor, children: " \u2590\u258C\u25C6\u25C6\u2590\u258C" }),
2135
+ /* @__PURE__ */ jsx2(Text, { color: mascotColor, children: " \u2590\u258C\u2584\u2584\u2590\u258C" }),
2136
+ /* @__PURE__ */ jsx2(Text, { color: mascotColor, children: " \u2580\u2580\u2580\u2580" })
2137
+ ] })
2126
2138
  ] }),
2127
- /* @__PURE__ */ jsx3(Box2, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ jsx3(Mascot, { mood }) }),
2128
- /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { children: [
2139
+ /* @__PURE__ */ jsx2(Text, { children: " " }),
2140
+ /* @__PURE__ */ jsxs(Text, { children: [
2129
2141
  " ",
2130
- bar.map((d, i) => /* @__PURE__ */ jsx3(Text2, { color: d.color, children: d.char }, i))
2131
- ] }) }),
2132
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, marginLeft: 2, children: [
2133
- /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "model " }),
2134
- model ? /* @__PURE__ */ jsx3(Text2, { color: theme.cyan ?? "#5eead4", bold: true, children: model }) : /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "--" })
2142
+ bar.map((d, i) => /* @__PURE__ */ jsx2(Text, { color: d.color, children: d.char }, i))
2143
+ ] }),
2144
+ /* @__PURE__ */ jsxs(Box, { marginTop: 1, marginLeft: 2, children: [
2145
+ /* @__PURE__ */ jsx2(Text, { color: theme.dim ?? "#7e8ea6", children: "model " }),
2146
+ model ? /* @__PURE__ */ jsx2(Text, { color: theme.cyan ?? "#5eead4", bold: true, children: model }) : /* @__PURE__ */ jsx2(Text, { color: theme.dim ?? "#7e8ea6", children: "--" })
2135
2147
  ] }),
2136
- /* @__PURE__ */ jsxs2(Box2, { marginLeft: 2, children: [
2137
- /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "via " }),
2138
- /* @__PURE__ */ jsx3(Text2, { color: theme.text ?? "#e2e8f0", children: providerName }),
2139
- /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: " " }),
2140
- /* @__PURE__ */ jsx3(Text2, { color: statusColor, children: statusTag })
2148
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 2, children: [
2149
+ /* @__PURE__ */ jsx2(Text, { color: theme.dim ?? "#7e8ea6", children: "via " }),
2150
+ /* @__PURE__ */ jsx2(Text, { color: theme.text ?? "#e2e8f0", children: providerName }),
2151
+ /* @__PURE__ */ jsx2(Text, { color: theme.dim ?? "#7e8ea6", children: " " }),
2152
+ /* @__PURE__ */ jsx2(Text, { color: statusColor, children: statusTag })
2141
2153
  ] }),
2142
- /* @__PURE__ */ jsxs2(Box2, { marginLeft: 2, children: [
2143
- /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "cwd " }),
2144
- /* @__PURE__ */ jsx3(Text2, { color: theme.text ?? "#e2e8f0", children: cwd })
2154
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 2, children: [
2155
+ /* @__PURE__ */ jsx2(Text, { color: theme.dim ?? "#7e8ea6", children: "cwd " }),
2156
+ /* @__PURE__ */ jsx2(Text, { color: theme.text ?? "#e2e8f0", children: cwd })
2145
2157
  ] }),
2146
- /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { children: [
2158
+ /* @__PURE__ */ jsx2(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
2147
2159
  " ",
2148
- bar.map((d, i) => /* @__PURE__ */ jsx3(Text2, { color: d.color, children: d.char }, i))
2160
+ bar.map((d, i) => /* @__PURE__ */ jsx2(Text, { color: d.color, children: d.char }, i))
2149
2161
  ] }) })
2150
2162
  ] });
2151
2163
  });
@@ -2153,8 +2165,8 @@ var Banner = memo2(function Banner2({ model, cwd, providerName, providerOnline,
2153
2165
  // src/components/Messages.tsx
2154
2166
  init_theme();
2155
2167
  init_format();
2156
- import { memo as memo3 } from "react";
2157
- import { Box as Box3, Text as Text3 } from "ink";
2168
+ import { memo as memo2 } from "react";
2169
+ import { Box as Box2, Text as Text2 } from "ink";
2158
2170
 
2159
2171
  // src/utils/markdown.ts
2160
2172
  function renderMarkdown(text) {
@@ -2218,31 +2230,31 @@ function renderInline(text) {
2218
2230
  }
2219
2231
 
2220
2232
  // src/components/Messages.tsx
2221
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2222
- var Messages = memo3(function Messages2({ messages }) {
2223
- return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", children: messages.map((msg) => /* @__PURE__ */ jsx4(MessageRow, { message: msg }, msg.id)) });
2233
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2234
+ var Messages = memo2(function Messages2({ messages }) {
2235
+ return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", children: messages.map((msg) => /* @__PURE__ */ jsx3(MessageRow, { message: msg }, msg.id)) });
2224
2236
  });
2225
- function MessageRow({ message }) {
2237
+ var MessageRow = memo2(function MessageRow2({ message }) {
2226
2238
  switch (message.role) {
2227
2239
  case "user":
2228
- return /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, marginBottom: 1, children: [
2229
- /* @__PURE__ */ jsx4(Text3, { color: theme.cyan, bold: true, children: "\u276F " }),
2230
- /* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.text, children: message.content })
2240
+ return /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, marginBottom: 1, children: [
2241
+ /* @__PURE__ */ jsx3(Text2, { color: theme.cyan, bold: true, children: "\u276F " }),
2242
+ /* @__PURE__ */ jsx3(Text2, { bold: true, color: theme.text, children: message.content })
2231
2243
  ] });
2232
2244
  case "assistant": {
2233
2245
  if (!message.content && message.toolCalls) return null;
2234
2246
  if (!message.content) return null;
2235
2247
  const rendered = renderMarkdown(message.content);
2236
- return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs3(Box3, { children: [
2237
- /* @__PURE__ */ jsx4(Text3, { color: theme.cyan, children: "\u23BF " }),
2238
- /* @__PURE__ */ jsx4(Text3, { wrap: "wrap", children: rendered })
2248
+ return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs2(Box2, { children: [
2249
+ /* @__PURE__ */ jsx3(Text2, { color: theme.cyan, children: "\u23BF " }),
2250
+ /* @__PURE__ */ jsx3(Text2, { wrap: "wrap", children: rendered })
2239
2251
  ] }) });
2240
2252
  }
2241
2253
  case "tool": {
2242
2254
  const lines = message.content.split("\n");
2243
2255
  const preview = lines.length > 8 ? lines.slice(0, 8).join("\n") + `
2244
2256
  ... (${lines.length - 8} more lines)` : message.content;
2245
- return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
2257
+ return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
2246
2258
  "\u23BF ",
2247
2259
  truncate(preview, 1200)
2248
2260
  ] }) });
@@ -2250,46 +2262,47 @@ function MessageRow({ message }) {
2250
2262
  default:
2251
2263
  return null;
2252
2264
  }
2253
- }
2265
+ });
2254
2266
 
2255
2267
  // src/components/ToolCall.tsx
2256
2268
  init_theme();
2257
2269
  init_format();
2258
- import { Box as Box4, Text as Text4 } from "ink";
2259
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2260
- function ActiveToolCall({ toolName, args }) {
2261
- return /* @__PURE__ */ jsxs4(Box4, { marginLeft: 2, children: [
2262
- /* @__PURE__ */ jsx5(Text4, { color: theme.cyan, children: "..." }),
2263
- /* @__PURE__ */ jsxs4(Text4, { bold: true, color: theme.yellow, children: [
2270
+ import { memo as memo3 } from "react";
2271
+ import { Box as Box3, Text as Text3 } from "ink";
2272
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2273
+ var ActiveToolCall = memo3(function ActiveToolCall2({ toolName, args }) {
2274
+ return /* @__PURE__ */ jsxs3(Box3, { marginLeft: 2, children: [
2275
+ /* @__PURE__ */ jsx4(Text3, { color: theme.cyan, children: "..." }),
2276
+ /* @__PURE__ */ jsxs3(Text3, { bold: true, color: theme.yellow, children: [
2264
2277
  " ",
2265
2278
  toolName
2266
2279
  ] }),
2267
- args ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2280
+ args ? /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
2268
2281
  " ",
2269
2282
  formatToolArgs(args)
2270
2283
  ] }) : null
2271
2284
  ] });
2272
- }
2273
- function ToolResultDisplay({ toolName, output, isError }) {
2285
+ });
2286
+ var ToolResultDisplay = memo3(function ToolResultDisplay2({ toolName, output, isError }) {
2274
2287
  const icon = isError ? "\u2718" : "\u2714";
2275
2288
  const iconColor = isError ? theme.pink : theme.green;
2276
2289
  const lines = output.split("\n");
2277
2290
  const preview = lines.length > 6 ? lines.slice(0, 6).join("\n") + `
2278
2291
  ... (${lines.length - 6} more lines)` : output;
2279
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
2280
- /* @__PURE__ */ jsxs4(Box4, { children: [
2281
- /* @__PURE__ */ jsxs4(Text4, { color: iconColor, children: [
2292
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
2293
+ /* @__PURE__ */ jsxs3(Box3, { children: [
2294
+ /* @__PURE__ */ jsxs3(Text3, { color: iconColor, children: [
2282
2295
  icon,
2283
2296
  " "
2284
2297
  ] }),
2285
- /* @__PURE__ */ jsx5(Text4, { bold: true, color: theme.yellow, children: toolName })
2298
+ /* @__PURE__ */ jsx4(Text3, { bold: true, color: theme.yellow, children: toolName })
2286
2299
  ] }),
2287
- /* @__PURE__ */ jsx5(Box4, { marginLeft: 2, children: /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2300
+ /* @__PURE__ */ jsx4(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
2288
2301
  "\u23BF ",
2289
2302
  truncate(preview, 1200)
2290
2303
  ] }) })
2291
2304
  ] });
2292
- }
2305
+ });
2293
2306
  function formatToolArgs(args) {
2294
2307
  const entries = Object.entries(args);
2295
2308
  if (entries.length === 0) return "";
@@ -2306,8 +2319,8 @@ function formatToolArgs(args) {
2306
2319
  init_theme();
2307
2320
  init_state();
2308
2321
  import { memo as memo4 } from "react";
2309
- import { Box as Box5, Text as Text5 } from "ink";
2310
- import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2322
+ import { Box as Box4, Text as Text4 } from "ink";
2323
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2311
2324
  function getContextLimit(model) {
2312
2325
  const lower = model.toLowerCase();
2313
2326
  if (lower.includes("llama3.1")) return 131072;
@@ -2325,40 +2338,40 @@ var StatusLine = memo4(function StatusLine2({ model, messageCount, tokenEstimate
2325
2338
  const pct = (usage * 100).toFixed(0);
2326
2339
  const usageColor = usage > 0.8 ? theme.pink : usage > 0.5 ? theme.yellow : theme.cyan;
2327
2340
  const activeTasks = state2.tasks.filter((t) => t.status === "in_progress").length;
2328
- return /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
2329
- /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
2341
+ return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
2342
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2330
2343
  "\u2500".repeat(2),
2331
2344
  " "
2332
2345
  ] }),
2333
- /* @__PURE__ */ jsx6(Text5, { color: theme.cyan, bold: true, children: model ? model.split(":")[0] : "--" }),
2334
- /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: " \u2502 " }),
2335
- /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
2346
+ /* @__PURE__ */ jsx5(Text4, { color: theme.cyan, bold: true, children: model ? model.split(":")[0] : "--" }),
2347
+ /* @__PURE__ */ jsx5(Text4, { color: theme.dim, children: " \u2502 " }),
2348
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2336
2349
  messageCount,
2337
2350
  " msgs"
2338
2351
  ] }),
2339
- /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: " \u2502 " }),
2340
- /* @__PURE__ */ jsxs5(Text5, { color: usageColor, children: [
2352
+ /* @__PURE__ */ jsx5(Text4, { color: theme.dim, children: " \u2502 " }),
2353
+ /* @__PURE__ */ jsxs4(Text4, { color: usageColor, children: [
2341
2354
  "ctx ",
2342
2355
  pct,
2343
2356
  "%"
2344
2357
  ] }),
2345
- state2.planMode ? /* @__PURE__ */ jsxs5(Fragment, { children: [
2346
- /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: " \u2502 " }),
2347
- /* @__PURE__ */ jsx6(Text5, { color: theme.yellow, bold: true, children: "PLAN" })
2358
+ state2.planMode ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2359
+ /* @__PURE__ */ jsx5(Text4, { color: theme.dim, children: " \u2502 " }),
2360
+ /* @__PURE__ */ jsx5(Text4, { color: theme.yellow, bold: true, children: "PLAN" })
2348
2361
  ] }) : null,
2349
- activeTasks > 0 ? /* @__PURE__ */ jsxs5(Fragment, { children: [
2350
- /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: " \u2502 " }),
2351
- /* @__PURE__ */ jsxs5(Text5, { color: theme.green, children: [
2362
+ activeTasks > 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2363
+ /* @__PURE__ */ jsx5(Text4, { color: theme.dim, children: " \u2502 " }),
2364
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.green, children: [
2352
2365
  activeTasks,
2353
2366
  " task",
2354
2367
  activeTasks > 1 ? "s" : ""
2355
2368
  ] })
2356
2369
  ] }) : null,
2357
- isStreaming ? /* @__PURE__ */ jsxs5(Fragment, { children: [
2358
- /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: " \u2502 " }),
2359
- /* @__PURE__ */ jsx6(Text5, { color: theme.cyan, children: "streaming" })
2370
+ isStreaming ? /* @__PURE__ */ jsxs4(Fragment, { children: [
2371
+ /* @__PURE__ */ jsx5(Text4, { color: theme.dim, children: " \u2502 " }),
2372
+ /* @__PURE__ */ jsx5(Text4, { color: theme.cyan, children: "streaming" })
2360
2373
  ] }) : null,
2361
- /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
2374
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2362
2375
  " ",
2363
2376
  "\u2500".repeat(2)
2364
2377
  ] })
@@ -2368,7 +2381,7 @@ var StatusLine = memo4(function StatusLine2({ model, messageCount, tokenEstimate
2368
2381
  // src/components/UserInput.tsx
2369
2382
  init_theme();
2370
2383
  import { useState, useCallback, memo as memo5 } from "react";
2371
- import { Box as Box6, Text as Text6, useInput } from "ink";
2384
+ import { Box as Box5, Text as Text5, useInput } from "ink";
2372
2385
  import TextInput from "ink-text-input";
2373
2386
 
2374
2387
  // src/commands/help.ts
@@ -3404,7 +3417,7 @@ function getCommandNames() {
3404
3417
  }
3405
3418
 
3406
3419
  // src/components/UserInput.tsx
3407
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3420
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3408
3421
  var UserInput = memo5(function UserInput2({ value, onChange, onSubmit, disabled, history }) {
3409
3422
  const [historyIdx, setHistoryIdx] = useState(-1);
3410
3423
  const [suggestion, setSuggestion] = useState("");
@@ -3453,20 +3466,20 @@ var UserInput = memo5(function UserInput2({ value, onChange, onSubmit, disabled,
3453
3466
  const borderColor = disabled ? theme.dim : isBash ? theme.yellow : isCommand ? theme.purple : theme.cyan;
3454
3467
  const promptChar = isBash ? "!" : "\u276F";
3455
3468
  const promptColor = isBash ? theme.yellow : theme.cyan;
3456
- return /* @__PURE__ */ jsxs6(
3457
- Box6,
3469
+ return /* @__PURE__ */ jsxs5(
3470
+ Box5,
3458
3471
  {
3459
3472
  borderStyle: "round",
3460
3473
  borderColor,
3461
3474
  paddingLeft: 1,
3462
3475
  paddingRight: 1,
3463
3476
  children: [
3464
- /* @__PURE__ */ jsxs6(Text6, { color: disabled ? theme.dim : promptColor, bold: true, children: [
3477
+ /* @__PURE__ */ jsxs5(Text5, { color: disabled ? theme.dim : promptColor, bold: true, children: [
3465
3478
  promptChar,
3466
3479
  " "
3467
3480
  ] }),
3468
- disabled ? /* @__PURE__ */ jsx7(Text6, { color: theme.dim, children: "..." }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
3469
- /* @__PURE__ */ jsx7(
3481
+ disabled ? /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: "..." }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
3482
+ /* @__PURE__ */ jsx6(
3470
3483
  TextInput,
3471
3484
  {
3472
3485
  value,
@@ -3480,7 +3493,7 @@ var UserInput = memo5(function UserInput2({ value, onChange, onSubmit, disabled,
3480
3493
  }
3481
3494
  }
3482
3495
  ),
3483
- suggestion ? /* @__PURE__ */ jsx7(Text6, { color: theme.dim, children: suggestion }) : null
3496
+ suggestion ? /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: suggestion }) : null
3484
3497
  ] })
3485
3498
  ]
3486
3499
  }
@@ -3496,7 +3509,7 @@ init_tools();
3496
3509
  init_bash();
3497
3510
  init_hooks();
3498
3511
  init_theme();
3499
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
3512
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3500
3513
  function getContextLimit3(model) {
3501
3514
  const lower = model.toLowerCase();
3502
3515
  if (lower.includes("llama3.1")) return 131072;
@@ -3507,6 +3520,7 @@ function getContextLimit3(model) {
3507
3520
  if (lower.includes("deepseek")) return 32768;
3508
3521
  return 8192;
3509
3522
  }
3523
+ var STREAM_THROTTLE_MS = 80;
3510
3524
  function REPL({ initialPrompt }) {
3511
3525
  const { model: initialModel, systemPromptOverride, maxTurns, initialMessages, initialSessionId } = useDarkfooContext();
3512
3526
  const { exit } = useApp();
@@ -3526,6 +3540,31 @@ function REPL({ initialPrompt }) {
3526
3540
  const abortRef = useRef(null);
3527
3541
  const hasRun = useRef(false);
3528
3542
  const sessionId = useRef(initialSessionId ?? createSessionId());
3543
+ const streamBufferRef = useRef("");
3544
+ const streamFlushTimer = useRef(null);
3545
+ const startStreamFlush = useCallback2(() => {
3546
+ if (streamFlushTimer.current) return;
3547
+ streamFlushTimer.current = setInterval(() => {
3548
+ const buf = streamBufferRef.current;
3549
+ if (buf) {
3550
+ setStreamingText(buf);
3551
+ }
3552
+ }, STREAM_THROTTLE_MS);
3553
+ }, []);
3554
+ const stopStreamFlush = useCallback2(() => {
3555
+ if (streamFlushTimer.current) {
3556
+ clearInterval(streamFlushTimer.current);
3557
+ streamFlushTimer.current = null;
3558
+ }
3559
+ if (streamBufferRef.current) {
3560
+ setStreamingText(streamBufferRef.current);
3561
+ }
3562
+ }, []);
3563
+ useEffect(() => {
3564
+ return () => {
3565
+ if (streamFlushTimer.current) clearInterval(streamFlushTimer.current);
3566
+ };
3567
+ }, []);
3529
3568
  const messagesRef = useRef(messages);
3530
3569
  messagesRef.current = messages;
3531
3570
  const modelRef = useRef(model);
@@ -3534,6 +3573,7 @@ function REPL({ initialPrompt }) {
3534
3573
  systemPromptRef.current = systemPrompt;
3535
3574
  const tools = getTools();
3536
3575
  const cwd = process.cwd();
3576
+ const providerName = useMemo(() => getActiveProviderName(), [model, providerOnline]);
3537
3577
  useEffect(() => {
3538
3578
  if (systemPromptOverride) {
3539
3579
  setSystemPrompt(systemPromptOverride);
@@ -3606,6 +3646,7 @@ function REPL({ initialPrompt }) {
3606
3646
  };
3607
3647
  setMessages((prev) => [...prev, userMsg]);
3608
3648
  setIsStreaming(true);
3649
+ streamBufferRef.current = "";
3609
3650
  setStreamingText("");
3610
3651
  setToolResults([]);
3611
3652
  setCommandOutput(null);
@@ -3615,6 +3656,8 @@ function REPL({ initialPrompt }) {
3615
3656
  const allMessages = [...messagesRef.current, userMsg];
3616
3657
  const currentModel = modelRef.current;
3617
3658
  const currentSystemPrompt = systemPromptRef.current;
3659
+ let receivedFirstText = false;
3660
+ startStreamFlush();
3618
3661
  try {
3619
3662
  for await (const event of query({
3620
3663
  model: currentModel,
@@ -3627,10 +3670,14 @@ function REPL({ initialPrompt }) {
3627
3670
  if (controller.signal.aborted) break;
3628
3671
  switch (event.type) {
3629
3672
  case "text_delta":
3630
- setStreamingText((prev) => prev + event.text);
3631
- setMascotMood("idle");
3673
+ streamBufferRef.current += event.text;
3674
+ if (!receivedFirstText) {
3675
+ receivedFirstText = true;
3676
+ setMascotMood("idle");
3677
+ }
3632
3678
  break;
3633
3679
  case "tool_call":
3680
+ stopStreamFlush();
3634
3681
  setActiveTool({ name: event.toolCall.function.name, args: event.toolCall.function.arguments });
3635
3682
  setMascotMood("working");
3636
3683
  break;
@@ -3649,15 +3696,20 @@ function REPL({ initialPrompt }) {
3649
3696
  }));
3650
3697
  break;
3651
3698
  case "assistant_message":
3699
+ stopStreamFlush();
3652
3700
  setMessages((prev) => {
3653
3701
  const updated = [...prev, event.message];
3654
3702
  saveSession(sessionId.current, updated, currentModel, cwd).catch(() => {
3655
3703
  });
3656
3704
  return updated;
3657
3705
  });
3706
+ streamBufferRef.current = "";
3658
3707
  setStreamingText("");
3708
+ receivedFirstText = false;
3709
+ startStreamFlush();
3659
3710
  break;
3660
3711
  case "error":
3712
+ stopStreamFlush();
3661
3713
  setMascotMood("error");
3662
3714
  setMessages((prev) => [
3663
3715
  ...prev,
@@ -3675,6 +3727,8 @@ function REPL({ initialPrompt }) {
3675
3727
  ]);
3676
3728
  }
3677
3729
  } finally {
3730
+ stopStreamFlush();
3731
+ streamBufferRef.current = "";
3678
3732
  setIsStreaming(false);
3679
3733
  setStreamingText("");
3680
3734
  setActiveTool(null);
@@ -3684,7 +3738,7 @@ function REPL({ initialPrompt }) {
3684
3738
  abortRef.current = null;
3685
3739
  }
3686
3740
  },
3687
- [tools, cwd]
3741
+ [tools, cwd, startStreamFlush, stopStreamFlush]
3688
3742
  );
3689
3743
  const handleSubmit = useCallback2(
3690
3744
  async (value) => {
@@ -3790,22 +3844,21 @@ ${result.content}
3790
3844
  }
3791
3845
  }
3792
3846
  });
3793
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", padding: 1, children: [
3794
- /* @__PURE__ */ jsx8(Banner, { model, cwd, providerName: getActiveProviderName(), providerOnline, mood: mascotMood }),
3795
- /* @__PURE__ */ jsx8(Messages, { messages }),
3796
- commandOutput ? /* @__PURE__ */ jsx8(Box7, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx8(Text7, { children: commandOutput }) }) : null,
3797
- toolResults.map((tr) => /* @__PURE__ */ jsx8(ToolResultDisplay, { toolName: tr.toolName, output: tr.output, isError: tr.isError }, tr.id)),
3798
- activeTool ? /* @__PURE__ */ jsx8(ActiveToolCall, { toolName: activeTool.name, args: activeTool.args }) : null,
3799
- isStreaming && streamingText ? /* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, children: [
3800
- /* @__PURE__ */ jsx8(Text7, { color: theme.cyan, children: "\u23BF " }),
3801
- /* @__PURE__ */ jsx8(Text7, { color: theme.text, wrap: "wrap", children: streamingText }),
3802
- /* @__PURE__ */ jsx8(Text7, { color: theme.dim, children: " ..." })
3847
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
3848
+ /* @__PURE__ */ jsx7(Banner, { model, cwd, providerName, providerOnline, mood: mascotMood }),
3849
+ /* @__PURE__ */ jsx7(Messages, { messages }),
3850
+ commandOutput ? /* @__PURE__ */ jsx7(Box6, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx7(Text6, { children: commandOutput }) }) : null,
3851
+ toolResults.map((tr) => /* @__PURE__ */ jsx7(ToolResultDisplay, { toolName: tr.toolName, output: tr.output, isError: tr.isError }, tr.id)),
3852
+ activeTool ? /* @__PURE__ */ jsx7(ActiveToolCall, { toolName: activeTool.name, args: activeTool.args }) : null,
3853
+ isStreaming && streamingText ? /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, children: [
3854
+ /* @__PURE__ */ jsx7(Text6, { color: theme.cyan, children: "\u23BF " }),
3855
+ /* @__PURE__ */ jsx7(Text6, { color: theme.text, wrap: "wrap", children: streamingText })
3803
3856
  ] }) : null,
3804
- isStreaming && !streamingText && !activeTool ? /* @__PURE__ */ jsxs7(Box7, { marginLeft: 2, children: [
3805
- /* @__PURE__ */ jsx8(Text7, { color: theme.cyan, children: "..." }),
3806
- /* @__PURE__ */ jsx8(Text7, { color: theme.dim, children: " Thinking" })
3857
+ isStreaming && !streamingText && !activeTool ? /* @__PURE__ */ jsxs6(Box6, { marginLeft: 2, children: [
3858
+ /* @__PURE__ */ jsx7(Text6, { color: theme.cyan, children: "..." }),
3859
+ /* @__PURE__ */ jsx7(Text6, { color: theme.dim, children: " Thinking" })
3807
3860
  ] }) : null,
3808
- /* @__PURE__ */ jsx8(
3861
+ /* @__PURE__ */ jsx7(
3809
3862
  UserInput,
3810
3863
  {
3811
3864
  value: inputValue,
@@ -3815,7 +3868,7 @@ ${result.content}
3815
3868
  history: inputHistory
3816
3869
  }
3817
3870
  ),
3818
- /* @__PURE__ */ jsx8(
3871
+ /* @__PURE__ */ jsx7(
3819
3872
  StatusLine,
3820
3873
  {
3821
3874
  model,
@@ -3829,9 +3882,9 @@ ${result.content}
3829
3882
 
3830
3883
  // src/main.tsx
3831
3884
  init_providers();
3832
- import { jsx as jsx9 } from "react/jsx-runtime";
3885
+ import { jsx as jsx8 } from "react/jsx-runtime";
3833
3886
  var program = new Command();
3834
- program.name("darkfoo").description("Darkfoo Code \u2014 local AI coding assistant powered by local LLM providers").version("0.3.0").option("-m, --model <model>", "Model to use", "llama3.1:8b").option("-p, --prompt <prompt>", "Run a single prompt (non-interactive)").option("-c, --continue", "Resume the most recent session").option("--resume <id>", "Resume a specific session by ID").option("--max-turns <n>", "Maximum tool-use turns per query", "30").option("--debug", "Enable debug logging to stderr").option("--output-format <format>", "Output format for non-interactive mode (text, json)").option("--provider <name>", "LLM provider backend (ollama, llama-cpp, vllm, tgi, etc.)").option("--system-prompt <prompt>", "Override the system prompt").action(async (options) => {
3887
+ program.name("darkfoo").description("Darkfoo Code \u2014 local AI coding assistant powered by local LLM providers").version("0.4.1").option("-m, --model <model>", "Model to use", "llama3.1:8b").option("-p, --prompt <prompt>", "Run a single prompt (non-interactive)").option("-c, --continue", "Resume the most recent session").option("--resume <id>", "Resume a specific session by ID").option("--max-turns <n>", "Maximum tool-use turns per query", "30").option("--debug", "Enable debug logging to stderr").option("--output-format <format>", "Output format for non-interactive mode (text, json)").option("--provider <name>", "LLM provider backend (ollama, llama-cpp, vllm, tgi, etc.)").option("--system-prompt <prompt>", "Override the system prompt").action(async (options) => {
3835
3888
  const { model, prompt, provider, systemPrompt } = options;
3836
3889
  if (options.debug) {
3837
3890
  const { setDebugMode: setDebugMode2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
@@ -3966,7 +4019,7 @@ Error: ${event.error}
3966
4019
  }
3967
4020
  }
3968
4021
  const { waitUntilExit } = render(
3969
- /* @__PURE__ */ jsx9(
4022
+ /* @__PURE__ */ jsx8(
3970
4023
  App,
3971
4024
  {
3972
4025
  model: resolvedModel,
@@ -3974,7 +4027,7 @@ Error: ${event.error}
3974
4027
  maxTurns,
3975
4028
  initialMessages,
3976
4029
  initialSessionId,
3977
- children: /* @__PURE__ */ jsx9(REPL, {})
4030
+ children: /* @__PURE__ */ jsx8(REPL, {})
3978
4031
  }
3979
4032
  )
3980
4033
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkfoo-code",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Darkfoo Code — local AI coding assistant powered by Ollama, vLLM, llama.cpp, and other LLM providers",
5
5
  "type": "module",
6
6
  "license": "MIT",