code-ollama 0.24.0 → 0.24.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.
@@ -1411,7 +1411,7 @@ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, o
1411
1411
  }
1412
1412
  //#endregion
1413
1413
  //#region src/components/Chat/constants.ts
1414
- var ACTION_NOT_PERFORMED = "The requested action was NOT performed";
1414
+ var ACTION_NOT_PERFORMED = "The requested action did not complete successfully";
1415
1415
  var PLAN_CHECKLIST_REMINDER = "Then display the plan using either the Plan Needs Input or Proposed Plan Markdown template";
1416
1416
  var PLAN_EXECUTION_REMINDER = `Do not claim success and do not call ${Array.from(WRITE_TOOLS).join(", ")} until the user approves execution`;
1417
1417
  var ChatActionType = /* @__PURE__ */ function(ChatActionType) {
@@ -1445,6 +1445,18 @@ function hasExecutablePlan(content) {
1445
1445
  const nextSectionIndex = lines.findIndex((line, index) => index > executionStepsIndex && /^#{1,6}\s+\S/.test(line.trim()));
1446
1446
  return lines.slice(executionStepsIndex + 1, nextSectionIndex === -1 ? void 0 : nextSectionIndex).some((line) => /^(?:[-*]|\d+[.)])\s+\S/.test(line.trim()));
1447
1447
  }
1448
+ function isPlanModeFinal(content) {
1449
+ const firstHeading = content.split("\n").find((line) => line.trim())?.trim().toLowerCase();
1450
+ return isPlanNeedsInput(content) || firstHeading === "## proposed plan";
1451
+ }
1452
+ function isPlanNeedsInput(content) {
1453
+ return content.split("\n").find((line) => line.trim())?.trim().toLowerCase() === "## plan needs input";
1454
+ }
1455
+ function isDirectPlanAnswer(content) {
1456
+ const normalized = content.trim();
1457
+ if (!normalized || isPlanModeFinal(normalized)) return false;
1458
+ return !/^(?:research(?: is)? complete|done)\.?$/i.test(normalized);
1459
+ }
1448
1460
  //#endregion
1449
1461
  //#region src/components/Chat/reducer.ts
1450
1462
  function createInitialChatState(messages = []) {
@@ -1667,7 +1679,9 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1667
1679
  toolResultMessages.push(buildToolResultMessage(toolCall.function.name, {
1668
1680
  content: "",
1669
1681
  // v8 ignore next
1670
- error: error instanceof Error ? error.message : String(error)
1682
+ error: error instanceof Error ? error.message : String(error),
1683
+ // v8 ignore next
1684
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {}
1671
1685
  }));
1672
1686
  }
1673
1687
  nextMessages = [...updatedMessages, ...toolResultMessages];
@@ -1746,7 +1760,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1746
1760
  mode,
1747
1761
  theme
1748
1762
  ]);
1749
- const processStreamReadOnly = useCallback(async (currentMessages) => {
1763
+ const processStreamReadOnly = useCallback(async (currentMessages, toolIntentCorrections = 0) => {
1750
1764
  const modelName = model;
1751
1765
  // v8 ignore next
1752
1766
  if (!modelName) throw new Error("Model is required");
@@ -1843,7 +1857,9 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1843
1857
  /* v8 ignore start */
1844
1858
  const toolResultMessage = buildToolResultMessage(toolCall.function.name, {
1845
1859
  content: "",
1846
- error: error instanceof Error ? error.message : String(error)
1860
+ error: error instanceof Error ? error.message : String(error),
1861
+ // v8 ignore next
1862
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {}
1847
1863
  });
1848
1864
  const newMessages = [...updatedMessages, toolResultMessage];
1849
1865
  dispatch({
@@ -1866,6 +1882,13 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1866
1882
  }
1867
1883
  await prewarmCodeBlocks(assistantMessage.content, theme);
1868
1884
  const researchMessages = commitAssistantMessage();
1885
+ if (isPlanNeedsInput(assistantMessage.content)) {
1886
+ dispatch({
1887
+ type: ChatActionType.SetLoading,
1888
+ isLoading: false
1889
+ });
1890
+ return;
1891
+ }
1869
1892
  if (hasExecutablePlan(assistantMessage.content)) {
1870
1893
  dispatch({
1871
1894
  type: ChatActionType.RequestPlanReview,
@@ -1876,6 +1899,32 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1876
1899
  });
1877
1900
  return;
1878
1901
  }
1902
+ if (hasUncalledToolIntent(assistantMessage.content) && toolIntentCorrections < MAX_TOOL_INTENT_CORRECTIONS) {
1903
+ const correctedMessages = [...researchMessages, {
1904
+ role: SYSTEM,
1905
+ content: TOOL_INTENT_CORRECTION
1906
+ }];
1907
+ dispatch({
1908
+ type: ChatActionType.CommitMessages,
1909
+ messages: correctedMessages
1910
+ });
1911
+ await processStreamReadOnly(correctedMessages, toolIntentCorrections + 1);
1912
+ return;
1913
+ }
1914
+ if (isPlanModeFinal(assistantMessage.content)) {
1915
+ dispatch({
1916
+ type: ChatActionType.SetLoading,
1917
+ isLoading: false
1918
+ });
1919
+ return;
1920
+ }
1921
+ if (currentMessages.some((message) => !!message.toolResult) && isDirectPlanAnswer(assistantMessage.content)) {
1922
+ dispatch({
1923
+ type: ChatActionType.SetLoading,
1924
+ isLoading: false
1925
+ });
1926
+ return;
1927
+ }
1879
1928
  const planInstruction = {
1880
1929
  role: SYSTEM,
1881
1930
  content: PLAN_GENERATION_INSTRUCTION
@@ -2003,6 +2052,13 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
2003
2052
  });
2004
2053
  switch (decision) {
2005
2054
  case APPROVE: {
2055
+ dispatch({
2056
+ type: ChatActionType.SetStreamingMessage,
2057
+ message: {
2058
+ role: ASSISTANT,
2059
+ content: ""
2060
+ }
2061
+ });
2006
2062
  const result = await executeToolCall(toolCall);
2007
2063
  const toolResultMessage = buildToolResultMessage(toolCall.function.name, result, toolCall.function.arguments);
2008
2064
  const newMessages = [...approvedMessages, toolResultMessage];
package/dist/cli.js CHANGED
@@ -50,7 +50,7 @@ var LIST$1 = [
50
50
  //#endregion
51
51
  //#region package.json
52
52
  var name = "code-ollama";
53
- var version = "0.24.0";
53
+ var version = "0.24.1";
54
54
  //#endregion
55
55
  //#region src/constants/package.ts
56
56
  var NAME = name;
@@ -230,10 +230,18 @@ Use the exact headings shown below
230
230
  ${PLAN_RESPONSE_TEMPLATE}`;
231
231
  var PLAN_INSTRUCTION = `Plan mode is active
232
232
 
233
+ Explore first:
234
+ - If the user provides an exact file path, inspect it with read_file before planning changes
235
+ - If the user asks "where", names an identifier/symbol, or asks where behavior is implemented, search the codebase with grep_search before answering
236
+ - If the user asks about project structure without a target identifier or path, use list_dir or find_files to locate likely files
237
+ - Prefer targeted grep_search for exact names over broad directory listing when the user provides an identifier
238
+ - After each read-only tool result, decide whether another read-only tool would materially improve the answer
239
+ - Do not produce Plan Needs Input while also saying you will use another read-only tool; call that tool instead
240
+
233
241
  Only use read-only tools: ${PLAN_READ_TOOLS}
234
242
  Do not call ${PLAN_WRITE_TOOLS} during Plan mode
235
243
  Use read-only tools to resolve discoverable facts before asking questions
236
- If the user asks to search, inspect, find, read, or locate something, use read-only tools immediately
244
+ If the user asks to search, inspect, find, read, locate, change, adjust, update, edit, configure, or identify something, use read-only tools immediately
237
245
  Only ask questions for user preferences or product decisions that cannot be discovered from available tools
238
246
  When enough context is available, stop calling tools and produce either Plan Needs Input or Proposed Plan using the required template
239
247
 
@@ -956,6 +964,11 @@ var SHELL_EXEC_OPTIONS = {
956
964
  timeout: 3e4,
957
965
  maxBuffer: 1024 * 1024
958
966
  };
967
+ function getErrorOutput(error) {
968
+ if (typeof error !== "object" || error === null) return "";
969
+ const output = error;
970
+ return [output.stdout, output.stderr].filter((value) => typeof value === "string" && !!value).join("\n");
971
+ }
959
972
  /**
960
973
  * Execute shell command with shared options (throws on error)
961
974
  */
@@ -970,9 +983,12 @@ async function runShell(command) {
970
983
  const { stdout, stderr } = await execShell(command);
971
984
  return { content: stdout || stderr };
972
985
  } catch (error) {
986
+ const message = error instanceof Error ? error.message : String(error);
973
987
  return {
974
- content: "",
975
- error: `Command failed: ${error instanceof Error ? error.message : String(error)}`
988
+ content: getErrorOutput(error),
989
+ error: `Command failed: ${message}`,
990
+ // v8 ignore next
991
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {}
976
992
  };
977
993
  }
978
994
  }
@@ -1617,14 +1633,16 @@ function normalizeToolCall(toolCall) {
1617
1633
  }
1618
1634
  function formatToolResultContent(toolName, result, args) {
1619
1635
  const formattedArgs = args ? `(${formatToolArguments(args)})` : "";
1620
- const status = result.error ? "The requested action was NOT performed" : "";
1636
+ const status = result.error ? "The requested action did not complete successfully" : "";
1621
1637
  const content = result.content ? `\n${result.content}` : "";
1622
1638
  const error = result.error ? `\nError: ${result.error}` : "";
1639
+ const stack = result.error && result.stack ? `\nStack trace:\n${result.stack}` : "";
1623
1640
  return [
1624
1641
  `Tool ${toolName}${formattedArgs} result:`,
1625
1642
  status,
1626
1643
  content.trim(),
1627
- error.trim()
1644
+ error.trim(),
1645
+ stack.trim()
1628
1646
  ].filter(Boolean).join("\n");
1629
1647
  }
1630
1648
  function formatToolArguments(args) {
@@ -1642,7 +1660,9 @@ async function executeToolCall(toolCall, options) {
1642
1660
  return {
1643
1661
  content: "",
1644
1662
  // v8 ignore next
1645
- error: error instanceof Error ? error.message : String(error)
1663
+ error: error instanceof Error ? error.message : String(error),
1664
+ // v8 ignore next
1665
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {}
1646
1666
  };
1647
1667
  }
1648
1668
  }
@@ -1820,7 +1840,7 @@ async function main(args = process.argv.slice(2)) {
1820
1840
  else await launchTui();
1821
1841
  }
1822
1842
  async function launchTui(sessionId) {
1823
- const { renderApp } = await import("./assets/tui-DEmaVgHT.js");
1843
+ const { renderApp } = await import("./assets/tui-CboegfoT.js");
1824
1844
  reset();
1825
1845
  renderApp(sessionId);
1826
1846
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
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",
@@ -20,7 +20,7 @@
20
20
  "prepublishOnly": "npm run build && npm run lint && npm run lint:tsc && npm run test:ci",
21
21
  "test": "vitest run",
22
22
  "test:ci": "CI=true npm test -- --color --coverage",
23
- "test:watch": "vitest --coverage.enabled=false"
23
+ "test:watch": "vitest"
24
24
  },
25
25
  "repository": {
26
26
  "type": "git",