darkfoo-code 0.4.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 +60 -19
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2068,7 +2068,7 @@ function App({ model, systemPromptOverride, maxTurns, initialMessages, initialSe
2068
2068
  }
2069
2069
 
2070
2070
  // src/repl.tsx
2071
- import { useState as useState2, useCallback as useCallback2, useEffect, useRef } from "react";
2071
+ import { useState as useState2, useCallback as useCallback2, useEffect, useRef, useMemo } from "react";
2072
2072
  import { Box as Box6, Text as Text6, useApp, useInput as useInput2 } from "ink";
2073
2073
  import { nanoid as nanoid6 } from "nanoid";
2074
2074
 
@@ -2077,7 +2077,7 @@ init_theme();
2077
2077
  import { memo } from "react";
2078
2078
  import { Box, Text } from "ink";
2079
2079
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
2080
- var version = "0.4.0";
2080
+ var version = "0.4.1";
2081
2081
  var LOGO_LINES = [
2082
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 ",
2083
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",
@@ -2234,7 +2234,7 @@ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2234
2234
  var Messages = memo2(function Messages2({ messages }) {
2235
2235
  return /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", children: messages.map((msg) => /* @__PURE__ */ jsx3(MessageRow, { message: msg }, msg.id)) });
2236
2236
  });
2237
- function MessageRow({ message }) {
2237
+ var MessageRow = memo2(function MessageRow2({ message }) {
2238
2238
  switch (message.role) {
2239
2239
  case "user":
2240
2240
  return /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, marginBottom: 1, children: [
@@ -2262,14 +2262,15 @@ function MessageRow({ message }) {
2262
2262
  default:
2263
2263
  return null;
2264
2264
  }
2265
- }
2265
+ });
2266
2266
 
2267
2267
  // src/components/ToolCall.tsx
2268
2268
  init_theme();
2269
2269
  init_format();
2270
+ import { memo as memo3 } from "react";
2270
2271
  import { Box as Box3, Text as Text3 } from "ink";
2271
2272
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2272
- function ActiveToolCall({ toolName, args }) {
2273
+ var ActiveToolCall = memo3(function ActiveToolCall2({ toolName, args }) {
2273
2274
  return /* @__PURE__ */ jsxs3(Box3, { marginLeft: 2, children: [
2274
2275
  /* @__PURE__ */ jsx4(Text3, { color: theme.cyan, children: "..." }),
2275
2276
  /* @__PURE__ */ jsxs3(Text3, { bold: true, color: theme.yellow, children: [
@@ -2281,8 +2282,8 @@ function ActiveToolCall({ toolName, args }) {
2281
2282
  formatToolArgs(args)
2282
2283
  ] }) : null
2283
2284
  ] });
2284
- }
2285
- function ToolResultDisplay({ toolName, output, isError }) {
2285
+ });
2286
+ var ToolResultDisplay = memo3(function ToolResultDisplay2({ toolName, output, isError }) {
2286
2287
  const icon = isError ? "\u2718" : "\u2714";
2287
2288
  const iconColor = isError ? theme.pink : theme.green;
2288
2289
  const lines = output.split("\n");
@@ -2301,7 +2302,7 @@ function ToolResultDisplay({ toolName, output, isError }) {
2301
2302
  truncate(preview, 1200)
2302
2303
  ] }) })
2303
2304
  ] });
2304
- }
2305
+ });
2305
2306
  function formatToolArgs(args) {
2306
2307
  const entries = Object.entries(args);
2307
2308
  if (entries.length === 0) return "";
@@ -2317,7 +2318,7 @@ function formatToolArgs(args) {
2317
2318
  // src/components/StatusLine.tsx
2318
2319
  init_theme();
2319
2320
  init_state();
2320
- import { memo as memo3 } from "react";
2321
+ import { memo as memo4 } from "react";
2321
2322
  import { Box as Box4, Text as Text4 } from "ink";
2322
2323
  import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2323
2324
  function getContextLimit(model) {
@@ -2330,7 +2331,7 @@ function getContextLimit(model) {
2330
2331
  if (lower.includes("deepseek")) return 32768;
2331
2332
  return 8192;
2332
2333
  }
2333
- var StatusLine = memo3(function StatusLine2({ model, messageCount, tokenEstimate, isStreaming }) {
2334
+ var StatusLine = memo4(function StatusLine2({ model, messageCount, tokenEstimate, isStreaming }) {
2334
2335
  const state2 = getAppState();
2335
2336
  const contextLimit = getContextLimit(model);
2336
2337
  const usage = Math.min(tokenEstimate / contextLimit, 1);
@@ -2379,7 +2380,7 @@ var StatusLine = memo3(function StatusLine2({ model, messageCount, tokenEstimate
2379
2380
 
2380
2381
  // src/components/UserInput.tsx
2381
2382
  init_theme();
2382
- import { useState, useCallback, memo as memo4 } from "react";
2383
+ import { useState, useCallback, memo as memo5 } from "react";
2383
2384
  import { Box as Box5, Text as Text5, useInput } from "ink";
2384
2385
  import TextInput from "ink-text-input";
2385
2386
 
@@ -3417,7 +3418,7 @@ function getCommandNames() {
3417
3418
 
3418
3419
  // src/components/UserInput.tsx
3419
3420
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3420
- var UserInput = memo4(function UserInput2({ value, onChange, onSubmit, disabled, history }) {
3421
+ var UserInput = memo5(function UserInput2({ value, onChange, onSubmit, disabled, history }) {
3421
3422
  const [historyIdx, setHistoryIdx] = useState(-1);
3422
3423
  const [suggestion, setSuggestion] = useState("");
3423
3424
  useInput((_input, key) => {
@@ -3519,6 +3520,7 @@ function getContextLimit3(model) {
3519
3520
  if (lower.includes("deepseek")) return 32768;
3520
3521
  return 8192;
3521
3522
  }
3523
+ var STREAM_THROTTLE_MS = 80;
3522
3524
  function REPL({ initialPrompt }) {
3523
3525
  const { model: initialModel, systemPromptOverride, maxTurns, initialMessages, initialSessionId } = useDarkfooContext();
3524
3526
  const { exit } = useApp();
@@ -3538,6 +3540,31 @@ function REPL({ initialPrompt }) {
3538
3540
  const abortRef = useRef(null);
3539
3541
  const hasRun = useRef(false);
3540
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
+ }, []);
3541
3568
  const messagesRef = useRef(messages);
3542
3569
  messagesRef.current = messages;
3543
3570
  const modelRef = useRef(model);
@@ -3546,6 +3573,7 @@ function REPL({ initialPrompt }) {
3546
3573
  systemPromptRef.current = systemPrompt;
3547
3574
  const tools = getTools();
3548
3575
  const cwd = process.cwd();
3576
+ const providerName = useMemo(() => getActiveProviderName(), [model, providerOnline]);
3549
3577
  useEffect(() => {
3550
3578
  if (systemPromptOverride) {
3551
3579
  setSystemPrompt(systemPromptOverride);
@@ -3618,6 +3646,7 @@ function REPL({ initialPrompt }) {
3618
3646
  };
3619
3647
  setMessages((prev) => [...prev, userMsg]);
3620
3648
  setIsStreaming(true);
3649
+ streamBufferRef.current = "";
3621
3650
  setStreamingText("");
3622
3651
  setToolResults([]);
3623
3652
  setCommandOutput(null);
@@ -3627,6 +3656,8 @@ function REPL({ initialPrompt }) {
3627
3656
  const allMessages = [...messagesRef.current, userMsg];
3628
3657
  const currentModel = modelRef.current;
3629
3658
  const currentSystemPrompt = systemPromptRef.current;
3659
+ let receivedFirstText = false;
3660
+ startStreamFlush();
3630
3661
  try {
3631
3662
  for await (const event of query({
3632
3663
  model: currentModel,
@@ -3639,10 +3670,14 @@ function REPL({ initialPrompt }) {
3639
3670
  if (controller.signal.aborted) break;
3640
3671
  switch (event.type) {
3641
3672
  case "text_delta":
3642
- setStreamingText((prev) => prev + event.text);
3643
- setMascotMood("idle");
3673
+ streamBufferRef.current += event.text;
3674
+ if (!receivedFirstText) {
3675
+ receivedFirstText = true;
3676
+ setMascotMood("idle");
3677
+ }
3644
3678
  break;
3645
3679
  case "tool_call":
3680
+ stopStreamFlush();
3646
3681
  setActiveTool({ name: event.toolCall.function.name, args: event.toolCall.function.arguments });
3647
3682
  setMascotMood("working");
3648
3683
  break;
@@ -3661,15 +3696,20 @@ function REPL({ initialPrompt }) {
3661
3696
  }));
3662
3697
  break;
3663
3698
  case "assistant_message":
3699
+ stopStreamFlush();
3664
3700
  setMessages((prev) => {
3665
3701
  const updated = [...prev, event.message];
3666
3702
  saveSession(sessionId.current, updated, currentModel, cwd).catch(() => {
3667
3703
  });
3668
3704
  return updated;
3669
3705
  });
3706
+ streamBufferRef.current = "";
3670
3707
  setStreamingText("");
3708
+ receivedFirstText = false;
3709
+ startStreamFlush();
3671
3710
  break;
3672
3711
  case "error":
3712
+ stopStreamFlush();
3673
3713
  setMascotMood("error");
3674
3714
  setMessages((prev) => [
3675
3715
  ...prev,
@@ -3687,6 +3727,8 @@ function REPL({ initialPrompt }) {
3687
3727
  ]);
3688
3728
  }
3689
3729
  } finally {
3730
+ stopStreamFlush();
3731
+ streamBufferRef.current = "";
3690
3732
  setIsStreaming(false);
3691
3733
  setStreamingText("");
3692
3734
  setActiveTool(null);
@@ -3696,7 +3738,7 @@ function REPL({ initialPrompt }) {
3696
3738
  abortRef.current = null;
3697
3739
  }
3698
3740
  },
3699
- [tools, cwd]
3741
+ [tools, cwd, startStreamFlush, stopStreamFlush]
3700
3742
  );
3701
3743
  const handleSubmit = useCallback2(
3702
3744
  async (value) => {
@@ -3803,15 +3845,14 @@ ${result.content}
3803
3845
  }
3804
3846
  });
3805
3847
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", padding: 1, children: [
3806
- /* @__PURE__ */ jsx7(Banner, { model, cwd, providerName: getActiveProviderName(), providerOnline, mood: mascotMood }),
3848
+ /* @__PURE__ */ jsx7(Banner, { model, cwd, providerName, providerOnline, mood: mascotMood }),
3807
3849
  /* @__PURE__ */ jsx7(Messages, { messages }),
3808
3850
  commandOutput ? /* @__PURE__ */ jsx7(Box6, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx7(Text6, { children: commandOutput }) }) : null,
3809
3851
  toolResults.map((tr) => /* @__PURE__ */ jsx7(ToolResultDisplay, { toolName: tr.toolName, output: tr.output, isError: tr.isError }, tr.id)),
3810
3852
  activeTool ? /* @__PURE__ */ jsx7(ActiveToolCall, { toolName: activeTool.name, args: activeTool.args }) : null,
3811
3853
  isStreaming && streamingText ? /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, children: [
3812
3854
  /* @__PURE__ */ jsx7(Text6, { color: theme.cyan, children: "\u23BF " }),
3813
- /* @__PURE__ */ jsx7(Text6, { color: theme.text, wrap: "wrap", children: streamingText }),
3814
- /* @__PURE__ */ jsx7(Text6, { color: theme.dim, children: " ..." })
3855
+ /* @__PURE__ */ jsx7(Text6, { color: theme.text, wrap: "wrap", children: streamingText })
3815
3856
  ] }) : null,
3816
3857
  isStreaming && !streamingText && !activeTool ? /* @__PURE__ */ jsxs6(Box6, { marginLeft: 2, children: [
3817
3858
  /* @__PURE__ */ jsx7(Text6, { color: theme.cyan, children: "..." }),
@@ -3843,7 +3884,7 @@ ${result.content}
3843
3884
  init_providers();
3844
3885
  import { jsx as jsx8 } from "react/jsx-runtime";
3845
3886
  var program = new Command();
3846
- program.name("darkfoo").description("Darkfoo Code \u2014 local AI coding assistant powered by local LLM providers").version("0.4.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) => {
3847
3888
  const { model, prompt, provider, systemPrompt } = options;
3848
3889
  if (options.debug) {
3849
3890
  const { setDebugMode: setDebugMode2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkfoo-code",
3
- "version": "0.4.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",