code-ollama 0.21.0 → 0.22.0

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/README.md CHANGED
@@ -21,6 +21,9 @@
21
21
  npx code-ollama
22
22
  ```
23
23
 
24
+ > [!IMPORTANT]
25
+ > If you see an error that says server/model is unavailable, then follow these [steps](https://github.com/ai-action/code-ollama/wiki/Ollama).
26
+
24
27
  ## Install
25
28
 
26
29
  Install the [CLI](https://www.npmjs.com/package/code-ollama) globally:
@@ -1,4 +1,4 @@
1
- import { A as HEADER_PREFIX, B as AUTO, C as streamChat, D as saveClipboardImage, E as removeClipboardImage, F as SYSTEM, G as REJECT, H as PLAN, I as USER, J as LIST, K as NAME, L as PLAN_GENERATION_INSTRUCTION, M as LIST$1, N as getTheme, O as resetSystemMessage, P as ASSISTANT, R as BACK, S as pullModel, T as saveConfig, U as SAFE, V as LABEL, W as APPROVE, _ as reset, a as WRITE_TOOLS, b as deleteModel, c as write, d as deleteSession, f as deleteSessionIfEmpty, g as clear, h as updateSessionModel, i as TOOLS, j as WARNING, k as withSystemMessage, l as appendMessage, m as loadSession, n as executeTool, o as tick, p as listSessions, q as VERSION, r as READ_TOOLS, s as color, t as checkForUpdate, u as createSession, v as setClearHandler, w as loadConfig, x as listModels, y as checkHealth, z as CATALOG } from "../cli.js";
1
+ import { $ as VERSION, A as loadConfig, B as ASSISTANT, C as checkHealth, D as pullModel, E as listModels, F as withSystemMessage, G as CATALOG, H as USER, I as HEADER_PREFIX, J as PLAN, K as AUTO, L as WARNING, M as removeClipboardImage, N as saveClipboardImage, O as sanitizeAssistantContent, P as resetSystemMessage, Q as NAME, R as LIST$1, S as TOOL_INTENT_CORRECTION, T as hasUncalledToolIntent, U as PLAN_GENERATION_INSTRUCTION, V as SYSTEM, W as BACK, X as APPROVE, Y as SAFE, Z as REJECT, _ as loadSession, a as normalizeToolCall, b as reset, c as WRITE_TOOLS, d as write, et as LIST, f as appendMessage, g as listSessions, h as deleteSessionIfEmpty, i as formatToolResultContent, j as saveConfig, k as streamChat, l as tick, m as deleteSession, n as executeTool, o as READ_TOOLS, p as createSession, q as LABEL, r as executeToolCall, s as TOOLS, t as checkForUpdate, u as color, v as updateSessionModel, w as deleteModel, x as setClearHandler, y as clear, z as getTheme } from "../cli.js";
2
2
  import { existsSync, readdirSync, statSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { basename, extname, isAbsolute, join, relative, resolve } from "node:path";
@@ -11,6 +11,16 @@ import { Marked } from "marked";
11
11
  import { markedTerminal } from "marked-terminal";
12
12
  //#region src/components/CodeBlock/CodeBlock.tsx
13
13
  var highlightCache = /* @__PURE__ */ new Map();
14
+ var DIFF_LANGUAGE = "diff";
15
+ function getDiffLineColor(line, isSystem, theme) {
16
+ switch (true) {
17
+ case isSystem: return theme.colors.messageSystem;
18
+ case line.startsWith("+") && !line.startsWith("+++"): return "green";
19
+ case line.startsWith("-") && !line.startsWith("---"): return "red";
20
+ case line.startsWith("@@"): return theme.colors.accent;
21
+ case line.startsWith("---") || line.startsWith("+++"): return theme.colors.secondary;
22
+ }
23
+ }
14
24
  var CODE_BLOCK_REGEX = /^(?<indent>[ \t]*)(`{3,})(\w+)?[ \t]*\n([\s\S]*?)^\k<indent>\2[ \t]*$/gm;
15
25
  function normalizeCodeBlockContent(content, indent = "") {
16
26
  if (!indent) return content.trim();
@@ -48,6 +58,7 @@ async function highlightCode(code, language = "text", codeTheme = getTheme().cod
48
58
  }
49
59
  }
50
60
  var CodeBlock = memo(function CodeBlock({ code, language, role, theme = getTheme() }) {
61
+ const isDiff = language === DIFF_LANGUAGE;
51
62
  const cacheKey = `${theme.codeTheme}:${language ?? ""}:${code}`;
52
63
  const [highlighted, setHighlighted] = useState(() => highlightCache.get(cacheKey) ?? code);
53
64
  useEffect(() => {
@@ -76,7 +87,13 @@ var CodeBlock = memo(function CodeBlock({ code, language, role, theme = getTheme
76
87
  borderColor: isSystem ? theme.colors.secondary : theme.colors.codeBorder,
77
88
  paddingX: 1,
78
89
  marginY: 1,
79
- children: /* @__PURE__ */ jsx(Text, {
90
+ children: isDiff ? code.split("\n").map((line, index) => {
91
+ return /* @__PURE__ */ jsx(Text, {
92
+ color: getDiffLineColor(line, isSystem, theme),
93
+ dimColor: isSystem,
94
+ children: line || " "
95
+ }, index);
96
+ }) : /* @__PURE__ */ jsx(Text, {
80
97
  color: isSystem ? theme.colors.messageSystem : void 0,
81
98
  dimColor: isSystem,
82
99
  children: highlighted
@@ -393,6 +410,24 @@ function renderStickyPaddingLines(count) {
393
410
  index
394
411
  ));
395
412
  }
413
+ function ToolResultMessage({ message, messageColor, theme }) {
414
+ const diffContent = message.toolResult?.diff?.visible;
415
+ return /* @__PURE__ */ jsxs(Box, {
416
+ flexDirection: "column",
417
+ marginBottom: 1,
418
+ marginX: 2,
419
+ children: [/* @__PURE__ */ jsx(Text, {
420
+ color: messageColor,
421
+ dimColor: true,
422
+ children: message.content
423
+ }), diffContent && /* @__PURE__ */ jsx(CodeBlock, {
424
+ code: diffContent,
425
+ language: "diff",
426
+ role: "assistant",
427
+ theme
428
+ })]
429
+ });
430
+ }
396
431
  function Message({ message, isStreaming = false, theme }) {
397
432
  const messageColor = getMessageColor(message.role, theme);
398
433
  const isSystem = message.role === SYSTEM;
@@ -403,16 +438,23 @@ function Message({ message, isStreaming = false, theme }) {
403
438
  columns: stdout.columns,
404
439
  maxHeight: 0
405
440
  });
406
- if (isSystem) return /* @__PURE__ */ jsx(Box, {
407
- flexDirection: "column",
408
- marginBottom: 1,
409
- marginX: 2,
410
- children: /* @__PURE__ */ jsx(Text, {
411
- color: messageColor,
412
- dimColor: true,
413
- children: message.content
414
- })
415
- });
441
+ if (isSystem) {
442
+ if (message.toolResult?.diff) return /* @__PURE__ */ jsx(ToolResultMessage, {
443
+ message,
444
+ messageColor,
445
+ theme
446
+ });
447
+ return /* @__PURE__ */ jsx(Box, {
448
+ flexDirection: "column",
449
+ marginBottom: 1,
450
+ marginX: 2,
451
+ children: /* @__PURE__ */ jsx(Text, {
452
+ color: messageColor,
453
+ dimColor: true,
454
+ children: message.content
455
+ })
456
+ });
457
+ }
416
458
  if (isUser) {
417
459
  // v8 ignore start
418
460
  const attachmentPrefix = (message.images ?? []).map((path) => `[${path.split(/[\\/]/).at(-1) ?? path}]`).join(" ");
@@ -1362,6 +1404,8 @@ function hasExecutablePlan(content) {
1362
1404
  }
1363
1405
  //#endregion
1364
1406
  //#region src/components/Chat/Chat.tsx
1407
+ var MAX_TOOL_TURNS = 25;
1408
+ var MAX_TOOL_INTENT_CORRECTIONS = 2;
1365
1409
  function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onModeChange, sessionId, theme = getTheme() }) {
1366
1410
  const sessionMessages = initialMessages ?? [];
1367
1411
  const history = useMemo(() => sessionMessages.flatMap(({ role, content }) => role === "user" && !content.startsWith("/") ? [content] : []), [sessionMessages]);
@@ -1388,7 +1432,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1388
1432
  persistedSnapshotRef.current = snapshot;
1389
1433
  onMessagesChange?.(messages);
1390
1434
  }, [messages, onMessagesChange]);
1391
- const buildToolResultMessage = useCallback((toolName, result) => {
1435
+ const buildToolResultMessage = useCallback((toolName, result, args) => {
1392
1436
  if (result.error?.startsWith("Tool not allowed:")) return {
1393
1437
  role: SYSTEM,
1394
1438
  content: [
@@ -1400,7 +1444,11 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1400
1444
  };
1401
1445
  return {
1402
1446
  role: SYSTEM,
1403
- content: `Tool ${toolName} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
1447
+ content: formatToolResultContent(toolName, result, args),
1448
+ toolResult: {
1449
+ name: toolName,
1450
+ ...result.diff ? { diff: result.diff } : {}
1451
+ }
1404
1452
  };
1405
1453
  }, []);
1406
1454
  const buildPlanModeCorrectionMessage = useCallback((toolName) => ({
@@ -1425,72 +1473,124 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1425
1473
  }]);
1426
1474
  }, []);
1427
1475
  const processStream = useCallback(async (currentMessages, executionMode = mode) => {
1476
+ const modelName = model;
1428
1477
  // v8 ignore next
1429
- if (!model) throw new Error("Model is required");
1478
+ if (!modelName) throw new Error("Model is required");
1430
1479
  const controller = new AbortController();
1431
1480
  abortControllerRef.current = controller;
1432
- const assistantMessage = {
1433
- role: ASSISTANT,
1434
- content: ""
1435
- };
1436
- let committedMessages = currentMessages;
1437
- let assistantCommitted = false;
1438
- const commitAssistantMessage = () => {
1439
- if (assistantCommitted) {
1440
- // v8 ignore next
1441
- if (committedMessages.at(-1)?.role === "assistant") {
1442
- committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
1443
- setMessages(committedMessages);
1444
- }
1445
- return committedMessages;
1446
- }
1447
- assistantCommitted = true;
1448
- setStreamingMessage(null);
1449
- if (!assistantMessage.content) {
1450
- setMessages(committedMessages);
1451
- return committedMessages;
1452
- }
1453
- committedMessages = [...committedMessages, { ...assistantMessage }];
1454
- setMessages(committedMessages);
1455
- return committedMessages;
1456
- };
1457
- setStreamingMessage(assistantMessage);
1481
+ let activeMessages = currentMessages;
1482
+ let toolTurns = 0;
1483
+ let toolIntentCorrections = 0;
1458
1484
  try {
1459
- for await (const chunk of streamChat(withSystemMessage(currentMessages), model, TOOLS, controller.signal)) {
1460
- // v8 ignore next 3
1461
- if (controller.signal.aborted) return;
1462
- if (chunk.type === "content") {
1463
- assistantMessage.content += chunk.content;
1464
- setStreamingMessage({ ...assistantMessage });
1465
- } else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
1466
- const requiresApproval = WRITE_TOOLS.has(toolCall.function.name);
1485
+ while (!controller.signal.aborted) {
1486
+ const assistantMessage = {
1487
+ role: ASSISTANT,
1488
+ content: ""
1489
+ };
1490
+ let committedMessages = activeMessages;
1491
+ let assistantCommitted = false;
1492
+ const commitAssistantMessage = () => {
1493
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content);
1467
1494
  // v8 ignore start
1468
- const allowedTools = executionMode === "plan" ? READ_TOOLS : void 0;
1495
+ if (assistantCommitted) {
1496
+ if (committedMessages.at(-1)?.role === "assistant") {
1497
+ committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
1498
+ setMessages(committedMessages);
1499
+ }
1500
+ return committedMessages;
1501
+ }
1469
1502
  // v8 ignore stop
1503
+ assistantCommitted = true;
1504
+ setStreamingMessage(null);
1505
+ if (!assistantMessage.content) {
1506
+ setMessages(committedMessages);
1507
+ return committedMessages;
1508
+ }
1509
+ committedMessages = [...committedMessages, { ...assistantMessage }];
1510
+ setMessages(committedMessages);
1511
+ return committedMessages;
1512
+ };
1513
+ setStreamingMessage(assistantMessage);
1514
+ let nextMessages = null;
1515
+ for await (const chunk of streamChat(withSystemMessage(activeMessages), modelName, TOOLS, controller.signal)) {
1516
+ if (chunk.type === "content") {
1517
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content + chunk.content);
1518
+ setStreamingMessage({ ...assistantMessage });
1519
+ continue;
1520
+ }
1521
+ if (chunk.tool_calls.length === 0) continue;
1470
1522
  const updatedMessages = commitAssistantMessage();
1471
- if (executionMode === "safe" && requiresApproval) {
1472
- setPendingToolCall(toolCall);
1473
- setIsLoading(false);
1474
- return;
1523
+ const toolResultMessages = [];
1524
+ for (const toolCall of chunk.tool_calls) try {
1525
+ const normalized = normalizeToolCall(toolCall);
1526
+ if (executionMode === "safe" && normalized.requiresApproval) {
1527
+ setPendingToolCall({
1528
+ toolCall,
1529
+ messages: [...updatedMessages, ...toolResultMessages],
1530
+ executionMode
1531
+ });
1532
+ setIsLoading(false);
1533
+ return;
1534
+ }
1535
+ // v8 ignore next
1536
+ const allowedTools = executionMode === "plan" ? READ_TOOLS : void 0;
1537
+ const result = await executeTool(normalized.name, normalized.arguments, { allowedTools });
1538
+ toolResultMessages.push(buildToolResultMessage(normalized.name, result, normalized.arguments));
1539
+ } catch (error) {
1540
+ toolResultMessages.push(buildToolResultMessage(toolCall.function.name, {
1541
+ content: "",
1542
+ // v8 ignore next
1543
+ error: error instanceof Error ? error.message : String(error)
1544
+ }));
1545
+ }
1546
+ nextMessages = [...updatedMessages, ...toolResultMessages];
1547
+ setMessages(nextMessages);
1548
+ break;
1549
+ }
1550
+ if (!nextMessages) {
1551
+ await prewarmCodeBlocks(assistantMessage.content, theme);
1552
+ const updatedMessages = commitAssistantMessage();
1553
+ if (hasUncalledToolIntent(assistantMessage.content) && toolIntentCorrections < MAX_TOOL_INTENT_CORRECTIONS) {
1554
+ toolIntentCorrections += 1;
1555
+ activeMessages = [...updatedMessages, {
1556
+ role: SYSTEM,
1557
+ content: TOOL_INTENT_CORRECTION
1558
+ }];
1559
+ setMessages(activeMessages);
1560
+ continue;
1475
1561
  }
1476
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments, { allowedTools });
1477
- const toolResultMessage = buildToolResultMessage(toolCall.function.name, result);
1478
- const newMessages = [...updatedMessages, toolResultMessage];
1479
- setMessages(newMessages);
1480
- await processStream(newMessages, executionMode);
1481
1562
  return;
1482
1563
  }
1564
+ toolTurns += 1;
1565
+ toolIntentCorrections = 0;
1566
+ /* v8 ignore start */
1567
+ if (toolTurns >= MAX_TOOL_TURNS) {
1568
+ setMessages([...nextMessages, {
1569
+ role: SYSTEM,
1570
+ content: [
1571
+ "Tool execution stopped because the maximum tool turn limit was reached",
1572
+ ACTION_NOT_PERFORMED,
1573
+ "Summarize completed work and explain what remains without calling more tools."
1574
+ ].join("\n")
1575
+ }]);
1576
+ return;
1577
+ }
1578
+ /* v8 ignore stop */
1579
+ activeMessages = nextMessages;
1483
1580
  }
1484
- await prewarmCodeBlocks(assistantMessage.content, theme);
1485
- commitAssistantMessage();
1486
1581
  } catch (error) {
1487
1582
  // v8 ignore next
1488
1583
  if (!controller.signal.aborted) {
1489
- assistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
1490
- await prewarmCodeBlocks(assistantMessage.content, theme);
1491
- commitAssistantMessage();
1584
+ const errorMessage = {
1585
+ role: ASSISTANT,
1586
+ content: `Error: ${error instanceof Error ? error.message : String(error)}`
1587
+ };
1588
+ await prewarmCodeBlocks(errorMessage.content, theme);
1589
+ setStreamingMessage(null);
1590
+ setMessages([...activeMessages, errorMessage]);
1492
1591
  }
1493
1592
  } finally {
1593
+ // v8 ignore next
1494
1594
  if (abortControllerRef.current === controller) abortControllerRef.current = null;
1495
1595
  setIsLoading(false);
1496
1596
  }
@@ -1513,6 +1613,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1513
1613
  let committedMessages = currentMessages;
1514
1614
  let assistantCommitted = false;
1515
1615
  const commitAssistantMessage = () => {
1616
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content);
1516
1617
  if (assistantCommitted) {
1517
1618
  // v8 ignore next
1518
1619
  if (committedMessages.at(-1)?.role === "assistant") {
@@ -1538,19 +1639,33 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1538
1639
  // v8 ignore next 3
1539
1640
  if (controller.signal.aborted) return;
1540
1641
  if (chunk.type === "content") {
1541
- assistantMessage.content += chunk.content;
1642
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content + chunk.content);
1542
1643
  setStreamingMessage({ ...assistantMessage });
1543
1644
  } else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
1544
1645
  const updatedMessages = commitAssistantMessage();
1545
- if (!READ_TOOLS.has(toolCall.function.name)) {
1546
- const correctionMessage = buildPlanModeCorrectionMessage(toolCall.function.name);
1646
+ let normalized;
1647
+ try {
1648
+ normalized = normalizeToolCall(toolCall);
1649
+ } catch (error) {
1650
+ /* v8 ignore start */
1651
+ const toolResultMessage = buildToolResultMessage(toolCall.function.name, {
1652
+ content: "",
1653
+ error: error instanceof Error ? error.message : String(error)
1654
+ });
1655
+ const newMessages = [...updatedMessages, toolResultMessage];
1656
+ setMessages(newMessages);
1657
+ await processStreamReadOnly(newMessages);
1658
+ return;
1659
+ }
1660
+ if (!READ_TOOLS.has(normalized.name)) {
1661
+ const correctionMessage = buildPlanModeCorrectionMessage(normalized.name);
1547
1662
  const newMessages = [...updatedMessages, correctionMessage];
1548
1663
  setMessages(newMessages);
1549
1664
  await processStreamReadOnly(newMessages);
1550
1665
  return;
1551
1666
  }
1552
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments, { allowedTools: READ_TOOLS });
1553
- const toolResultMessage = buildToolResultMessage(toolCall.function.name, result);
1667
+ const result = await executeTool(normalized.name, normalized.arguments, { allowedTools: READ_TOOLS });
1668
+ const toolResultMessage = buildToolResultMessage(normalized.name, result, normalized.arguments);
1554
1669
  const newMessages = [...updatedMessages, toolResultMessage];
1555
1670
  setMessages(newMessages);
1556
1671
  await processStreamReadOnly(newMessages);
@@ -1574,7 +1689,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1574
1689
  // v8 ignore next 3
1575
1690
  if (controller.signal.aborted) return;
1576
1691
  if (chunk.type === "content") {
1577
- planAssistantMessage.content += chunk.content;
1692
+ planAssistantMessage.content = sanitizeAssistantContent(planAssistantMessage.content + chunk.content);
1578
1693
  setStreamingMessage({ ...planAssistantMessage });
1579
1694
  }
1580
1695
  }
@@ -1641,33 +1756,35 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1641
1756
  const handleToolApproval = useCallback(async (decision) => {
1642
1757
  // v8 ignore next
1643
1758
  if (!pendingToolCall) return;
1644
- const toolCall = pendingToolCall;
1759
+ const { executionMode, messages: approvedMessages, toolCall } = pendingToolCall;
1645
1760
  setPendingToolCall(null);
1646
1761
  setIsLoading(true);
1647
1762
  switch (decision) {
1648
1763
  case APPROVE: {
1649
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments);
1764
+ const result = await executeToolCall(toolCall);
1765
+ const toolResultMessage = buildToolResultMessage(toolCall.function.name, result, toolCall.function.arguments);
1766
+ const newMessages = [...approvedMessages, toolResultMessage];
1767
+ setMessages(newMessages);
1768
+ await processStream(newMessages, executionMode);
1769
+ break;
1770
+ }
1771
+ case REJECT: {
1650
1772
  const toolResultMessage = {
1651
1773
  role: SYSTEM,
1652
- content: `Tool ${toolCall.function.name} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
1774
+ content: formatToolResultContent(toolCall.function.name, {
1775
+ content: "",
1776
+ error: "Tool call rejected by user"
1777
+ }, toolCall.function.arguments)
1653
1778
  };
1654
- const newMessages = [...messages, toolResultMessage];
1655
- setMessages((previousMessages) => [...previousMessages, toolResultMessage]);
1656
- await processStream(newMessages);
1657
- break;
1658
- }
1659
- case REJECT:
1660
- setMessages((previousMessages) => [...previousMessages, {
1661
- role: USER,
1662
- content: TURN_ABORTED_MESSAGE
1663
- }]);
1779
+ setMessages([...approvedMessages, toolResultMessage]);
1664
1780
  setIsLoading(false);
1665
1781
  setInterruptReason(InterruptReason.Rejected);
1666
1782
  break;
1783
+ }
1667
1784
  }
1668
1785
  }, [
1786
+ buildToolResultMessage,
1669
1787
  pendingToolCall,
1670
- messages,
1671
1788
  processStream
1672
1789
  ]);
1673
1790
  const handleSubmit = useCallback(async ({ content, images }) => {
@@ -1711,7 +1828,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1711
1828
  theme
1712
1829
  }),
1713
1830
  !pendingPlan && pendingToolCall && /* @__PURE__ */ jsx(ToolApproval, {
1714
- toolCall: pendingToolCall,
1831
+ toolCall: pendingToolCall.toolCall,
1715
1832
  onDecision: handleToolApproval,
1716
1833
  theme
1717
1834
  }),
package/dist/cli.js CHANGED
@@ -8,6 +8,18 @@ import { randomUUID } from "node:crypto";
8
8
  import { Ollama } from "ollama";
9
9
  import { v7 } from "uuid";
10
10
  import { promisify } from "node:util";
11
+ //#region \0rolldown/runtime.js
12
+ var __defProp = Object.defineProperty;
13
+ var __exportAll = (all, no_symbols) => {
14
+ let target = {};
15
+ for (var name in all) __defProp(target, name, {
16
+ get: all[name],
17
+ enumerable: true
18
+ });
19
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
20
+ return target;
21
+ };
22
+ //#endregion
11
23
  //#region src/constants/command.ts
12
24
  var LIST$1 = [
13
25
  {
@@ -38,7 +50,7 @@ var LIST$1 = [
38
50
  //#endregion
39
51
  //#region package.json
40
52
  var name = "code-ollama";
41
- var version = "0.21.0";
53
+ var version = "0.22.0";
42
54
  //#endregion
43
55
  //#region src/constants/package.ts
44
56
  var NAME = name;
@@ -97,11 +109,11 @@ var BACK = {
97
109
  var BASE_SYSTEM_PROMPT = `You are a coding assistant that helps users write, edit, and understand code. You have access to tools for reading files, writing files, running shell commands, searching code, and searching the web
98
110
 
99
111
  Follow these rules:
100
- 1. Always use available tools rather than guessing file contents or code behavior
101
- 2. Read files before editing them to understand context
102
- 3. When writing files, provide complete, working code
103
- 4. Explain your reasoning when making non-trivial changes
104
- 5. Prefer minimal changes that achieve the goal
112
+ 1. Use available tools rather than guessing file contents, paths, or code behavior
113
+ 2. When a tool is needed, call it immediately instead of saying you will call it
114
+ 3. Read files before editing them to understand context
115
+ 4. Make the smallest exact change that satisfies the request
116
+ 5. Explain after tool results are available, unless the user asks for discussion or a plan
105
117
  6. Confirm with the user before destructive operations
106
118
 
107
119
  When tools return results, incorporate them into your response naturally`;
@@ -119,7 +131,12 @@ Always use tools when you need to:
119
131
  - Make file changes
120
132
  - Explore project structure
121
133
  - Search the codebase
122
- - Look up current or external information`;
134
+ - Look up current or external information
135
+
136
+ Path rules:
137
+ - Paths are relative to the project root unless absolute
138
+ - Preserve parent directories from listings; if list_dir("src") returns [d] utils, use src/utils
139
+ - If a path fails, inspect the parent directory or search before retrying`;
123
140
  var PLAN_GENERATION_INSTRUCTION = `Based on the research above, decide whether the user request needs code or shell execution
124
141
 
125
142
  If the request needs changes or commands, respond with a plan checklist only
@@ -280,6 +297,17 @@ function getTheme(themeId = DEFAULT_THEME_ID) {
280
297
  }
281
298
  //#endregion
282
299
  //#region src/constants/tool.ts
300
+ var tool_exports = /* @__PURE__ */ __exportAll({
301
+ EDIT_FILE: () => EDIT_FILE,
302
+ GREP_SEARCH: () => GREP_SEARCH,
303
+ LIST_DIR: () => LIST_DIR,
304
+ READ_FILE: () => READ_FILE,
305
+ RUN_SHELL: () => RUN_SHELL,
306
+ VIEW_RANGE: () => VIEW_RANGE,
307
+ WEB_FETCH: () => WEB_FETCH,
308
+ WEB_SEARCH: () => WEB_SEARCH,
309
+ WRITE_FILE: () => WRITE_FILE
310
+ });
283
311
  var READ_FILE = "read_file";
284
312
  var WRITE_FILE = "write_file";
285
313
  var EDIT_FILE = "edit_file";
@@ -467,6 +495,15 @@ function saveConfig(patch) {
467
495
  //#region src/utils/ollama.ts
468
496
  var { host } = loadConfig();
469
497
  var client = new Ollama({ host });
498
+ var TRAILING_CONTROL_TOKEN_REGEX = /(?:\s*<\|?channel\|?>)+\s*$/;
499
+ var TOOL_INTENT_REGEX = /\b(?:i\s+(?:will|am going to)|next,\s*i\s+will|now\s+i\s+will|first,\s*i\s+will)\b[\s\S]*\b(?:read|inspect|check|list|search|update|edit|write|modify|run)\b/i;
500
+ var TOOL_INTENT_CORRECTION = "You said you would use a tool but did not call one. Continue by calling the appropriate tool now. Do not describe the tool call.";
501
+ function sanitizeAssistantContent(content) {
502
+ return content.replace(TRAILING_CONTROL_TOKEN_REGEX, "");
503
+ }
504
+ function hasUncalledToolIntent(content) {
505
+ return TOOL_INTENT_REGEX.test(content);
506
+ }
470
507
  async function checkHealth() {
471
508
  try {
472
509
  return (await fetch(host)).ok;
@@ -475,9 +512,15 @@ async function checkHealth() {
475
512
  }
476
513
  }
477
514
  async function* streamChat(messages, model, tools, signal) {
515
+ const providerMessages = messages.map(({ role, content, images, tool_calls }) => ({
516
+ role,
517
+ content,
518
+ ...images ? { images } : {},
519
+ ...tool_calls ? { tool_calls } : {}
520
+ }));
478
521
  const response = await client.chat({
479
522
  model,
480
- messages,
523
+ messages: providerMessages,
481
524
  stream: true,
482
525
  tools,
483
526
  // v8 ignore next
@@ -759,10 +802,10 @@ var TOOLS = [
759
802
  type: "string",
760
803
  description: "The path to the directory to list"
761
804
  } }, ["path"]),
762
- defineTool(GREP_SEARCH, "Search for a pattern in files within a directory", {
805
+ defineTool(GREP_SEARCH, "Search files within a directory; multi-word queries also match common code identifier forms", {
763
806
  pattern: {
764
807
  type: "string",
765
- description: "The regex pattern to search for"
808
+ description: "The regex, phrase, or code concept to search for"
766
809
  },
767
810
  path: {
768
811
  type: "string",
@@ -838,6 +881,76 @@ async function runShell(command) {
838
881
  }
839
882
  //#endregion
840
883
  //#region src/utils/tools/filesystem.ts
884
+ var DIFF_CONTEXT_LINES = 3;
885
+ var DIFF_MAX_LINES = 120;
886
+ var DIFF_MAX_CHARS = 12e3;
887
+ function splitLines(content) {
888
+ return content.split("\n");
889
+ }
890
+ function createUnifiedDiff(filePath, beforeContent, afterContent) {
891
+ const beforeLines = splitLines(beforeContent);
892
+ const afterLines = splitLines(afterContent);
893
+ let commonPrefix = 0;
894
+ while (commonPrefix < beforeLines.length && commonPrefix < afterLines.length && beforeLines[commonPrefix] === afterLines[commonPrefix]) commonPrefix += 1;
895
+ let commonSuffix = 0;
896
+ while (commonSuffix < beforeLines.length - commonPrefix && commonSuffix < afterLines.length - commonPrefix && beforeLines[beforeLines.length - 1 - commonSuffix] === afterLines[afterLines.length - 1 - commonSuffix]) commonSuffix += 1;
897
+ const beforeChangeEnd = beforeLines.length - commonSuffix;
898
+ const afterChangeEnd = afterLines.length - commonSuffix;
899
+ const hunkStart = Math.max(0, commonPrefix - DIFF_CONTEXT_LINES);
900
+ const beforeHunkEnd = Math.min(beforeLines.length, beforeChangeEnd + DIFF_CONTEXT_LINES);
901
+ const afterHunkEnd = Math.min(afterLines.length, afterChangeEnd + DIFF_CONTEXT_LINES);
902
+ const beforeHunkLines = beforeLines.slice(hunkStart, beforeHunkEnd);
903
+ const afterHunkLines = afterLines.slice(hunkStart, afterHunkEnd);
904
+ const beforeChangedLines = beforeLines.slice(commonPrefix, beforeChangeEnd);
905
+ const afterChangedLines = afterLines.slice(commonPrefix, afterChangeEnd);
906
+ const contextBefore = beforeLines.slice(hunkStart, commonPrefix);
907
+ const contextAfter = beforeLines.slice(beforeChangeEnd, beforeHunkEnd);
908
+ return [
909
+ `--- ${filePath}`,
910
+ `+++ ${filePath}`,
911
+ `@@ -${String(hunkStart + 1)},${String(beforeHunkLines.length)} +${String(hunkStart + 1)},${String(afterHunkLines.length)} @@`,
912
+ ...contextBefore.map((line) => ` ${line}`),
913
+ ...beforeChangedLines.map((line) => `-${line}`),
914
+ ...afterChangedLines.map((line) => `+${line}`),
915
+ ...contextAfter.map((line) => ` ${line}`)
916
+ ].join("\n");
917
+ }
918
+ function truncateDiff(diff) {
919
+ const lines = diff.split("\n");
920
+ let visibleLines = lines.slice(0, DIFF_MAX_LINES);
921
+ let truncated = lines.length > DIFF_MAX_LINES;
922
+ while (visibleLines.join("\n").length > DIFF_MAX_CHARS) {
923
+ visibleLines = visibleLines.slice(0, -1);
924
+ truncated = true;
925
+ }
926
+ if (truncated) visibleLines = [...visibleLines, `[diff truncated: showing ${String(visibleLines.length)} of ${String(lines.length)} lines]`];
927
+ return {
928
+ visible: visibleLines.join("\n"),
929
+ truncated,
930
+ totalLines: lines.length,
931
+ visibleLines: Math.min(visibleLines.length, lines.length)
932
+ };
933
+ }
934
+ function buildSearchPatterns(pattern) {
935
+ const words = pattern.trim().split(/[^a-zA-Z0-9]+/).filter(Boolean);
936
+ if (words.length < 2) return [pattern];
937
+ const camelCase = words.map((word, index) => index === 0 ? word.toLowerCase() : capitalize(word.toLowerCase())).join("");
938
+ const pascalCase = words.map((word) => capitalize(word.toLowerCase())).join("");
939
+ const snakeCase = words.map((word) => word.toLowerCase()).join("_");
940
+ const upperSnakeCase = snakeCase.toUpperCase();
941
+ const flexibleWhitespace = words.join(String.raw`\s+`);
942
+ return Array.from(new Set([
943
+ pattern,
944
+ flexibleWhitespace,
945
+ snakeCase,
946
+ upperSnakeCase,
947
+ camelCase,
948
+ pascalCase
949
+ ]));
950
+ }
951
+ function capitalize(value) {
952
+ return value.charAt(0).toUpperCase() + value.slice(1);
953
+ }
841
954
  /**
842
955
  * Read file contents
843
956
  */
@@ -887,8 +1000,19 @@ function editFile(filePath, oldText, newText) {
887
1000
  content: "",
888
1001
  error: `Exact text matched multiple locations in file: ${filePath}`
889
1002
  };
890
- writeFileSync(filePath, content.replace(oldText, newText), "utf8");
891
- return { content: `File edited successfully: ${filePath}` };
1003
+ const updatedContent = content.replace(oldText, newText);
1004
+ const truncatedDiff = truncateDiff(createUnifiedDiff(filePath, content, updatedContent));
1005
+ writeFileSync(filePath, updatedContent, "utf8");
1006
+ return {
1007
+ content: `File edited successfully: ${filePath}`,
1008
+ diff: {
1009
+ path: filePath,
1010
+ visible: truncatedDiff.visible,
1011
+ truncated: truncatedDiff.truncated,
1012
+ totalLines: truncatedDiff.totalLines,
1013
+ visibleLines: truncatedDiff.visibleLines
1014
+ }
1015
+ };
892
1016
  } catch (error) {
893
1017
  return {
894
1018
  content: "",
@@ -943,17 +1067,17 @@ function listDir(dirPath) {
943
1067
  * Search for pattern in files using ripgrep if available, fallback to Node.js
944
1068
  */
945
1069
  async function grepSearch(pattern, dirPath) {
946
- try {
947
- const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${pattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
948
- // v8 ignore next
949
- return { content: stdout || "No matches found" };
1070
+ const patterns = buildSearchPatterns(pattern);
1071
+ for (const searchPattern of patterns) try {
1072
+ const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${searchPattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
1073
+ if (stdout) return { content: stdout };
950
1074
  } catch {}
951
1075
  try {
952
1076
  if (!existsSync(dirPath)) return {
953
1077
  content: "",
954
1078
  error: `Directory not found: ${dirPath}`
955
1079
  };
956
- const regex = new RegExp(pattern, "g");
1080
+ const regexes = patterns.map((searchPattern) => new RegExp(searchPattern));
957
1081
  const results = [];
958
1082
  function searchDirectory(currentPath) {
959
1083
  const entries = readdirSync(currentPath, { withFileTypes: true });
@@ -963,9 +1087,9 @@ async function grepSearch(pattern, dirPath) {
963
1087
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") searchDirectory(fullPath);
964
1088
  } else if (entry.isFile()) try {
965
1089
  const lines = readFileSync(fullPath, "utf8").split("\n");
966
- for (let i = 0; i < lines.length; i++) {
967
- if (regex.test(lines[i])) results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
968
- regex.lastIndex = 0;
1090
+ for (let i = 0; i < lines.length; i++) for (const regex of regexes) if (regex.test(lines[i])) {
1091
+ results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
1092
+ break;
969
1093
  }
970
1094
  } catch {}
971
1095
  }
@@ -1170,18 +1294,93 @@ var REQUIRED_STRING_ARGS = {
1170
1294
  [WEB_SEARCH]: ["query"],
1171
1295
  [WEB_FETCH]: ["url"]
1172
1296
  };
1297
+ var TOOL_NAMES = new Set(Object.values(tool_exports));
1298
+ function isToolName(name) {
1299
+ return TOOL_NAMES.has(name);
1300
+ }
1173
1301
  function validateArgs(name, args) {
1174
- const required = REQUIRED_STRING_ARGS[name] ?? [];
1302
+ const required = REQUIRED_STRING_ARGS[name];
1175
1303
  const received = Object.keys(args).join(", ") || "none";
1176
- for (const key of required) if (typeof args[key] !== "string") return {
1304
+ for (const key of required) if (typeof args[key] !== "string" || !args[key]) return {
1177
1305
  content: "",
1178
1306
  error: `Missing required argument: ${key} (received keys: ${received})`
1179
1307
  };
1308
+ if (name === "view_range") {
1309
+ if (!Number.isInteger(args.start) || !Number.isInteger(args.end)) return {
1310
+ content: "",
1311
+ error: `Missing required numeric arguments: start, end (received keys: ${received})`
1312
+ };
1313
+ if (args.start < 1 || args.end < args.start) return {
1314
+ content: "",
1315
+ error: "Invalid line range: start must be >= 1 and end must be >= start"
1316
+ };
1317
+ }
1318
+ if (name === "web_fetch") try {
1319
+ const url = new URL(args.url);
1320
+ if (url.protocol !== "http:" && url.protocol !== "https:") return {
1321
+ content: "",
1322
+ error: "URL must use http or https"
1323
+ };
1324
+ } catch {
1325
+ return {
1326
+ content: "",
1327
+ error: "Invalid URL"
1328
+ };
1329
+ }
1330
+ }
1331
+ function normalizeToolCall(toolCall) {
1332
+ const name = toolCall.function.name;
1333
+ const rawArguments = toolCall.function.arguments;
1334
+ if (!isToolName(name)) throw new Error(`Unknown tool: ${name}`);
1335
+ if (typeof rawArguments !== "object" || rawArguments === null || Array.isArray(rawArguments)) throw new Error(`Invalid arguments for tool: ${name}`);
1336
+ const normalizedArguments = rawArguments;
1337
+ const invalid = validateArgs(name, normalizedArguments);
1338
+ if (invalid?.error) throw new Error(invalid.error);
1339
+ return {
1340
+ name,
1341
+ arguments: normalizedArguments,
1342
+ requiresApproval: WRITE_TOOLS.has(name)
1343
+ };
1344
+ }
1345
+ function formatToolResultContent(toolName, result, args) {
1346
+ const formattedArgs = args ? `(${formatToolArguments(args)})` : "";
1347
+ const status = result.error ? "The requested action was NOT performed" : "";
1348
+ const content = result.content ? `\n${result.content}` : "";
1349
+ const error = result.error ? `\nError: ${result.error}` : "";
1350
+ return [
1351
+ `Tool ${toolName}${formattedArgs} result:`,
1352
+ status,
1353
+ content.trim(),
1354
+ error.trim()
1355
+ ].filter(Boolean).join("\n");
1356
+ }
1357
+ function formatToolArguments(args) {
1358
+ return JSON.stringify(args, (_, value) => {
1359
+ if (typeof value !== "string") return value;
1360
+ if (value.length <= 80 && !value.includes("\n")) return value;
1361
+ return `<${String(value.length)} chars>`;
1362
+ });
1363
+ }
1364
+ async function executeToolCall(toolCall, options) {
1365
+ try {
1366
+ const normalized = normalizeToolCall(toolCall);
1367
+ return await executeTool(normalized.name, normalized.arguments, options);
1368
+ } catch (error) {
1369
+ return {
1370
+ content: "",
1371
+ // v8 ignore next
1372
+ error: error instanceof Error ? error.message : String(error)
1373
+ };
1374
+ }
1180
1375
  }
1181
1376
  /**
1182
1377
  * Execute a tool by name with arguments
1183
1378
  */
1184
1379
  async function executeTool(name, args, options) {
1380
+ if (!isToolName(name)) return {
1381
+ content: "",
1382
+ error: `Unknown tool: ${name}`
1383
+ };
1185
1384
  if (options?.allowedTools && !options.allowedTools.has(name)) return {
1186
1385
  content: "",
1187
1386
  error: `Tool not allowed: ${name}`
@@ -1199,6 +1398,7 @@ async function executeTool(name, args, options) {
1199
1398
  case VIEW_RANGE: return viewRange(stringArgs.path, args.start, args.end);
1200
1399
  case WEB_SEARCH: return await webSearch(stringArgs.query);
1201
1400
  case WEB_FETCH: return await webFetch(stringArgs.url);
1401
+ // v8 ignore next 2
1202
1402
  default: return {
1203
1403
  content: "",
1204
1404
  error: `Unknown tool: ${name}`
@@ -1240,6 +1440,8 @@ async function checkForUpdate() {
1240
1440
  //#endregion
1241
1441
  //#region src/cli.ts
1242
1442
  var cli = cac("code-ollama");
1443
+ var MAX_TOOL_TURNS = 25;
1444
+ var MAX_TOOL_INTENT_CORRECTIONS = 2;
1243
1445
  cli.version(VERSION);
1244
1446
  cli.help();
1245
1447
  cli.command("run <model> <prompt>", "Run a one-off prompt").action(async (model, prompt) => {
@@ -1272,30 +1474,58 @@ async function runPrompt(model, prompt) {
1272
1474
  write("\n");
1273
1475
  }
1274
1476
  async function processRunStream(messages, model) {
1275
- const assistantMessage = {
1276
- role: ASSISTANT,
1277
- content: ""
1278
- };
1279
- for await (const chunk of streamChat(messages, model, TOOLS)) {
1280
- if (chunk.type === "content") {
1281
- assistantMessage.content += chunk.content;
1282
- write(chunk.content);
1283
- continue;
1477
+ let activeMessages = messages;
1478
+ let toolTurns = 0;
1479
+ let toolIntentCorrections = 0;
1480
+ while (toolTurns < MAX_TOOL_TURNS) {
1481
+ const assistantMessage = {
1482
+ role: ASSISTANT,
1483
+ content: ""
1484
+ };
1485
+ let nextMessages = null;
1486
+ for await (const chunk of streamChat(activeMessages, model, TOOLS)) {
1487
+ if (chunk.type === "content") {
1488
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content + chunk.content);
1489
+ write(chunk.content);
1490
+ continue;
1491
+ }
1492
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content);
1493
+ // v8 ignore next 3
1494
+ if (chunk.tool_calls.length === 0) continue;
1495
+ const committedMessages = [...activeMessages, assistantMessage];
1496
+ const toolResultMessages = [];
1497
+ for (const toolCall of chunk.tool_calls) {
1498
+ const result = await executeToolCall(toolCall);
1499
+ toolResultMessages.push({
1500
+ role: SYSTEM,
1501
+ content: formatToolResultContent(toolCall.function.name, result, toolCall.function.arguments)
1502
+ });
1503
+ }
1504
+ nextMessages = [...committedMessages, ...toolResultMessages];
1505
+ break;
1284
1506
  }
1285
- for (const toolCall of chunk.tool_calls) {
1286
- const result = await executeTool(toolCall.function.name, toolCall.function.arguments);
1287
- const toolResultMessage = {
1288
- role: SYSTEM,
1289
- content: `Tool ${toolCall.function.name} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
1290
- };
1291
- await processRunStream([
1292
- ...messages,
1293
- assistantMessage,
1294
- toolResultMessage
1295
- ], model);
1507
+ if (!nextMessages) {
1508
+ assistantMessage.content = sanitizeAssistantContent(assistantMessage.content);
1509
+ if (hasUncalledToolIntent(assistantMessage.content) && toolIntentCorrections < MAX_TOOL_INTENT_CORRECTIONS) {
1510
+ toolIntentCorrections += 1;
1511
+ activeMessages = [
1512
+ ...activeMessages,
1513
+ assistantMessage,
1514
+ {
1515
+ role: SYSTEM,
1516
+ content: TOOL_INTENT_CORRECTION
1517
+ }
1518
+ ];
1519
+ continue;
1520
+ }
1296
1521
  return;
1297
1522
  }
1523
+ activeMessages = nextMessages;
1524
+ toolTurns += 1;
1525
+ toolIntentCorrections = 0;
1298
1526
  }
1527
+ // v8 ignore next 3
1528
+ writeError("Tool execution stopped because the maximum tool turn limit was reached\n");
1299
1529
  }
1300
1530
  async function main(args = process.argv.slice(2)) {
1301
1531
  if (args.length) cli.parse([
@@ -1306,7 +1536,7 @@ async function main(args = process.argv.slice(2)) {
1306
1536
  else await launchTui();
1307
1537
  }
1308
1538
  async function launchTui(sessionId) {
1309
- const { renderApp } = await import("./assets/tui-48fT4HLs.js");
1539
+ const { renderApp } = await import("./assets/tui-CzkVRFXf.js");
1310
1540
  reset();
1311
1541
  renderApp(sessionId);
1312
1542
  }
@@ -1322,4 +1552,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
1322
1552
  if (isEntrypoint()) main();
1323
1553
  // v8 ignore stop
1324
1554
  //#endregion
1325
- export { HEADER_PREFIX as A, AUTO as B, streamChat as C, saveClipboardImage as D, removeClipboardImage as E, SYSTEM as F, REJECT as G, PLAN as H, USER as I, LIST$1 as J, NAME as K, PLAN_GENERATION_INSTRUCTION as L, LIST as M, getTheme as N, resetSystemMessage as O, ASSISTANT as P, BACK as R, pullModel as S, saveConfig as T, SAFE as U, LABEL as V, APPROVE as W, reset as _, WRITE_TOOLS as a, deleteModel as b, write as c, deleteSession as d, deleteSessionIfEmpty as f, clear as g, updateSessionModel as h, TOOLS as i, WARNING as j, withSystemMessage as k, appendMessage as l, loadSession as m, main, executeTool as n, tick as o, listSessions as p, VERSION as q, READ_TOOLS as r, color as s, checkForUpdate as t, createSession as u, setClearHandler as v, loadConfig as w, listModels as x, checkHealth as y, CATALOG as z };
1555
+ export { VERSION as $, loadConfig as A, ASSISTANT as B, checkHealth as C, pullModel as D, listModels as E, withSystemMessage as F, CATALOG as G, USER as H, HEADER_PREFIX as I, PLAN as J, AUTO as K, WARNING as L, removeClipboardImage as M, saveClipboardImage as N, sanitizeAssistantContent as O, resetSystemMessage as P, NAME as Q, LIST as R, TOOL_INTENT_CORRECTION as S, hasUncalledToolIntent as T, PLAN_GENERATION_INSTRUCTION as U, SYSTEM as V, BACK as W, APPROVE as X, SAFE as Y, REJECT as Z, loadSession as _, normalizeToolCall as a, reset as b, WRITE_TOOLS as c, write as d, LIST$1 as et, appendMessage as f, listSessions as g, deleteSessionIfEmpty as h, formatToolResultContent as i, saveConfig as j, streamChat as k, tick as l, deleteSession as m, main, executeTool as n, READ_TOOLS as o, createSession as p, LABEL as q, executeToolCall as r, TOOLS as s, checkForUpdate as t, color as u, updateSessionModel as v, deleteModel as w, setClearHandler as x, clear as y, getTheme as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Ollama coding agent that runs in your terminal",
5
5
  "author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
6
6
  "type": "module",
@@ -42,34 +42,34 @@
42
42
  "@inkjs/ui": "2.0.0",
43
43
  "@shikijs/cli": "4.1.0",
44
44
  "cac": "7.0.0",
45
- "ink": "7.0.3",
45
+ "ink": "7.0.5",
46
46
  "marked": "15.0.12",
47
47
  "marked-terminal": "7.3.0",
48
48
  "ollama": "0.6.3",
49
- "react": "19.2.6",
49
+ "react": "19.2.7",
50
50
  "uuid": "14.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@commitlint/cli": "21.0.1",
54
- "@commitlint/config-conventional": "21.0.1",
53
+ "@commitlint/cli": "21.0.2",
54
+ "@commitlint/config-conventional": "21.0.2",
55
55
  "@eslint/js": "10.0.1",
56
56
  "@types/node": "25.9.1",
57
- "@types/react": "19.2.15",
58
- "@vitest/coverage-v8": "4.1.7",
59
- "eslint": "10.4.0",
60
- "eslint-plugin-prettier": "5.5.5",
57
+ "@types/react": "19.2.16",
58
+ "@vitest/coverage-v8": "4.1.8",
59
+ "eslint": "10.4.1",
60
+ "eslint-plugin-prettier": "5.5.6",
61
61
  "eslint-plugin-simple-import-sort": "13.0.0",
62
62
  "globals": "17.6.0",
63
63
  "husky": "9.1.7",
64
64
  "ink-testing-library": "4.0.0",
65
- "lint-staged": "17.0.5",
65
+ "lint-staged": "17.0.7",
66
66
  "prettier": "3.8.3",
67
67
  "publint": "0.3.21",
68
- "tsx": "4.22.3",
68
+ "tsx": "4.22.4",
69
69
  "typescript": "6.0.3",
70
- "typescript-eslint": "8.59.4",
71
- "vite": "8.0.14",
72
- "vitest": "4.1.7"
70
+ "typescript-eslint": "8.60.1",
71
+ "vite": "8.0.16",
72
+ "vitest": "4.1.8"
73
73
  },
74
74
  "files": [
75
75
  "dist/"