darkfoo-code 0.2.2 → 0.2.4

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 +63 -40
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1933,11 +1933,12 @@ import { nanoid as nanoid6 } from "nanoid";
1933
1933
 
1934
1934
  // src/components/Banner.tsx
1935
1935
  init_theme();
1936
+ import { memo as memo2 } from "react";
1936
1937
  import { Box as Box2, Text as Text2 } from "ink";
1937
1938
 
1938
1939
  // src/components/Fox.tsx
1939
1940
  init_theme();
1940
- import { useState, useEffect, useRef } from "react";
1941
+ import { useState, useEffect, useRef, memo } from "react";
1941
1942
  import { Box, Text } from "ink";
1942
1943
 
1943
1944
  // src/fox-state.ts
@@ -2195,11 +2196,11 @@ var SUCCESS = [
2195
2196
  " \\~/ * "
2196
2197
  ],
2197
2198
  [
2198
- " ",
2199
2199
  " /\\_/\\ ",
2200
2200
  " ( ^.^ ) ",
2201
2201
  " > w < ",
2202
2202
  " /| * |\\ ",
2203
+ " (_| |_)",
2203
2204
  " \\~/ "
2204
2205
  ],
2205
2206
  [
@@ -2377,15 +2378,15 @@ var FRAME_SETS = {
2377
2378
  sleeping: SLEEPING
2378
2379
  };
2379
2380
  var FRAME_SPEEDS = {
2380
- idle: 500,
2381
- thinking: 350,
2382
- working: 200,
2383
- success: 400,
2384
- error: 800,
2385
- greeting: 350,
2386
- pet: 300,
2387
- eating: 350,
2388
- sleeping: 900
2381
+ idle: 800,
2382
+ thinking: 600,
2383
+ working: 500,
2384
+ success: 600,
2385
+ error: 1e3,
2386
+ greeting: 600,
2387
+ pet: 500,
2388
+ eating: 600,
2389
+ sleeping: 1200
2389
2390
  };
2390
2391
  var BODY_COLORS = {
2391
2392
  idle: "#5eead4",
@@ -2409,7 +2410,7 @@ var FACE_COLORS = {
2409
2410
  eating: "#fbbf24",
2410
2411
  sleeping: "#7e8ea6"
2411
2412
  };
2412
- function Fox({ mood = "idle" }) {
2413
+ var Fox = memo(function Fox2({ mood = "idle" }) {
2413
2414
  const [frameIdx, setFrameIdx] = useState(0);
2414
2415
  const [bubble, setBubble] = useState(null);
2415
2416
  const frames = FRAME_SETS[mood];
@@ -2448,7 +2449,7 @@ function Fox({ mood = "idle" }) {
2448
2449
  bubble ? /* @__PURE__ */ jsx2(FoxBubble, { text: bubble }) : null,
2449
2450
  frame.map((line, i) => /* @__PURE__ */ jsx2(Text, { color: i <= 1 ? faceColor : bodyColor, children: line }, i))
2450
2451
  ] });
2451
- }
2452
+ });
2452
2453
  function FoxBubble({ text }) {
2453
2454
  if (!text) return null;
2454
2455
  const maxW = 28;
@@ -2495,7 +2496,7 @@ function gradientBar(width) {
2495
2496
  color: GRADIENT[Math.round(i / (width - 1) * (GRADIENT.length - 1))]
2496
2497
  }));
2497
2498
  }
2498
- function Banner({ model, cwd, providerName, providerOnline }) {
2499
+ var Banner = memo2(function Banner2({ model, cwd, providerName, providerOnline }) {
2499
2500
  const bar = gradientBar(60);
2500
2501
  const statusTag = providerOnline ? "[connected]" : "[offline]";
2501
2502
  const statusColor = providerOnline ? theme.green ?? "#4ade80" : theme.pink ?? "#f472b6";
@@ -2517,7 +2518,7 @@ function Banner({ model, cwd, providerName, providerOnline }) {
2517
2518
  ] }),
2518
2519
  /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, marginLeft: 2, children: [
2519
2520
  /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "model " }),
2520
- /* @__PURE__ */ jsx3(Text2, { color: theme.cyan ?? "#5eead4", bold: true, children: model })
2521
+ model ? /* @__PURE__ */ jsx3(Text2, { color: theme.cyan ?? "#5eead4", bold: true, children: model }) : /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "--" })
2521
2522
  ] }),
2522
2523
  /* @__PURE__ */ jsxs2(Box2, { marginLeft: 2, children: [
2523
2524
  /* @__PURE__ */ jsx3(Text2, { color: theme.dim ?? "#7e8ea6", children: "via " }),
@@ -2534,11 +2535,12 @@ function Banner({ model, cwd, providerName, providerOnline }) {
2534
2535
  bar.map((d, i) => /* @__PURE__ */ jsx3(Text2, { color: d.color, children: d.char }, i))
2535
2536
  ] }) })
2536
2537
  ] });
2537
- }
2538
+ });
2538
2539
 
2539
2540
  // src/components/Messages.tsx
2540
2541
  init_theme();
2541
2542
  init_format();
2543
+ import { memo as memo3 } from "react";
2542
2544
  import { Box as Box3, Text as Text3 } from "ink";
2543
2545
 
2544
2546
  // src/utils/markdown.ts
@@ -2604,9 +2606,9 @@ function renderInline(text) {
2604
2606
 
2605
2607
  // src/components/Messages.tsx
2606
2608
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2607
- function Messages({ messages }) {
2609
+ var Messages = memo3(function Messages2({ messages }) {
2608
2610
  return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", children: messages.map((msg) => /* @__PURE__ */ jsx4(MessageRow, { message: msg }, msg.id)) });
2609
- }
2611
+ });
2610
2612
  function MessageRow({ message }) {
2611
2613
  switch (message.role) {
2612
2614
  case "user":
@@ -2691,6 +2693,7 @@ function formatToolArgs(args) {
2691
2693
  // src/components/StatusLine.tsx
2692
2694
  init_theme();
2693
2695
  init_state();
2696
+ import { memo as memo4 } from "react";
2694
2697
  import { Box as Box5, Text as Text5 } from "ink";
2695
2698
  import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2696
2699
  function getContextLimit(model) {
@@ -2703,7 +2706,7 @@ function getContextLimit(model) {
2703
2706
  if (lower.includes("deepseek")) return 32768;
2704
2707
  return 8192;
2705
2708
  }
2706
- function StatusLine({ model, messageCount, tokenEstimate, isStreaming }) {
2709
+ var StatusLine = memo4(function StatusLine2({ model, messageCount, tokenEstimate, isStreaming }) {
2707
2710
  const state2 = getAppState();
2708
2711
  const contextLimit = getContextLimit(model);
2709
2712
  const usage = Math.min(tokenEstimate / contextLimit, 1);
@@ -2715,7 +2718,7 @@ function StatusLine({ model, messageCount, tokenEstimate, isStreaming }) {
2715
2718
  "\u2500".repeat(2),
2716
2719
  " "
2717
2720
  ] }),
2718
- /* @__PURE__ */ jsx6(Text5, { color: theme.cyan, bold: true, children: model.split(":")[0] }),
2721
+ /* @__PURE__ */ jsx6(Text5, { color: theme.cyan, bold: true, children: model ? model.split(":")[0] : "--" }),
2719
2722
  /* @__PURE__ */ jsx6(Text5, { color: theme.dim, children: " \u2502 " }),
2720
2723
  /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
2721
2724
  messageCount,
@@ -2748,11 +2751,11 @@ function StatusLine({ model, messageCount, tokenEstimate, isStreaming }) {
2748
2751
  "\u2500".repeat(2)
2749
2752
  ] })
2750
2753
  ] });
2751
- }
2754
+ });
2752
2755
 
2753
2756
  // src/components/UserInput.tsx
2754
2757
  init_theme();
2755
- import { useState as useState2, useCallback } from "react";
2758
+ import { useState as useState2, useCallback, memo as memo5 } from "react";
2756
2759
  import { Box as Box6, Text as Text6, useInput } from "ink";
2757
2760
  import TextInput from "ink-text-input";
2758
2761
 
@@ -3814,7 +3817,7 @@ function getCommandNames() {
3814
3817
 
3815
3818
  // src/components/UserInput.tsx
3816
3819
  import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3817
- function UserInput({ value, onChange, onSubmit, disabled, history }) {
3820
+ var UserInput = memo5(function UserInput2({ value, onChange, onSubmit, disabled, history }) {
3818
3821
  const [historyIdx, setHistoryIdx] = useState2(-1);
3819
3822
  const [suggestion, setSuggestion] = useState2("");
3820
3823
  useInput((_input, key) => {
@@ -3894,7 +3897,7 @@ function UserInput({ value, onChange, onSubmit, disabled, history }) {
3894
3897
  ]
3895
3898
  }
3896
3899
  );
3897
- }
3900
+ });
3898
3901
 
3899
3902
  // src/repl.tsx
3900
3903
  init_providers();
@@ -3934,6 +3937,12 @@ function REPL({ initialPrompt }) {
3934
3937
  const abortRef = useRef2(null);
3935
3938
  const hasRun = useRef2(false);
3936
3939
  const sessionId = useRef2(createSessionId());
3940
+ const messagesRef = useRef2(messages);
3941
+ messagesRef.current = messages;
3942
+ const modelRef = useRef2(model);
3943
+ modelRef.current = model;
3944
+ const systemPromptRef = useRef2(systemPrompt);
3945
+ systemPromptRef.current = systemPrompt;
3937
3946
  const tools = getTools();
3938
3947
  const cwd = process.cwd();
3939
3948
  useEffect2(() => {
@@ -3947,9 +3956,15 @@ function REPL({ initialPrompt }) {
3947
3956
  const provider = getProvider();
3948
3957
  provider.healthCheck().then((ok) => {
3949
3958
  setProviderOnline(ok);
3950
- if (!ok) return;
3959
+ if (!ok) {
3960
+ setModel("");
3961
+ return;
3962
+ }
3951
3963
  provider.listModels().then((models) => {
3952
- if (models.length === 0) return;
3964
+ if (models.length === 0) {
3965
+ setModel("");
3966
+ return;
3967
+ }
3953
3968
  const requested = model.toLowerCase();
3954
3969
  const match = models.find((m) => m.name.toLowerCase() === requested);
3955
3970
  if (match) {
@@ -3960,20 +3975,26 @@ function REPL({ initialPrompt }) {
3960
3975
  if (preferred) {
3961
3976
  setModel(preferred.name);
3962
3977
  setCommandOutput(`Model "${model}" not found. Using ${preferred.name} instead.`);
3978
+ } else {
3979
+ setModel("");
3963
3980
  }
3964
- }).catch(() => {
3965
- });
3966
- }).catch(() => setProviderOnline(false));
3981
+ }).catch(() => setModel(""));
3982
+ }).catch(() => {
3983
+ setProviderOnline(false);
3984
+ setModel("");
3985
+ });
3967
3986
  }, []);
3968
- const commandContext = {
3987
+ const clearMessages = useCallback2(() => setMessages([]), []);
3988
+ const commandContextRef = useRef2({
3969
3989
  messages,
3970
3990
  model,
3971
3991
  cwd,
3972
3992
  setModel,
3973
- clearMessages: () => setMessages([]),
3993
+ clearMessages,
3974
3994
  exit,
3975
3995
  tokenCounts
3976
- };
3996
+ });
3997
+ commandContextRef.current = { messages, model, cwd, setModel, clearMessages, exit, tokenCounts };
3977
3998
  const addToHistory = useCallback2((input) => {
3978
3999
  setInputHistory((prev) => {
3979
4000
  const filtered = prev.filter((h) => h !== input);
@@ -3996,17 +4017,19 @@ function REPL({ initialPrompt }) {
3996
4017
  setFoxMood("thinking");
3997
4018
  const controller = new AbortController();
3998
4019
  abortRef.current = controller;
3999
- const allMessages = [...messages, userMsg];
4020
+ const allMessages = [...messagesRef.current, userMsg];
4021
+ const currentModel = modelRef.current;
4022
+ const currentSystemPrompt = systemPromptRef.current;
4000
4023
  setTokenCounts((prev) => ({
4001
4024
  ...prev,
4002
4025
  input: prev.input + Math.ceil(userMessage.length / 4)
4003
4026
  }));
4004
4027
  try {
4005
4028
  for await (const event of query({
4006
- model,
4029
+ model: currentModel,
4007
4030
  messages: allMessages,
4008
4031
  tools,
4009
- systemPrompt,
4032
+ systemPrompt: currentSystemPrompt,
4010
4033
  signal: controller.signal
4011
4034
  })) {
4012
4035
  if (controller.signal.aborted) break;
@@ -4030,7 +4053,7 @@ function REPL({ initialPrompt }) {
4030
4053
  case "assistant_message":
4031
4054
  setMessages((prev) => {
4032
4055
  const updated = [...prev, event.message];
4033
- saveSession(sessionId.current, updated, model, cwd).catch(() => {
4056
+ saveSession(sessionId.current, updated, currentModel, cwd).catch(() => {
4034
4057
  });
4035
4058
  return updated;
4036
4059
  });
@@ -4067,7 +4090,7 @@ function REPL({ initialPrompt }) {
4067
4090
  abortRef.current = null;
4068
4091
  }
4069
4092
  },
4070
- [model, messages, tools, systemPrompt]
4093
+ [tools, cwd]
4071
4094
  );
4072
4095
  const handleSubmit = useCallback2(
4073
4096
  async (value) => {
@@ -4080,7 +4103,7 @@ function REPL({ initialPrompt }) {
4080
4103
  return;
4081
4104
  }
4082
4105
  if (value.startsWith("/")) {
4083
- const result = await dispatchCommand(value, commandContext);
4106
+ const result = await dispatchCommand(value, commandContextRef.current);
4084
4107
  if (result) {
4085
4108
  if (result.replaceMessages) {
4086
4109
  setMessages(result.replaceMessages);
@@ -4121,7 +4144,7 @@ function REPL({ initialPrompt }) {
4121
4144
  }
4122
4145
  runQuery(value);
4123
4146
  },
4124
- [addToHistory, commandContext, cwd, runQuery]
4147
+ [addToHistory, exit, cwd, runQuery]
4125
4148
  );
4126
4149
  useEffect2(() => {
4127
4150
  if (initialPrompt && !hasRun.current) {
@@ -4234,7 +4257,7 @@ program.name("darkfoo").description("Darkfoo Code \u2014 local AI coding assista
4234
4257
  }
4235
4258
  }
4236
4259
  const { getProvider: getProvider2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
4237
- let resolvedModel = model;
4260
+ let resolvedModel = "";
4238
4261
  try {
4239
4262
  const models = await getProvider2().listModels();
4240
4263
  if (models.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkfoo-code",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",