miii-agent 0.1.4 → 0.1.6
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/cli.js +400 -139
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,16 +6,27 @@ import { createElement } from "react";
|
|
|
6
6
|
|
|
7
7
|
// src/ui/App.tsx
|
|
8
8
|
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
9
|
-
import { Box as
|
|
9
|
+
import { Box as Box10, Text as Text10, useApp } from "ink";
|
|
10
10
|
import { homedir as homedir2 } from "os";
|
|
11
11
|
import { sep } from "path";
|
|
12
12
|
|
|
13
13
|
// src/ollama/client.ts
|
|
14
14
|
import { Ollama } from "ollama";
|
|
15
|
+
import { execFileSync } from "child_process";
|
|
15
16
|
var ollama = new Ollama({
|
|
16
17
|
host: process.env.OLLAMA_HOST ?? "http://localhost:11434"
|
|
17
18
|
});
|
|
19
|
+
var OLLAMA_NOT_INSTALLED = "Ollama is not installed. Install it with: npm i -g ollama\nOr download from https://ollama.com/download";
|
|
18
20
|
var OLLAMA_NOT_RUNNING = "Ollama is not running. Start it with: ollama serve";
|
|
21
|
+
function ollamaInstalled() {
|
|
22
|
+
try {
|
|
23
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
24
|
+
execFileSync(cmd, ["ollama"], { stdio: "ignore" });
|
|
25
|
+
return true;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
19
30
|
var HARMONY_RE = /<\|?\/?(?:channel|message|start|end|return|constrain|assistant|user|system|developer|tool|tool_call|tool_response|final|analysis|commentary)\|?>/gi;
|
|
20
31
|
var CHANNEL_LABEL_RE = /^(?:analysis|commentary|final)\s*(?=\w)/i;
|
|
21
32
|
function stripHarmony(s) {
|
|
@@ -266,19 +277,53 @@ function ModelsView({ models, cursor, model, ollamaHost, effort }) {
|
|
|
266
277
|
] });
|
|
267
278
|
}
|
|
268
279
|
|
|
269
|
-
// src/ui/
|
|
280
|
+
// src/ui/SessionsView.tsx
|
|
270
281
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
271
282
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
283
|
+
function relativeTime(iso) {
|
|
284
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
285
|
+
const min = Math.floor(diff / 6e4);
|
|
286
|
+
if (min < 1) return "just now";
|
|
287
|
+
if (min < 60) return `${min}m ago`;
|
|
288
|
+
const hr = Math.floor(min / 60);
|
|
289
|
+
if (hr < 24) return `${hr}h ago`;
|
|
290
|
+
const d = Math.floor(hr / 24);
|
|
291
|
+
return `${d}d ago`;
|
|
292
|
+
}
|
|
293
|
+
function SessionsView({ sessions, cursor }) {
|
|
294
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginLeft: 2, children: [
|
|
295
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "resume session" }),
|
|
296
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: sessions.length === 0 ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "no saved sessions yet" }) : sessions.map((s, i) => {
|
|
297
|
+
const active = i === cursor;
|
|
298
|
+
const label = s.title;
|
|
299
|
+
return /* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
300
|
+
/* @__PURE__ */ jsxs5(Text5, { color: active ? "blue" : void 0, dimColor: !active, children: [
|
|
301
|
+
active ? "\u276F " : " ",
|
|
302
|
+
label
|
|
303
|
+
] }),
|
|
304
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: `\xB7 ${s.messageCount} msgs \xB7 ${relativeTime(s.updatedAt)}` })
|
|
305
|
+
] }, s.id);
|
|
306
|
+
}) }),
|
|
307
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191\u2193 navigate enter resume d delete esc cancel" }) })
|
|
308
|
+
] });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/ui/CommandPalette.tsx
|
|
312
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
313
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
272
314
|
var COMMANDS = [
|
|
273
315
|
{ name: "/models", description: "switch model or adjust effort" },
|
|
316
|
+
{ name: "/new", description: "save current session and start fresh" },
|
|
317
|
+
{ name: "/sessions", description: "list sessions and resume one" },
|
|
274
318
|
{ name: "/clear", description: "clear chat and reset context" },
|
|
275
319
|
{ name: "/exit", description: "quit miii" }
|
|
276
320
|
];
|
|
277
321
|
function CommandPalette({ filter, cursor }) {
|
|
278
322
|
const filtered = COMMANDS.filter((c) => c.name.startsWith(filter));
|
|
279
323
|
if (filtered.length === 0) return null;
|
|
280
|
-
|
|
281
|
-
|
|
324
|
+
const nameWidth = Math.max(...filtered.map((c) => c.name.length));
|
|
325
|
+
return /* @__PURE__ */ jsxs6(
|
|
326
|
+
Box6,
|
|
282
327
|
{
|
|
283
328
|
flexDirection: "column",
|
|
284
329
|
borderStyle: "round",
|
|
@@ -289,15 +334,15 @@ function CommandPalette({ filter, cursor }) {
|
|
|
289
334
|
children: [
|
|
290
335
|
filtered.map((cmd, i) => {
|
|
291
336
|
const active = i === cursor;
|
|
292
|
-
return /* @__PURE__ */
|
|
293
|
-
/* @__PURE__ */
|
|
337
|
+
return /* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
|
|
338
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: active, color: active ? "blue" : void 0, dimColor: !active, children: [
|
|
294
339
|
active ? "\u276F " : " ",
|
|
295
|
-
cmd.name
|
|
340
|
+
cmd.name.padEnd(nameWidth)
|
|
296
341
|
] }),
|
|
297
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: cmd.description })
|
|
298
343
|
] }, cmd.name);
|
|
299
344
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
345
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 0, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2191\u2193 navigate tab/enter autocomplete esc dismiss" }) })
|
|
301
346
|
]
|
|
302
347
|
}
|
|
303
348
|
);
|
|
@@ -306,11 +351,150 @@ function filteredCommands(filter) {
|
|
|
306
351
|
return COMMANDS.filter((c) => c.name.startsWith(filter));
|
|
307
352
|
}
|
|
308
353
|
|
|
354
|
+
// src/session/store.ts
|
|
355
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, rmSync } from "fs";
|
|
356
|
+
import { join as join2 } from "path";
|
|
357
|
+
import { randomUUID } from "crypto";
|
|
358
|
+
var SESSION_DIR = join2(process.cwd(), ".miii", "session");
|
|
359
|
+
function newSessionId() {
|
|
360
|
+
return randomUUID();
|
|
361
|
+
}
|
|
362
|
+
function sessionPath(id) {
|
|
363
|
+
return join2(SESSION_DIR, `${id}.jsonl`);
|
|
364
|
+
}
|
|
365
|
+
function messageText(m) {
|
|
366
|
+
if (typeof m.content === "string") return m.content;
|
|
367
|
+
return m.content.map((b) => {
|
|
368
|
+
if (b.type === "text") return b.text;
|
|
369
|
+
if (b.type === "tool_use") return `[tool ${b.name}]`;
|
|
370
|
+
if (b.type === "tool_result") return "[result]";
|
|
371
|
+
return "";
|
|
372
|
+
}).join(" ");
|
|
373
|
+
}
|
|
374
|
+
function firstUserText(messages) {
|
|
375
|
+
const first = messages.find((m) => m.role === "user");
|
|
376
|
+
if (!first) return "untitled";
|
|
377
|
+
return messageText(first).trim().slice(0, 80) || "untitled";
|
|
378
|
+
}
|
|
379
|
+
function readMeta(id) {
|
|
380
|
+
try {
|
|
381
|
+
const raw = readFileSync2(sessionPath(id), "utf-8");
|
|
382
|
+
const firstLine = raw.slice(0, raw.indexOf("\n") === -1 ? raw.length : raw.indexOf("\n"));
|
|
383
|
+
const parsed = JSON.parse(firstLine);
|
|
384
|
+
if (parsed.type !== "meta") return null;
|
|
385
|
+
const { type: _t, ...meta } = parsed;
|
|
386
|
+
return meta;
|
|
387
|
+
} catch {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function persistSession(id, messages, title) {
|
|
392
|
+
if (!messages.length) return;
|
|
393
|
+
mkdirSync2(SESSION_DIR, { recursive: true });
|
|
394
|
+
const existing = readMeta(id);
|
|
395
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
396
|
+
const meta = {
|
|
397
|
+
id,
|
|
398
|
+
createdAt: existing?.createdAt ?? now,
|
|
399
|
+
updatedAt: now,
|
|
400
|
+
title: title ?? existing?.title ?? firstUserText(messages),
|
|
401
|
+
messageCount: messages.length
|
|
402
|
+
};
|
|
403
|
+
const lines = [JSON.stringify({ type: "meta", ...meta })];
|
|
404
|
+
for (const message of messages) {
|
|
405
|
+
lines.push(JSON.stringify({ type: "message", message }));
|
|
406
|
+
}
|
|
407
|
+
writeFileSync2(sessionPath(id), lines.join("\n") + "\n", "utf-8");
|
|
408
|
+
}
|
|
409
|
+
function listSessions() {
|
|
410
|
+
if (!existsSync2(SESSION_DIR)) return [];
|
|
411
|
+
const metas = [];
|
|
412
|
+
for (const file of readdirSync(SESSION_DIR)) {
|
|
413
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
414
|
+
const meta = readMeta(file.replace(/\.jsonl$/, ""));
|
|
415
|
+
if (meta) metas.push(meta);
|
|
416
|
+
}
|
|
417
|
+
return metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
418
|
+
}
|
|
419
|
+
function deleteSession(id) {
|
|
420
|
+
try {
|
|
421
|
+
rmSync(sessionPath(id), { force: true });
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function loadSession(id) {
|
|
426
|
+
try {
|
|
427
|
+
const raw = readFileSync2(sessionPath(id), "utf-8");
|
|
428
|
+
const messages = [];
|
|
429
|
+
for (const line of raw.split("\n")) {
|
|
430
|
+
if (!line.trim()) continue;
|
|
431
|
+
const parsed = JSON.parse(line);
|
|
432
|
+
if (parsed.type === "message") messages.push(parsed.message);
|
|
433
|
+
}
|
|
434
|
+
return messages;
|
|
435
|
+
} catch {
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function toDisplayMessages(history) {
|
|
440
|
+
const out = [];
|
|
441
|
+
for (const m of history) {
|
|
442
|
+
if (m.role === "system") continue;
|
|
443
|
+
const blocks = Array.isArray(m.content) ? m.content : [{ type: "text", text: m.content }];
|
|
444
|
+
if (m.role === "user") {
|
|
445
|
+
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
446
|
+
const results = blocks.filter((b) => b.type === "tool_result");
|
|
447
|
+
if (results.length && out.length) {
|
|
448
|
+
const last = out[out.length - 1];
|
|
449
|
+
last.tool_results = [
|
|
450
|
+
...last.tool_results ?? [],
|
|
451
|
+
...results.map((r) => ({
|
|
452
|
+
tool_use_id: r.tool_use_id,
|
|
453
|
+
content: r.content,
|
|
454
|
+
is_error: r.is_error
|
|
455
|
+
}))
|
|
456
|
+
];
|
|
457
|
+
}
|
|
458
|
+
if (text.trim()) out.push({ role: "user", content: text });
|
|
459
|
+
} else {
|
|
460
|
+
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
461
|
+
const uses = blocks.filter((b) => b.type === "tool_use").map((b) => ({ id: b.id, name: b.name, input: b.input }));
|
|
462
|
+
out.push({
|
|
463
|
+
role: "assistant",
|
|
464
|
+
content: text,
|
|
465
|
+
tool_uses: uses.length ? uses : void 0
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return out;
|
|
470
|
+
}
|
|
471
|
+
async function summarizeMessage(model, text) {
|
|
472
|
+
const fallback = text.trim().slice(0, 80) || "untitled";
|
|
473
|
+
const prompt = `Summarize this user request as a short title, 3-6 words, no punctuation. Reply with the title only.
|
|
474
|
+
|
|
475
|
+
Request:
|
|
476
|
+
${text.slice(0, 2e3)}`;
|
|
477
|
+
try {
|
|
478
|
+
let out = "";
|
|
479
|
+
for await (const chunk of chat(
|
|
480
|
+
model,
|
|
481
|
+
[{ role: "user", content: prompt }],
|
|
482
|
+
void 0,
|
|
483
|
+
{ temperature: 0.2, num_predict: 32 }
|
|
484
|
+
)) {
|
|
485
|
+
if (chunk.content) out += chunk.content;
|
|
486
|
+
}
|
|
487
|
+
return out.trim().split("\n").filter(Boolean)[0]?.trim() || fallback;
|
|
488
|
+
} catch {
|
|
489
|
+
return fallback;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
309
493
|
// src/ui/FilePicker.tsx
|
|
310
|
-
import { Box as
|
|
311
|
-
import { readdirSync } from "fs";
|
|
312
|
-
import { join as
|
|
313
|
-
import { jsx as
|
|
494
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
495
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
496
|
+
import { join as join3, relative } from "path";
|
|
497
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
314
498
|
var IGNORE = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage", ".miii"]);
|
|
315
499
|
var MAX_RESULTS = 10;
|
|
316
500
|
var MAX_SCAN = 2e3;
|
|
@@ -323,13 +507,13 @@ function listFiles(cwd) {
|
|
|
323
507
|
const dir = stack.pop();
|
|
324
508
|
let entries;
|
|
325
509
|
try {
|
|
326
|
-
entries =
|
|
510
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
327
511
|
} catch {
|
|
328
512
|
continue;
|
|
329
513
|
}
|
|
330
514
|
for (const e of entries) {
|
|
331
515
|
if (IGNORE.has(e.name) || e.name.startsWith(".")) continue;
|
|
332
|
-
const full =
|
|
516
|
+
const full = join3(dir, e.name);
|
|
333
517
|
if (e.isDirectory()) stack.push(full);
|
|
334
518
|
else if (e.isFile()) out.push(relative(cwd, full));
|
|
335
519
|
if (out.length >= MAX_SCAN) break;
|
|
@@ -363,8 +547,8 @@ function searchFiles(cwd, query) {
|
|
|
363
547
|
}
|
|
364
548
|
function FilePicker({ matches, cursor }) {
|
|
365
549
|
if (matches.length === 0) return null;
|
|
366
|
-
return /* @__PURE__ */
|
|
367
|
-
|
|
550
|
+
return /* @__PURE__ */ jsxs7(
|
|
551
|
+
Box7,
|
|
368
552
|
{
|
|
369
553
|
flexDirection: "column",
|
|
370
554
|
borderStyle: "round",
|
|
@@ -375,24 +559,24 @@ function FilePicker({ matches, cursor }) {
|
|
|
375
559
|
children: [
|
|
376
560
|
matches.map((f, i) => {
|
|
377
561
|
const active = i === cursor;
|
|
378
|
-
return /* @__PURE__ */
|
|
562
|
+
return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { bold: active, color: active ? "blue" : void 0, dimColor: !active, children: [
|
|
379
563
|
active ? "\u276F " : " ",
|
|
380
564
|
f
|
|
381
565
|
] }) }, f);
|
|
382
566
|
}),
|
|
383
|
-
/* @__PURE__ */
|
|
567
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 0, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2191\u2193 navigate tab insert esc dismiss" }) })
|
|
384
568
|
]
|
|
385
569
|
}
|
|
386
570
|
);
|
|
387
571
|
}
|
|
388
572
|
|
|
389
573
|
// src/ui/ChatView.tsx
|
|
390
|
-
import { Box as
|
|
574
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
391
575
|
|
|
392
576
|
// src/ui/ThinkingBlock.tsx
|
|
393
577
|
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
394
|
-
import { Box as
|
|
395
|
-
import { jsx as
|
|
578
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
579
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
396
580
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
397
581
|
var globalThinkingVisible = false;
|
|
398
582
|
var listeners = /* @__PURE__ */ new Set();
|
|
@@ -418,20 +602,20 @@ function ThinkingBlock({ content }) {
|
|
|
418
602
|
const t = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length), 80);
|
|
419
603
|
return () => clearInterval(t);
|
|
420
604
|
}, []);
|
|
421
|
-
return /* @__PURE__ */
|
|
422
|
-
/* @__PURE__ */
|
|
423
|
-
/* @__PURE__ */
|
|
605
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
|
|
606
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
607
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "blue", children: [
|
|
424
608
|
FRAMES[frame],
|
|
425
609
|
" "
|
|
426
610
|
] }),
|
|
427
|
-
/* @__PURE__ */
|
|
428
|
-
/* @__PURE__ */
|
|
611
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: "thinking\u2026" }),
|
|
612
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
429
613
|
" \xB7 ctrl+t to ",
|
|
430
614
|
visible ? "hide" : "show",
|
|
431
615
|
" thoughts"
|
|
432
616
|
] })
|
|
433
617
|
] }),
|
|
434
|
-
visible && content ? /* @__PURE__ */
|
|
618
|
+
visible && content ? /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: content }) }) : null
|
|
435
619
|
] });
|
|
436
620
|
}
|
|
437
621
|
|
|
@@ -439,12 +623,14 @@ function ThinkingBlock({ content }) {
|
|
|
439
623
|
var EMPTY_STATE_HINTS = [
|
|
440
624
|
"\u2022 explain @file \u2014 reference a file with @",
|
|
441
625
|
"\u2022 /models \u2014 switch model or effort",
|
|
442
|
-
"\u2022
|
|
626
|
+
"\u2022 /new \u2014 start a new chat",
|
|
627
|
+
"\u2022 /sessions \u2014 view saved chats",
|
|
628
|
+
"\u2022 ctrl+t \u2014 toggle thinking"
|
|
443
629
|
];
|
|
444
630
|
var EMPTY_STATE_TITLE = "Ask anything, or try:";
|
|
445
631
|
|
|
446
632
|
// src/ui/ChatView.tsx
|
|
447
|
-
import { jsx as
|
|
633
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
448
634
|
function formatTokens(n) {
|
|
449
635
|
if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1) + "k";
|
|
450
636
|
return String(n);
|
|
@@ -470,27 +656,27 @@ function FileEditBlock({
|
|
|
470
656
|
const MAX = 16;
|
|
471
657
|
const shown = previewLines.slice(0, MAX);
|
|
472
658
|
const extra = previewLines.length - shown.length;
|
|
473
|
-
return /* @__PURE__ */
|
|
474
|
-
/* @__PURE__ */
|
|
475
|
-
/* @__PURE__ */
|
|
476
|
-
/* @__PURE__ */
|
|
659
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
|
|
660
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
661
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25CF " }),
|
|
662
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
477
663
|
label,
|
|
478
664
|
" "
|
|
479
665
|
] }),
|
|
480
|
-
/* @__PURE__ */
|
|
481
|
-
/* @__PURE__ */
|
|
482
|
-
/* @__PURE__ */
|
|
666
|
+
/* @__PURE__ */ jsx9(Text9, { children: "(" }),
|
|
667
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: path }),
|
|
668
|
+
/* @__PURE__ */ jsx9(Text9, { children: ")" })
|
|
483
669
|
] }),
|
|
484
|
-
/* @__PURE__ */
|
|
670
|
+
/* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
485
671
|
"\u23BF ",
|
|
486
672
|
removed > 0 ? `Added ${added} lines, removed ${removed} lines` : `Added ${added} lines`
|
|
487
673
|
] }) }),
|
|
488
|
-
shown.map((ln, i) => /* @__PURE__ */
|
|
674
|
+
shown.map((ln, i) => /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsxs9(Text9, { color: ln.sign === "+" ? "green" : ln.sign === "-" ? "red" : void 0, dimColor: ln.sign === " ", children: [
|
|
489
675
|
ln.sign,
|
|
490
676
|
" ",
|
|
491
677
|
ln.text
|
|
492
678
|
] }) }, i)),
|
|
493
|
-
extra > 0 && /* @__PURE__ */
|
|
679
|
+
extra > 0 && /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
494
680
|
"\u2026 ",
|
|
495
681
|
extra,
|
|
496
682
|
" more lines"
|
|
@@ -563,7 +749,7 @@ function ToolResultBlock({ result, toolName }) {
|
|
|
563
749
|
const lines = content.split("\n");
|
|
564
750
|
const showMulti = (toolName === "run_bash" || toolName === "grep" || toolName === "glob" || result.is_error) && lines.length > 1;
|
|
565
751
|
if (!showMulti) {
|
|
566
|
-
return /* @__PURE__ */
|
|
752
|
+
return /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
|
|
567
753
|
"\u23BF ",
|
|
568
754
|
summarizeResult(result, toolName)
|
|
569
755
|
] }) });
|
|
@@ -572,13 +758,13 @@ function ToolResultBlock({ result, toolName }) {
|
|
|
572
758
|
const MAX_LINE_WIDTH = 200;
|
|
573
759
|
const shown = lines.slice(0, MAX_LINES).map((l) => truncate(l, MAX_LINE_WIDTH));
|
|
574
760
|
const extra = lines.length - shown.length;
|
|
575
|
-
return /* @__PURE__ */
|
|
576
|
-
/* @__PURE__ */
|
|
761
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
|
|
762
|
+
/* @__PURE__ */ jsxs9(Text9, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
|
|
577
763
|
"\u23BF ",
|
|
578
764
|
summarizeResult(result, toolName)
|
|
579
765
|
] }),
|
|
580
|
-
shown.map((ln, i) => /* @__PURE__ */
|
|
581
|
-
extra > 0 && /* @__PURE__ */
|
|
766
|
+
shown.map((ln, i) => /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsx9(Text9, { color: result.is_error ? "red" : void 0, dimColor: true, children: ln || " " }) }, i)),
|
|
767
|
+
extra > 0 && /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
582
768
|
"\u2026 ",
|
|
583
769
|
extra,
|
|
584
770
|
" more lines"
|
|
@@ -591,7 +777,7 @@ function ToolUseLine({ use, result }) {
|
|
|
591
777
|
const content = input.content ?? "";
|
|
592
778
|
const added = countLines(content);
|
|
593
779
|
const preview = content.split("\n").map((t) => ({ sign: "+", text: t }));
|
|
594
|
-
return /* @__PURE__ */
|
|
780
|
+
return /* @__PURE__ */ jsx9(FileEditBlock, { label: "Write", path: input.path ?? "", added, removed: 0, previewLines: preview });
|
|
595
781
|
}
|
|
596
782
|
if (use.name === "edit_file" && !result?.is_error) {
|
|
597
783
|
const input = use.input;
|
|
@@ -603,34 +789,34 @@ function ToolUseLine({ use, result }) {
|
|
|
603
789
|
...oldS.split("\n").map((t) => ({ sign: "-", text: t })),
|
|
604
790
|
...newS.split("\n").map((t) => ({ sign: "+", text: t }))
|
|
605
791
|
];
|
|
606
|
-
return /* @__PURE__ */
|
|
792
|
+
return /* @__PURE__ */ jsx9(FileEditBlock, { label: "Update", path: input.path ?? "", added, removed, previewLines: preview });
|
|
607
793
|
}
|
|
608
794
|
const { label, arg } = toolHeader(use);
|
|
609
|
-
return /* @__PURE__ */
|
|
610
|
-
/* @__PURE__ */
|
|
611
|
-
/* @__PURE__ */
|
|
612
|
-
/* @__PURE__ */
|
|
795
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
|
|
796
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
797
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25CF " }),
|
|
798
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
613
799
|
label,
|
|
614
800
|
" "
|
|
615
801
|
] }),
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
/* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
802
|
+
/* @__PURE__ */ jsx9(Text9, { children: "(" }),
|
|
803
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: arg }),
|
|
804
|
+
/* @__PURE__ */ jsx9(Text9, { children: ")" })
|
|
619
805
|
] }),
|
|
620
|
-
result && /* @__PURE__ */
|
|
806
|
+
result && /* @__PURE__ */ jsx9(ToolResultBlock, { result, toolName: use.name })
|
|
621
807
|
] });
|
|
622
808
|
}
|
|
623
809
|
function AssistantMessage({ msg }) {
|
|
624
|
-
return /* @__PURE__ */
|
|
625
|
-
msg.content && /* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
810
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
811
|
+
msg.content && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
|
|
812
|
+
/* @__PURE__ */ jsx9(Text9, { color: "white", children: "\u25CF " }),
|
|
813
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text9, { children: msg.content }) })
|
|
628
814
|
] }),
|
|
629
815
|
msg.tool_uses?.map((u) => {
|
|
630
816
|
const r = msg.tool_results?.find((x) => x.tool_use_id === u.id);
|
|
631
|
-
return /* @__PURE__ */
|
|
817
|
+
return /* @__PURE__ */ jsx9(ToolUseLine, { use: u, result: r }, u.id);
|
|
632
818
|
}),
|
|
633
|
-
msg.tokens && /* @__PURE__ */
|
|
819
|
+
msg.tokens && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
634
820
|
`\u21B3 Completed \xB7 ${formatTokens(msg.tokens.prompt_eval + msg.tokens.eval)} tokens`,
|
|
635
821
|
msg.duration != null ? ` \xB7 ${formatDuration(msg.duration)}` : ""
|
|
636
822
|
] }) })
|
|
@@ -659,15 +845,15 @@ function PermissionPrompt({ req, cursor }) {
|
|
|
659
845
|
{ label: "No", key: "no" }
|
|
660
846
|
];
|
|
661
847
|
const summary = summarizeInput(req.input);
|
|
662
|
-
return /* @__PURE__ */
|
|
663
|
-
/* @__PURE__ */
|
|
664
|
-
/* @__PURE__ */
|
|
848
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
|
|
849
|
+
/* @__PURE__ */ jsx9(Text9, { color: "blue", bold: true, children: "Tool use" }),
|
|
850
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
665
851
|
"Allow ",
|
|
666
|
-
/* @__PURE__ */
|
|
852
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: label }),
|
|
667
853
|
"?"
|
|
668
854
|
] }) }),
|
|
669
|
-
summary && /* @__PURE__ */
|
|
670
|
-
/* @__PURE__ */
|
|
855
|
+
summary && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: summary }) }),
|
|
856
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsxs9(Text9, { color: i === cursor ? "blue" : void 0, children: [
|
|
671
857
|
i === cursor ? "\u276F " : " ",
|
|
672
858
|
i + 1,
|
|
673
859
|
". ",
|
|
@@ -688,33 +874,33 @@ function ChatView({
|
|
|
688
874
|
activeToolResults
|
|
689
875
|
}) {
|
|
690
876
|
const empty = messages.length === 0 && !streaming && !thinking && !pendingPermission && !error;
|
|
691
|
-
return /* @__PURE__ */
|
|
692
|
-
empty && /* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
EMPTY_STATE_HINTS.map((h, i) => /* @__PURE__ */
|
|
877
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 1, marginBottom: 1, children: [
|
|
878
|
+
empty && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
879
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: EMPTY_STATE_TITLE }),
|
|
880
|
+
EMPTY_STATE_HINTS.map((h, i) => /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
695
881
|
" ",
|
|
696
882
|
h
|
|
697
883
|
] }, i))
|
|
698
884
|
] }),
|
|
699
885
|
messages.map(
|
|
700
|
-
(msg, i) => msg.role === "user" ? /* @__PURE__ */
|
|
701
|
-
/* @__PURE__ */
|
|
702
|
-
/* @__PURE__ */
|
|
703
|
-
] }, i) : /* @__PURE__ */
|
|
886
|
+
(msg, i) => msg.role === "user" ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
|
|
887
|
+
/* @__PURE__ */ jsx9(Text9, { color: "blue", children: "\u25CF " }),
|
|
888
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text9, { children: msg.content }) })
|
|
889
|
+
] }, i) : /* @__PURE__ */ jsx9(AssistantMessage, { msg }, i)
|
|
704
890
|
),
|
|
705
|
-
thinking && /* @__PURE__ */
|
|
706
|
-
streaming && streamingContent && /* @__PURE__ */
|
|
707
|
-
/* @__PURE__ */
|
|
708
|
-
/* @__PURE__ */
|
|
891
|
+
thinking && /* @__PURE__ */ jsx9(ThinkingBlock, { content: thinkingContent }),
|
|
892
|
+
streaming && streamingContent && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
|
|
893
|
+
/* @__PURE__ */ jsx9(Text9, { color: "white", children: "\u25CF " }),
|
|
894
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text9, { children: streamingContent }) })
|
|
709
895
|
] }),
|
|
710
896
|
activeToolUses?.map((u) => {
|
|
711
897
|
const r = activeToolResults?.find((x) => x.tool_use_id === u.id);
|
|
712
|
-
return /* @__PURE__ */
|
|
898
|
+
return /* @__PURE__ */ jsx9(ToolUseLine, { use: u, result: r }, u.id);
|
|
713
899
|
}),
|
|
714
|
-
pendingPermission && /* @__PURE__ */
|
|
715
|
-
error && /* @__PURE__ */
|
|
716
|
-
/* @__PURE__ */
|
|
717
|
-
/* @__PURE__ */
|
|
900
|
+
pendingPermission && /* @__PURE__ */ jsx9(PermissionPrompt, { req: pendingPermission, cursor: permissionCursor }),
|
|
901
|
+
error && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
|
|
902
|
+
/* @__PURE__ */ jsx9(Text9, { color: "red", children: "\u25CF " }),
|
|
903
|
+
/* @__PURE__ */ jsx9(Text9, { color: "red", children: error })
|
|
718
904
|
] })
|
|
719
905
|
] });
|
|
720
906
|
}
|
|
@@ -723,7 +909,7 @@ function ChatView({
|
|
|
723
909
|
import { useState as useState3, useRef } from "react";
|
|
724
910
|
|
|
725
911
|
// src/tools/edit_file.ts
|
|
726
|
-
import { readFileSync as
|
|
912
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
727
913
|
var edit_file = {
|
|
728
914
|
name: "edit_file",
|
|
729
915
|
description: "Replace an exact string in a file. old_str must be unique.",
|
|
@@ -737,7 +923,7 @@ var edit_file = {
|
|
|
737
923
|
required: ["path", "old_str", "new_str"]
|
|
738
924
|
},
|
|
739
925
|
handler: ({ path, old_str, new_str }) => {
|
|
740
|
-
const src =
|
|
926
|
+
const src = readFileSync3(path, "utf-8");
|
|
741
927
|
const first = src.indexOf(old_str);
|
|
742
928
|
if (first === -1) {
|
|
743
929
|
return { content: `old_str not found in ${path}`, is_error: true };
|
|
@@ -745,13 +931,13 @@ var edit_file = {
|
|
|
745
931
|
if (src.indexOf(old_str, first + 1) !== -1) {
|
|
746
932
|
return { content: `old_str not unique in ${path}`, is_error: true };
|
|
747
933
|
}
|
|
748
|
-
|
|
934
|
+
writeFileSync3(path, src.slice(0, first) + new_str + src.slice(first + old_str.length), "utf-8");
|
|
749
935
|
return { content: `Edited ${path}` };
|
|
750
936
|
}
|
|
751
937
|
};
|
|
752
938
|
|
|
753
939
|
// src/tools/read_file.ts
|
|
754
|
-
import { readFileSync as
|
|
940
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
755
941
|
var read_file = {
|
|
756
942
|
name: "read_file",
|
|
757
943
|
description: "Read entire file contents as UTF-8 text.",
|
|
@@ -765,7 +951,7 @@ var read_file = {
|
|
|
765
951
|
handler: ({ path }) => {
|
|
766
952
|
try {
|
|
767
953
|
const MAX = 2e5;
|
|
768
|
-
const raw =
|
|
954
|
+
const raw = readFileSync4(path, "utf-8");
|
|
769
955
|
const truncated = raw.length > MAX;
|
|
770
956
|
const body = truncated ? raw.slice(0, MAX) + `
|
|
771
957
|
[truncated: ${raw.length - MAX} more chars]` : raw;
|
|
@@ -777,7 +963,7 @@ var read_file = {
|
|
|
777
963
|
};
|
|
778
964
|
|
|
779
965
|
// src/tools/write_file.ts
|
|
780
|
-
import { writeFileSync as
|
|
966
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
781
967
|
import { dirname } from "path";
|
|
782
968
|
var write_file = {
|
|
783
969
|
name: "write_file",
|
|
@@ -792,8 +978,8 @@ var write_file = {
|
|
|
792
978
|
},
|
|
793
979
|
handler: ({ path, content }) => {
|
|
794
980
|
try {
|
|
795
|
-
|
|
796
|
-
|
|
981
|
+
mkdirSync3(dirname(path), { recursive: true });
|
|
982
|
+
writeFileSync4(path, content, "utf-8");
|
|
797
983
|
return { content: `Wrote ${path} (${content.length} bytes)` };
|
|
798
984
|
} catch (err) {
|
|
799
985
|
return { content: err instanceof Error ? err.message : String(err), is_error: true };
|
|
@@ -1582,19 +1768,28 @@ function useKeyboard(opts) {
|
|
|
1582
1768
|
cfg,
|
|
1583
1769
|
setCfg,
|
|
1584
1770
|
setActiveCtx,
|
|
1585
|
-
|
|
1586
|
-
permissionCursor,
|
|
1587
|
-
setPermissionCursor,
|
|
1588
|
-
resolvePermission,
|
|
1589
|
-
busyRef,
|
|
1590
|
-
abortRef,
|
|
1771
|
+
agent,
|
|
1591
1772
|
input,
|
|
1592
1773
|
setInput,
|
|
1593
1774
|
paletteCursor,
|
|
1594
1775
|
setPaletteCursor,
|
|
1595
1776
|
filePickerCursor,
|
|
1596
1777
|
setFilePickerCursor,
|
|
1778
|
+
sessionId,
|
|
1779
|
+
setSessionId,
|
|
1780
|
+
sessions,
|
|
1781
|
+
setSessions,
|
|
1782
|
+
setNotice
|
|
1783
|
+
} = opts;
|
|
1784
|
+
const {
|
|
1785
|
+
pendingPermissionRef,
|
|
1786
|
+
permissionCursor,
|
|
1787
|
+
setPermissionCursor,
|
|
1788
|
+
resolvePermission,
|
|
1789
|
+
busyRef,
|
|
1790
|
+
abortRef,
|
|
1597
1791
|
sendMessage,
|
|
1792
|
+
agentHistory,
|
|
1598
1793
|
setMessages,
|
|
1599
1794
|
setAgentHistory,
|
|
1600
1795
|
setStreamingContent,
|
|
@@ -1602,7 +1797,17 @@ function useKeyboard(opts) {
|
|
|
1602
1797
|
setActiveToolUses,
|
|
1603
1798
|
setActiveToolResults,
|
|
1604
1799
|
setError
|
|
1605
|
-
} =
|
|
1800
|
+
} = agent;
|
|
1801
|
+
function clearSession() {
|
|
1802
|
+
setMessages(() => []);
|
|
1803
|
+
setAgentHistory([]);
|
|
1804
|
+
setStreamingContent("");
|
|
1805
|
+
setThinkingContent("");
|
|
1806
|
+
setActiveToolUses([]);
|
|
1807
|
+
setActiveToolResults([]);
|
|
1808
|
+
setError(null);
|
|
1809
|
+
setNotice(null);
|
|
1810
|
+
}
|
|
1606
1811
|
const effort = cfg.effort ?? "medium";
|
|
1607
1812
|
useInput((char, key) => {
|
|
1608
1813
|
if (key.ctrl && char === "c") {
|
|
@@ -1649,6 +1854,44 @@ function useKeyboard(opts) {
|
|
|
1649
1854
|
}
|
|
1650
1855
|
return;
|
|
1651
1856
|
}
|
|
1857
|
+
if (state === "sessions") {
|
|
1858
|
+
if (key.upArrow) {
|
|
1859
|
+
setCursor((i) => Math.max(0, i - 1));
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
if (key.downArrow) {
|
|
1863
|
+
setCursor((i) => Math.min(sessions.length - 1, i + 1));
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
if (key.escape) {
|
|
1867
|
+
setState("ready");
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
if ((char === "d" || char === "x" || key.delete || key.backspace) && sessions[cursor]) {
|
|
1871
|
+
const meta = sessions[cursor];
|
|
1872
|
+
deleteSession(meta.id);
|
|
1873
|
+
const next = listSessions();
|
|
1874
|
+
setSessions(next);
|
|
1875
|
+
setCursor((i) => Math.max(0, Math.min(i, next.length - 1)));
|
|
1876
|
+
setNotice(`deleted \xB7 ${meta.title}`);
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
if (key.return && sessions[cursor]) {
|
|
1880
|
+
const meta = sessions[cursor];
|
|
1881
|
+
const history = loadSession(meta.id);
|
|
1882
|
+
setAgentHistory(history);
|
|
1883
|
+
setMessages(toDisplayMessages(history));
|
|
1884
|
+
setStreamingContent("");
|
|
1885
|
+
setThinkingContent("");
|
|
1886
|
+
setActiveToolUses([]);
|
|
1887
|
+
setActiveToolResults([]);
|
|
1888
|
+
setError(null);
|
|
1889
|
+
setSessionId(meta.id);
|
|
1890
|
+
setNotice(`resumed \xB7 ${meta.title}`);
|
|
1891
|
+
setState("ready");
|
|
1892
|
+
}
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1652
1895
|
if (state === "ready" && pendingPermissionRef.current) {
|
|
1653
1896
|
if (key.upArrow) {
|
|
1654
1897
|
setPermissionCursor((i) => Math.max(0, i - 1));
|
|
@@ -1713,16 +1956,30 @@ function useKeyboard(opts) {
|
|
|
1713
1956
|
setCursor(() => Math.max(0, models.findIndex((m) => m === cfg.model)));
|
|
1714
1957
|
setState("models");
|
|
1715
1958
|
} else if (trimmed === "/clear") {
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1959
|
+
clearSession();
|
|
1960
|
+
} else if (trimmed === "/new") {
|
|
1961
|
+
if (agentHistory.length) setNotice("session saved");
|
|
1962
|
+
setSessionId(newSessionId());
|
|
1963
|
+
clearSession();
|
|
1964
|
+
} else if (trimmed === "/sessions") {
|
|
1965
|
+
setSessions(listSessions());
|
|
1966
|
+
setCursor(() => 0);
|
|
1967
|
+
setState("sessions");
|
|
1723
1968
|
} else if (trimmed === "/exit") {
|
|
1724
1969
|
exit();
|
|
1725
1970
|
} else if (trimmed) {
|
|
1971
|
+
setNotice(null);
|
|
1972
|
+
if (!agentHistory.length && cfg.model) {
|
|
1973
|
+
const id = sessionId;
|
|
1974
|
+
const model = cfg.model;
|
|
1975
|
+
void (async () => {
|
|
1976
|
+
try {
|
|
1977
|
+
const title = await summarizeMessage(model, trimmed);
|
|
1978
|
+
persistSession(id, [{ role: "user", content: trimmed }], title);
|
|
1979
|
+
} catch {
|
|
1980
|
+
}
|
|
1981
|
+
})();
|
|
1982
|
+
}
|
|
1726
1983
|
sendMessage(trimmed);
|
|
1727
1984
|
}
|
|
1728
1985
|
setInput(() => "");
|
|
@@ -1782,7 +2039,7 @@ async function checkForUpdate() {
|
|
|
1782
2039
|
}
|
|
1783
2040
|
|
|
1784
2041
|
// src/ui/App.tsx
|
|
1785
|
-
import { Fragment as Fragment2, jsx as
|
|
2042
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1786
2043
|
function App() {
|
|
1787
2044
|
const { exit } = useApp();
|
|
1788
2045
|
const cwd = process.cwd().replace(homedir2(), "~").split(sep).join("/");
|
|
@@ -1793,6 +2050,10 @@ function App() {
|
|
|
1793
2050
|
const [state, setState] = useState4("loading");
|
|
1794
2051
|
const [cursor, setCursor] = useState4(0);
|
|
1795
2052
|
const [updateAvailable, setUpdateAvailable] = useState4(null);
|
|
2053
|
+
const [ollamaDown, setOllamaDown] = useState4(false);
|
|
2054
|
+
const [sessionId, setSessionId] = useState4(() => newSessionId());
|
|
2055
|
+
const [sessions, setSessions] = useState4([]);
|
|
2056
|
+
const [notice, setNotice] = useState4(null);
|
|
1796
2057
|
const [input, setInput] = useState4("");
|
|
1797
2058
|
const [paletteCursor, setPaletteCursor] = useState4(0);
|
|
1798
2059
|
const [filePickerCursor, setFilePickerCursor] = useState4(0);
|
|
@@ -1802,6 +2063,9 @@ function App() {
|
|
|
1802
2063
|
if (v) setUpdateAvailable(v);
|
|
1803
2064
|
});
|
|
1804
2065
|
}, []);
|
|
2066
|
+
useEffect3(() => {
|
|
2067
|
+
if (agent.agentHistory.length) persistSession(sessionId, agent.agentHistory);
|
|
2068
|
+
}, [agent.agentHistory, sessionId]);
|
|
1805
2069
|
useEffect3(() => {
|
|
1806
2070
|
listModels().then((m) => {
|
|
1807
2071
|
setModels(m);
|
|
@@ -1815,7 +2079,8 @@ function App() {
|
|
|
1815
2079
|
});
|
|
1816
2080
|
}).catch((err) => {
|
|
1817
2081
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1818
|
-
agent.setError(msg);
|
|
2082
|
+
agent.setError(ollamaInstalled() ? msg : OLLAMA_NOT_INSTALLED);
|
|
2083
|
+
setOllamaDown(true);
|
|
1819
2084
|
setModels([]);
|
|
1820
2085
|
setState(cfg.model ? "ready" : "select-model");
|
|
1821
2086
|
});
|
|
@@ -1831,26 +2096,18 @@ function App() {
|
|
|
1831
2096
|
cfg,
|
|
1832
2097
|
setCfg,
|
|
1833
2098
|
setActiveCtx,
|
|
1834
|
-
|
|
1835
|
-
permissionCursor: agent.permissionCursor,
|
|
1836
|
-
setPermissionCursor: agent.setPermissionCursor,
|
|
1837
|
-
resolvePermission: agent.resolvePermission,
|
|
1838
|
-
busyRef: agent.busyRef,
|
|
1839
|
-
abortRef: agent.abortRef,
|
|
2099
|
+
agent,
|
|
1840
2100
|
input,
|
|
1841
2101
|
setInput,
|
|
1842
2102
|
paletteCursor,
|
|
1843
2103
|
setPaletteCursor,
|
|
1844
2104
|
filePickerCursor,
|
|
1845
2105
|
setFilePickerCursor,
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
setActiveToolUses: agent.setActiveToolUses,
|
|
1852
|
-
setActiveToolResults: agent.setActiveToolResults,
|
|
1853
|
-
setError: agent.setError
|
|
2106
|
+
sessionId,
|
|
2107
|
+
setSessionId,
|
|
2108
|
+
sessions,
|
|
2109
|
+
setSessions,
|
|
2110
|
+
setNotice
|
|
1854
2111
|
});
|
|
1855
2112
|
const effort = cfg.effort ?? "medium";
|
|
1856
2113
|
const contextWarning = (() => {
|
|
@@ -1860,11 +2117,11 @@ function App() {
|
|
|
1860
2117
|
if (used < activeCtx * 0.7) return null;
|
|
1861
2118
|
return Math.round(used / activeCtx * 100);
|
|
1862
2119
|
})();
|
|
1863
|
-
return /* @__PURE__ */
|
|
1864
|
-
/* @__PURE__ */
|
|
1865
|
-
updateAvailable && /* @__PURE__ */
|
|
1866
|
-
state === "loading" && !agent.error && /* @__PURE__ */
|
|
1867
|
-
agent.error && state !== "ready" && /* @__PURE__ */
|
|
2120
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingX: 1, children: [
|
|
2121
|
+
/* @__PURE__ */ jsx10(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error }),
|
|
2122
|
+
updateAvailable && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: npm i -g miii-agent` }) }),
|
|
2123
|
+
state === "loading" && !agent.error && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "connecting to ollama\u2026" }) }),
|
|
2124
|
+
agent.error && state !== "ready" && /* @__PURE__ */ jsx10(
|
|
1868
2125
|
ChatView,
|
|
1869
2126
|
{
|
|
1870
2127
|
messages: [],
|
|
@@ -1874,12 +2131,12 @@ function App() {
|
|
|
1874
2131
|
error: agent.error
|
|
1875
2132
|
}
|
|
1876
2133
|
),
|
|
1877
|
-
state === "select-model" && /* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1879
|
-
/* @__PURE__ */
|
|
1880
|
-
models.length > 0 && /* @__PURE__ */
|
|
2134
|
+
state === "select-model" && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginLeft: 2, children: [
|
|
2135
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "no model configured \u2014 select one" }),
|
|
2136
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(ModelList, { models, cursor }) }),
|
|
2137
|
+
models.length > 0 && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2191\u2193 navigate enter select ctrl+c quit" }) })
|
|
1881
2138
|
] }),
|
|
1882
|
-
state === "models" && /* @__PURE__ */
|
|
2139
|
+
state === "models" && /* @__PURE__ */ jsx10(
|
|
1883
2140
|
ModelsView,
|
|
1884
2141
|
{
|
|
1885
2142
|
models,
|
|
@@ -1889,8 +2146,10 @@ function App() {
|
|
|
1889
2146
|
effort
|
|
1890
2147
|
}
|
|
1891
2148
|
),
|
|
1892
|
-
state === "
|
|
1893
|
-
|
|
2149
|
+
state === "sessions" && /* @__PURE__ */ jsx10(SessionsView, { sessions, cursor }),
|
|
2150
|
+
state === "ready" && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2151
|
+
notice && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "green", children: `\u2713 ${notice}` }) }),
|
|
2152
|
+
/* @__PURE__ */ jsx10(
|
|
1894
2153
|
ChatView,
|
|
1895
2154
|
{
|
|
1896
2155
|
messages: agent.messages,
|
|
@@ -1905,15 +2164,17 @@ function App() {
|
|
|
1905
2164
|
activeToolResults: agent.activeToolResults
|
|
1906
2165
|
}
|
|
1907
2166
|
),
|
|
1908
|
-
input.startsWith("/") && /* @__PURE__ */
|
|
1909
|
-
contextWarning !== null && /* @__PURE__ */
|
|
2167
|
+
input.startsWith("/") && /* @__PURE__ */ jsx10(CommandPalette, { filter: input, cursor: paletteCursor }),
|
|
2168
|
+
contextWarning !== null && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: `\u26A0 context ${contextWarning}% full \u2014 run /clear and start fresh` }) }),
|
|
1910
2169
|
!input.startsWith("/") && (() => {
|
|
1911
2170
|
const m = parseMention(input);
|
|
1912
2171
|
if (!m) return null;
|
|
1913
|
-
return /* @__PURE__ */
|
|
2172
|
+
return /* @__PURE__ */ jsx10(FilePicker, { matches: searchFiles(process.cwd(), m.query), cursor: filePickerCursor });
|
|
1914
2173
|
})(),
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
|
|
2174
|
+
!ollamaDown && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2175
|
+
/* @__PURE__ */ jsx10(InputBar, { input, disabled: agent.busy, processingLabel: agent.processingLabel }),
|
|
2176
|
+
!agent.busy && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "type / to see commands" }) })
|
|
2177
|
+
] })
|
|
1917
2178
|
] })
|
|
1918
2179
|
] });
|
|
1919
2180
|
}
|