code-ollama 0.19.0 → 0.20.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.
@@ -227,8 +227,6 @@ var Markdown = memo(function Markdown({ content, color, dimColor, theme = getThe
227
227
  //#endregion
228
228
  //#region src/components/Messages/layout.ts
229
229
  var ANSI_REGEX = new RegExp(String.raw`\u001B\[[0-9;]*m`, "g");
230
- var CODE_BLOCK_MARGIN_Y = 2;
231
- var CODE_BLOCK_BORDER_Y = 2;
232
230
  var CODE_BLOCK_CHROME_X = 4;
233
231
  function stripAnsi(value) {
234
232
  return value.replaceAll(ANSI_REGEX, "");
@@ -264,8 +262,7 @@ function countWrappedLines(content, width) {
264
262
  * @returns The total height in lines.
265
263
  */
266
264
  function getCodeBlockHeight(content, width) {
267
- const contentWidth = Math.max(1, width - CODE_BLOCK_CHROME_X);
268
- return CODE_BLOCK_MARGIN_Y + CODE_BLOCK_BORDER_Y + countWrappedLines(content, contentWidth);
265
+ return 4 + countWrappedLines(content, Math.max(1, width - CODE_BLOCK_CHROME_X));
269
266
  }
270
267
  /**
271
268
  * Calculates the total height of streaming text content based on wrapped lines.
package/dist/cli.js CHANGED
@@ -38,7 +38,7 @@ var LIST$1 = [
38
38
  //#endregion
39
39
  //#region package.json
40
40
  var name = "code-ollama";
41
- var version = "0.19.0";
41
+ var version = "0.20.0";
42
42
  //#endregion
43
43
  //#region src/constants/package.ts
44
44
  var NAME = name;
@@ -604,7 +604,8 @@ function createSession(model) {
604
604
  createdAt: now,
605
605
  updatedAt: now,
606
606
  title: DEFAULT_TITLE,
607
- model
607
+ model,
608
+ directory: process.cwd()
608
609
  };
609
610
  ensureSessionDirectory(id);
610
611
  writeMetadata(metadata);
@@ -614,7 +615,7 @@ function createSession(model) {
614
615
  messages: []
615
616
  };
616
617
  }
617
- function listSessions() {
618
+ function listSessions(directory = process.cwd()) {
618
619
  if (!existsSync(SESSIONS_DIRECTORY)) return [];
619
620
  return readdirSync(SESSIONS_DIRECTORY, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
620
621
  try {
@@ -622,7 +623,7 @@ function listSessions() {
622
623
  } catch {
623
624
  return [];
624
625
  }
625
- }).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
626
+ }).filter((metadata) => metadata.directory === directory).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
626
627
  }
627
628
  function loadSession(id) {
628
629
  return {
@@ -1154,6 +1155,29 @@ function formatSearchResults(source, results, note) {
1154
1155
  }
1155
1156
  //#endregion
1156
1157
  //#region src/utils/tools/dispatcher.ts
1158
+ var REQUIRED_STRING_ARGS = {
1159
+ [READ_FILE]: ["path"],
1160
+ [WRITE_FILE]: ["path", "content"],
1161
+ [EDIT_FILE]: [
1162
+ "path",
1163
+ "oldText",
1164
+ "newText"
1165
+ ],
1166
+ [RUN_SHELL]: ["command"],
1167
+ [LIST_DIR]: ["path"],
1168
+ [GREP_SEARCH]: ["pattern", "path"],
1169
+ [VIEW_RANGE]: ["path"],
1170
+ [WEB_SEARCH]: ["query"],
1171
+ [WEB_FETCH]: ["url"]
1172
+ };
1173
+ function validateArgs(name, args) {
1174
+ const required = REQUIRED_STRING_ARGS[name] ?? [];
1175
+ const received = Object.keys(args).join(", ") || "none";
1176
+ for (const key of required) if (typeof args[key] !== "string") return {
1177
+ content: "",
1178
+ error: `Missing required argument: ${key} (received keys: ${received})`
1179
+ };
1180
+ }
1157
1181
  /**
1158
1182
  * Execute a tool by name with arguments
1159
1183
  */
@@ -1162,16 +1186,19 @@ async function executeTool(name, args, options) {
1162
1186
  content: "",
1163
1187
  error: `Tool not allowed: ${name}`
1164
1188
  };
1189
+ const invalid = validateArgs(name, args);
1190
+ if (invalid) return invalid;
1191
+ const stringArgs = args;
1165
1192
  switch (name) {
1166
- case READ_FILE: return readFile(args.path);
1167
- case WRITE_FILE: return writeFile(args.path, args.content);
1168
- case EDIT_FILE: return editFile(args.path, args.oldText, args.newText);
1169
- case RUN_SHELL: return runShell(args.command);
1170
- case LIST_DIR: return listDir(args.path);
1171
- case GREP_SEARCH: return await grepSearch(args.pattern, args.path);
1172
- case VIEW_RANGE: return viewRange(args.path, args.start, args.end);
1173
- case WEB_SEARCH: return await webSearch(args.query);
1174
- case WEB_FETCH: return await webFetch(args.url);
1193
+ case READ_FILE: return readFile(stringArgs.path);
1194
+ case WRITE_FILE: return writeFile(stringArgs.path, stringArgs.content);
1195
+ case EDIT_FILE: return editFile(stringArgs.path, stringArgs.oldText, stringArgs.newText);
1196
+ case RUN_SHELL: return runShell(stringArgs.command);
1197
+ case LIST_DIR: return listDir(stringArgs.path);
1198
+ case GREP_SEARCH: return await grepSearch(stringArgs.pattern, stringArgs.path);
1199
+ case VIEW_RANGE: return viewRange(stringArgs.path, args.start, args.end);
1200
+ case WEB_SEARCH: return await webSearch(stringArgs.query);
1201
+ case WEB_FETCH: return await webFetch(stringArgs.url);
1175
1202
  default: return {
1176
1203
  content: "",
1177
1204
  error: `Unknown tool: ${name}`
@@ -1193,7 +1220,12 @@ cli.command("run <model> <prompt>", "Run a one-off prompt").action(async (model,
1193
1220
  });
1194
1221
  cli.command("resume <sessionId>", "Resume a saved session").action(async (sessionId) => {
1195
1222
  try {
1196
- loadSession(sessionId);
1223
+ const loaded = loadSession(sessionId);
1224
+ if (loaded.metadata.directory && loaded.metadata.directory !== process.cwd()) {
1225
+ writeError(color(`${WARNING} Cannot resume: session belongs to ${loaded.metadata.directory}\n`, "yellow"));
1226
+ process.exitCode = 1;
1227
+ return;
1228
+ }
1197
1229
  await launchTui(sessionId);
1198
1230
  } catch (error) {
1199
1231
  writeError(`Error: ${error instanceof Error ? error.message : "Unknown error"}\n`);
@@ -1242,7 +1274,7 @@ async function main(args = process.argv.slice(2)) {
1242
1274
  else await launchTui();
1243
1275
  }
1244
1276
  async function launchTui(sessionId) {
1245
- const { renderApp } = await import("./assets/tui-JyjdXasW.js");
1277
+ const { renderApp } = await import("./assets/tui-DDbMjcMS.js");
1246
1278
  reset();
1247
1279
  renderApp(sessionId);
1248
1280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.19.0",
3
+ "version": "0.20.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",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "scripts": {
12
12
  "build": "vite build",
13
- "start": "tsx --tsconfig tsconfig.test.json src/cli.ts",
13
+ "start": "tsx src/cli.ts",
14
14
  "clean": "rm -rf coverage dist docs",
15
15
  "lint": "eslint .",
16
16
  "lint:fix": "npm run lint -- --fix",
@@ -53,9 +53,9 @@
53
53
  "@commitlint/cli": "21.0.1",
54
54
  "@commitlint/config-conventional": "21.0.1",
55
55
  "@eslint/js": "10.0.1",
56
- "@types/node": "25.9.0",
57
- "@types/react": "19.2.14",
58
- "@vitest/coverage-v8": "4.1.6",
56
+ "@types/node": "25.9.1",
57
+ "@types/react": "19.2.15",
58
+ "@vitest/coverage-v8": "4.1.7",
59
59
  "eslint": "10.4.0",
60
60
  "eslint-plugin-prettier": "5.5.5",
61
61
  "eslint-plugin-simple-import-sort": "13.0.0",
@@ -65,11 +65,11 @@
65
65
  "lint-staged": "17.0.5",
66
66
  "prettier": "3.8.3",
67
67
  "publint": "0.3.21",
68
- "tsx": "4.22.2",
68
+ "tsx": "4.22.3",
69
69
  "typescript": "6.0.3",
70
70
  "typescript-eslint": "8.59.4",
71
- "vite": "8.0.13",
72
- "vitest": "4.1.6"
71
+ "vite": "8.0.14",
72
+ "vitest": "4.1.7"
73
73
  },
74
74
  "files": [
75
75
  "dist/"