code-ollama 0.15.1 → 0.16.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.
@@ -734,16 +734,6 @@ function ToolApproval({ toolCall, onDecision }) {
734
734
  });
735
735
  }
736
736
  //#endregion
737
- //#region src/components/Chat/constants.ts
738
- var ACTION_NOT_PERFORMED = "The requested action was NOT performed";
739
- var PLAN_CHECKLIST_REMINDER = "Then display the execution plan as an unchecked Markdown checklist only";
740
- var PLAN_EXECUTION_REMINDER = `Do not claim success and do not call ${Array.from(WRITE_TOOLS).join(", ")} until the user approves execution`;
741
- var INTERRUPT_REASON = /* @__PURE__ */ function(INTERRUPT_REASON) {
742
- INTERRUPT_REASON["INTERRUPTED"] = "interrupted";
743
- INTERRUPT_REASON["REJECTED"] = "rejected";
744
- return INTERRUPT_REASON;
745
- }({});
746
- //#endregion
747
737
  //#region src/components/TextInput/TextInput.tsx
748
738
  function buildLineSegments(displayValue, cursorPosition, width) {
749
739
  const safeWidth = Math.max(1, width);
@@ -1043,17 +1033,28 @@ function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
1043
1033
  });
1044
1034
  }
1045
1035
  //#endregion
1046
- //#region src/components/Chat/Input.tsx
1036
+ //#region src/components/Chat/ChatInput.tsx
1047
1037
  function hasFileSuggestionQuery(input) {
1048
1038
  return /(^|.)@\S+/.test(input);
1049
1039
  }
1050
- function Input({ isDisabled = false, onInterrupt, onSubmit }) {
1040
+ function ChatInput({ history: sessionHistory, isDisabled = false, onInterrupt, onSubmit }) {
1051
1041
  const { exit } = useApp();
1042
+ const [history, setHistory] = useState(sessionHistory);
1043
+ const [historyIndex, setHistoryIndex] = useState(null);
1052
1044
  const [input, setInput] = useState("");
1053
1045
  const [cursorPosition, setCursorPosition] = useState(void 0);
1054
1046
  const fileSuggestionRef = useRef(null);
1047
+ useEffect(() => {
1048
+ setHistory(sessionHistory);
1049
+ setHistoryIndex(null);
1050
+ setInput("");
1051
+ setCursorPosition(void 0);
1052
+ fileSuggestionRef.current = null;
1053
+ }, [sessionHistory]);
1055
1054
  const resetInput = useCallback(() => {
1056
1055
  setInput("");
1056
+ setCursorPosition(void 0);
1057
+ setHistoryIndex(null);
1057
1058
  }, []);
1058
1059
  const handleSelectFileSuggestion = useCallback((nextInput) => {
1059
1060
  setInput(nextInput.value);
@@ -1077,15 +1078,58 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
1077
1078
  };
1078
1079
  } else fileSuggestionRef.current = null;
1079
1080
  }, [input]);
1081
+ const handleInputChange = useCallback((nextInput) => {
1082
+ setInput(nextInput);
1083
+ setHistoryIndex(null);
1084
+ }, []);
1080
1085
  const submitAndReset = useCallback((input) => {
1081
1086
  const trimmedInput = input.trim();
1082
1087
  if (!trimmedInput) return;
1083
1088
  onSubmit(trimmedInput);
1089
+ if (!trimmedInput.startsWith("/")) setHistory((previousHistory) => [...previousHistory, trimmedInput]);
1084
1090
  resetInput();
1085
1091
  fileSuggestionRef.current = null;
1086
1092
  }, [onSubmit, resetInput]);
1087
1093
  const showCommandMenu = input.startsWith("/");
1088
1094
  const showFileSuggestions = !showCommandMenu && hasFileSuggestionQuery(input);
1095
+ const handleHistoryNavigation = useCallback((direction) => {
1096
+ if (!history.length || showFileSuggestions) return;
1097
+ if (direction === "up") {
1098
+ if (historyIndex === null) {
1099
+ if (input) return;
1100
+ const nextIndex = history.length - 1;
1101
+ const nextInput = history[nextIndex];
1102
+ setHistoryIndex(nextIndex);
1103
+ setInput(nextInput);
1104
+ setCursorPosition(nextInput.length);
1105
+ return;
1106
+ }
1107
+ if (historyIndex === 0) return;
1108
+ const nextIndex = historyIndex - 1;
1109
+ const nextInput = history[nextIndex];
1110
+ setHistoryIndex(nextIndex);
1111
+ setInput(nextInput);
1112
+ setCursorPosition(nextInput.length);
1113
+ return;
1114
+ }
1115
+ if (historyIndex === null) return;
1116
+ if (historyIndex === history.length - 1) {
1117
+ setHistoryIndex(null);
1118
+ setInput("");
1119
+ setCursorPosition(0);
1120
+ return;
1121
+ }
1122
+ const nextIndex = historyIndex + 1;
1123
+ const nextInput = history[nextIndex];
1124
+ setHistoryIndex(nextIndex);
1125
+ setInput(nextInput);
1126
+ setCursorPosition(nextInput.length);
1127
+ }, [
1128
+ history,
1129
+ historyIndex,
1130
+ input,
1131
+ showFileSuggestions
1132
+ ]);
1089
1133
  const handleSubmitText = useCallback((input) => {
1090
1134
  if (input.startsWith("/")) return;
1091
1135
  if (hasFileSuggestionQuery(input)) {
@@ -1097,8 +1141,8 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
1097
1141
  const handleSubmitCommand = useCallback((input) => {
1098
1142
  if (LIST.find(({ name }) => name === input)) submitAndReset(input);
1099
1143
  }, [submitAndReset]);
1100
- useInput((_input, key) => {
1101
- const isCtrlC = key.ctrl && _input === "c";
1144
+ useInput((inputKey, key) => {
1145
+ const isCtrlC = key.ctrl && inputKey === "c";
1102
1146
  if (isDisabled) {
1103
1147
  if (key.escape || isCtrlC) onInterrupt?.();
1104
1148
  return;
@@ -1110,6 +1154,11 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
1110
1154
  }
1111
1155
  exit();
1112
1156
  }
1157
+ if (key.upArrow) {
1158
+ handleHistoryNavigation("up");
1159
+ return;
1160
+ }
1161
+ if (key.downArrow) handleHistoryNavigation("down");
1113
1162
  });
1114
1163
  return /* @__PURE__ */ jsxs(Box, {
1115
1164
  flexDirection: "column",
@@ -1119,7 +1168,7 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
1119
1168
  isDisabled,
1120
1169
  cursorPosition,
1121
1170
  wrapIndent: 2,
1122
- onChange: setInput,
1171
+ onChange: handleInputChange,
1123
1172
  onSubmit: handleSubmitText,
1124
1173
  placeholder: "Ask anything... (/ commands, @ files)"
1125
1174
  })] }),
@@ -1137,6 +1186,16 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
1137
1186
  });
1138
1187
  }
1139
1188
  //#endregion
1189
+ //#region src/components/Chat/constants.ts
1190
+ var ACTION_NOT_PERFORMED = "The requested action was NOT performed";
1191
+ var PLAN_CHECKLIST_REMINDER = "Then display the execution plan as an unchecked Markdown checklist only";
1192
+ var PLAN_EXECUTION_REMINDER = `Do not claim success and do not call ${Array.from(WRITE_TOOLS).join(", ")} until the user approves execution`;
1193
+ var INTERRUPT_REASON = /* @__PURE__ */ function(INTERRUPT_REASON) {
1194
+ INTERRUPT_REASON["INTERRUPTED"] = "interrupted";
1195
+ INTERRUPT_REASON["REJECTED"] = "rejected";
1196
+ return INTERRUPT_REASON;
1197
+ }({});
1198
+ //#endregion
1140
1199
  //#region src/components/Chat/plan.ts
1141
1200
  function hasExecutablePlan(content) {
1142
1201
  return content.split("\n").some((line) => {
@@ -1148,6 +1207,7 @@ function hasExecutablePlan(content) {
1148
1207
  //#region src/components/Chat/Chat.tsx
1149
1208
  function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onModeChange, sessionId }) {
1150
1209
  const sessionMessages = initialMessages ?? [];
1210
+ const history = useMemo(() => sessionMessages.flatMap(({ role, content }) => role === "user" && !content.startsWith("/") ? [content] : []), [sessionMessages]);
1151
1211
  const [messages, setMessages] = useState(sessionMessages);
1152
1212
  const [streamingMessage, setStreamingMessage] = useState(null);
1153
1213
  const [isLoading, setIsLoading] = useState(false);
@@ -1496,7 +1556,8 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1496
1556
  }),
1497
1557
  !pendingPlan && !pendingToolCall && /* @__PURE__ */ jsx(Box, {
1498
1558
  marginTop: 1,
1499
- children: /* @__PURE__ */ jsx(Input, {
1559
+ children: /* @__PURE__ */ jsx(ChatInput, {
1560
+ history,
1500
1561
  isDisabled: isLoading,
1501
1562
  onInterrupt: handleInterrupt,
1502
1563
  onSubmit: handleSubmit
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var LIST = [
33
33
  //#endregion
34
34
  //#region package.json
35
35
  var name = "code-ollama";
36
- var version = "0.15.1";
36
+ var version = "0.16.0";
37
37
  //#endregion
38
38
  //#region src/constants/package.ts
39
39
  var NAME = name;
@@ -931,7 +931,7 @@ async function main(args = process.argv.slice(2)) {
931
931
  else await launchTui();
932
932
  }
933
933
  async function launchTui(sessionId) {
934
- const { renderApp } = await import("./assets/tui-CSRbnCod.js");
934
+ const { renderApp } = await import("./assets/tui-85A3pZD2.js");
935
935
  reset();
936
936
  renderApp(sessionId);
937
937
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.15.1",
3
+ "version": "0.16.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",
@@ -57,7 +57,7 @@
57
57
  "@types/node": "25.8.0",
58
58
  "@types/react": "19.2.14",
59
59
  "@vitest/coverage-v8": "4.1.6",
60
- "eslint": "10.3.0",
60
+ "eslint": "10.4.0",
61
61
  "eslint-plugin-prettier": "5.5.5",
62
62
  "eslint-plugin-simple-import-sort": "13.0.0",
63
63
  "globals": "17.6.0",