code-ollama 0.9.0 → 0.10.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-VKBxlYAz.js → tui-D2NgQSV7.js} +218 -111
- package/dist/cli.js +22 -5
- package/package.json +1 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { a as tick, c as
|
|
1
|
+
import { _ as VERSION, a as tick, c as setClearHandler, d as loadConfig, f as saveConfig, g as PLAN_GENERATION_INSTRUCTION, h as ROLE, i as executeTool, l as listModels, m as withSystemMessage, n as TOOLS, o as clear, p as resetSystemMessage, r as WRITE_TOOLS, s as reset, t as READ_TOOLS, u as streamChat } from "../cli.js";
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
3
|
import { join, relative } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { exec } from "node:child_process";
|
|
6
|
-
import { Box, Text, render, useApp, useInput } from "ink";
|
|
6
|
+
import { Box, Static, Text, render, useApp, useInput, useStdout } from "ink";
|
|
7
7
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { Select, Spinner } from "@inkjs/ui";
|
|
9
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { marked } from "marked";
|
|
11
|
-
import
|
|
11
|
+
import { markedTerminal } from "marked-terminal";
|
|
12
12
|
//#region src/constants/command.ts
|
|
13
13
|
var LIST = [
|
|
14
14
|
{
|
|
@@ -45,22 +45,46 @@ var LABEL = {
|
|
|
45
45
|
var HEADER_PREFIX = "🦙 ";
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/components/CodeBlock/CodeBlock.tsx
|
|
48
|
+
var highlightCache = /* @__PURE__ */ new Map();
|
|
49
|
+
var CODE_BLOCK_REGEX = /^(`{3,})(\w+)?[ \t]*\n([\s\S]*?)^\1[ \t]*$/gm;
|
|
50
|
+
async function prewarmCodeBlocks(content) {
|
|
51
|
+
const promises = [];
|
|
52
|
+
let match;
|
|
53
|
+
CODE_BLOCK_REGEX.lastIndex = 0;
|
|
54
|
+
while ((match = CODE_BLOCK_REGEX.exec(content)) !== null) {
|
|
55
|
+
const language = match[2];
|
|
56
|
+
const code = match[3].trim();
|
|
57
|
+
// v8 ignore next 2
|
|
58
|
+
if (code) promises.push(prewarmHighlight(code, language));
|
|
59
|
+
}
|
|
60
|
+
await Promise.all(promises);
|
|
61
|
+
}
|
|
62
|
+
async function prewarmHighlight(code, language) {
|
|
63
|
+
// v8 ignore start
|
|
64
|
+
const cacheKey = `${language ?? ""}:${code}`;
|
|
65
|
+
if (highlightCache.has(cacheKey)) return;
|
|
66
|
+
// v8 ignore stop
|
|
67
|
+
const result = await highlightCode(code, language);
|
|
68
|
+
highlightCache.set(cacheKey, result);
|
|
69
|
+
}
|
|
48
70
|
async function highlightCode(code, language = "text") {
|
|
49
71
|
const { codeToANSI } = await import("@shikijs/cli");
|
|
50
72
|
try {
|
|
51
73
|
return await codeToANSI(code, language, "github-light");
|
|
52
74
|
} catch {
|
|
53
|
-
// v8 ignore next
|
|
75
|
+
// v8 ignore next
|
|
54
76
|
return code;
|
|
55
77
|
}
|
|
56
78
|
}
|
|
57
79
|
var CodeBlock = memo(function CodeBlock({ code, language, role }) {
|
|
58
|
-
const
|
|
80
|
+
const cacheKey = `${language ?? ""}:${code}`;
|
|
81
|
+
const [highlighted, setHighlighted] = useState(() => highlightCache.get(cacheKey) ?? code);
|
|
59
82
|
useEffect(() => {
|
|
60
83
|
let canceled = false;
|
|
61
84
|
async function loadHighlight() {
|
|
62
85
|
try {
|
|
63
86
|
const result = await highlightCode(code, language);
|
|
87
|
+
highlightCache.set(cacheKey, result);
|
|
64
88
|
if (!canceled) setHighlighted(result);
|
|
65
89
|
} catch {}
|
|
66
90
|
}
|
|
@@ -68,7 +92,11 @@ var CodeBlock = memo(function CodeBlock({ code, language, role }) {
|
|
|
68
92
|
return () => {
|
|
69
93
|
canceled = true;
|
|
70
94
|
};
|
|
71
|
-
}, [
|
|
95
|
+
}, [
|
|
96
|
+
cacheKey,
|
|
97
|
+
code,
|
|
98
|
+
language
|
|
99
|
+
]);
|
|
72
100
|
const isSystem = role === ROLE.SYSTEM;
|
|
73
101
|
return /* @__PURE__ */ jsx(Box, {
|
|
74
102
|
flexDirection: "column",
|
|
@@ -76,41 +104,34 @@ var CodeBlock = memo(function CodeBlock({ code, language, role }) {
|
|
|
76
104
|
borderColor: isSystem ? "gray" : "dim",
|
|
77
105
|
paddingX: 1,
|
|
78
106
|
marginY: 1,
|
|
79
|
-
children: /* @__PURE__ */ jsx(
|
|
107
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
80
108
|
dimColor: isSystem,
|
|
81
109
|
children: highlighted
|
|
82
|
-
})
|
|
110
|
+
})
|
|
83
111
|
});
|
|
84
112
|
});
|
|
85
113
|
//#endregion
|
|
86
114
|
//#region src/components/Markdown/Markdown.tsx
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
115
|
+
var HR_PLACEHOLDER = "__CODE_OLLAMA_HR_PLACEHOLDER__";
|
|
116
|
+
marked.use(markedTerminal({ theme: "gitHub" }));
|
|
117
|
+
marked.use({ renderer: { hr: () => `${HR_PLACEHOLDER}\n` } });
|
|
118
|
+
function renderMarkdown(content, hrWidth) {
|
|
119
|
+
const hr = "─".repeat(Math.max(1, hrWidth));
|
|
120
|
+
try {
|
|
121
|
+
const result = marked.parse(content);
|
|
122
|
+
return (typeof result === "string" ? result.trim() : content).replaceAll(HR_PLACEHOLDER, hr);
|
|
123
|
+
} catch {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
// v8 ignore stop
|
|
92
127
|
}
|
|
93
128
|
var Markdown = memo(function Markdown({ content, color, dimColor }) {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
let canceled = false;
|
|
97
|
-
function loadMarkdown() {
|
|
98
|
-
try {
|
|
99
|
-
const result = renderMarkdown(content);
|
|
100
|
-
// v8 ignore start
|
|
101
|
-
if (!canceled) setRendered(result);
|
|
102
|
-
} catch {}
|
|
103
|
-
// v8 ignore stop
|
|
104
|
-
}
|
|
105
|
-
loadMarkdown();
|
|
106
|
-
return () => {
|
|
107
|
-
canceled = true;
|
|
108
|
-
};
|
|
109
|
-
}, [content]);
|
|
129
|
+
const { stdout } = useStdout();
|
|
130
|
+
const availableWidth = stdout.columns - 4;
|
|
110
131
|
return /* @__PURE__ */ jsx(Text, {
|
|
111
132
|
color,
|
|
112
133
|
dimColor,
|
|
113
|
-
children:
|
|
134
|
+
children: useMemo(() => renderMarkdown(content, availableWidth), [content, availableWidth])
|
|
114
135
|
});
|
|
115
136
|
});
|
|
116
137
|
//#endregion
|
|
@@ -132,10 +153,10 @@ function getMessageColor(role) {
|
|
|
132
153
|
}
|
|
133
154
|
function parseContent(content) {
|
|
134
155
|
const segments = [];
|
|
135
|
-
const codeBlockRegex = /```(\w+)?\n?([\s\S]*?)```/g;
|
|
136
156
|
let lastIndex = 0;
|
|
137
157
|
let match;
|
|
138
|
-
|
|
158
|
+
CODE_BLOCK_REGEX.lastIndex = 0;
|
|
159
|
+
while ((match = CODE_BLOCK_REGEX.exec(content)) !== null) {
|
|
139
160
|
if (match.index > lastIndex) {
|
|
140
161
|
const textContent = content.slice(lastIndex, match.index).trim();
|
|
141
162
|
// v8 ignore next 2 - Defensive check for empty trimmed content
|
|
@@ -144,8 +165,8 @@ function parseContent(content) {
|
|
|
144
165
|
content: textContent
|
|
145
166
|
});
|
|
146
167
|
}
|
|
147
|
-
const language = match[
|
|
148
|
-
const codeContent = match[
|
|
168
|
+
const language = match[2];
|
|
169
|
+
const codeContent = match[3].trim();
|
|
149
170
|
// v8 ignore next 2 - Defensive check for empty code block
|
|
150
171
|
if (codeContent) segments.push({
|
|
151
172
|
type: "code",
|
|
@@ -163,7 +184,7 @@ function parseContent(content) {
|
|
|
163
184
|
});
|
|
164
185
|
}
|
|
165
186
|
// v8 ignore next 2 - Defensive fallback for edge case
|
|
166
|
-
if (segments.length
|
|
187
|
+
if (!segments.length && content.trim()) segments.push({
|
|
167
188
|
type: "text",
|
|
168
189
|
content: content.trim()
|
|
169
190
|
});
|
|
@@ -212,11 +233,14 @@ var Message = memo(function Message({ message }) {
|
|
|
212
233
|
})
|
|
213
234
|
});
|
|
214
235
|
});
|
|
215
|
-
function Messages({ messages, isLoading, streamingMessage }) {
|
|
236
|
+
function Messages({ messages, isLoading, sessionId = 0, streamingMessage }) {
|
|
216
237
|
return /* @__PURE__ */ jsxs(Box, {
|
|
217
238
|
flexDirection: "column",
|
|
218
239
|
children: [
|
|
219
|
-
|
|
240
|
+
/* @__PURE__ */ jsx(Static, {
|
|
241
|
+
items: messages.filter(({ content }) => content !== TURN_ABORTED_MESSAGE),
|
|
242
|
+
children: (message, index) => /* @__PURE__ */ jsx(Message, { message }, index)
|
|
243
|
+
}, sessionId),
|
|
220
244
|
streamingMessage && /* @__PURE__ */ jsx(Message, { message: streamingMessage }),
|
|
221
245
|
isLoading && !streamingMessage?.content && /* @__PURE__ */ jsx(Box, {
|
|
222
246
|
marginTop: -1,
|
|
@@ -229,9 +253,9 @@ function Messages({ messages, isLoading, streamingMessage }) {
|
|
|
229
253
|
}
|
|
230
254
|
//#endregion
|
|
231
255
|
//#region src/components/SelectPrompt.tsx
|
|
232
|
-
function SelectPrompt({ children,
|
|
233
|
-
useInput((
|
|
234
|
-
if (key.escape)
|
|
256
|
+
function SelectPrompt({ children, onCancel, ...selectProps }) {
|
|
257
|
+
useInput((input, key) => {
|
|
258
|
+
if (key.escape || key.ctrl && input === "c") onCancel?.();
|
|
235
259
|
});
|
|
236
260
|
return /* @__PURE__ */ jsxs(Box, {
|
|
237
261
|
flexDirection: "column",
|
|
@@ -299,7 +323,7 @@ function PlanApproval({ planContent, onModeChange }) {
|
|
|
299
323
|
onChange: useCallback((value) => {
|
|
300
324
|
onModeChange(value);
|
|
301
325
|
}, [onModeChange]),
|
|
302
|
-
|
|
326
|
+
onCancel: useCallback(() => {
|
|
303
327
|
onModeChange(NAME.PLAN);
|
|
304
328
|
}, [onModeChange]),
|
|
305
329
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
@@ -340,7 +364,7 @@ function ToolApproval({ toolCall, onDecision }) {
|
|
|
340
364
|
return /* @__PURE__ */ jsxs(SelectPrompt, {
|
|
341
365
|
options,
|
|
342
366
|
onChange: handleChange,
|
|
343
|
-
|
|
367
|
+
onCancel: handleEscape,
|
|
344
368
|
children: [
|
|
345
369
|
/* @__PURE__ */ jsx(Text, {
|
|
346
370
|
color: "yellow",
|
|
@@ -385,16 +409,27 @@ var INTERRUPT_REASON = /* @__PURE__ */ function(INTERRUPT_REASON) {
|
|
|
385
409
|
}({});
|
|
386
410
|
//#endregion
|
|
387
411
|
//#region src/components/TextInput/TextInput.tsx
|
|
388
|
-
function TextInput({ value, isDisabled = false, placeholder, onChange, onSubmit }) {
|
|
412
|
+
function TextInput({ value, isDisabled = false, placeholder, cursorPosition: externalCursorPosition, onChange, onSubmit }) {
|
|
389
413
|
const [cursorPosition, setCursorPosition] = useState(value.length);
|
|
390
414
|
const prevValueRef = useRef(value);
|
|
415
|
+
const prevExternalCursorRef = useRef(externalCursorPosition);
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
if (externalCursorPosition !== void 0 && externalCursorPosition !== prevExternalCursorRef.current) {
|
|
418
|
+
prevExternalCursorRef.current = externalCursorPosition;
|
|
419
|
+
setCursorPosition(externalCursorPosition);
|
|
420
|
+
}
|
|
421
|
+
}, [externalCursorPosition]);
|
|
391
422
|
useEffect(() => {
|
|
392
423
|
const prevValue = prevValueRef.current;
|
|
393
424
|
prevValueRef.current = value;
|
|
394
425
|
if (value === "") setCursorPosition(0);
|
|
395
|
-
else if (value.length > prevValue.length && cursorPosition <= prevValue.length) setCursorPosition(value.length);
|
|
426
|
+
else if (value.length > prevValue.length + 1 && cursorPosition <= prevValue.length && externalCursorPosition === void 0) setCursorPosition(value.length);
|
|
396
427
|
else if (cursorPosition > value.length) setCursorPosition(value.length);
|
|
397
|
-
}, [
|
|
428
|
+
}, [
|
|
429
|
+
value,
|
|
430
|
+
cursorPosition,
|
|
431
|
+
externalCursorPosition
|
|
432
|
+
]);
|
|
398
433
|
useInput((input, key) => {
|
|
399
434
|
// v8 ignore next
|
|
400
435
|
if (isDisabled) return;
|
|
@@ -441,12 +476,23 @@ function TextInput({ value, isDisabled = false, placeholder, onChange, onSubmit
|
|
|
441
476
|
}, { isActive: !isDisabled });
|
|
442
477
|
const displayValue = value || (placeholder ?? "");
|
|
443
478
|
const isPlaceholder = Boolean(!value && placeholder);
|
|
444
|
-
const
|
|
479
|
+
const cursorChar = displayValue[cursorPosition] || " ";
|
|
445
480
|
const before = displayValue.slice(0, cursorPosition);
|
|
446
481
|
const after = displayValue.slice(cursorPosition + 1);
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
482
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
483
|
+
/* @__PURE__ */ jsx(Text, {
|
|
484
|
+
dimColor: isPlaceholder,
|
|
485
|
+
children: before
|
|
486
|
+
}),
|
|
487
|
+
/* @__PURE__ */ jsx(Text, {
|
|
488
|
+
inverse: true,
|
|
489
|
+
children: cursorChar
|
|
490
|
+
}),
|
|
491
|
+
/* @__PURE__ */ jsx(Text, {
|
|
492
|
+
dimColor: isPlaceholder,
|
|
493
|
+
children: after
|
|
494
|
+
})
|
|
495
|
+
] });
|
|
450
496
|
}
|
|
451
497
|
//#endregion
|
|
452
498
|
//#region src/components/Chat/CommandMenu.tsx
|
|
@@ -470,7 +516,7 @@ function CommandMenu({ input, onSubmit }) {
|
|
|
470
516
|
//#endregion
|
|
471
517
|
//#region src/components/Chat/FileSuggestions.tsx
|
|
472
518
|
var MAX_VISIBLE_OPTIONS = 5;
|
|
473
|
-
var MENTION_PATTERN = /(
|
|
519
|
+
var MENTION_PATTERN = /(^|.)@(\S+)/;
|
|
474
520
|
var RIPGREP_MAX_BUFFER = 10 * 1024 * 1024;
|
|
475
521
|
function normalizePath(filePath) {
|
|
476
522
|
return filePath.replaceAll("\\", "/");
|
|
@@ -486,8 +532,17 @@ function getMentionMatch(input) {
|
|
|
486
532
|
function buildNextInput(input, filePath) {
|
|
487
533
|
const mentionMatch = getMentionMatch(input);
|
|
488
534
|
// v8 ignore next 3
|
|
489
|
-
if (!mentionMatch) return
|
|
490
|
-
|
|
535
|
+
if (!mentionMatch) return {
|
|
536
|
+
value: input,
|
|
537
|
+
cursorPosition: input.length
|
|
538
|
+
};
|
|
539
|
+
const mentionEndIndex = mentionMatch.prefix.length + 1 + mentionMatch.query.length;
|
|
540
|
+
const suffix = input.slice(mentionEndIndex);
|
|
541
|
+
const separator = !suffix.length || !/\s/.test(suffix[0]) ? " " : "";
|
|
542
|
+
return {
|
|
543
|
+
value: `${mentionMatch.prefix}${filePath}${separator}${suffix}`,
|
|
544
|
+
cursorPosition: mentionMatch.prefix.length + filePath.length + separator.length
|
|
545
|
+
};
|
|
491
546
|
}
|
|
492
547
|
function listProjectFilesFallback(rootDir) {
|
|
493
548
|
const filePaths = [];
|
|
@@ -558,7 +613,7 @@ function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
|
|
|
558
613
|
onChange(null);
|
|
559
614
|
return;
|
|
560
615
|
}
|
|
561
|
-
onChange(buildNextInput(input, options[focusedIndex]));
|
|
616
|
+
onChange(buildNextInput(input, options[focusedIndex]).value);
|
|
562
617
|
}, [
|
|
563
618
|
focusedIndex,
|
|
564
619
|
input,
|
|
@@ -596,21 +651,38 @@ function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
|
|
|
596
651
|
//#endregion
|
|
597
652
|
//#region src/components/Chat/Input.tsx
|
|
598
653
|
function hasFileSuggestionQuery(input) {
|
|
599
|
-
return /(
|
|
654
|
+
return /(^|.)@\S+/.test(input);
|
|
600
655
|
}
|
|
601
656
|
function Input({ isDisabled = false, onInterrupt, onSubmit }) {
|
|
602
657
|
const { exit } = useApp();
|
|
603
658
|
const [input, setInput] = useState("");
|
|
659
|
+
const [cursorPosition, setCursorPosition] = useState(void 0);
|
|
604
660
|
const fileSuggestionRef = useRef(null);
|
|
605
661
|
const resetInput = useCallback(() => {
|
|
606
662
|
setInput("");
|
|
607
663
|
}, []);
|
|
608
664
|
const handleSelectFileSuggestion = useCallback((nextInput) => {
|
|
609
|
-
setInput(nextInput);
|
|
665
|
+
setInput(nextInput.value);
|
|
666
|
+
setCursorPosition(nextInput.cursorPosition);
|
|
610
667
|
}, []);
|
|
611
668
|
const handleFileSuggestionChange = useCallback((nextInput) => {
|
|
612
|
-
|
|
613
|
-
|
|
669
|
+
if (nextInput) {
|
|
670
|
+
const mentionMatch = /(^|.)@(\S+)/.exec(input);
|
|
671
|
+
// v8 ignore start
|
|
672
|
+
if (mentionMatch) {
|
|
673
|
+
const prefixLength = mentionMatch.index + mentionMatch[1].length;
|
|
674
|
+
const queryLength = mentionMatch[2].length;
|
|
675
|
+
const suffix = input.slice(prefixLength + 1 + queryLength);
|
|
676
|
+
fileSuggestionRef.current = {
|
|
677
|
+
value: nextInput,
|
|
678
|
+
cursorPosition: nextInput.length - suffix.length
|
|
679
|
+
};
|
|
680
|
+
} else fileSuggestionRef.current = {
|
|
681
|
+
value: nextInput,
|
|
682
|
+
cursorPosition: nextInput.length
|
|
683
|
+
};
|
|
684
|
+
} else fileSuggestionRef.current = null;
|
|
685
|
+
}, [input]);
|
|
614
686
|
const submitAndReset = useCallback((input) => {
|
|
615
687
|
const trimmedInput = input.trim();
|
|
616
688
|
if (!trimmedInput) return;
|
|
@@ -652,6 +724,7 @@ function Input({ isDisabled = false, onInterrupt, onSubmit }) {
|
|
|
652
724
|
/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, { children: "> " }), /* @__PURE__ */ jsx(TextInput, {
|
|
653
725
|
value: input,
|
|
654
726
|
isDisabled,
|
|
727
|
+
cursorPosition,
|
|
655
728
|
onChange: setInput,
|
|
656
729
|
onSubmit: handleSubmitText,
|
|
657
730
|
placeholder: "Ask anything... (/ commands, @ files)"
|
|
@@ -786,11 +859,13 @@ function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
|
786
859
|
return;
|
|
787
860
|
}
|
|
788
861
|
}
|
|
862
|
+
await prewarmCodeBlocks(assistantMessage.content);
|
|
789
863
|
commitAssistantMessage();
|
|
790
864
|
} catch (error) {
|
|
791
865
|
// v8 ignore next
|
|
792
866
|
if (!controller.signal.aborted) {
|
|
793
867
|
assistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
868
|
+
await prewarmCodeBlocks(assistantMessage.content);
|
|
794
869
|
commitAssistantMessage();
|
|
795
870
|
}
|
|
796
871
|
} finally {
|
|
@@ -856,6 +931,7 @@ function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
|
856
931
|
return;
|
|
857
932
|
}
|
|
858
933
|
}
|
|
934
|
+
await prewarmCodeBlocks(assistantMessage.content);
|
|
859
935
|
const researchMessages = commitAssistantMessage();
|
|
860
936
|
const planInstruction = {
|
|
861
937
|
role: ROLE.SYSTEM,
|
|
@@ -896,6 +972,7 @@ function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
|
896
972
|
// v8 ignore next
|
|
897
973
|
if (!controller.signal.aborted) {
|
|
898
974
|
assistantMessage.content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
975
|
+
await prewarmCodeBlocks(assistantMessage.content);
|
|
899
976
|
commitAssistantMessage();
|
|
900
977
|
}
|
|
901
978
|
} finally {
|
|
@@ -996,6 +1073,7 @@ function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
|
996
1073
|
/* @__PURE__ */ jsx(Messages, {
|
|
997
1074
|
messages,
|
|
998
1075
|
isLoading,
|
|
1076
|
+
sessionId,
|
|
999
1077
|
streamingMessage
|
|
1000
1078
|
}),
|
|
1001
1079
|
pendingPlan && /* @__PURE__ */ jsx(PlanApproval, {
|
|
@@ -1013,10 +1091,13 @@ function Chat({ model, onCommand, mode, onModeChange, sessionId }) {
|
|
|
1013
1091
|
children: interruptReason === INTERRUPT_REASON.REJECTED ? "❗ Tool call rejected." : "❗ Execution interrupted."
|
|
1014
1092
|
})
|
|
1015
1093
|
}),
|
|
1016
|
-
!pendingPlan && !pendingToolCall && /* @__PURE__ */ jsx(
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1094
|
+
!pendingPlan && !pendingToolCall && /* @__PURE__ */ jsx(Box, {
|
|
1095
|
+
marginTop: 1,
|
|
1096
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
1097
|
+
isDisabled: isLoading,
|
|
1098
|
+
onInterrupt: handleInterrupt,
|
|
1099
|
+
onSubmit: handleSubmit
|
|
1100
|
+
})
|
|
1020
1101
|
})
|
|
1021
1102
|
]
|
|
1022
1103
|
});
|
|
@@ -1027,11 +1108,12 @@ function getModeColor(mode) {
|
|
|
1027
1108
|
switch (mode) {
|
|
1028
1109
|
case NAME.PLAN: return "blue";
|
|
1029
1110
|
case NAME.AUTO: return "red";
|
|
1030
|
-
case NAME.SAFE:
|
|
1031
|
-
|
|
1111
|
+
case NAME.SAFE: return "green";
|
|
1112
|
+
// v8 ignore next
|
|
1113
|
+
default: return;
|
|
1032
1114
|
}
|
|
1033
1115
|
}
|
|
1034
|
-
function Footer({ mode, onToggleMode }) {
|
|
1116
|
+
function Footer({ mode, model, onToggleMode }) {
|
|
1035
1117
|
useInput((_, key) => {
|
|
1036
1118
|
if (key.tab && key.shift) onToggleMode();
|
|
1037
1119
|
});
|
|
@@ -1047,9 +1129,13 @@ function Footer({ mode, onToggleMode }) {
|
|
|
1047
1129
|
color: getModeColor(mode),
|
|
1048
1130
|
children: modeLabel
|
|
1049
1131
|
}),
|
|
1132
|
+
" (Shift+Tab to toggle)",
|
|
1133
|
+
" ",
|
|
1134
|
+
"❖",
|
|
1135
|
+
" Model: ",
|
|
1050
1136
|
/* @__PURE__ */ jsx(Text, {
|
|
1051
|
-
|
|
1052
|
-
children:
|
|
1137
|
+
color: "cyan",
|
|
1138
|
+
children: model
|
|
1053
1139
|
})
|
|
1054
1140
|
]
|
|
1055
1141
|
})
|
|
@@ -1061,45 +1147,53 @@ function abbreviatePath(dir) {
|
|
|
1061
1147
|
const home = homedir();
|
|
1062
1148
|
return dir.startsWith(home) ? `~${dir.slice(home.length)}` : dir;
|
|
1063
1149
|
}
|
|
1064
|
-
function Header({ model }) {
|
|
1150
|
+
function Header({ model, onLoad }) {
|
|
1065
1151
|
const directory = abbreviatePath(process.cwd());
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
children: [
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
]
|
|
1081
|
-
})] }),
|
|
1082
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1083
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1084
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1152
|
+
useEffect(() => {
|
|
1153
|
+
onLoad();
|
|
1154
|
+
}, []);
|
|
1155
|
+
return /* @__PURE__ */ jsx(Static, {
|
|
1156
|
+
items: [0],
|
|
1157
|
+
children: (key) => /* @__PURE__ */ jsxs(Box, {
|
|
1158
|
+
borderStyle: "round",
|
|
1159
|
+
flexDirection: "column",
|
|
1160
|
+
paddingX: 1,
|
|
1161
|
+
children: [
|
|
1162
|
+
/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
1163
|
+
bold: true,
|
|
1164
|
+
children: [HEADER_PREFIX, "Code Ollama"]
|
|
1165
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
1085
1166
|
dimColor: true,
|
|
1086
|
-
children:
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1167
|
+
children: [
|
|
1168
|
+
" (v",
|
|
1169
|
+
VERSION,
|
|
1170
|
+
")"
|
|
1171
|
+
]
|
|
1172
|
+
})] }),
|
|
1173
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
1174
|
+
marginTop: 1,
|
|
1175
|
+
children: [
|
|
1176
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1177
|
+
dimColor: true,
|
|
1178
|
+
children: "model:".padEnd(11)
|
|
1179
|
+
}),
|
|
1180
|
+
/* @__PURE__ */ jsx(Text, { children: model.padEnd(model.length + 3) }),
|
|
1181
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1182
|
+
color: "cyan",
|
|
1183
|
+
children: "/model"
|
|
1184
|
+
}),
|
|
1185
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1186
|
+
dimColor: true,
|
|
1187
|
+
children: " to switch"
|
|
1188
|
+
})
|
|
1189
|
+
]
|
|
1092
1190
|
}),
|
|
1093
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1191
|
+
/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
1094
1192
|
dimColor: true,
|
|
1095
|
-
children: "
|
|
1096
|
-
})
|
|
1097
|
-
]
|
|
1098
|
-
|
|
1099
|
-
dimColor: true,
|
|
1100
|
-
children: "directory:".padEnd(11)
|
|
1101
|
-
}), /* @__PURE__ */ jsx(Text, { children: directory })] })
|
|
1102
|
-
]
|
|
1193
|
+
children: "directory:".padEnd(11)
|
|
1194
|
+
}), /* @__PURE__ */ jsx(Text, { children: directory })] })
|
|
1195
|
+
]
|
|
1196
|
+
}, key)
|
|
1103
1197
|
});
|
|
1104
1198
|
}
|
|
1105
1199
|
//#endregion
|
|
@@ -1107,8 +1201,12 @@ function Header({ model }) {
|
|
|
1107
1201
|
function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
1108
1202
|
const [options, setOptions] = useState([]);
|
|
1109
1203
|
const [error, setError] = useState(null);
|
|
1110
|
-
useInput((
|
|
1111
|
-
if (options.length
|
|
1204
|
+
useInput(async (_input, key) => {
|
|
1205
|
+
if (!options.length) return;
|
|
1206
|
+
if (key.return) {
|
|
1207
|
+
await tick();
|
|
1208
|
+
onClose();
|
|
1209
|
+
}
|
|
1112
1210
|
});
|
|
1113
1211
|
useEffect(() => {
|
|
1114
1212
|
async function load() {
|
|
@@ -1137,7 +1235,7 @@ function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
|
1137
1235
|
options,
|
|
1138
1236
|
defaultValue: currentModel,
|
|
1139
1237
|
onChange: onSelect,
|
|
1140
|
-
|
|
1238
|
+
onCancel: onClose,
|
|
1141
1239
|
children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Select a model" })
|
|
1142
1240
|
});
|
|
1143
1241
|
}
|
|
@@ -1149,6 +1247,10 @@ function App() {
|
|
|
1149
1247
|
const [picking, setPicking] = useState(false);
|
|
1150
1248
|
const [mode, setMode] = useState(NAME.SAFE);
|
|
1151
1249
|
const [sessionId, setSessionId] = useState(0);
|
|
1250
|
+
const [isHeaderLoaded, setIsHeaderLoaded] = useState(false);
|
|
1251
|
+
const handleHeaderLoad = useCallback(() => {
|
|
1252
|
+
setIsHeaderLoaded(true);
|
|
1253
|
+
}, []);
|
|
1152
1254
|
const handleCommand = useCallback((command) => {
|
|
1153
1255
|
switch (command) {
|
|
1154
1256
|
case "/model":
|
|
@@ -1156,6 +1258,7 @@ function App() {
|
|
|
1156
1258
|
break;
|
|
1157
1259
|
case "/clear":
|
|
1158
1260
|
resetSystemMessage();
|
|
1261
|
+
clear();
|
|
1159
1262
|
setPicking(false);
|
|
1160
1263
|
setSessionId((sessionId) => sessionId + 1);
|
|
1161
1264
|
break;
|
|
@@ -1204,10 +1307,14 @@ function App() {
|
|
|
1204
1307
|
return /* @__PURE__ */ jsxs(Box, {
|
|
1205
1308
|
flexDirection: "column",
|
|
1206
1309
|
children: [
|
|
1207
|
-
/* @__PURE__ */ jsx(Header, {
|
|
1208
|
-
|
|
1310
|
+
/* @__PURE__ */ jsx(Header, {
|
|
1311
|
+
model,
|
|
1312
|
+
onLoad: handleHeaderLoad
|
|
1313
|
+
}),
|
|
1314
|
+
isHeaderLoaded && body,
|
|
1209
1315
|
/* @__PURE__ */ jsx(Footer, {
|
|
1210
1316
|
mode,
|
|
1317
|
+
model,
|
|
1211
1318
|
onToggleMode: handleToggleMode
|
|
1212
1319
|
})
|
|
1213
1320
|
]
|
|
@@ -1216,14 +1323,14 @@ function App() {
|
|
|
1216
1323
|
//#endregion
|
|
1217
1324
|
//#region src/tui.tsx
|
|
1218
1325
|
function renderApp() {
|
|
1219
|
-
|
|
1220
|
-
const app = render(
|
|
1326
|
+
let resetKey = 0;
|
|
1327
|
+
const app = render(/* @__PURE__ */ jsx(App, {}, resetKey), {
|
|
1221
1328
|
exitOnCtrlC: false,
|
|
1222
1329
|
maxFps: 60
|
|
1223
1330
|
});
|
|
1224
1331
|
setClearHandler(() => {
|
|
1225
|
-
|
|
1226
|
-
app.rerender(
|
|
1332
|
+
reset();
|
|
1333
|
+
app.rerender(/* @__PURE__ */ jsx(App, {}, ++resetKey));
|
|
1227
1334
|
});
|
|
1228
1335
|
}
|
|
1229
1336
|
//#endregion
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { exec } from "node:child_process";
|
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
//#endregion
|
|
10
10
|
//#region src/constants/package.ts
|
|
11
|
-
var VERSION = "0.
|
|
11
|
+
var VERSION = "0.10.0";
|
|
12
12
|
//#endregion
|
|
13
13
|
//#region src/constants/prompt.ts
|
|
14
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
|
|
@@ -166,7 +166,24 @@ async function listModels() {
|
|
|
166
166
|
const { models } = await client.list();
|
|
167
167
|
return models.map(({ name }) => name);
|
|
168
168
|
}
|
|
169
|
-
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/utils/screen.ts
|
|
171
|
+
var clearHandler = null;
|
|
172
|
+
function setClearHandler(handler) {
|
|
173
|
+
clearHandler = handler;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Clear the screen with Ink.
|
|
177
|
+
*/
|
|
178
|
+
function clear() {
|
|
179
|
+
clearHandler?.();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Reset the screen with ANSI escape sequence.
|
|
183
|
+
*/
|
|
184
|
+
function reset() {
|
|
185
|
+
process.stdout.write("\x1Bc\x1B[?25l");
|
|
186
|
+
}
|
|
170
187
|
//#endregion
|
|
171
188
|
//#region src/utils/time.ts
|
|
172
189
|
var tick = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -512,8 +529,8 @@ async function processRunStream(messages, model) {
|
|
|
512
529
|
}
|
|
513
530
|
async function main(args = process.argv.slice(2)) {
|
|
514
531
|
if (!args.length) {
|
|
515
|
-
const { renderApp } = await import("./assets/tui-
|
|
516
|
-
|
|
532
|
+
const { renderApp } = await import("./assets/tui-D2NgQSV7.js");
|
|
533
|
+
reset();
|
|
517
534
|
renderApp();
|
|
518
535
|
return;
|
|
519
536
|
}
|
|
@@ -535,4 +552,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
|
|
|
535
552
|
if (isEntrypoint()) main();
|
|
536
553
|
// v8 ignore stop
|
|
537
554
|
//#endregion
|
|
538
|
-
export { tick as a,
|
|
555
|
+
export { VERSION as _, tick as a, setClearHandler as c, loadConfig as d, saveConfig as f, PLAN_GENERATION_INSTRUCTION as g, ROLE as h, executeTool as i, listModels as l, withSystemMessage as m, main, TOOLS as n, clear as o, resetSystemMessage as p, WRITE_TOOLS as r, reset as s, READ_TOOLS as t, streamChat as u };
|