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.
- package/dist/main.js +60 -19
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
3643
|
-
|
|
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
|
|
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.
|
|
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));
|