code-ollama 0.3.1 → 0.5.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 +4 -0
- package/dist/assets/{tui-CVsodXv3.js → tui-Da6uWrqo.js} +234 -104
- package/dist/cli.js +15 -35
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
> [!NOTE]
|
|
2
2
|
> TUI is under active development. APIs may change.
|
|
3
3
|
|
|
4
|
+
<p align="center">
|
|
5
|
+
<img alt="Ollama" height="200" src="https://github.com/ai-action/assets/blob/master/logos/ollama.svg?raw=true">
|
|
6
|
+
</p>
|
|
7
|
+
|
|
4
8
|
# Code Ollama
|
|
5
9
|
|
|
6
10
|
[](https://www.npmjs.com/package/code-ollama)
|
|
@@ -1,9 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as setClearHandler, c as loadConfig, d as withSystemMessage, f as ROLE, i as executeTool, l as saveConfig, m as VERSION, n as READ_ONLY_TOOLS, o as listModels, p as PLAN_GENERATION_INSTRUCTION, r as TOOLS, s as streamChat, t as DANGEROUS_TOOLS, u as resetSystemMessage } from "../cli.js";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { Box, Text, render, useInput } from "ink";
|
|
4
|
-
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { memo, useCallback, useEffect, useMemo, useState } from "react";
|
|
5
5
|
import { Select, Spinner, TextInput } from "@inkjs/ui";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/constants/command.ts
|
|
8
|
+
var LIST = [{
|
|
9
|
+
name: "/clear",
|
|
10
|
+
description: "clear the current session"
|
|
11
|
+
}, {
|
|
12
|
+
name: "/model",
|
|
13
|
+
description: "switch the model"
|
|
14
|
+
}];
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/constants/decision.ts
|
|
17
|
+
var APPROVE = "approve";
|
|
18
|
+
var REJECT = "reject";
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/constants/mode.ts
|
|
21
|
+
var NAME = {
|
|
22
|
+
SAFE: "safe",
|
|
23
|
+
AUTO: "auto",
|
|
24
|
+
PLAN: "plan"
|
|
25
|
+
};
|
|
26
|
+
var LABEL = {
|
|
27
|
+
safe: "Safe",
|
|
28
|
+
auto: "Auto",
|
|
29
|
+
plan: "Plan"
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/constants/ui.ts
|
|
33
|
+
var HEADER_PREFIX = "🦙";
|
|
34
|
+
//#endregion
|
|
7
35
|
//#region src/components/Messages.tsx
|
|
8
36
|
function getMessageColor(role) {
|
|
9
37
|
switch (role) {
|
|
@@ -13,21 +41,28 @@ function getMessageColor(role) {
|
|
|
13
41
|
default: return;
|
|
14
42
|
}
|
|
15
43
|
}
|
|
16
|
-
function
|
|
44
|
+
var MessageRow = memo(function MessageRow({ message }) {
|
|
45
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
46
|
+
marginBottom: 1,
|
|
47
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
48
|
+
color: getMessageColor(message.role),
|
|
49
|
+
dimColor: message.role === ROLE.SYSTEM,
|
|
50
|
+
children: [message.role === ROLE.USER ? "> " : "", message.content]
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
function Messages({ messages, isLoading, streamingMessage }) {
|
|
17
55
|
return /* @__PURE__ */ jsxs(Box, {
|
|
18
56
|
flexDirection: "column",
|
|
19
|
-
children: [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
57
|
+
children: [
|
|
58
|
+
messages.map((message, index) => /* @__PURE__ */ jsx(MessageRow, { message }, `${String(index)}-${message.role}-${message.content.slice(0, 16)}`)),
|
|
59
|
+
streamingMessage && /* @__PURE__ */ jsx(MessageRow, { message: streamingMessage }),
|
|
60
|
+
isLoading && !streamingMessage?.content && /* @__PURE__ */ jsx(Box, {
|
|
61
|
+
marginTop: -1,
|
|
62
|
+
marginBottom: 1,
|
|
63
|
+
children: /* @__PURE__ */ jsx(Spinner, { label: "Thinking..." })
|
|
25
64
|
})
|
|
26
|
-
|
|
27
|
-
marginTop: -1,
|
|
28
|
-
marginBottom: 1,
|
|
29
|
-
children: /* @__PURE__ */ jsx(Spinner, { label: "Thinking..." })
|
|
30
|
-
})]
|
|
65
|
+
]
|
|
31
66
|
});
|
|
32
67
|
}
|
|
33
68
|
//#endregion
|
|
@@ -146,20 +181,56 @@ var ACTION_NOT_PERFORMED = "The requested action was NOT performed";
|
|
|
146
181
|
var PLAN_CHECKLIST_REMINDER = "Then display the execution plan as an unchecked Markdown checklist only";
|
|
147
182
|
var PLAN_EXECUTION_REMINDER = "Do not claim success and do not call write_file or run_shell until the user approves execution";
|
|
148
183
|
//#endregion
|
|
184
|
+
//#region src/components/Chat/CommandMenu.tsx
|
|
185
|
+
function getMatchingCommands(input) {
|
|
186
|
+
const normalizedInput = input.trim().toLowerCase();
|
|
187
|
+
if (!normalizedInput.startsWith("/")) return [];
|
|
188
|
+
return LIST.filter(({ name }) => name.toLowerCase().startsWith(normalizedInput)).map(({ name, description }) => ({
|
|
189
|
+
label: `${name} - ${description}`,
|
|
190
|
+
value: name
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
function CommandMenu({ input, onSubmit }) {
|
|
194
|
+
const commandOptions = useMemo(() => getMatchingCommands(input), [input]);
|
|
195
|
+
if (!commandOptions.length) return null;
|
|
196
|
+
return /* @__PURE__ */ jsx(SelectPrompt, {
|
|
197
|
+
highlightText: input,
|
|
198
|
+
onChange: onSubmit,
|
|
199
|
+
options: commandOptions
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
149
203
|
//#region src/components/Chat/Input.tsx
|
|
150
204
|
function Input({ isDisabled = false, onSubmit }) {
|
|
205
|
+
const [input, setInput] = useState("");
|
|
151
206
|
const [resetKey, setResetKey] = useState(0);
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
207
|
+
const handleSubmitText = useCallback((input) => {
|
|
208
|
+
setTimeout(() => {
|
|
209
|
+
if (input.startsWith("/")) return;
|
|
210
|
+
const trimmedInput = input.trim();
|
|
211
|
+
if (!trimmedInput) return;
|
|
212
|
+
onSubmit(trimmedInput);
|
|
213
|
+
setInput("");
|
|
214
|
+
setResetKey((key) => key + 1);
|
|
215
|
+
});
|
|
216
|
+
}, [onSubmit]);
|
|
217
|
+
const handleSubmitCommand = useCallback((input) => {
|
|
218
|
+
if (!LIST.find(({ name }) => name === input)) return;
|
|
219
|
+
onSubmit(input);
|
|
220
|
+
setInput("");
|
|
156
221
|
setResetKey((key) => key + 1);
|
|
157
222
|
}, [onSubmit]);
|
|
158
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
223
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
224
|
+
flexDirection: "column",
|
|
225
|
+
children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, { children: "> " }), /* @__PURE__ */ jsx(TextInput, {
|
|
226
|
+
isDisabled,
|
|
227
|
+
onChange: setInput,
|
|
228
|
+
onSubmit: handleSubmitText
|
|
229
|
+
}, resetKey)] }), input.startsWith("/") && /* @__PURE__ */ jsx(CommandMenu, {
|
|
230
|
+
input,
|
|
231
|
+
onSubmit: handleSubmitCommand
|
|
232
|
+
})]
|
|
233
|
+
});
|
|
163
234
|
}
|
|
164
235
|
//#endregion
|
|
165
236
|
//#region src/components/Chat/plan.ts
|
|
@@ -171,11 +242,19 @@ function hasExecutablePlan(content) {
|
|
|
171
242
|
}
|
|
172
243
|
//#endregion
|
|
173
244
|
//#region src/components/Chat/Chat.tsx
|
|
174
|
-
function Chat({ model, onCommand, mode, onModeChange }) {
|
|
175
|
-
const [messages, setMessages] = useState([
|
|
245
|
+
function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
246
|
+
const [messages, setMessages] = useState([]);
|
|
247
|
+
const [streamingMessage, setStreamingMessage] = useState(null);
|
|
176
248
|
const [isLoading, setIsLoading] = useState(false);
|
|
177
249
|
const [pendingToolCall, setPendingToolCall] = useState(null);
|
|
178
250
|
const [pendingPlan, setPendingPlan] = useState(null);
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
setMessages([]);
|
|
253
|
+
setStreamingMessage(null);
|
|
254
|
+
setIsLoading(false);
|
|
255
|
+
setPendingToolCall(null);
|
|
256
|
+
setPendingPlan(null);
|
|
257
|
+
}, [sessionId]);
|
|
179
258
|
const buildToolResultMessage = useCallback((toolName, result) => {
|
|
180
259
|
if (result.error?.startsWith("Tool not allowed:")) return {
|
|
181
260
|
role: ROLE.SYSTEM,
|
|
@@ -206,20 +285,38 @@ function Chat({ model, onCommand, mode, onModeChange }) {
|
|
|
206
285
|
role: ROLE.ASSISTANT,
|
|
207
286
|
content: ""
|
|
208
287
|
};
|
|
209
|
-
|
|
288
|
+
let committedMessages = currentMessages;
|
|
289
|
+
let assistantCommitted = false;
|
|
290
|
+
const commitAssistantMessage = () => {
|
|
291
|
+
if (assistantCommitted) {
|
|
292
|
+
// v8 ignore next
|
|
293
|
+
if (committedMessages.at(-1)?.role === ROLE.ASSISTANT) {
|
|
294
|
+
committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
|
|
295
|
+
setMessages(committedMessages);
|
|
296
|
+
}
|
|
297
|
+
return committedMessages;
|
|
298
|
+
}
|
|
299
|
+
assistantCommitted = true;
|
|
300
|
+
setStreamingMessage(null);
|
|
301
|
+
if (!assistantMessage.content) {
|
|
302
|
+
setMessages(committedMessages);
|
|
303
|
+
return committedMessages;
|
|
304
|
+
}
|
|
305
|
+
committedMessages = [...committedMessages, { ...assistantMessage }];
|
|
306
|
+
setMessages(committedMessages);
|
|
307
|
+
return committedMessages;
|
|
308
|
+
};
|
|
309
|
+
setStreamingMessage(assistantMessage);
|
|
210
310
|
try {
|
|
211
|
-
for await (const chunk of streamChat(currentMessages, model, TOOLS)) if (chunk.type === "content") {
|
|
311
|
+
for await (const chunk of streamChat(withSystemMessage(currentMessages), model, TOOLS)) if (chunk.type === "content") {
|
|
212
312
|
assistantMessage.content += chunk.content;
|
|
213
|
-
|
|
214
|
-
const newMessages = [...previousMessages];
|
|
215
|
-
newMessages[newMessages.length - 1] = { ...assistantMessage };
|
|
216
|
-
return newMessages;
|
|
217
|
-
});
|
|
313
|
+
setStreamingMessage({ ...assistantMessage });
|
|
218
314
|
} else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
|
|
219
315
|
const requiresApproval = DANGEROUS_TOOLS.has(toolCall.function.name);
|
|
220
316
|
// v8 ignore start
|
|
221
317
|
const allowedTools = executionMode === NAME.PLAN ? READ_ONLY_TOOLS : void 0;
|
|
222
318
|
// v8 ignore stop
|
|
319
|
+
const updatedMessages = commitAssistantMessage();
|
|
223
320
|
if (executionMode === NAME.SAFE && requiresApproval) {
|
|
224
321
|
setPendingToolCall(toolCall);
|
|
225
322
|
setIsLoading(false);
|
|
@@ -227,22 +324,15 @@ function Chat({ model, onCommand, mode, onModeChange }) {
|
|
|
227
324
|
}
|
|
228
325
|
const result = await executeTool(toolCall.function.name, toolCall.function.arguments, { allowedTools });
|
|
229
326
|
const toolResultMessage = buildToolResultMessage(toolCall.function.name, result);
|
|
230
|
-
const newMessages = [
|
|
231
|
-
|
|
232
|
-
assistantMessage,
|
|
233
|
-
toolResultMessage
|
|
234
|
-
];
|
|
235
|
-
setMessages((previousMessages) => [...previousMessages, toolResultMessage]);
|
|
327
|
+
const newMessages = [...updatedMessages, toolResultMessage];
|
|
328
|
+
setMessages(newMessages);
|
|
236
329
|
await processStream(newMessages, executionMode);
|
|
237
330
|
return;
|
|
238
331
|
}
|
|
332
|
+
commitAssistantMessage();
|
|
239
333
|
} catch (error) {
|
|
240
334
|
assistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
241
|
-
|
|
242
|
-
const newMessages = [...previousMessages];
|
|
243
|
-
newMessages[newMessages.length - 1] = { ...assistantMessage };
|
|
244
|
-
return newMessages;
|
|
245
|
-
});
|
|
335
|
+
commitAssistantMessage();
|
|
246
336
|
} finally {
|
|
247
337
|
setIsLoading(false);
|
|
248
338
|
}
|
|
@@ -256,73 +346,83 @@ function Chat({ model, onCommand, mode, onModeChange }) {
|
|
|
256
346
|
role: ROLE.ASSISTANT,
|
|
257
347
|
content: ""
|
|
258
348
|
};
|
|
259
|
-
|
|
349
|
+
let committedMessages = currentMessages;
|
|
350
|
+
let assistantCommitted = false;
|
|
351
|
+
const commitAssistantMessage = () => {
|
|
352
|
+
if (assistantCommitted) {
|
|
353
|
+
// v8 ignore next
|
|
354
|
+
if (committedMessages.at(-1)?.role === ROLE.ASSISTANT) {
|
|
355
|
+
committedMessages = [...committedMessages.slice(0, -1), { ...assistantMessage }];
|
|
356
|
+
setMessages(committedMessages);
|
|
357
|
+
}
|
|
358
|
+
return committedMessages;
|
|
359
|
+
}
|
|
360
|
+
assistantCommitted = true;
|
|
361
|
+
setStreamingMessage(null);
|
|
362
|
+
if (!assistantMessage.content) {
|
|
363
|
+
setMessages(committedMessages);
|
|
364
|
+
return committedMessages;
|
|
365
|
+
}
|
|
366
|
+
committedMessages = [...committedMessages, { ...assistantMessage }];
|
|
367
|
+
setMessages(committedMessages);
|
|
368
|
+
return committedMessages;
|
|
369
|
+
};
|
|
370
|
+
setStreamingMessage(assistantMessage);
|
|
260
371
|
try {
|
|
261
372
|
const readOnlyTools = TOOLS.filter((tool) => READ_ONLY_TOOLS.has(tool.function.name));
|
|
262
|
-
for await (const chunk of streamChat(currentMessages, model, readOnlyTools)) if (chunk.type === "content") {
|
|
373
|
+
for await (const chunk of streamChat(withSystemMessage(currentMessages), model, readOnlyTools)) if (chunk.type === "content") {
|
|
263
374
|
assistantMessage.content += chunk.content;
|
|
264
|
-
|
|
265
|
-
const newMessages = [...previousMessages];
|
|
266
|
-
newMessages[newMessages.length - 1] = { ...assistantMessage };
|
|
267
|
-
return newMessages;
|
|
268
|
-
});
|
|
375
|
+
setStreamingMessage({ ...assistantMessage });
|
|
269
376
|
} else if (chunk.type === "tool_calls") for (const toolCall of chunk.tool_calls) {
|
|
377
|
+
const updatedMessages = commitAssistantMessage();
|
|
270
378
|
if (!READ_ONLY_TOOLS.has(toolCall.function.name)) {
|
|
271
379
|
const correctionMessage = buildPlanModeCorrectionMessage(toolCall.function.name);
|
|
272
|
-
const newMessages = [
|
|
273
|
-
|
|
274
|
-
assistantMessage,
|
|
275
|
-
correctionMessage
|
|
276
|
-
];
|
|
277
|
-
setMessages((previousMessages) => [...previousMessages, correctionMessage]);
|
|
380
|
+
const newMessages = [...updatedMessages, correctionMessage];
|
|
381
|
+
setMessages(newMessages);
|
|
278
382
|
await processStreamReadOnly(newMessages);
|
|
279
383
|
return;
|
|
280
384
|
}
|
|
281
385
|
const result = await executeTool(toolCall.function.name, toolCall.function.arguments, { allowedTools: READ_ONLY_TOOLS });
|
|
282
386
|
const toolResultMessage = buildToolResultMessage(toolCall.function.name, result);
|
|
283
|
-
const newMessages = [
|
|
284
|
-
|
|
285
|
-
assistantMessage,
|
|
286
|
-
toolResultMessage
|
|
287
|
-
];
|
|
288
|
-
setMessages((previousMessages) => [...previousMessages, toolResultMessage]);
|
|
387
|
+
const newMessages = [...updatedMessages, toolResultMessage];
|
|
388
|
+
setMessages(newMessages);
|
|
289
389
|
await processStreamReadOnly(newMessages);
|
|
290
390
|
return;
|
|
291
391
|
}
|
|
392
|
+
const researchMessages = commitAssistantMessage();
|
|
292
393
|
const planInstruction = {
|
|
293
394
|
role: ROLE.SYSTEM,
|
|
294
395
|
content: PLAN_GENERATION_INSTRUCTION
|
|
295
396
|
};
|
|
296
|
-
const planMessages = [
|
|
297
|
-
...currentMessages,
|
|
298
|
-
assistantMessage,
|
|
299
|
-
planInstruction
|
|
300
|
-
];
|
|
397
|
+
const planMessages = [...researchMessages, planInstruction];
|
|
301
398
|
const planAssistantMessage = {
|
|
302
399
|
role: ROLE.ASSISTANT,
|
|
303
400
|
content: ""
|
|
304
401
|
};
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
402
|
+
setStreamingMessage(planAssistantMessage);
|
|
403
|
+
try {
|
|
404
|
+
for await (const chunk of streamChat(withSystemMessage(planMessages), model, [])) if (chunk.type === "content") {
|
|
405
|
+
planAssistantMessage.content += chunk.content;
|
|
406
|
+
setStreamingMessage({ ...planAssistantMessage });
|
|
407
|
+
}
|
|
408
|
+
} catch (error) {
|
|
409
|
+
planAssistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
410
|
+
setMessages([...planMessages, { ...planAssistantMessage }]);
|
|
411
|
+
setStreamingMessage(null);
|
|
412
|
+
setIsLoading(false);
|
|
413
|
+
return;
|
|
313
414
|
}
|
|
415
|
+
const finalPlanMessages = [...planMessages, { ...planAssistantMessage }];
|
|
416
|
+
setMessages(finalPlanMessages);
|
|
417
|
+
setStreamingMessage(null);
|
|
314
418
|
if (hasExecutablePlan(planAssistantMessage.content)) setPendingPlan({
|
|
315
419
|
planContent: planAssistantMessage.content,
|
|
316
|
-
messages:
|
|
420
|
+
messages: finalPlanMessages
|
|
317
421
|
});
|
|
318
422
|
setIsLoading(false);
|
|
319
423
|
} catch (error) {
|
|
320
424
|
assistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
321
|
-
|
|
322
|
-
const newMessages = [...previousMessages];
|
|
323
|
-
newMessages[newMessages.length - 1] = { ...assistantMessage };
|
|
324
|
-
return newMessages;
|
|
325
|
-
});
|
|
425
|
+
commitAssistantMessage();
|
|
326
426
|
} finally {
|
|
327
427
|
setIsLoading(false);
|
|
328
428
|
}
|
|
@@ -404,8 +504,8 @@ function Chat({ model, onCommand, mode, onModeChange }) {
|
|
|
404
504
|
role: ROLE.USER,
|
|
405
505
|
content: userContent
|
|
406
506
|
};
|
|
407
|
-
setMessages((previousMessages) => [...previousMessages, userMessage]);
|
|
408
507
|
const updatedMessages = [...messages, userMessage];
|
|
508
|
+
setMessages(updatedMessages);
|
|
409
509
|
if (mode === NAME.PLAN) await processStreamReadOnly(updatedMessages);
|
|
410
510
|
else await processStream(updatedMessages);
|
|
411
511
|
}, [
|
|
@@ -419,8 +519,9 @@ function Chat({ model, onCommand, mode, onModeChange }) {
|
|
|
419
519
|
flexDirection: "column",
|
|
420
520
|
children: [
|
|
421
521
|
/* @__PURE__ */ jsx(Messages, {
|
|
422
|
-
messages
|
|
423
|
-
isLoading
|
|
522
|
+
messages,
|
|
523
|
+
isLoading,
|
|
524
|
+
streamingMessage
|
|
424
525
|
}),
|
|
425
526
|
pendingPlan && /* @__PURE__ */ jsx(PlanApproval, {
|
|
426
527
|
planContent: pendingPlan.planContent,
|
|
@@ -566,8 +667,18 @@ function App() {
|
|
|
566
667
|
const [model, setModel] = useState(() => loadConfig().model);
|
|
567
668
|
const [picking, setPicking] = useState(false);
|
|
568
669
|
const [mode, setMode] = useState(NAME.SAFE);
|
|
670
|
+
const [sessionId, setSessionId] = useState(0);
|
|
569
671
|
const handleCommand = useCallback((command) => {
|
|
570
|
-
|
|
672
|
+
switch (command) {
|
|
673
|
+
case "/model":
|
|
674
|
+
setPicking(true);
|
|
675
|
+
break;
|
|
676
|
+
case "/clear":
|
|
677
|
+
resetSystemMessage();
|
|
678
|
+
setPicking(false);
|
|
679
|
+
setSessionId((sessionId) => sessionId + 1);
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
571
682
|
}, []);
|
|
572
683
|
const handleSelect = useCallback((selected) => {
|
|
573
684
|
setModel(selected);
|
|
@@ -577,32 +688,43 @@ function App() {
|
|
|
577
688
|
const handleClose = useCallback(() => {
|
|
578
689
|
setPicking(false);
|
|
579
690
|
}, []);
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
691
|
+
const handleToggleMode = useCallback(() => {
|
|
692
|
+
setMode((mode) => {
|
|
693
|
+
switch (mode) {
|
|
694
|
+
case NAME.SAFE: return NAME.AUTO;
|
|
695
|
+
case NAME.AUTO: return NAME.PLAN;
|
|
696
|
+
case NAME.PLAN:
|
|
697
|
+
default: return NAME.SAFE;
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
}, []);
|
|
701
|
+
let body;
|
|
702
|
+
switch (true) {
|
|
703
|
+
case picking:
|
|
704
|
+
body = /* @__PURE__ */ jsx(ModelPicker, {
|
|
585
705
|
currentModel: model,
|
|
586
706
|
onSelect: handleSelect,
|
|
587
707
|
onClose: handleClose
|
|
588
|
-
})
|
|
708
|
+
});
|
|
709
|
+
break;
|
|
710
|
+
default:
|
|
711
|
+
body = /* @__PURE__ */ jsx(Chat, {
|
|
589
712
|
model,
|
|
590
713
|
onCommand: handleCommand,
|
|
591
714
|
mode,
|
|
592
|
-
onModeChange: setMode
|
|
593
|
-
|
|
715
|
+
onModeChange: setMode,
|
|
716
|
+
sessionId
|
|
717
|
+
});
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
721
|
+
flexDirection: "column",
|
|
722
|
+
children: [
|
|
723
|
+
/* @__PURE__ */ jsx(Header, { model }),
|
|
724
|
+
body,
|
|
594
725
|
/* @__PURE__ */ jsx(Footer, {
|
|
595
726
|
mode,
|
|
596
|
-
onToggleMode:
|
|
597
|
-
setMode((mode) => {
|
|
598
|
-
switch (mode) {
|
|
599
|
-
case NAME.SAFE: return NAME.AUTO;
|
|
600
|
-
case NAME.AUTO: return NAME.PLAN;
|
|
601
|
-
case NAME.PLAN:
|
|
602
|
-
default: return NAME.SAFE;
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
}
|
|
727
|
+
onToggleMode: handleToggleMode
|
|
606
728
|
})
|
|
607
729
|
]
|
|
608
730
|
});
|
|
@@ -610,7 +732,15 @@ function App() {
|
|
|
610
732
|
//#endregion
|
|
611
733
|
//#region src/tui.tsx
|
|
612
734
|
function renderApp() {
|
|
613
|
-
|
|
735
|
+
const tree = /* @__PURE__ */ jsx(App, {});
|
|
736
|
+
const app = render(tree, {
|
|
737
|
+
incrementalRendering: true,
|
|
738
|
+
maxFps: 60
|
|
739
|
+
});
|
|
740
|
+
setClearHandler(() => {
|
|
741
|
+
app.clear();
|
|
742
|
+
app.rerender(tree);
|
|
743
|
+
});
|
|
614
744
|
}
|
|
615
745
|
//#endregion
|
|
616
746
|
export { renderApp };
|
package/dist/cli.js
CHANGED
|
@@ -6,29 +6,9 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { Ollama } from "ollama";
|
|
7
7
|
import { exec } from "node:child_process";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
|
-
var NAMES = [{
|
|
10
|
-
name: "/model",
|
|
11
|
-
description: "switch the model"
|
|
12
|
-
}].map(({ name }) => name);
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/constants/decision.ts
|
|
15
|
-
var APPROVE = "approve";
|
|
16
|
-
var REJECT = "reject";
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/constants/mode.ts
|
|
19
|
-
var NAME$1 = {
|
|
20
|
-
SAFE: "safe",
|
|
21
|
-
AUTO: "auto",
|
|
22
|
-
PLAN: "plan"
|
|
23
|
-
};
|
|
24
|
-
var LABEL = {
|
|
25
|
-
safe: "Safe",
|
|
26
|
-
auto: "Auto",
|
|
27
|
-
plan: "Plan"
|
|
28
|
-
};
|
|
29
9
|
//#endregion
|
|
30
10
|
//#region src/constants/package.ts
|
|
31
|
-
var VERSION = "0.
|
|
11
|
+
var VERSION = "0.5.0";
|
|
32
12
|
//#endregion
|
|
33
13
|
//#region src/constants/prompt.ts
|
|
34
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
|
|
@@ -87,9 +67,6 @@ var NAME = {
|
|
|
87
67
|
VIEW_RANGE: "view_range"
|
|
88
68
|
};
|
|
89
69
|
//#endregion
|
|
90
|
-
//#region src/constants/ui.ts
|
|
91
|
-
var HEADER_PREFIX = "🦙";
|
|
92
|
-
//#endregion
|
|
93
70
|
//#region src/utils/agents.ts
|
|
94
71
|
var AGENTS_FILE = "AGENTS.md";
|
|
95
72
|
function loadAgentsContent() {
|
|
@@ -108,12 +85,20 @@ function buildSystemPrompt() {
|
|
|
108
85
|
parts.push("\n\n", TOOL_INSTRUCTIONS);
|
|
109
86
|
return parts.join("");
|
|
110
87
|
}
|
|
88
|
+
var systemMessage = null;
|
|
111
89
|
function createSystemMessage() {
|
|
112
90
|
return {
|
|
113
91
|
role: ROLE.SYSTEM,
|
|
114
92
|
content: buildSystemPrompt()
|
|
115
93
|
};
|
|
116
94
|
}
|
|
95
|
+
function resetSystemMessage() {
|
|
96
|
+
systemMessage = null;
|
|
97
|
+
}
|
|
98
|
+
function withSystemMessage(messages) {
|
|
99
|
+
systemMessage ??= createSystemMessage();
|
|
100
|
+
return [systemMessage, ...messages];
|
|
101
|
+
}
|
|
117
102
|
//#endregion
|
|
118
103
|
//#region src/utils/config.ts
|
|
119
104
|
var CONFIG_DIR = join(homedir(), ".code-ollama");
|
|
@@ -171,12 +156,7 @@ async function listModels() {
|
|
|
171
156
|
const { models } = await client.list();
|
|
172
157
|
return models.map(({ name }) => name);
|
|
173
158
|
}
|
|
174
|
-
|
|
175
|
-
//#region src/utils/screen.ts
|
|
176
|
-
var CLEAR = "\x1Bc";
|
|
177
|
-
function clear() {
|
|
178
|
-
process.stdout.write(CLEAR);
|
|
179
|
-
}
|
|
159
|
+
function setClearHandler(handler) {}
|
|
180
160
|
//#endregion
|
|
181
161
|
//#region src/utils/tools.ts
|
|
182
162
|
var execAsync = promisify(exec);
|
|
@@ -519,8 +499,8 @@ async function processRunStream(messages, model) {
|
|
|
519
499
|
}
|
|
520
500
|
async function main(args = process.argv.slice(2)) {
|
|
521
501
|
if (!args.length) {
|
|
522
|
-
const { renderApp } = await import("./assets/tui-
|
|
523
|
-
|
|
502
|
+
const { renderApp } = await import("./assets/tui-Da6uWrqo.js");
|
|
503
|
+
process.stdout.write("\x1Bc");
|
|
524
504
|
renderApp();
|
|
525
505
|
return;
|
|
526
506
|
}
|
|
@@ -530,7 +510,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
530
510
|
...args
|
|
531
511
|
]);
|
|
532
512
|
}
|
|
533
|
-
|
|
513
|
+
// v8 ignore start
|
|
534
514
|
function isEntrypoint(argv1 = process.argv[1]) {
|
|
535
515
|
if (!argv1) return false;
|
|
536
516
|
try {
|
|
@@ -540,6 +520,6 @@ function isEntrypoint(argv1 = process.argv[1]) {
|
|
|
540
520
|
}
|
|
541
521
|
}
|
|
542
522
|
if (isEntrypoint()) main();
|
|
543
|
-
|
|
523
|
+
// v8 ignore stop
|
|
544
524
|
//#endregion
|
|
545
|
-
export {
|
|
525
|
+
export { setClearHandler as a, loadConfig as c, withSystemMessage as d, ROLE as f, executeTool as i, saveConfig as l, VERSION as m, main, READ_ONLY_TOOLS as n, listModels as o, PLAN_GENERATION_INSTRUCTION as p, TOOLS as r, streamChat as s, DANGEROUS_TOOLS as t, resetSystemMessage as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ollama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@commitlint/config-conventional": "20.5.3",
|
|
51
51
|
"@eslint/compat": "2.0.5",
|
|
52
52
|
"@eslint/js": "10.0.1",
|
|
53
|
-
"@types/node": "25.6.
|
|
53
|
+
"@types/node": "25.6.1",
|
|
54
54
|
"@types/react": "19.2.14",
|
|
55
55
|
"@vitest/coverage-v8": "4.1.5",
|
|
56
56
|
"eslint": "10.3.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"tsx": "4.21.0",
|
|
66
66
|
"typescript": "6.0.3",
|
|
67
67
|
"typescript-eslint": "8.59.2",
|
|
68
|
-
"vite": "8.0.
|
|
68
|
+
"vite": "8.0.11",
|
|
69
69
|
"vitest": "4.1.5"
|
|
70
70
|
},
|
|
71
71
|
"files": [
|