code-ollama 0.1.0 → 0.2.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/dist/assets/tui-DSR1MJGd.js +438 -0
- package/dist/cli.js +471 -42
- package/package.json +2 -2
- package/dist/tui-Bu6wAbeu.js +0 -559
- package/dist/utils-DBXrYZEs.js +0 -283
package/dist/cli.js
CHANGED
|
@@ -1,70 +1,499 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
3
|
+
import cac from "cac";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { Ollama } from "ollama";
|
|
7
|
+
import { exec } from "node:child_process";
|
|
8
|
+
import { promisify } from "node:util";
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/constants/package.ts
|
|
11
|
+
var VERSION = "0.2.0";
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/constants/prompt.ts
|
|
14
|
+
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, and searching code
|
|
15
|
+
|
|
16
|
+
Follow these rules:
|
|
17
|
+
1. Always use available tools rather than guessing file contents or code behavior
|
|
18
|
+
2. Read files before editing them to understand context
|
|
19
|
+
3. When writing files, provide complete, working code
|
|
20
|
+
4. Explain your reasoning when making non-trivial changes
|
|
21
|
+
5. Prefer minimal changes that achieve the goal
|
|
22
|
+
6. Confirm with the user before destructive operations
|
|
23
|
+
|
|
24
|
+
When tools return results, incorporate them into your response naturally`;
|
|
25
|
+
var TOOL_INSTRUCTIONS = `Available tools:
|
|
26
|
+
- read_file: Read file contents at a path
|
|
27
|
+
- write_file: Write content to a file (requires approval)
|
|
28
|
+
- edit_file: Replace one exact text match in a file (requires approval)
|
|
29
|
+
- list_dir: List files in a directory
|
|
30
|
+
- grep_search: Search code with regex
|
|
31
|
+
- run_shell: Execute shell commands (requires approval)
|
|
32
|
+
|
|
33
|
+
Always use tools when you need to:
|
|
34
|
+
- Check file contents before referencing them
|
|
35
|
+
- Make file changes
|
|
36
|
+
- Explore project structure
|
|
37
|
+
- Search the codebase`;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/constants/role.ts
|
|
40
|
+
var ROLE = {
|
|
41
|
+
USER: "user",
|
|
42
|
+
ASSISTANT: "assistant",
|
|
43
|
+
SYSTEM: "system"
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/constants/tool.ts
|
|
47
|
+
var NAME = {
|
|
48
|
+
READ_FILE: "read_file",
|
|
49
|
+
WRITE_FILE: "write_file",
|
|
50
|
+
EDIT_FILE: "edit_file",
|
|
51
|
+
RUN_SHELL: "run_shell",
|
|
52
|
+
LIST_DIR: "list_dir",
|
|
53
|
+
GREP_SEARCH: "grep_search",
|
|
54
|
+
VIEW_RANGE: "view_range"
|
|
55
|
+
};
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/utils/agents.ts
|
|
58
|
+
var AGENTS_FILE = "AGENTS.md";
|
|
59
|
+
function loadAgentsContent() {
|
|
60
|
+
const agentsPath = join(process.cwd(), AGENTS_FILE);
|
|
61
|
+
if (!existsSync(agentsPath)) return null;
|
|
62
|
+
try {
|
|
63
|
+
return readFileSync(agentsPath, "utf8");
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function buildSystemPrompt() {
|
|
69
|
+
const parts = [BASE_SYSTEM_PROMPT];
|
|
70
|
+
const agentsContent = loadAgentsContent();
|
|
71
|
+
if (agentsContent) parts.push("\n\nProject context from AGENTS.md:\n", agentsContent);
|
|
72
|
+
parts.push("\n\n", TOOL_INSTRUCTIONS);
|
|
73
|
+
return parts.join("");
|
|
74
|
+
}
|
|
75
|
+
function createSystemMessage() {
|
|
76
|
+
return {
|
|
77
|
+
role: ROLE.SYSTEM,
|
|
78
|
+
content: buildSystemPrompt()
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/utils/config.ts
|
|
83
|
+
var CONFIG_DIR = join(homedir(), ".code-ollama");
|
|
84
|
+
var CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
85
|
+
var DEFAULTS = {
|
|
86
|
+
host: "http://localhost:11434",
|
|
87
|
+
model: "gemma4"
|
|
88
|
+
};
|
|
89
|
+
function readFile$1() {
|
|
90
|
+
if (!existsSync(CONFIG_PATH)) return {};
|
|
91
|
+
try {
|
|
92
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
93
|
+
} catch {
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function loadConfig() {
|
|
98
|
+
const file = readFile$1();
|
|
99
|
+
return {
|
|
100
|
+
host: process.env.OLLAMA_HOST ?? file.host ?? DEFAULTS.host,
|
|
101
|
+
model: process.env.OLLAMA_MODEL ?? file.model ?? DEFAULTS.model
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function saveConfig(patch) {
|
|
105
|
+
const updated = {
|
|
106
|
+
...readFile$1(),
|
|
107
|
+
...patch
|
|
108
|
+
};
|
|
109
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
110
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/utils/ollama.ts
|
|
114
|
+
var { host, model: DEFAULT_MODEL } = loadConfig();
|
|
115
|
+
var client = new Ollama({ host });
|
|
116
|
+
async function* streamChat(messages, model = DEFAULT_MODEL, tools) {
|
|
117
|
+
const response = await client.chat({
|
|
118
|
+
model,
|
|
119
|
+
messages,
|
|
120
|
+
stream: true,
|
|
121
|
+
tools
|
|
122
|
+
});
|
|
123
|
+
for await (const chunk of response) {
|
|
124
|
+
if (chunk.message.content) yield {
|
|
125
|
+
type: "content",
|
|
126
|
+
content: chunk.message.content
|
|
127
|
+
};
|
|
128
|
+
if (chunk.message.tool_calls) yield {
|
|
129
|
+
type: "tool_calls",
|
|
130
|
+
tool_calls: chunk.message.tool_calls
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function listModels() {
|
|
135
|
+
const { models } = await client.list();
|
|
136
|
+
return models.map(({ name }) => name);
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/utils/screen.ts
|
|
140
|
+
var CLEAR = "\x1Bc";
|
|
141
|
+
function clear() {
|
|
142
|
+
process.stdout.write(CLEAR);
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/utils/tools.ts
|
|
146
|
+
var execAsync = promisify(exec);
|
|
147
|
+
/**
|
|
148
|
+
* Helper to define tool parameters
|
|
149
|
+
*/
|
|
150
|
+
function defineTool(name, description, params, required) {
|
|
151
|
+
return {
|
|
152
|
+
type: "function",
|
|
153
|
+
function: {
|
|
154
|
+
name,
|
|
155
|
+
description,
|
|
156
|
+
parameters: {
|
|
157
|
+
type: "object",
|
|
158
|
+
properties: params,
|
|
159
|
+
required
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Tool definitions for Ollama API
|
|
166
|
+
*/
|
|
167
|
+
var TOOLS = [
|
|
168
|
+
defineTool(NAME.READ_FILE, "Read the contents of a file at the specified path", { path: {
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "The path to the file to read"
|
|
171
|
+
} }, ["path"]),
|
|
172
|
+
defineTool(NAME.WRITE_FILE, "Write content to a file at the specified path", {
|
|
173
|
+
path: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "The path to the file to write"
|
|
176
|
+
},
|
|
177
|
+
content: {
|
|
178
|
+
type: "string",
|
|
179
|
+
description: "The content to write to the file"
|
|
180
|
+
}
|
|
181
|
+
}, ["path", "content"]),
|
|
182
|
+
defineTool(NAME.EDIT_FILE, "Replace one exact text match in an existing file at the specified path", {
|
|
183
|
+
path: {
|
|
184
|
+
type: "string",
|
|
185
|
+
description: "The path to the file to edit"
|
|
186
|
+
},
|
|
187
|
+
oldText: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description: "The exact existing text to replace"
|
|
190
|
+
},
|
|
191
|
+
newText: {
|
|
192
|
+
type: "string",
|
|
193
|
+
description: "The replacement text to write in place of oldText"
|
|
194
|
+
}
|
|
195
|
+
}, [
|
|
196
|
+
"path",
|
|
197
|
+
"oldText",
|
|
198
|
+
"newText"
|
|
199
|
+
]),
|
|
200
|
+
defineTool(NAME.RUN_SHELL, "Execute a shell command", { command: {
|
|
201
|
+
type: "string",
|
|
202
|
+
description: "The shell command to execute"
|
|
203
|
+
} }, ["command"]),
|
|
204
|
+
defineTool(NAME.LIST_DIR, "List the contents of a directory", { path: {
|
|
205
|
+
type: "string",
|
|
206
|
+
description: "The path to the directory to list"
|
|
207
|
+
} }, ["path"]),
|
|
208
|
+
defineTool(NAME.GREP_SEARCH, "Search for a pattern in files within a directory", {
|
|
209
|
+
pattern: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "The regex pattern to search for"
|
|
212
|
+
},
|
|
213
|
+
path: {
|
|
214
|
+
type: "string",
|
|
215
|
+
description: "The directory path to search in"
|
|
216
|
+
}
|
|
217
|
+
}, ["pattern", "path"]),
|
|
218
|
+
defineTool(NAME.VIEW_RANGE, "View a specific range of lines from a file", {
|
|
219
|
+
path: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "The path to the file"
|
|
222
|
+
},
|
|
223
|
+
start: {
|
|
224
|
+
type: "number",
|
|
225
|
+
description: "The starting line number (1-indexed)"
|
|
226
|
+
},
|
|
227
|
+
end: {
|
|
228
|
+
type: "number",
|
|
229
|
+
description: "The ending line number (inclusive)"
|
|
230
|
+
}
|
|
231
|
+
}, [
|
|
232
|
+
"path",
|
|
233
|
+
"start",
|
|
234
|
+
"end"
|
|
235
|
+
])
|
|
236
|
+
];
|
|
237
|
+
var TOOLS_REQUIRING_APPROVAL = new Set([
|
|
238
|
+
NAME.WRITE_FILE,
|
|
239
|
+
NAME.EDIT_FILE,
|
|
240
|
+
NAME.RUN_SHELL
|
|
241
|
+
]);
|
|
242
|
+
/**
|
|
243
|
+
* Execute a tool by name with arguments
|
|
244
|
+
*/
|
|
245
|
+
async function executeTool(name, args) {
|
|
246
|
+
switch (name) {
|
|
247
|
+
case NAME.READ_FILE: return readFile(args.path);
|
|
248
|
+
case NAME.WRITE_FILE: return writeFile(args.path, args.content);
|
|
249
|
+
case NAME.EDIT_FILE: return editFile(args.path, args.oldText, args.newText);
|
|
250
|
+
case NAME.RUN_SHELL: return runShell(args.command);
|
|
251
|
+
case NAME.LIST_DIR: return listDir(args.path);
|
|
252
|
+
case NAME.GREP_SEARCH: return await grepSearch(args.pattern, args.path);
|
|
253
|
+
case NAME.VIEW_RANGE: return viewRange(args.path, args.start, args.end);
|
|
254
|
+
default: return {
|
|
255
|
+
content: "",
|
|
256
|
+
error: `Unknown tool: ${name}`
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Read file contents
|
|
262
|
+
*/
|
|
263
|
+
function readFile(filePath) {
|
|
264
|
+
try {
|
|
265
|
+
if (!existsSync(filePath)) return {
|
|
266
|
+
content: "",
|
|
267
|
+
error: `File not found: ${filePath}`
|
|
268
|
+
};
|
|
269
|
+
return { content: readFileSync(filePath, "utf8") };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
return {
|
|
272
|
+
content: "",
|
|
273
|
+
error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Write content to file
|
|
279
|
+
*/
|
|
280
|
+
function writeFile(filePath, content) {
|
|
281
|
+
try {
|
|
282
|
+
writeFileSync(filePath, content, "utf8");
|
|
283
|
+
return { content: `File written successfully: ${filePath}` };
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return {
|
|
286
|
+
content: "",
|
|
287
|
+
error: `Failed to write file: ${error instanceof Error ? error.message : String(error)}`
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Replace one exact text match in an existing file
|
|
293
|
+
*/
|
|
294
|
+
function editFile(filePath, oldText, newText) {
|
|
295
|
+
try {
|
|
296
|
+
if (!existsSync(filePath)) return {
|
|
297
|
+
content: "",
|
|
298
|
+
error: `File not found: ${filePath}`
|
|
299
|
+
};
|
|
300
|
+
const content = readFileSync(filePath, "utf8");
|
|
301
|
+
if (!content.includes(oldText)) return {
|
|
302
|
+
content: "",
|
|
303
|
+
error: `Exact text not found in file: ${filePath}`
|
|
304
|
+
};
|
|
305
|
+
if (content.split(oldText).length - 1 > 1) return {
|
|
306
|
+
content: "",
|
|
307
|
+
error: `Exact text matched multiple locations in file: ${filePath}`
|
|
308
|
+
};
|
|
309
|
+
writeFileSync(filePath, content.replace(oldText, newText), "utf8");
|
|
310
|
+
return { content: `File edited successfully: ${filePath}` };
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return {
|
|
313
|
+
content: "",
|
|
314
|
+
error: `Failed to edit file: ${error instanceof Error ? error.message : String(error)}`
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
var SHELL_EXEC_OPTIONS = {
|
|
319
|
+
timeout: 3e4,
|
|
320
|
+
maxBuffer: 1024 * 1024
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Execute shell command with shared options (throws on error)
|
|
324
|
+
*/
|
|
325
|
+
function execShell(command) {
|
|
326
|
+
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Execute shell command
|
|
330
|
+
*/
|
|
331
|
+
async function runShell(command) {
|
|
332
|
+
try {
|
|
333
|
+
const { stdout, stderr } = await execShell(command);
|
|
334
|
+
return { content: stdout || stderr };
|
|
335
|
+
} catch (error) {
|
|
336
|
+
return {
|
|
337
|
+
content: "",
|
|
338
|
+
error: `Command failed: ${error instanceof Error ? error.message : String(error)}`
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* List directory contents
|
|
344
|
+
*/
|
|
345
|
+
function listDir(dirPath) {
|
|
346
|
+
try {
|
|
347
|
+
if (!existsSync(dirPath)) return {
|
|
348
|
+
content: "",
|
|
349
|
+
error: `Directory not found: ${dirPath}`
|
|
350
|
+
};
|
|
351
|
+
return { content: readdirSync(dirPath, { withFileTypes: true }).map((entry) => {
|
|
352
|
+
return `[${entry.isDirectory() ? "d" : "f"}] ${entry.name}`;
|
|
353
|
+
}).join("\n") };
|
|
354
|
+
} catch (error) {
|
|
355
|
+
return {
|
|
356
|
+
content: "",
|
|
357
|
+
error: `Failed to list directory: ${error instanceof Error ? error.message : String(error)}`
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Search for pattern in files using ripgrep if available, fallback to Node.js
|
|
363
|
+
*/
|
|
364
|
+
async function grepSearch(pattern, dirPath) {
|
|
365
|
+
try {
|
|
366
|
+
const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${pattern.replace(/"/g, "\\\"")}" "${dirPath}"`);
|
|
367
|
+
// v8 ignore next
|
|
368
|
+
return { content: stdout || "No matches found" };
|
|
369
|
+
} catch {}
|
|
370
|
+
try {
|
|
371
|
+
if (!existsSync(dirPath)) return {
|
|
372
|
+
content: "",
|
|
373
|
+
error: `Directory not found: ${dirPath}`
|
|
374
|
+
};
|
|
375
|
+
const regex = new RegExp(pattern, "g");
|
|
376
|
+
const results = [];
|
|
377
|
+
function searchDirectory(currentPath) {
|
|
378
|
+
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
379
|
+
for (const entry of entries) {
|
|
380
|
+
const fullPath = join(currentPath, entry.name);
|
|
381
|
+
if (entry.isDirectory()) {
|
|
382
|
+
if (!entry.name.startsWith(".") && entry.name !== "node_modules") searchDirectory(fullPath);
|
|
383
|
+
} else if (entry.isFile()) try {
|
|
384
|
+
const lines = readFileSync(fullPath, "utf8").split("\n");
|
|
385
|
+
for (let i = 0; i < lines.length; i++) {
|
|
386
|
+
if (regex.test(lines[i])) results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
|
|
387
|
+
regex.lastIndex = 0;
|
|
388
|
+
}
|
|
389
|
+
} catch {}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
searchDirectory(dirPath);
|
|
393
|
+
if (results.length === 0) return { content: "No matches found" };
|
|
394
|
+
return { content: results.join("\n") };
|
|
395
|
+
} catch (error) {
|
|
396
|
+
return {
|
|
397
|
+
content: "",
|
|
398
|
+
error: `Search failed: ${error instanceof Error ? error.message : String(error)}`
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* View specific line range from file
|
|
404
|
+
*/
|
|
405
|
+
function viewRange(filePath, start, end) {
|
|
406
|
+
try {
|
|
407
|
+
if (!existsSync(filePath)) return {
|
|
408
|
+
content: "",
|
|
409
|
+
error: `File not found: ${filePath}`
|
|
410
|
+
};
|
|
411
|
+
const lines = readFileSync(filePath, "utf8").split("\n");
|
|
412
|
+
const startIdx = Math.max(0, start - 1);
|
|
413
|
+
const endIdx = Math.min(lines.length, end);
|
|
414
|
+
if (startIdx >= lines.length || startIdx > endIdx) return {
|
|
415
|
+
content: "",
|
|
416
|
+
error: "Invalid line range"
|
|
417
|
+
};
|
|
418
|
+
return { content: lines.slice(startIdx, endIdx).join("\n") };
|
|
419
|
+
} catch (error) {
|
|
420
|
+
return {
|
|
421
|
+
content: "",
|
|
422
|
+
error: `Failed to view range: ${error instanceof Error ? error.message : String(error)}`
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
//#endregion
|
|
5
427
|
//#region src/cli.ts
|
|
6
|
-
var
|
|
7
|
-
|
|
428
|
+
var cli = cac("code-ollama");
|
|
429
|
+
cli.version(VERSION);
|
|
430
|
+
cli.help();
|
|
431
|
+
cli.command("run <model> <prompt>", "Run a one-off prompt").action(async (model, prompt) => {
|
|
8
432
|
try {
|
|
9
|
-
await
|
|
10
|
-
} catch (
|
|
433
|
+
await runPrompt(model, prompt);
|
|
434
|
+
} catch (error) {
|
|
11
435
|
// v8 ignore next
|
|
12
|
-
|
|
13
|
-
process.stderr.write(`Error: ${
|
|
436
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
437
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
438
|
+
process.exitCode = 1;
|
|
14
439
|
}
|
|
15
440
|
});
|
|
16
|
-
async function
|
|
17
|
-
await
|
|
18
|
-
role:
|
|
19
|
-
content:
|
|
20
|
-
}],
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
441
|
+
async function runPrompt(model, prompt) {
|
|
442
|
+
await processRunStream([createSystemMessage(), {
|
|
443
|
+
role: ROLE.USER,
|
|
444
|
+
content: prompt
|
|
445
|
+
}], model);
|
|
446
|
+
process.stdout.write("\n");
|
|
447
|
+
}
|
|
448
|
+
async function processRunStream(messages, model) {
|
|
449
|
+
const assistantMessage = {
|
|
450
|
+
role: ROLE.ASSISTANT,
|
|
25
451
|
content: ""
|
|
26
452
|
};
|
|
27
|
-
for await (
|
|
28
|
-
if (
|
|
29
|
-
|
|
453
|
+
for await (const chunk of streamChat(messages, model, TOOLS)) {
|
|
454
|
+
if (chunk.type === "content") {
|
|
455
|
+
assistantMessage.content += chunk.content;
|
|
456
|
+
process.stdout.write(chunk.content);
|
|
30
457
|
continue;
|
|
31
458
|
}
|
|
32
|
-
for (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
459
|
+
for (const toolCall of chunk.tool_calls) {
|
|
460
|
+
const result = await executeTool(toolCall.function.name, toolCall.function.arguments);
|
|
461
|
+
const toolResultMessage = {
|
|
462
|
+
role: ROLE.SYSTEM,
|
|
463
|
+
content: `Tool ${toolCall.function.name} result:\n${result.content}${result.error ? `\nError: ${result.error}` : ""}`
|
|
36
464
|
};
|
|
37
|
-
await
|
|
38
|
-
...
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
],
|
|
465
|
+
await processRunStream([
|
|
466
|
+
...messages,
|
|
467
|
+
assistantMessage,
|
|
468
|
+
toolResultMessage
|
|
469
|
+
], model);
|
|
42
470
|
return;
|
|
43
471
|
}
|
|
44
472
|
}
|
|
45
473
|
}
|
|
46
|
-
async function
|
|
47
|
-
if (!
|
|
48
|
-
|
|
49
|
-
|
|
474
|
+
async function main(args = process.argv.slice(2)) {
|
|
475
|
+
if (!args.length) {
|
|
476
|
+
const { renderApp } = await import("./assets/tui-DSR1MJGd.js");
|
|
477
|
+
clear();
|
|
478
|
+
renderApp();
|
|
50
479
|
return;
|
|
51
480
|
}
|
|
52
|
-
|
|
481
|
+
cli.parse([
|
|
53
482
|
"node",
|
|
54
483
|
"code-ollama",
|
|
55
|
-
...
|
|
484
|
+
...args
|
|
56
485
|
]);
|
|
57
486
|
}
|
|
58
487
|
/* v8 ignore start */
|
|
59
|
-
function
|
|
60
|
-
if (!
|
|
488
|
+
function isEntrypoint(argv1 = process.argv[1]) {
|
|
489
|
+
if (!argv1) return false;
|
|
61
490
|
try {
|
|
62
|
-
return
|
|
491
|
+
return realpathSync(argv1) === import.meta.filename;
|
|
63
492
|
} catch {
|
|
64
|
-
return
|
|
493
|
+
return false;
|
|
65
494
|
}
|
|
66
495
|
}
|
|
67
|
-
|
|
496
|
+
if (isEntrypoint()) main();
|
|
68
497
|
/* v8 ignore stop */
|
|
69
498
|
//#endregion
|
|
70
|
-
export {
|
|
499
|
+
export { streamChat as a, createSystemMessage as c, listModels as i, ROLE as l, main, TOOLS_REQUIRING_APPROVAL as n, loadConfig as o, executeTool as r, saveConfig as s, TOOLS as t, VERSION as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ollama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"publint": "0.3.18",
|
|
65
65
|
"tsx": "4.21.0",
|
|
66
66
|
"typescript": "6.0.3",
|
|
67
|
-
"typescript-eslint": "8.59.
|
|
67
|
+
"typescript-eslint": "8.59.2",
|
|
68
68
|
"vite": "8.0.10",
|
|
69
69
|
"vitest": "4.1.5"
|
|
70
70
|
},
|