darkfoo-code 0.4.1 → 0.4.3
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 +289 -249
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2068,8 +2068,8 @@ function App({ model, systemPromptOverride, maxTurns, initialMessages, initialSe
|
|
|
2068
2068
|
}
|
|
2069
2069
|
|
|
2070
2070
|
// src/repl.tsx
|
|
2071
|
-
import { useState as
|
|
2072
|
-
import { Box as Box6, Text as Text6, useApp, useInput as useInput2 } from "ink";
|
|
2071
|
+
import { useState as useState3, useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useMemo } from "react";
|
|
2072
|
+
import { Box as Box6, Text as Text6, Static, useApp, useInput as useInput2 } from "ink";
|
|
2073
2073
|
import { nanoid as nanoid6 } from "nanoid";
|
|
2074
2074
|
|
|
2075
2075
|
// src/components/Banner.tsx
|
|
@@ -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.3";
|
|
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",
|
|
@@ -2162,107 +2162,56 @@ var Banner = memo(function Banner2({ model, cwd, providerName, providerOnline, m
|
|
|
2162
2162
|
] });
|
|
2163
2163
|
});
|
|
2164
2164
|
|
|
2165
|
-
// src/components/
|
|
2165
|
+
// src/components/StreamingDisplay.tsx
|
|
2166
2166
|
init_theme();
|
|
2167
|
-
|
|
2168
|
-
import { memo as memo2 } from "react";
|
|
2167
|
+
import { useState, useRef, useEffect, useImperativeHandle, forwardRef, memo as memo2 } from "react";
|
|
2169
2168
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
2170
|
-
|
|
2171
|
-
// src/utils/markdown.ts
|
|
2172
|
-
function renderMarkdown(text) {
|
|
2173
|
-
const lines = text.split("\n");
|
|
2174
|
-
const result = [];
|
|
2175
|
-
let inCodeBlock = false;
|
|
2176
|
-
let codeBlockLang = "";
|
|
2177
|
-
for (const line of lines) {
|
|
2178
|
-
if (line.trimStart().startsWith("```")) {
|
|
2179
|
-
if (!inCodeBlock) {
|
|
2180
|
-
codeBlockLang = line.trimStart().slice(3).trim();
|
|
2181
|
-
const label = codeBlockLang ? ` ${codeBlockLang}` : "";
|
|
2182
|
-
result.push(`\x1B[2m\x1B[38;2;94;234;212m\u2500\u2500\u2500 code${label} \u2500\u2500\u2500\x1B[0m`);
|
|
2183
|
-
inCodeBlock = true;
|
|
2184
|
-
} else {
|
|
2185
|
-
result.push(`\x1B[2m\x1B[38;2;94;234;212m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m`);
|
|
2186
|
-
inCodeBlock = false;
|
|
2187
|
-
codeBlockLang = "";
|
|
2188
|
-
}
|
|
2189
|
-
continue;
|
|
2190
|
-
}
|
|
2191
|
-
if (inCodeBlock) {
|
|
2192
|
-
result.push(`\x1B[38;2;251;191;36m ${line}\x1B[0m`);
|
|
2193
|
-
continue;
|
|
2194
|
-
}
|
|
2195
|
-
const headerMatch = line.match(/^(#{1,6})\s+(.+)/);
|
|
2196
|
-
if (headerMatch) {
|
|
2197
|
-
const level = headerMatch[1].length;
|
|
2198
|
-
const text2 = headerMatch[2];
|
|
2199
|
-
if (level <= 2) {
|
|
2200
|
-
result.push(`\x1B[1m\x1B[38;2;94;234;212m${text2}\x1B[0m`);
|
|
2201
|
-
} else {
|
|
2202
|
-
result.push(`\x1B[1m${text2}\x1B[0m`);
|
|
2203
|
-
}
|
|
2204
|
-
continue;
|
|
2205
|
-
}
|
|
2206
|
-
const listMatch = line.match(/^(\s*)[*-]\s+(.+)/);
|
|
2207
|
-
if (listMatch) {
|
|
2208
|
-
const indent = listMatch[1] || "";
|
|
2209
|
-
const content = renderInline(listMatch[2]);
|
|
2210
|
-
result.push(`${indent}\x1B[38;2;94;234;212m\u2022\x1B[0m ${content}`);
|
|
2211
|
-
continue;
|
|
2212
|
-
}
|
|
2213
|
-
const orderedMatch = line.match(/^(\s*)\d+\.\s+(.+)/);
|
|
2214
|
-
if (orderedMatch) {
|
|
2215
|
-
const indent = orderedMatch[1] || "";
|
|
2216
|
-
const content = renderInline(orderedMatch[2]);
|
|
2217
|
-
result.push(`${indent}${content}`);
|
|
2218
|
-
continue;
|
|
2219
|
-
}
|
|
2220
|
-
if (/^---+$|^\*\*\*+$|^___+$/.test(line.trim())) {
|
|
2221
|
-
result.push(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`);
|
|
2222
|
-
continue;
|
|
2223
|
-
}
|
|
2224
|
-
result.push(renderInline(line));
|
|
2225
|
-
}
|
|
2226
|
-
return result.join("\n");
|
|
2227
|
-
}
|
|
2228
|
-
function renderInline(text) {
|
|
2229
|
-
return text.replace(/`([^`]+)`/g, "\x1B[38;2;251;191;36m$1\x1B[0m").replace(/\*\*\*(.+?)\*\*\*/g, "\x1B[1m\x1B[3m$1\x1B[0m").replace(/\*\*(.+?)\*\*/g, "\x1B[1m$1\x1B[0m").replace(/\*(.+?)\*/g, "\x1B[3m$1\x1B[0m").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "\x1B[4m\x1B[38;2;94;234;212m$1\x1B[0m");
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
// src/components/Messages.tsx
|
|
2233
2169
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2234
|
-
var
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2170
|
+
var FLUSH_INTERVAL_MS = 150;
|
|
2171
|
+
var StreamingDisplay = memo2(forwardRef(function StreamingDisplay2(_, ref) {
|
|
2172
|
+
const bufferRef = useRef("");
|
|
2173
|
+
const [displayText, setDisplayText] = useState("");
|
|
2174
|
+
const timerRef = useRef(null);
|
|
2175
|
+
function startTimer() {
|
|
2176
|
+
if (timerRef.current) return;
|
|
2177
|
+
timerRef.current = setInterval(() => {
|
|
2178
|
+
const buf = bufferRef.current;
|
|
2179
|
+
if (!buf) return;
|
|
2180
|
+
const lastNL = buf.lastIndexOf("\n");
|
|
2181
|
+
setDisplayText(lastNL >= 0 ? buf.substring(0, lastNL + 1) : buf);
|
|
2182
|
+
}, FLUSH_INTERVAL_MS);
|
|
2183
|
+
}
|
|
2184
|
+
function stopTimer() {
|
|
2185
|
+
if (timerRef.current) {
|
|
2186
|
+
clearInterval(timerRef.current);
|
|
2187
|
+
timerRef.current = null;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
useImperativeHandle(ref, () => ({
|
|
2191
|
+
append(text) {
|
|
2192
|
+
bufferRef.current += text;
|
|
2193
|
+
startTimer();
|
|
2194
|
+
},
|
|
2195
|
+
clear() {
|
|
2196
|
+
stopTimer();
|
|
2197
|
+
if (bufferRef.current) {
|
|
2198
|
+
setDisplayText("");
|
|
2199
|
+
}
|
|
2200
|
+
bufferRef.current = "";
|
|
2201
|
+
},
|
|
2202
|
+
getText() {
|
|
2203
|
+
return bufferRef.current;
|
|
2261
2204
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
});
|
|
2205
|
+
}));
|
|
2206
|
+
useEffect(() => {
|
|
2207
|
+
return () => stopTimer();
|
|
2208
|
+
}, []);
|
|
2209
|
+
if (!displayText) return null;
|
|
2210
|
+
return /* @__PURE__ */ jsxs2(Box2, { marginBottom: 1, children: [
|
|
2211
|
+
/* @__PURE__ */ jsx3(Text2, { color: theme.cyan, children: "\u23BF " }),
|
|
2212
|
+
/* @__PURE__ */ jsx3(Text2, { color: theme.text, wrap: "wrap", children: displayText })
|
|
2213
|
+
] });
|
|
2214
|
+
}));
|
|
2266
2215
|
|
|
2267
2216
|
// src/components/ToolCall.tsx
|
|
2268
2217
|
init_theme();
|
|
@@ -2380,7 +2329,7 @@ var StatusLine = memo4(function StatusLine2({ model, messageCount, tokenEstimate
|
|
|
2380
2329
|
|
|
2381
2330
|
// src/components/UserInput.tsx
|
|
2382
2331
|
init_theme();
|
|
2383
|
-
import { useState, useCallback, memo as memo5 } from "react";
|
|
2332
|
+
import { useState as useState2, useCallback, memo as memo5 } from "react";
|
|
2384
2333
|
import { Box as Box5, Text as Text5, useInput } from "ink";
|
|
2385
2334
|
import TextInput from "ink-text-input";
|
|
2386
2335
|
|
|
@@ -3419,8 +3368,8 @@ function getCommandNames() {
|
|
|
3419
3368
|
// src/components/UserInput.tsx
|
|
3420
3369
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3421
3370
|
var UserInput = memo5(function UserInput2({ value, onChange, onSubmit, disabled, history }) {
|
|
3422
|
-
const [historyIdx, setHistoryIdx] =
|
|
3423
|
-
const [suggestion, setSuggestion] =
|
|
3371
|
+
const [historyIdx, setHistoryIdx] = useState2(-1);
|
|
3372
|
+
const [suggestion, setSuggestion] = useState2("");
|
|
3424
3373
|
useInput((_input, key) => {
|
|
3425
3374
|
if (disabled || !history || history.length === 0) return;
|
|
3426
3375
|
if (key.upArrow) {
|
|
@@ -3509,7 +3458,71 @@ init_tools();
|
|
|
3509
3458
|
init_bash();
|
|
3510
3459
|
init_hooks();
|
|
3511
3460
|
init_theme();
|
|
3512
|
-
|
|
3461
|
+
|
|
3462
|
+
// src/utils/markdown.ts
|
|
3463
|
+
function renderMarkdown(text) {
|
|
3464
|
+
const lines = text.split("\n");
|
|
3465
|
+
const result = [];
|
|
3466
|
+
let inCodeBlock = false;
|
|
3467
|
+
let codeBlockLang = "";
|
|
3468
|
+
for (const line of lines) {
|
|
3469
|
+
if (line.trimStart().startsWith("```")) {
|
|
3470
|
+
if (!inCodeBlock) {
|
|
3471
|
+
codeBlockLang = line.trimStart().slice(3).trim();
|
|
3472
|
+
const label = codeBlockLang ? ` ${codeBlockLang}` : "";
|
|
3473
|
+
result.push(`\x1B[2m\x1B[38;2;94;234;212m\u2500\u2500\u2500 code${label} \u2500\u2500\u2500\x1B[0m`);
|
|
3474
|
+
inCodeBlock = true;
|
|
3475
|
+
} else {
|
|
3476
|
+
result.push(`\x1B[2m\x1B[38;2;94;234;212m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m`);
|
|
3477
|
+
inCodeBlock = false;
|
|
3478
|
+
codeBlockLang = "";
|
|
3479
|
+
}
|
|
3480
|
+
continue;
|
|
3481
|
+
}
|
|
3482
|
+
if (inCodeBlock) {
|
|
3483
|
+
result.push(`\x1B[38;2;251;191;36m ${line}\x1B[0m`);
|
|
3484
|
+
continue;
|
|
3485
|
+
}
|
|
3486
|
+
const headerMatch = line.match(/^(#{1,6})\s+(.+)/);
|
|
3487
|
+
if (headerMatch) {
|
|
3488
|
+
const level = headerMatch[1].length;
|
|
3489
|
+
const text2 = headerMatch[2];
|
|
3490
|
+
if (level <= 2) {
|
|
3491
|
+
result.push(`\x1B[1m\x1B[38;2;94;234;212m${text2}\x1B[0m`);
|
|
3492
|
+
} else {
|
|
3493
|
+
result.push(`\x1B[1m${text2}\x1B[0m`);
|
|
3494
|
+
}
|
|
3495
|
+
continue;
|
|
3496
|
+
}
|
|
3497
|
+
const listMatch = line.match(/^(\s*)[*-]\s+(.+)/);
|
|
3498
|
+
if (listMatch) {
|
|
3499
|
+
const indent = listMatch[1] || "";
|
|
3500
|
+
const content = renderInline(listMatch[2]);
|
|
3501
|
+
result.push(`${indent}\x1B[38;2;94;234;212m\u2022\x1B[0m ${content}`);
|
|
3502
|
+
continue;
|
|
3503
|
+
}
|
|
3504
|
+
const orderedMatch = line.match(/^(\s*)\d+\.\s+(.+)/);
|
|
3505
|
+
if (orderedMatch) {
|
|
3506
|
+
const indent = orderedMatch[1] || "";
|
|
3507
|
+
const content = renderInline(orderedMatch[2]);
|
|
3508
|
+
result.push(`${indent}${content}`);
|
|
3509
|
+
continue;
|
|
3510
|
+
}
|
|
3511
|
+
if (/^---+$|^\*\*\*+$|^___+$/.test(line.trim())) {
|
|
3512
|
+
result.push(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`);
|
|
3513
|
+
continue;
|
|
3514
|
+
}
|
|
3515
|
+
result.push(renderInline(line));
|
|
3516
|
+
}
|
|
3517
|
+
return result.join("\n");
|
|
3518
|
+
}
|
|
3519
|
+
function renderInline(text) {
|
|
3520
|
+
return text.replace(/`([^`]+)`/g, "\x1B[38;2;251;191;36m$1\x1B[0m").replace(/\*\*\*(.+?)\*\*\*/g, "\x1B[1m\x1B[3m$1\x1B[0m").replace(/\*\*(.+?)\*\*/g, "\x1B[1m$1\x1B[0m").replace(/\*(.+?)\*/g, "\x1B[3m$1\x1B[0m").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "\x1B[4m\x1B[38;2;94;234;212m$1\x1B[0m");
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
// src/repl.tsx
|
|
3524
|
+
init_format();
|
|
3525
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3513
3526
|
function getContextLimit3(model) {
|
|
3514
3527
|
const lower = model.toLowerCase();
|
|
3515
3528
|
if (lower.includes("llama3.1")) return 131072;
|
|
@@ -3520,105 +3533,150 @@ function getContextLimit3(model) {
|
|
|
3520
3533
|
if (lower.includes("deepseek")) return 32768;
|
|
3521
3534
|
return 8192;
|
|
3522
3535
|
}
|
|
3523
|
-
|
|
3536
|
+
function FrozenItemView({ item }) {
|
|
3537
|
+
switch (item.kind) {
|
|
3538
|
+
case "banner":
|
|
3539
|
+
return /* @__PURE__ */ jsx7(Banner, { model: item.model, cwd: item.cwd, providerName: item.providerName, providerOnline: item.providerOnline });
|
|
3540
|
+
case "user":
|
|
3541
|
+
return /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, marginBottom: 1, children: [
|
|
3542
|
+
/* @__PURE__ */ jsx7(Text6, { color: theme.cyan, bold: true, children: "\u276F " }),
|
|
3543
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: theme.text, children: item.content })
|
|
3544
|
+
] });
|
|
3545
|
+
case "assistant": {
|
|
3546
|
+
if (!item.content) return null;
|
|
3547
|
+
return /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
3548
|
+
/* @__PURE__ */ jsx7(Text6, { color: theme.cyan, children: "\u23BF " }),
|
|
3549
|
+
/* @__PURE__ */ jsx7(Text6, { wrap: "wrap", children: renderMarkdown(item.content) })
|
|
3550
|
+
] }) });
|
|
3551
|
+
}
|
|
3552
|
+
case "tool": {
|
|
3553
|
+
const lines = item.content.split("\n");
|
|
3554
|
+
const preview = lines.length > 8 ? lines.slice(0, 8).join("\n") + `
|
|
3555
|
+
... (${lines.length - 8} more lines)` : item.content;
|
|
3556
|
+
return /* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
3557
|
+
"\u23BF ",
|
|
3558
|
+
truncate(preview, 1200)
|
|
3559
|
+
] }) });
|
|
3560
|
+
}
|
|
3561
|
+
case "tool-result": {
|
|
3562
|
+
const icon = item.isError ? "\u2718" : "\u2714";
|
|
3563
|
+
const iconColor = item.isError ? theme.pink : theme.green;
|
|
3564
|
+
const lines = item.output.split("\n");
|
|
3565
|
+
const preview = lines.length > 6 ? lines.slice(0, 6).join("\n") + `
|
|
3566
|
+
... (${lines.length - 6} more lines)` : item.output;
|
|
3567
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
|
|
3568
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
3569
|
+
/* @__PURE__ */ jsxs6(Text6, { color: iconColor, children: [
|
|
3570
|
+
icon,
|
|
3571
|
+
" "
|
|
3572
|
+
] }),
|
|
3573
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: theme.yellow, children: item.toolName })
|
|
3574
|
+
] }),
|
|
3575
|
+
/* @__PURE__ */ jsx7(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
3576
|
+
"\u23BF ",
|
|
3577
|
+
truncate(preview, 1200)
|
|
3578
|
+
] }) })
|
|
3579
|
+
] });
|
|
3580
|
+
}
|
|
3581
|
+
case "info":
|
|
3582
|
+
return /* @__PURE__ */ jsx7(Box6, { marginBottom: 1, marginLeft: 2, children: /* @__PURE__ */ jsx7(Text6, { children: item.text }) });
|
|
3583
|
+
default:
|
|
3584
|
+
return null;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3524
3587
|
function REPL({ initialPrompt }) {
|
|
3525
3588
|
const { model: initialModel, systemPromptOverride, maxTurns, initialMessages, initialSessionId } = useDarkfooContext();
|
|
3526
3589
|
const { exit } = useApp();
|
|
3527
|
-
const [model, setModel] =
|
|
3528
|
-
const [messages, setMessages] =
|
|
3529
|
-
const [inputValue, setInputValue] =
|
|
3530
|
-
const [isStreaming, setIsStreaming] =
|
|
3531
|
-
const [
|
|
3532
|
-
const [activeTool, setActiveTool] =
|
|
3533
|
-
const [
|
|
3534
|
-
const [
|
|
3535
|
-
const [
|
|
3536
|
-
const [
|
|
3537
|
-
const [
|
|
3538
|
-
const
|
|
3539
|
-
const
|
|
3540
|
-
const
|
|
3541
|
-
const
|
|
3542
|
-
const
|
|
3543
|
-
const
|
|
3544
|
-
const
|
|
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
|
-
}, []);
|
|
3568
|
-
const messagesRef = useRef(messages);
|
|
3590
|
+
const [model, setModel] = useState3(initialModel);
|
|
3591
|
+
const [messages, setMessages] = useState3(initialMessages ?? []);
|
|
3592
|
+
const [inputValue, setInputValue] = useState3("");
|
|
3593
|
+
const [isStreaming, setIsStreaming] = useState3(false);
|
|
3594
|
+
const [receivedText, setReceivedText] = useState3(false);
|
|
3595
|
+
const [activeTool, setActiveTool] = useState3(null);
|
|
3596
|
+
const [commandOutput, setCommandOutput] = useState3(null);
|
|
3597
|
+
const [inputHistory, setInputHistory] = useState3([]);
|
|
3598
|
+
const [tokenCounts, setTokenCounts] = useState3({ input: 0, output: 0 });
|
|
3599
|
+
const [systemPrompt, setSystemPrompt] = useState3("");
|
|
3600
|
+
const [providerOnline, setProviderOnline] = useState3(true);
|
|
3601
|
+
const abortRef = useRef2(null);
|
|
3602
|
+
const hasRun = useRef2(false);
|
|
3603
|
+
const sessionId = useRef2(initialSessionId ?? createSessionId());
|
|
3604
|
+
const [frozen, setFrozen] = useState3([]);
|
|
3605
|
+
const frozenBanner = useRef2(false);
|
|
3606
|
+
const streamingRef = useRef2(null);
|
|
3607
|
+
const messagesRef = useRef2(messages);
|
|
3569
3608
|
messagesRef.current = messages;
|
|
3570
|
-
const modelRef =
|
|
3609
|
+
const modelRef = useRef2(model);
|
|
3571
3610
|
modelRef.current = model;
|
|
3572
|
-
const systemPromptRef =
|
|
3611
|
+
const systemPromptRef = useRef2(systemPrompt);
|
|
3573
3612
|
systemPromptRef.current = systemPrompt;
|
|
3574
3613
|
const tools = getTools();
|
|
3575
3614
|
const cwd = process.cwd();
|
|
3576
3615
|
const providerName = useMemo(() => getActiveProviderName(), [model, providerOnline]);
|
|
3577
|
-
|
|
3616
|
+
const freeze = useCallback2((item) => {
|
|
3617
|
+
setFrozen((prev) => [...prev, item]);
|
|
3618
|
+
}, []);
|
|
3619
|
+
useEffect2(() => {
|
|
3578
3620
|
if (systemPromptOverride) {
|
|
3579
3621
|
setSystemPrompt(systemPromptOverride);
|
|
3580
3622
|
} else {
|
|
3581
3623
|
buildSystemPrompt(tools, cwd).then(setSystemPrompt);
|
|
3582
3624
|
}
|
|
3583
3625
|
}, []);
|
|
3584
|
-
|
|
3626
|
+
useEffect2(() => {
|
|
3585
3627
|
executeHooks("session_start", { cwd }).catch(() => {
|
|
3586
3628
|
});
|
|
3587
3629
|
}, []);
|
|
3588
|
-
|
|
3630
|
+
useEffect2(() => {
|
|
3589
3631
|
const provider = getProvider();
|
|
3590
3632
|
provider.healthCheck().then((ok) => {
|
|
3591
3633
|
setProviderOnline(ok);
|
|
3592
3634
|
if (!ok) {
|
|
3593
3635
|
setModel("");
|
|
3636
|
+
if (!frozenBanner.current) {
|
|
3637
|
+
frozenBanner.current = true;
|
|
3638
|
+
freeze({ id: "banner", kind: "banner", model: "", cwd, providerName: getActiveProviderName(), providerOnline: false });
|
|
3639
|
+
}
|
|
3594
3640
|
return;
|
|
3595
3641
|
}
|
|
3596
|
-
provider.listModels().then((
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3642
|
+
provider.listModels().then((resolvedModels) => {
|
|
3643
|
+
let resolved = "";
|
|
3644
|
+
if (resolvedModels.length > 0) {
|
|
3645
|
+
const requested = model.toLowerCase();
|
|
3646
|
+
const match = resolvedModels.find((m) => m.name.toLowerCase() === requested);
|
|
3647
|
+
if (match) {
|
|
3648
|
+
resolved = match.name;
|
|
3649
|
+
} else {
|
|
3650
|
+
const preferred = resolvedModels.find((m) => m.name.includes("llama3.1")) ?? resolvedModels.find((m) => m.name.includes("qwen")) ?? resolvedModels.find((m) => m.name.includes("instruct")) ?? resolvedModels[0];
|
|
3651
|
+
if (preferred) {
|
|
3652
|
+
resolved = preferred.name;
|
|
3653
|
+
freeze({ id: nanoid6(), kind: "info", text: `Model "${model}" not found. Using ${preferred.name} instead.` });
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3600
3656
|
}
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
return;
|
|
3657
|
+
setModel(resolved);
|
|
3658
|
+
if (!frozenBanner.current) {
|
|
3659
|
+
frozenBanner.current = true;
|
|
3660
|
+
freeze({ id: "banner", kind: "banner", model: resolved, cwd, providerName: getActiveProviderName(), providerOnline: true });
|
|
3606
3661
|
}
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
setModel("");
|
|
3662
|
+
}).catch(() => {
|
|
3663
|
+
setModel("");
|
|
3664
|
+
if (!frozenBanner.current) {
|
|
3665
|
+
frozenBanner.current = true;
|
|
3666
|
+
freeze({ id: "banner", kind: "banner", model: "", cwd, providerName: getActiveProviderName(), providerOnline: false });
|
|
3613
3667
|
}
|
|
3614
|
-
})
|
|
3668
|
+
});
|
|
3615
3669
|
}).catch(() => {
|
|
3616
3670
|
setProviderOnline(false);
|
|
3617
3671
|
setModel("");
|
|
3672
|
+
if (!frozenBanner.current) {
|
|
3673
|
+
frozenBanner.current = true;
|
|
3674
|
+
freeze({ id: "banner", kind: "banner", model: "", cwd, providerName: getActiveProviderName(), providerOnline: false });
|
|
3675
|
+
}
|
|
3618
3676
|
});
|
|
3619
3677
|
}, []);
|
|
3620
3678
|
const clearMessages = useCallback2(() => setMessages([]), []);
|
|
3621
|
-
const commandContextRef =
|
|
3679
|
+
const commandContextRef = useRef2({
|
|
3622
3680
|
messages,
|
|
3623
3681
|
model,
|
|
3624
3682
|
cwd,
|
|
@@ -3644,20 +3702,19 @@ function REPL({ initialPrompt }) {
|
|
|
3644
3702
|
content: userMessage,
|
|
3645
3703
|
timestamp: Date.now()
|
|
3646
3704
|
};
|
|
3705
|
+
freeze({ id: userMsg.id, kind: "user", content: userMessage });
|
|
3647
3706
|
setMessages((prev) => [...prev, userMsg]);
|
|
3648
3707
|
setIsStreaming(true);
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
setToolResults([]);
|
|
3708
|
+
setReceivedText(false);
|
|
3709
|
+
setActiveTool(null);
|
|
3652
3710
|
setCommandOutput(null);
|
|
3653
|
-
|
|
3711
|
+
streamingRef.current?.clear();
|
|
3654
3712
|
const controller = new AbortController();
|
|
3655
3713
|
abortRef.current = controller;
|
|
3656
3714
|
const allMessages = [...messagesRef.current, userMsg];
|
|
3657
3715
|
const currentModel = modelRef.current;
|
|
3658
3716
|
const currentSystemPrompt = systemPromptRef.current;
|
|
3659
|
-
let
|
|
3660
|
-
startStreamFlush();
|
|
3717
|
+
let gotFirstText = false;
|
|
3661
3718
|
try {
|
|
3662
3719
|
for await (const event of query({
|
|
3663
3720
|
model: currentModel,
|
|
@@ -3670,24 +3727,18 @@ function REPL({ initialPrompt }) {
|
|
|
3670
3727
|
if (controller.signal.aborted) break;
|
|
3671
3728
|
switch (event.type) {
|
|
3672
3729
|
case "text_delta":
|
|
3673
|
-
|
|
3674
|
-
if (!
|
|
3675
|
-
|
|
3676
|
-
|
|
3730
|
+
streamingRef.current?.append(event.text);
|
|
3731
|
+
if (!gotFirstText) {
|
|
3732
|
+
gotFirstText = true;
|
|
3733
|
+
setReceivedText(true);
|
|
3677
3734
|
}
|
|
3678
3735
|
break;
|
|
3679
3736
|
case "tool_call":
|
|
3680
|
-
stopStreamFlush();
|
|
3681
3737
|
setActiveTool({ name: event.toolCall.function.name, args: event.toolCall.function.arguments });
|
|
3682
|
-
setMascotMood("working");
|
|
3683
3738
|
break;
|
|
3684
3739
|
case "tool_result":
|
|
3685
3740
|
setActiveTool(null);
|
|
3686
|
-
|
|
3687
|
-
setToolResults((prev) => [
|
|
3688
|
-
...prev,
|
|
3689
|
-
{ id: nanoid6(), toolName: event.toolName, output: event.output, isError: event.isError }
|
|
3690
|
-
]);
|
|
3741
|
+
freeze({ id: nanoid6(), kind: "tool-result", toolName: event.toolName, output: event.output, isError: event.isError });
|
|
3691
3742
|
break;
|
|
3692
3743
|
case "usage":
|
|
3693
3744
|
setTokenCounts((prev) => ({
|
|
@@ -3696,21 +3747,22 @@ function REPL({ initialPrompt }) {
|
|
|
3696
3747
|
}));
|
|
3697
3748
|
break;
|
|
3698
3749
|
case "assistant_message":
|
|
3699
|
-
|
|
3750
|
+
streamingRef.current?.clear();
|
|
3751
|
+
setReceivedText(false);
|
|
3752
|
+
gotFirstText = false;
|
|
3753
|
+
if (event.message.content) {
|
|
3754
|
+
freeze({ id: event.message.id, kind: "assistant", content: event.message.content });
|
|
3755
|
+
}
|
|
3700
3756
|
setMessages((prev) => {
|
|
3701
3757
|
const updated = [...prev, event.message];
|
|
3702
3758
|
saveSession(sessionId.current, updated, currentModel, cwd).catch(() => {
|
|
3703
3759
|
});
|
|
3704
3760
|
return updated;
|
|
3705
3761
|
});
|
|
3706
|
-
streamBufferRef.current = "";
|
|
3707
|
-
setStreamingText("");
|
|
3708
|
-
receivedFirstText = false;
|
|
3709
|
-
startStreamFlush();
|
|
3710
3762
|
break;
|
|
3711
3763
|
case "error":
|
|
3712
|
-
|
|
3713
|
-
|
|
3764
|
+
streamingRef.current?.clear();
|
|
3765
|
+
freeze({ id: nanoid6(), kind: "assistant", content: `Error: ${event.error}` });
|
|
3714
3766
|
setMessages((prev) => [
|
|
3715
3767
|
...prev,
|
|
3716
3768
|
{ id: nanoid6(), role: "assistant", content: `Error: ${event.error}`, timestamp: Date.now() }
|
|
@@ -3721,24 +3773,21 @@ function REPL({ initialPrompt }) {
|
|
|
3721
3773
|
} catch (err) {
|
|
3722
3774
|
if (err.name !== "AbortError") {
|
|
3723
3775
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3776
|
+
freeze({ id: nanoid6(), kind: "assistant", content: `Error: ${msg}` });
|
|
3724
3777
|
setMessages((prev) => [
|
|
3725
3778
|
...prev,
|
|
3726
3779
|
{ id: nanoid6(), role: "assistant", content: `Error: ${msg}`, timestamp: Date.now() }
|
|
3727
3780
|
]);
|
|
3728
3781
|
}
|
|
3729
3782
|
} finally {
|
|
3730
|
-
|
|
3731
|
-
streamBufferRef.current = "";
|
|
3783
|
+
streamingRef.current?.clear();
|
|
3732
3784
|
setIsStreaming(false);
|
|
3733
|
-
|
|
3785
|
+
setReceivedText(false);
|
|
3734
3786
|
setActiveTool(null);
|
|
3735
|
-
setToolResults([]);
|
|
3736
|
-
setMascotMood((prev) => prev === "error" ? "error" : "success");
|
|
3737
|
-
setTimeout(() => setMascotMood("idle"), 2e3);
|
|
3738
3787
|
abortRef.current = null;
|
|
3739
3788
|
}
|
|
3740
3789
|
},
|
|
3741
|
-
[tools, cwd,
|
|
3790
|
+
[tools, cwd, freeze]
|
|
3742
3791
|
);
|
|
3743
3792
|
const handleSubmit = useCallback2(
|
|
3744
3793
|
async (value) => {
|
|
@@ -3756,17 +3805,13 @@ function REPL({ initialPrompt }) {
|
|
|
3756
3805
|
if (result.replaceMessages) {
|
|
3757
3806
|
setMessages(result.replaceMessages);
|
|
3758
3807
|
}
|
|
3759
|
-
if (result.foxMood) {
|
|
3760
|
-
setMascotMood(result.foxMood);
|
|
3761
|
-
setTimeout(() => setMascotMood("idle"), 3e3);
|
|
3762
|
-
}
|
|
3763
3808
|
if (result.exit) return;
|
|
3764
3809
|
if (!result.silent && result.output) {
|
|
3765
3810
|
runQuery(result.output);
|
|
3766
3811
|
return;
|
|
3767
3812
|
}
|
|
3768
3813
|
if (result.output) {
|
|
3769
|
-
|
|
3814
|
+
freeze({ id: nanoid6(), kind: "info", text: result.output });
|
|
3770
3815
|
}
|
|
3771
3816
|
}
|
|
3772
3817
|
return;
|
|
@@ -3775,16 +3820,15 @@ function REPL({ initialPrompt }) {
|
|
|
3775
3820
|
const cmd = value.slice(1).trim();
|
|
3776
3821
|
if (!cmd) return;
|
|
3777
3822
|
setIsStreaming(true);
|
|
3778
|
-
setCommandOutput(null);
|
|
3779
3823
|
try {
|
|
3780
3824
|
const result = await BashTool.call(
|
|
3781
3825
|
{ command: cmd },
|
|
3782
3826
|
{ cwd, abortSignal: new AbortController().signal }
|
|
3783
3827
|
);
|
|
3784
|
-
|
|
3828
|
+
freeze({ id: nanoid6(), kind: "info", text: result.output });
|
|
3785
3829
|
} catch (err) {
|
|
3786
3830
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3787
|
-
|
|
3831
|
+
freeze({ id: nanoid6(), kind: "info", text: `Error: ${msg}` });
|
|
3788
3832
|
} finally {
|
|
3789
3833
|
setIsStreaming(false);
|
|
3790
3834
|
}
|
|
@@ -3792,16 +3836,16 @@ function REPL({ initialPrompt }) {
|
|
|
3792
3836
|
}
|
|
3793
3837
|
runQuery(value);
|
|
3794
3838
|
},
|
|
3795
|
-
[addToHistory, exit, cwd, runQuery]
|
|
3839
|
+
[addToHistory, exit, cwd, runQuery, freeze]
|
|
3796
3840
|
);
|
|
3797
|
-
|
|
3841
|
+
useEffect2(() => {
|
|
3798
3842
|
if (initialPrompt && !hasRun.current) {
|
|
3799
3843
|
hasRun.current = true;
|
|
3800
3844
|
runQuery(initialPrompt);
|
|
3801
3845
|
}
|
|
3802
3846
|
}, [initialPrompt, runQuery]);
|
|
3803
|
-
const autoCompactRef =
|
|
3804
|
-
|
|
3847
|
+
const autoCompactRef = useRef2(false);
|
|
3848
|
+
useEffect2(() => {
|
|
3805
3849
|
if (isStreaming || autoCompactRef.current || messages.length < 6) return;
|
|
3806
3850
|
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
3807
3851
|
const estTokens = Math.ceil(totalChars / 4) + 2e3;
|
|
@@ -3809,7 +3853,7 @@ function REPL({ initialPrompt }) {
|
|
|
3809
3853
|
const usage = estTokens / limit;
|
|
3810
3854
|
if (usage > 0.85) {
|
|
3811
3855
|
autoCompactRef.current = true;
|
|
3812
|
-
|
|
3856
|
+
freeze({ id: nanoid6(), kind: "info", text: "\x1B[33m[!] Context 85%+ full \u2014 auto-compacting...\x1B[0m" });
|
|
3813
3857
|
const transcript = messages.filter((m) => m.role === "user" || m.role === "assistant" && m.content).map((m) => `${m.role}: ${m.content}`).join("\n\n");
|
|
3814
3858
|
getProvider().chat({
|
|
3815
3859
|
model,
|
|
@@ -3827,10 +3871,10 @@ ${result.content}
|
|
|
3827
3871
|
};
|
|
3828
3872
|
setMessages([summaryMsg]);
|
|
3829
3873
|
setTokenCounts({ input: Math.ceil(result.content.length / 4), output: 0 });
|
|
3830
|
-
|
|
3874
|
+
freeze({ id: nanoid6(), kind: "info", text: "\x1B[32m[ok] Auto-compacted conversation.\x1B[0m" });
|
|
3831
3875
|
autoCompactRef.current = false;
|
|
3832
3876
|
}).catch(() => {
|
|
3833
|
-
|
|
3877
|
+
freeze({ id: nanoid6(), kind: "info", text: "\x1B[31m[err] Auto-compact failed.\x1B[0m" });
|
|
3834
3878
|
autoCompactRef.current = false;
|
|
3835
3879
|
});
|
|
3836
3880
|
}
|
|
@@ -3844,39 +3888,35 @@ ${result.content}
|
|
|
3844
3888
|
}
|
|
3845
3889
|
}
|
|
3846
3890
|
});
|
|
3847
|
-
return /* @__PURE__ */ jsxs6(
|
|
3848
|
-
/* @__PURE__ */ jsx7(
|
|
3849
|
-
/* @__PURE__ */
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
tokenEstimate: tokenCounts.input + tokenCounts.output,
|
|
3877
|
-
isStreaming
|
|
3878
|
-
}
|
|
3879
|
-
)
|
|
3891
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
3892
|
+
/* @__PURE__ */ jsx7(Static, { items: frozen, children: (item) => /* @__PURE__ */ jsx7(FrozenItemView, { item }, item.id) }),
|
|
3893
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
3894
|
+
activeTool ? /* @__PURE__ */ jsx7(ActiveToolCall, { toolName: activeTool.name, args: activeTool.args }) : null,
|
|
3895
|
+
/* @__PURE__ */ jsx7(StreamingDisplay, { ref: streamingRef }),
|
|
3896
|
+
isStreaming && !receivedText && !activeTool ? /* @__PURE__ */ jsxs6(Box6, { marginLeft: 2, children: [
|
|
3897
|
+
/* @__PURE__ */ jsx7(Text6, { color: theme.cyan, children: "..." }),
|
|
3898
|
+
/* @__PURE__ */ jsx7(Text6, { color: theme.dim, children: " Thinking" })
|
|
3899
|
+
] }) : null,
|
|
3900
|
+
/* @__PURE__ */ jsx7(
|
|
3901
|
+
UserInput,
|
|
3902
|
+
{
|
|
3903
|
+
value: inputValue,
|
|
3904
|
+
onChange: setInputValue,
|
|
3905
|
+
onSubmit: handleSubmit,
|
|
3906
|
+
disabled: isStreaming,
|
|
3907
|
+
history: inputHistory
|
|
3908
|
+
}
|
|
3909
|
+
),
|
|
3910
|
+
/* @__PURE__ */ jsx7(
|
|
3911
|
+
StatusLine,
|
|
3912
|
+
{
|
|
3913
|
+
model,
|
|
3914
|
+
messageCount: messages.length,
|
|
3915
|
+
tokenEstimate: tokenCounts.input + tokenCounts.output,
|
|
3916
|
+
isStreaming
|
|
3917
|
+
}
|
|
3918
|
+
)
|
|
3919
|
+
] })
|
|
3880
3920
|
] });
|
|
3881
3921
|
}
|
|
3882
3922
|
|
|
@@ -3884,7 +3924,7 @@ ${result.content}
|
|
|
3884
3924
|
init_providers();
|
|
3885
3925
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
3886
3926
|
var program = new Command();
|
|
3887
|
-
program.name("darkfoo").description("Darkfoo Code \u2014 local AI coding assistant powered by local LLM providers").version("0.4.
|
|
3927
|
+
program.name("darkfoo").description("Darkfoo Code \u2014 local AI coding assistant powered by local LLM providers").version("0.4.3").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) => {
|
|
3888
3928
|
const { model, prompt, provider, systemPrompt } = options;
|
|
3889
3929
|
if (options.debug) {
|
|
3890
3930
|
const { setDebugMode: setDebugMode2 } = await Promise.resolve().then(() => (init_debug(), debug_exports));
|