code-ollama 0.14.1 → 0.14.2
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-CoX71F7Y.js → tui-Dse5XVJ_.js} +261 -102
- package/dist/cli.js +48 -4
- package/package.json +4 -4
- package/dist/assets/shell-CipXM_WI.js +0 -46
|
@@ -7,18 +7,24 @@ import { Box, Static, Text, render, useApp, useInput, useStdout } from "ink";
|
|
|
7
7
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { Select, Spinner } from "@inkjs/ui";
|
|
10
|
-
import {
|
|
10
|
+
import { Marked } from "marked";
|
|
11
11
|
import { markedTerminal } from "marked-terminal";
|
|
12
12
|
//#region src/components/CodeBlock/CodeBlock.tsx
|
|
13
13
|
var highlightCache = /* @__PURE__ */ new Map();
|
|
14
|
-
var CODE_BLOCK_REGEX = /^(`{3,})(\w+)?[ \t]*\n([\s\S]*?)^\
|
|
14
|
+
var CODE_BLOCK_REGEX = /^(?<indent>[ \t]*)(`{3,})(\w+)?[ \t]*\n([\s\S]*?)^\k<indent>\2[ \t]*$/gm;
|
|
15
|
+
function normalizeCodeBlockContent(content, indent = "") {
|
|
16
|
+
if (!indent) return content.trim();
|
|
17
|
+
const indentPattern = new RegExp(`^${indent}`, "gm");
|
|
18
|
+
return content.replace(indentPattern, "").trim();
|
|
19
|
+
}
|
|
15
20
|
async function prewarmCodeBlocks(content) {
|
|
16
21
|
const promises = [];
|
|
17
22
|
let match;
|
|
18
23
|
CODE_BLOCK_REGEX.lastIndex = 0;
|
|
19
24
|
while ((match = CODE_BLOCK_REGEX.exec(content)) !== null) {
|
|
20
|
-
const
|
|
21
|
-
const
|
|
25
|
+
const indent = match[1];
|
|
26
|
+
const language = match[3];
|
|
27
|
+
const code = normalizeCodeBlockContent(match[4], indent);
|
|
22
28
|
// v8 ignore next 2
|
|
23
29
|
if (code) promises.push(prewarmHighlight(code, language));
|
|
24
30
|
}
|
|
@@ -165,15 +171,29 @@ var inlineMathExtension = {
|
|
|
165
171
|
//#endregion
|
|
166
172
|
//#region src/components/Markdown/Markdown.tsx
|
|
167
173
|
var HR_PLACEHOLDER = "__CODE_OLLAMA_HR_PLACEHOLDER__";
|
|
168
|
-
marked.use(markedTerminal({ theme: "gitHub" }));
|
|
169
|
-
marked.use({
|
|
170
|
-
extensions: [inlineMathExtension],
|
|
171
|
-
renderer: { hr: () => `${HR_PLACEHOLDER}\n` }
|
|
172
|
-
});
|
|
173
174
|
function renderMarkdown(content, hrWidth) {
|
|
174
175
|
const hr = "─".repeat(Math.max(1, hrWidth));
|
|
176
|
+
const markdown = new Marked();
|
|
177
|
+
const rendererExtension = {
|
|
178
|
+
extensions: [inlineMathExtension],
|
|
179
|
+
useNewRenderer: true,
|
|
180
|
+
renderer: {
|
|
181
|
+
hr: () => `${HR_PLACEHOLDER}\n`,
|
|
182
|
+
text(token) {
|
|
183
|
+
const textToken = token;
|
|
184
|
+
if (typeof token === "object" && Array.isArray(textToken.tokens)) return this.parser.parseInline(textToken.tokens);
|
|
185
|
+
return String(textToken.text);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
markdown.use(markedTerminal({
|
|
190
|
+
theme: "gitHub",
|
|
191
|
+
reflowText: true,
|
|
192
|
+
width: Math.max(1, hrWidth)
|
|
193
|
+
}));
|
|
194
|
+
markdown.use(rendererExtension);
|
|
175
195
|
try {
|
|
176
|
-
const result =
|
|
196
|
+
const result = markdown.parse(content);
|
|
177
197
|
return (typeof result === "string" ? result.trim() : content).replaceAll(HR_PLACEHOLDER, hr);
|
|
178
198
|
} catch {
|
|
179
199
|
return content;
|
|
@@ -197,6 +217,101 @@ var TURN_ABORTED_MESSAGE = [
|
|
|
197
217
|
"</turn_aborted>"
|
|
198
218
|
].join("\n");
|
|
199
219
|
//#endregion
|
|
220
|
+
//#region src/components/Messages/utils.ts
|
|
221
|
+
function isWordCharacter(char) {
|
|
222
|
+
return char !== void 0 && /[A-Za-z0-9]/.test(char);
|
|
223
|
+
}
|
|
224
|
+
function isEscaped(content, index) {
|
|
225
|
+
let slashCount = 0;
|
|
226
|
+
for (let cursor = index - 1; cursor >= 0 && content[cursor] === "\\"; cursor--) slashCount += 1;
|
|
227
|
+
return slashCount % 2 === 1;
|
|
228
|
+
}
|
|
229
|
+
function canOpenEmphasis(content, index, length) {
|
|
230
|
+
const previous = content[index - 1];
|
|
231
|
+
const next = content[index + length];
|
|
232
|
+
if (!next || /\s/.test(next)) return false;
|
|
233
|
+
return !isWordCharacter(previous);
|
|
234
|
+
}
|
|
235
|
+
function canCloseEmphasis(content, index, length) {
|
|
236
|
+
const previous = content[index - 1];
|
|
237
|
+
const next = content[index + length];
|
|
238
|
+
if (!previous || /\s/.test(previous)) return false;
|
|
239
|
+
return !isWordCharacter(next);
|
|
240
|
+
}
|
|
241
|
+
function findUnmatchedInlineDelimiter(content) {
|
|
242
|
+
const stack = [];
|
|
243
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
244
|
+
const current = content[index];
|
|
245
|
+
if (isEscaped(content, index)) continue;
|
|
246
|
+
const top = stack.at(-1);
|
|
247
|
+
if (top?.kind === "code") {
|
|
248
|
+
if (current === "`") stack.pop();
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (top?.kind === "latex") {
|
|
252
|
+
if (current === "$") stack.pop();
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (current === "`") {
|
|
256
|
+
stack.push({
|
|
257
|
+
index,
|
|
258
|
+
length: 1,
|
|
259
|
+
kind: "code",
|
|
260
|
+
marker: "`"
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (current === "$") {
|
|
265
|
+
stack.push({
|
|
266
|
+
index,
|
|
267
|
+
length: 1,
|
|
268
|
+
kind: "latex",
|
|
269
|
+
marker: "$"
|
|
270
|
+
});
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (current !== "*") continue;
|
|
274
|
+
const marker = current;
|
|
275
|
+
const length = content[index + 1] === marker ? 2 : 1;
|
|
276
|
+
const token = marker.repeat(length);
|
|
277
|
+
const kind = length === 2 ? "bold" : "italic";
|
|
278
|
+
if (top?.marker === token && top.kind === kind && canCloseEmphasis(content, index, length)) {
|
|
279
|
+
stack.pop();
|
|
280
|
+
if (length === 2) index += 1;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (canOpenEmphasis(content, index, length)) {
|
|
284
|
+
stack.push({
|
|
285
|
+
index,
|
|
286
|
+
length,
|
|
287
|
+
kind,
|
|
288
|
+
marker: token
|
|
289
|
+
});
|
|
290
|
+
if (length === 2) index += 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return stack[0] ?? null;
|
|
294
|
+
}
|
|
295
|
+
function splitStreamingInlineContent(content) {
|
|
296
|
+
const unmatched = findUnmatchedInlineDelimiter(content);
|
|
297
|
+
if (!unmatched) return [{
|
|
298
|
+
type: "markdown",
|
|
299
|
+
content
|
|
300
|
+
}];
|
|
301
|
+
const parts = [];
|
|
302
|
+
const prefix = content.slice(0, unmatched.index);
|
|
303
|
+
const plainSuffix = content.slice(unmatched.index + unmatched.length);
|
|
304
|
+
if (prefix) parts.push({
|
|
305
|
+
type: "markdown",
|
|
306
|
+
content: prefix
|
|
307
|
+
});
|
|
308
|
+
if (plainSuffix) parts.push({
|
|
309
|
+
type: "plain",
|
|
310
|
+
content: plainSuffix
|
|
311
|
+
});
|
|
312
|
+
return parts;
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
200
315
|
//#region src/components/Messages/Messages.tsx
|
|
201
316
|
function getMessageColor(role) {
|
|
202
317
|
switch (role) {
|
|
@@ -206,46 +321,92 @@ function getMessageColor(role) {
|
|
|
206
321
|
default: return;
|
|
207
322
|
}
|
|
208
323
|
}
|
|
324
|
+
var FENCE_LINE_REGEX = /^(?<indent>[ \t]*)(?<fence>`{3,})(?<language>\w+)?[ \t]*$/;
|
|
325
|
+
function flushTextSegment(segments, textLines) {
|
|
326
|
+
const textContent = textLines.join("\n").trim();
|
|
327
|
+
if (textContent) segments.push({
|
|
328
|
+
type: "text",
|
|
329
|
+
content: textContent
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function flushCodeSegment(segments, codeLines, fenceState) {
|
|
333
|
+
if (fenceState.ambiguous) {
|
|
334
|
+
segments.push({
|
|
335
|
+
type: "raw",
|
|
336
|
+
content: fenceState.rawLines.join("\n")
|
|
337
|
+
});
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const codeContent = normalizeCodeBlockContent(codeLines.join("\n"), fenceState.indent);
|
|
341
|
+
if (codeContent) segments.push({
|
|
342
|
+
type: "code",
|
|
343
|
+
content: codeContent,
|
|
344
|
+
language: fenceState.language
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
function unwrapRawMarkdownFence(content) {
|
|
348
|
+
if (!content.startsWith("```markdown\n") || !content.endsWith("\n```")) return null;
|
|
349
|
+
return content.slice(12, -4);
|
|
350
|
+
}
|
|
209
351
|
function parseContent(content) {
|
|
210
352
|
const segments = [];
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
353
|
+
const lines = content.split("\n");
|
|
354
|
+
const textLines = [];
|
|
355
|
+
const codeLines = [];
|
|
356
|
+
let fenceState = null;
|
|
357
|
+
for (const line of lines) {
|
|
358
|
+
const fenceMatch = FENCE_LINE_REGEX.exec(line);
|
|
359
|
+
if (fenceMatch?.groups) {
|
|
360
|
+
const { indent, fence, language } = fenceMatch.groups;
|
|
361
|
+
if (!fenceState) {
|
|
362
|
+
flushTextSegment(segments, textLines);
|
|
363
|
+
textLines.length = 0;
|
|
364
|
+
fenceState = {
|
|
365
|
+
indent,
|
|
366
|
+
fence,
|
|
367
|
+
language,
|
|
368
|
+
rawLines: [line],
|
|
369
|
+
ambiguous: false,
|
|
370
|
+
rawFenceDepth: 1
|
|
371
|
+
};
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (indent === fenceState.indent && fence === fenceState.fence) {
|
|
375
|
+
fenceState.rawLines.push(line);
|
|
376
|
+
if (fenceState.ambiguous) {
|
|
377
|
+
if (language) {
|
|
378
|
+
fenceState.rawFenceDepth += 1;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
fenceState.rawFenceDepth -= 1;
|
|
382
|
+
if (fenceState.rawFenceDepth === 0) {
|
|
383
|
+
flushCodeSegment(segments, codeLines, fenceState);
|
|
384
|
+
codeLines.length = 0;
|
|
385
|
+
fenceState = null;
|
|
386
|
+
}
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (!language) {
|
|
390
|
+
flushCodeSegment(segments, codeLines, fenceState);
|
|
391
|
+
codeLines.length = 0;
|
|
392
|
+
fenceState = null;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
fenceState.ambiguous = true;
|
|
396
|
+
fenceState.rawFenceDepth += 1;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
222
399
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
type: "code",
|
|
228
|
-
content: codeContent,
|
|
229
|
-
language
|
|
230
|
-
});
|
|
231
|
-
lastIndex = match.index + match[0].length;
|
|
232
|
-
}
|
|
233
|
-
if (lastIndex < content.length) {
|
|
234
|
-
const textContent = content.slice(lastIndex).trim();
|
|
235
|
-
// v8 ignore next 2 - Defensive check for empty trimmed content
|
|
236
|
-
if (textContent) segments.push({
|
|
237
|
-
type: "text",
|
|
238
|
-
content: textContent
|
|
239
|
-
});
|
|
400
|
+
if (fenceState) {
|
|
401
|
+
fenceState.rawLines.push(line);
|
|
402
|
+
codeLines.push(line);
|
|
403
|
+
} else textLines.push(line);
|
|
240
404
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
type: "text",
|
|
244
|
-
content: content.trim()
|
|
245
|
-
});
|
|
405
|
+
if (fenceState) textLines.push(...fenceState.rawLines);
|
|
406
|
+
flushTextSegment(segments, textLines);
|
|
246
407
|
return segments;
|
|
247
408
|
}
|
|
248
|
-
var Message = memo(function Message({ message }) {
|
|
409
|
+
var Message = memo(function Message({ message, isStreaming = false }) {
|
|
249
410
|
const messageColor = getMessageColor(message.role);
|
|
250
411
|
const isSystem = message.role === SYSTEM;
|
|
251
412
|
const isUser = message.role === USER;
|
|
@@ -275,15 +436,34 @@ var Message = memo(function Message({ message }) {
|
|
|
275
436
|
role: message.role
|
|
276
437
|
})
|
|
277
438
|
}, index);
|
|
439
|
+
if (segment.type === "raw") {
|
|
440
|
+
const markdownSource = unwrapRawMarkdownFence(segment.content);
|
|
441
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
442
|
+
marginX: 2,
|
|
443
|
+
children: /* @__PURE__ */ jsx(CodeBlock, {
|
|
444
|
+
code: markdownSource ?? segment.content,
|
|
445
|
+
language: markdownSource ? "markdown" : segment.language,
|
|
446
|
+
role: message.role
|
|
447
|
+
})
|
|
448
|
+
}, index);
|
|
449
|
+
}
|
|
450
|
+
const textParts = isStreaming && !isUser ? splitStreamingInlineContent(segment.content) : [{
|
|
451
|
+
type: "markdown",
|
|
452
|
+
content: segment.content
|
|
453
|
+
}];
|
|
278
454
|
return isUser ? /* @__PURE__ */ jsx(Text, {
|
|
279
455
|
color: messageColor,
|
|
280
456
|
children: prefix + segment.content
|
|
281
457
|
}, index) : /* @__PURE__ */ jsx(Box, {
|
|
458
|
+
flexDirection: "column",
|
|
282
459
|
marginX: 2,
|
|
283
|
-
children: /* @__PURE__ */ jsx(
|
|
284
|
-
|
|
460
|
+
children: textParts.map((part, partIndex) => part.type === "plain" ? /* @__PURE__ */ jsx(Text, {
|
|
461
|
+
color: messageColor,
|
|
462
|
+
children: part.content
|
|
463
|
+
}, partIndex) : /* @__PURE__ */ jsx(Markdown, {
|
|
464
|
+
content: part.content,
|
|
285
465
|
color: messageColor
|
|
286
|
-
})
|
|
466
|
+
}, partIndex))
|
|
287
467
|
}, index);
|
|
288
468
|
})
|
|
289
469
|
});
|
|
@@ -296,7 +476,10 @@ function Messages({ messages, isLoading, sessionId = 0, streamingMessage }) {
|
|
|
296
476
|
items: messages.filter(({ content }) => content !== TURN_ABORTED_MESSAGE),
|
|
297
477
|
children: (message, index) => /* @__PURE__ */ jsx(Message, { message }, index)
|
|
298
478
|
}, sessionId),
|
|
299
|
-
streamingMessage && /* @__PURE__ */ jsx(Message, {
|
|
479
|
+
streamingMessage && /* @__PURE__ */ jsx(Message, {
|
|
480
|
+
isStreaming: true,
|
|
481
|
+
message: streamingMessage
|
|
482
|
+
}),
|
|
300
483
|
isLoading && !streamingMessage?.content && /* @__PURE__ */ jsx(Box, {
|
|
301
484
|
marginTop: -1,
|
|
302
485
|
marginBottom: 1,
|
|
@@ -1373,47 +1556,35 @@ function ModelPicker({ currentModel, onSelect, onClose }) {
|
|
|
1373
1556
|
}
|
|
1374
1557
|
//#endregion
|
|
1375
1558
|
//#region src/components/SearchSettings.tsx
|
|
1376
|
-
var View = /* @__PURE__ */ function(View) {
|
|
1377
|
-
View["Menu"] = "menu";
|
|
1378
|
-
View["Edit"] = "edit";
|
|
1379
|
-
return View;
|
|
1380
|
-
}(View || {});
|
|
1381
|
-
var Action = /* @__PURE__ */ function(Action) {
|
|
1382
|
-
Action["Set"] = "set";
|
|
1383
|
-
Action["Clear"] = "clear";
|
|
1384
|
-
Action["Cancel"] = "cancel";
|
|
1385
|
-
return Action;
|
|
1386
|
-
}(Action || {});
|
|
1387
1559
|
function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
1388
|
-
const [view, setView] = useState(
|
|
1560
|
+
const [view, setView] = useState("menu");
|
|
1389
1561
|
const [draftUrl, setDraftUrl] = useState(currentUrl ?? "");
|
|
1390
1562
|
const [error, setError] = useState(null);
|
|
1391
1563
|
const options = useMemo(() => {
|
|
1392
1564
|
const nextOptions = [{
|
|
1393
1565
|
label: currentUrl ? "Update SearXNG URL" : "Set SearXNG URL",
|
|
1394
|
-
value:
|
|
1566
|
+
value: "set"
|
|
1395
1567
|
}];
|
|
1396
1568
|
if (currentUrl) nextOptions.push({
|
|
1397
1569
|
label: "Clear SearXNG URL",
|
|
1398
|
-
value:
|
|
1570
|
+
value: "clear"
|
|
1399
1571
|
});
|
|
1400
1572
|
nextOptions.push({
|
|
1401
1573
|
label: "Cancel",
|
|
1402
|
-
value:
|
|
1574
|
+
value: "cancel"
|
|
1403
1575
|
});
|
|
1404
1576
|
return nextOptions;
|
|
1405
1577
|
}, [currentUrl]);
|
|
1406
1578
|
const handleChange = useCallback((value) => {
|
|
1407
1579
|
setError(null);
|
|
1408
1580
|
switch (value) {
|
|
1409
|
-
case
|
|
1581
|
+
case "set":
|
|
1410
1582
|
setDraftUrl(currentUrl ?? "");
|
|
1411
|
-
setView(
|
|
1583
|
+
setView("edit");
|
|
1412
1584
|
break;
|
|
1413
|
-
case
|
|
1585
|
+
case "clear":
|
|
1414
1586
|
onSave({ searxngBaseUrl: void 0 });
|
|
1415
1587
|
break;
|
|
1416
|
-
case Action.Cancel:
|
|
1417
1588
|
default: onClose();
|
|
1418
1589
|
}
|
|
1419
1590
|
}, [
|
|
@@ -1439,13 +1610,13 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1439
1610
|
}
|
|
1440
1611
|
}, [onSave]);
|
|
1441
1612
|
useInput((input, key) => {
|
|
1442
|
-
if (view ===
|
|
1613
|
+
if (view === "edit" && (key.escape || key.ctrl && input === "c")) {
|
|
1443
1614
|
setDraftUrl(currentUrl ?? "");
|
|
1444
1615
|
setError(null);
|
|
1445
|
-
setView(
|
|
1616
|
+
setView("menu");
|
|
1446
1617
|
}
|
|
1447
1618
|
});
|
|
1448
|
-
if (view ===
|
|
1619
|
+
if (view === "edit") return /* @__PURE__ */ jsxs(Box, {
|
|
1449
1620
|
flexDirection: "column",
|
|
1450
1621
|
children: [
|
|
1451
1622
|
/* @__PURE__ */ jsx(Text, { children: "Set the SearXNG base URL. DuckDuckGo remains the fallback." }),
|
|
@@ -1482,11 +1653,6 @@ function SearchSettings({ currentUrl, onClose, onSave }) {
|
|
|
1482
1653
|
}
|
|
1483
1654
|
//#endregion
|
|
1484
1655
|
//#region src/components/SessionManager.tsx
|
|
1485
|
-
var VIEW = /* @__PURE__ */ function(VIEW) {
|
|
1486
|
-
VIEW["MAIN"] = "main";
|
|
1487
|
-
VIEW["DELETE"] = "delete";
|
|
1488
|
-
return VIEW;
|
|
1489
|
-
}(VIEW || {});
|
|
1490
1656
|
var ACTION = {
|
|
1491
1657
|
BACK: "back",
|
|
1492
1658
|
CLOSE: "close",
|
|
@@ -1500,11 +1666,11 @@ function formatSessionLabel(session) {
|
|
|
1500
1666
|
return `${session.title} (${timestamp})`;
|
|
1501
1667
|
}
|
|
1502
1668
|
function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen }) {
|
|
1503
|
-
const [view, setView] = useState(
|
|
1669
|
+
const [view, setView] = useState("main");
|
|
1504
1670
|
const [error, setError] = useState();
|
|
1505
1671
|
const [, refreshSessionList] = useState(0);
|
|
1506
1672
|
const sessions = listSessions();
|
|
1507
|
-
const options = view ===
|
|
1673
|
+
const options = view === "delete" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
|
|
1508
1674
|
label: `Delete ${formatSessionLabel(session)}`,
|
|
1509
1675
|
value: `${ACTION.DELETE_PREFIX}${session.id}`
|
|
1510
1676
|
})), {
|
|
@@ -1537,10 +1703,10 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen })
|
|
|
1537
1703
|
onNew();
|
|
1538
1704
|
break;
|
|
1539
1705
|
case value === ACTION.DELETE_MENU:
|
|
1540
|
-
setView(
|
|
1706
|
+
setView("delete");
|
|
1541
1707
|
break;
|
|
1542
1708
|
case value === ACTION.BACK:
|
|
1543
|
-
setView(
|
|
1709
|
+
setView("main");
|
|
1544
1710
|
break;
|
|
1545
1711
|
case value.startsWith(ACTION.DELETE_PREFIX):
|
|
1546
1712
|
try {
|
|
@@ -1570,7 +1736,7 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen })
|
|
|
1570
1736
|
flexDirection: "column",
|
|
1571
1737
|
children: [
|
|
1572
1738
|
/* @__PURE__ */ jsx(Text, { children: "Sessions" }),
|
|
1573
|
-
/* @__PURE__ */ jsx(SelectPromptHint, { message: view ===
|
|
1739
|
+
/* @__PURE__ */ jsx(SelectPromptHint, { message: view === "delete" ? "Delete session" : "Select session" }),
|
|
1574
1740
|
error && /* @__PURE__ */ jsx(Box, {
|
|
1575
1741
|
marginBottom: 1,
|
|
1576
1742
|
children: /* @__PURE__ */ jsx(Text, {
|
|
@@ -1588,20 +1754,13 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen })
|
|
|
1588
1754
|
}
|
|
1589
1755
|
//#endregion
|
|
1590
1756
|
//#region src/components/App.tsx
|
|
1591
|
-
var SCREEN = /* @__PURE__ */ function(SCREEN) {
|
|
1592
|
-
SCREEN["CHAT"] = "chat";
|
|
1593
|
-
SCREEN["MODEL_PICKER"] = "model-picker";
|
|
1594
|
-
SCREEN["SEARCH_SETTINGS"] = "search-settings";
|
|
1595
|
-
SCREEN["SESSION_MANAGER"] = "session-manager";
|
|
1596
|
-
return SCREEN;
|
|
1597
|
-
}(SCREEN || {});
|
|
1598
1757
|
function createSession(sessionId, model) {
|
|
1599
1758
|
return sessionId ? loadSession(sessionId) : createSession$1(model);
|
|
1600
1759
|
}
|
|
1601
1760
|
function App({ sessionId }) {
|
|
1602
1761
|
const { exit } = useApp();
|
|
1603
1762
|
const [appConfig, setConfig] = useState(() => loadConfig());
|
|
1604
|
-
const [currentScreen, setScreen] = useState(
|
|
1763
|
+
const [currentScreen, setScreen] = useState("chat");
|
|
1605
1764
|
const [mode, setMode] = useState(SAFE);
|
|
1606
1765
|
const [activeSession, setSession] = useState(() => createSession(sessionId, loadConfig().model));
|
|
1607
1766
|
const [isHeaderLoaded, setIsHeaderLoaded] = useState(false);
|
|
@@ -1627,17 +1786,17 @@ function App({ sessionId }) {
|
|
|
1627
1786
|
const handleCreateSession = useCallback(() => {
|
|
1628
1787
|
const nextSession = createSession$1(appConfig.model);
|
|
1629
1788
|
setActiveSession(nextSession);
|
|
1630
|
-
setScreen(
|
|
1789
|
+
setScreen("chat");
|
|
1631
1790
|
clear(nextSession.metadata.id);
|
|
1632
1791
|
return nextSession;
|
|
1633
1792
|
}, [appConfig.model, setActiveSession]);
|
|
1634
1793
|
const handleOpenSession = useCallback((sessionId) => {
|
|
1635
1794
|
if (sessionRef.current.metadata.id === sessionId) {
|
|
1636
|
-
setScreen(
|
|
1795
|
+
setScreen("chat");
|
|
1637
1796
|
return;
|
|
1638
1797
|
}
|
|
1639
1798
|
setActiveSession(loadSession(sessionId));
|
|
1640
|
-
setScreen(
|
|
1799
|
+
setScreen("chat");
|
|
1641
1800
|
clear(sessionId);
|
|
1642
1801
|
}, [setActiveSession]);
|
|
1643
1802
|
const handleDeleteSession = useCallback((sessionId) => {
|
|
@@ -1646,7 +1805,7 @@ function App({ sessionId }) {
|
|
|
1646
1805
|
if (current.metadata.id !== sessionId) return current;
|
|
1647
1806
|
return createSession$1(appConfig.model);
|
|
1648
1807
|
});
|
|
1649
|
-
setScreen(
|
|
1808
|
+
setScreen("session-manager");
|
|
1650
1809
|
}, [appConfig.model]);
|
|
1651
1810
|
const handleMessagesChange = useCallback((messages) => {
|
|
1652
1811
|
setSession((current) => {
|
|
@@ -1663,17 +1822,17 @@ function App({ sessionId }) {
|
|
|
1663
1822
|
const handleCommand = useCallback((command) => {
|
|
1664
1823
|
switch (command) {
|
|
1665
1824
|
case "/session":
|
|
1666
|
-
setScreen(
|
|
1825
|
+
setScreen("session-manager");
|
|
1667
1826
|
break;
|
|
1668
1827
|
case "/model":
|
|
1669
|
-
setScreen(
|
|
1828
|
+
setScreen("model-picker");
|
|
1670
1829
|
break;
|
|
1671
1830
|
case "/search":
|
|
1672
|
-
setScreen(
|
|
1831
|
+
setScreen("search-settings");
|
|
1673
1832
|
break;
|
|
1674
1833
|
case "/clear": {
|
|
1675
1834
|
resetSystemMessage();
|
|
1676
|
-
setScreen(
|
|
1835
|
+
setScreen("chat");
|
|
1677
1836
|
const nextSession = createSession$1(appConfig.model);
|
|
1678
1837
|
setActiveSession(nextSession);
|
|
1679
1838
|
clear(nextSession.metadata.id);
|
|
@@ -1699,10 +1858,10 @@ function App({ sessionId }) {
|
|
|
1699
1858
|
...current,
|
|
1700
1859
|
metadata: updateSessionModel(current.metadata.id, newModel)
|
|
1701
1860
|
}));
|
|
1702
|
-
setScreen(
|
|
1861
|
+
setScreen("chat");
|
|
1703
1862
|
}, []);
|
|
1704
1863
|
const handleClose = useCallback(() => {
|
|
1705
|
-
setScreen(
|
|
1864
|
+
setScreen("chat");
|
|
1706
1865
|
}, []);
|
|
1707
1866
|
const handleToggleMode = useCallback(() => {
|
|
1708
1867
|
setMode((mode) => {
|
|
@@ -1716,21 +1875,21 @@ function App({ sessionId }) {
|
|
|
1716
1875
|
}, []);
|
|
1717
1876
|
let screenContent;
|
|
1718
1877
|
switch (currentScreen) {
|
|
1719
|
-
case
|
|
1878
|
+
case "model-picker":
|
|
1720
1879
|
screenContent = /* @__PURE__ */ jsx(ModelPicker, {
|
|
1721
1880
|
currentModel: appConfig.model,
|
|
1722
1881
|
onSelect: handleUpdateConfig,
|
|
1723
1882
|
onClose: handleClose
|
|
1724
1883
|
});
|
|
1725
1884
|
break;
|
|
1726
|
-
case
|
|
1885
|
+
case "search-settings":
|
|
1727
1886
|
screenContent = /* @__PURE__ */ jsx(SearchSettings, {
|
|
1728
1887
|
currentUrl: appConfig.searxngBaseUrl,
|
|
1729
1888
|
onSave: handleUpdateConfig,
|
|
1730
1889
|
onClose: handleClose
|
|
1731
1890
|
});
|
|
1732
1891
|
break;
|
|
1733
|
-
case
|
|
1892
|
+
case "session-manager":
|
|
1734
1893
|
screenContent = /* @__PURE__ */ jsx(SessionManager, {
|
|
1735
1894
|
currentSessionId: activeSession.metadata.id,
|
|
1736
1895
|
onClose: handleClose,
|
|
@@ -1739,7 +1898,7 @@ function App({ sessionId }) {
|
|
|
1739
1898
|
onOpen: handleOpenSession
|
|
1740
1899
|
});
|
|
1741
1900
|
break;
|
|
1742
|
-
case
|
|
1901
|
+
case "chat":
|
|
1743
1902
|
screenContent = /* @__PURE__ */ jsx(Chat, {
|
|
1744
1903
|
initialMessages: activeSession.messages,
|
|
1745
1904
|
model: appConfig.model,
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as runShell } from "./assets/shell-CipXM_WI.js";
|
|
3
2
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
4
3
|
import cac from "cac";
|
|
5
4
|
import { homedir } from "node:os";
|
|
6
5
|
import { join } from "node:path";
|
|
7
6
|
import { Ollama } from "ollama";
|
|
8
7
|
import { v7 } from "uuid";
|
|
8
|
+
import { exec } from "node:child_process";
|
|
9
|
+
import { promisify } from "node:util";
|
|
10
|
+
//#region \0rolldown/runtime.js
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __exportAll = (all, no_symbols) => {
|
|
13
|
+
let target = {};
|
|
14
|
+
for (var name in all) __defProp(target, name, {
|
|
15
|
+
get: all[name],
|
|
16
|
+
enumerable: true
|
|
17
|
+
});
|
|
18
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
19
|
+
return target;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
9
22
|
//#region src/constants/command.ts
|
|
10
23
|
var LIST = [
|
|
11
24
|
{
|
|
@@ -32,7 +45,7 @@ var LIST = [
|
|
|
32
45
|
//#endregion
|
|
33
46
|
//#region package.json
|
|
34
47
|
var name = "code-ollama";
|
|
35
|
-
var version = "0.14.
|
|
48
|
+
var version = "0.14.2";
|
|
36
49
|
//#endregion
|
|
37
50
|
//#region src/constants/package.ts
|
|
38
51
|
var NAME = name;
|
|
@@ -603,7 +616,7 @@ function listDir(dirPath) {
|
|
|
603
616
|
* Search for pattern in files using ripgrep if available, fallback to Node.js
|
|
604
617
|
*/
|
|
605
618
|
async function grepSearch(pattern, dirPath) {
|
|
606
|
-
const { execShell } = await
|
|
619
|
+
const { execShell } = await Promise.resolve().then(() => shell_exports);
|
|
607
620
|
try {
|
|
608
621
|
const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${pattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
|
|
609
622
|
// v8 ignore next
|
|
@@ -642,6 +655,37 @@ async function grepSearch(pattern, dirPath) {
|
|
|
642
655
|
}
|
|
643
656
|
}
|
|
644
657
|
//#endregion
|
|
658
|
+
//#region src/utils/tools/shell.ts
|
|
659
|
+
var shell_exports = /* @__PURE__ */ __exportAll({
|
|
660
|
+
execShell: () => execShell,
|
|
661
|
+
runShell: () => runShell
|
|
662
|
+
});
|
|
663
|
+
var execAsync = promisify(exec);
|
|
664
|
+
var SHELL_EXEC_OPTIONS = {
|
|
665
|
+
timeout: 3e4,
|
|
666
|
+
maxBuffer: 1024 * 1024
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* Execute shell command with shared options (throws on error)
|
|
670
|
+
*/
|
|
671
|
+
function execShell(command) {
|
|
672
|
+
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Execute shell command
|
|
676
|
+
*/
|
|
677
|
+
async function runShell(command) {
|
|
678
|
+
try {
|
|
679
|
+
const { stdout, stderr } = await execShell(command);
|
|
680
|
+
return { content: stdout || stderr };
|
|
681
|
+
} catch (error) {
|
|
682
|
+
return {
|
|
683
|
+
content: "",
|
|
684
|
+
error: `Command failed: ${error instanceof Error ? error.message : String(error)}`
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
//#endregion
|
|
645
689
|
//#region src/utils/tools/web/fetch.ts
|
|
646
690
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
647
691
|
var BASE_HEADERS = { "user-agent": `${NAME}/${VERSION}` };
|
|
@@ -904,7 +948,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
904
948
|
else await launchTui();
|
|
905
949
|
}
|
|
906
950
|
async function launchTui(sessionId) {
|
|
907
|
-
const { renderApp } = await import("./assets/tui-
|
|
951
|
+
const { renderApp } = await import("./assets/tui-Dse5XVJ_.js");
|
|
908
952
|
reset();
|
|
909
953
|
renderApp(sessionId);
|
|
910
954
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ollama",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
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",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@commitlint/config-conventional": "21.0.1",
|
|
55
55
|
"@eslint/config-helpers": "0.6.0",
|
|
56
56
|
"@eslint/js": "10.0.1",
|
|
57
|
-
"@types/node": "25.
|
|
57
|
+
"@types/node": "25.8.0",
|
|
58
58
|
"@types/react": "19.2.14",
|
|
59
59
|
"@vitest/coverage-v8": "4.1.6",
|
|
60
60
|
"eslint": "10.3.0",
|
|
@@ -66,10 +66,10 @@
|
|
|
66
66
|
"lint-staged": "17.0.4",
|
|
67
67
|
"prettier": "3.8.3",
|
|
68
68
|
"publint": "0.3.21",
|
|
69
|
-
"tsx": "4.
|
|
69
|
+
"tsx": "4.22.0",
|
|
70
70
|
"typescript": "6.0.3",
|
|
71
71
|
"typescript-eslint": "8.59.3",
|
|
72
|
-
"vite": "8.0.
|
|
72
|
+
"vite": "8.0.13",
|
|
73
73
|
"vitest": "4.1.6"
|
|
74
74
|
},
|
|
75
75
|
"files": [
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { exec } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
//#region \0rolldown/runtime.js
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __exportAll = (all, no_symbols) => {
|
|
6
|
-
let target = {};
|
|
7
|
-
for (var name in all) __defProp(target, name, {
|
|
8
|
-
get: all[name],
|
|
9
|
-
enumerable: true
|
|
10
|
-
});
|
|
11
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
12
|
-
return target;
|
|
13
|
-
};
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/utils/tools/shell.ts
|
|
16
|
-
var shell_exports = /* @__PURE__ */ __exportAll({
|
|
17
|
-
execShell: () => execShell,
|
|
18
|
-
runShell: () => runShell
|
|
19
|
-
});
|
|
20
|
-
var execAsync = promisify(exec);
|
|
21
|
-
var SHELL_EXEC_OPTIONS = {
|
|
22
|
-
timeout: 3e4,
|
|
23
|
-
maxBuffer: 1024 * 1024
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Execute shell command with shared options (throws on error)
|
|
27
|
-
*/
|
|
28
|
-
function execShell(command) {
|
|
29
|
-
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Execute shell command
|
|
33
|
-
*/
|
|
34
|
-
async function runShell(command) {
|
|
35
|
-
try {
|
|
36
|
-
const { stdout, stderr } = await execShell(command);
|
|
37
|
-
return { content: stdout || stderr };
|
|
38
|
-
} catch (error) {
|
|
39
|
-
return {
|
|
40
|
-
content: "",
|
|
41
|
-
error: `Command failed: ${error instanceof Error ? error.message : String(error)}`
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
export { shell_exports as n, runShell as t };
|