miii-agent 0.1.4 → 0.1.5
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 +384 -138
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ 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
|
|
|
@@ -266,19 +266,53 @@ function ModelsView({ models, cursor, model, ollamaHost, effort }) {
|
|
|
266
266
|
] });
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
// src/ui/
|
|
269
|
+
// src/ui/SessionsView.tsx
|
|
270
270
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
271
271
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
272
|
+
function relativeTime(iso) {
|
|
273
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
274
|
+
const min = Math.floor(diff / 6e4);
|
|
275
|
+
if (min < 1) return "just now";
|
|
276
|
+
if (min < 60) return `${min}m ago`;
|
|
277
|
+
const hr = Math.floor(min / 60);
|
|
278
|
+
if (hr < 24) return `${hr}h ago`;
|
|
279
|
+
const d = Math.floor(hr / 24);
|
|
280
|
+
return `${d}d ago`;
|
|
281
|
+
}
|
|
282
|
+
function SessionsView({ sessions, cursor }) {
|
|
283
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginLeft: 2, children: [
|
|
284
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "resume session" }),
|
|
285
|
+
/* @__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) => {
|
|
286
|
+
const active = i === cursor;
|
|
287
|
+
const label = s.title;
|
|
288
|
+
return /* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
|
|
289
|
+
/* @__PURE__ */ jsxs5(Text5, { color: active ? "blue" : void 0, dimColor: !active, children: [
|
|
290
|
+
active ? "\u276F " : " ",
|
|
291
|
+
label
|
|
292
|
+
] }),
|
|
293
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: `\xB7 ${s.messageCount} msgs \xB7 ${relativeTime(s.updatedAt)}` })
|
|
294
|
+
] }, s.id);
|
|
295
|
+
}) }),
|
|
296
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191\u2193 navigate enter resume d delete esc cancel" }) })
|
|
297
|
+
] });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/ui/CommandPalette.tsx
|
|
301
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
302
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
272
303
|
var COMMANDS = [
|
|
273
304
|
{ name: "/models", description: "switch model or adjust effort" },
|
|
305
|
+
{ name: "/new", description: "save current session and start fresh" },
|
|
306
|
+
{ name: "/sessions", description: "list sessions and resume one" },
|
|
274
307
|
{ name: "/clear", description: "clear chat and reset context" },
|
|
275
308
|
{ name: "/exit", description: "quit miii" }
|
|
276
309
|
];
|
|
277
310
|
function CommandPalette({ filter, cursor }) {
|
|
278
311
|
const filtered = COMMANDS.filter((c) => c.name.startsWith(filter));
|
|
279
312
|
if (filtered.length === 0) return null;
|
|
280
|
-
|
|
281
|
-
|
|
313
|
+
const nameWidth = Math.max(...filtered.map((c) => c.name.length));
|
|
314
|
+
return /* @__PURE__ */ jsxs6(
|
|
315
|
+
Box6,
|
|
282
316
|
{
|
|
283
317
|
flexDirection: "column",
|
|
284
318
|
borderStyle: "round",
|
|
@@ -289,15 +323,15 @@ function CommandPalette({ filter, cursor }) {
|
|
|
289
323
|
children: [
|
|
290
324
|
filtered.map((cmd, i) => {
|
|
291
325
|
const active = i === cursor;
|
|
292
|
-
return /* @__PURE__ */
|
|
293
|
-
/* @__PURE__ */
|
|
326
|
+
return /* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
|
|
327
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: active, color: active ? "blue" : void 0, dimColor: !active, children: [
|
|
294
328
|
active ? "\u276F " : " ",
|
|
295
|
-
cmd.name
|
|
329
|
+
cmd.name.padEnd(nameWidth)
|
|
296
330
|
] }),
|
|
297
|
-
/* @__PURE__ */
|
|
331
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: cmd.description })
|
|
298
332
|
] }, cmd.name);
|
|
299
333
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
334
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 0, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2191\u2193 navigate tab/enter autocomplete esc dismiss" }) })
|
|
301
335
|
]
|
|
302
336
|
}
|
|
303
337
|
);
|
|
@@ -306,11 +340,150 @@ function filteredCommands(filter) {
|
|
|
306
340
|
return COMMANDS.filter((c) => c.name.startsWith(filter));
|
|
307
341
|
}
|
|
308
342
|
|
|
343
|
+
// src/session/store.ts
|
|
344
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, rmSync } from "fs";
|
|
345
|
+
import { join as join2 } from "path";
|
|
346
|
+
import { randomUUID } from "crypto";
|
|
347
|
+
var SESSION_DIR = join2(process.cwd(), ".miii", "session");
|
|
348
|
+
function newSessionId() {
|
|
349
|
+
return randomUUID();
|
|
350
|
+
}
|
|
351
|
+
function sessionPath(id) {
|
|
352
|
+
return join2(SESSION_DIR, `${id}.jsonl`);
|
|
353
|
+
}
|
|
354
|
+
function messageText(m) {
|
|
355
|
+
if (typeof m.content === "string") return m.content;
|
|
356
|
+
return m.content.map((b) => {
|
|
357
|
+
if (b.type === "text") return b.text;
|
|
358
|
+
if (b.type === "tool_use") return `[tool ${b.name}]`;
|
|
359
|
+
if (b.type === "tool_result") return "[result]";
|
|
360
|
+
return "";
|
|
361
|
+
}).join(" ");
|
|
362
|
+
}
|
|
363
|
+
function firstUserText(messages) {
|
|
364
|
+
const first = messages.find((m) => m.role === "user");
|
|
365
|
+
if (!first) return "untitled";
|
|
366
|
+
return messageText(first).trim().slice(0, 80) || "untitled";
|
|
367
|
+
}
|
|
368
|
+
function readMeta(id) {
|
|
369
|
+
try {
|
|
370
|
+
const raw = readFileSync2(sessionPath(id), "utf-8");
|
|
371
|
+
const firstLine = raw.slice(0, raw.indexOf("\n") === -1 ? raw.length : raw.indexOf("\n"));
|
|
372
|
+
const parsed = JSON.parse(firstLine);
|
|
373
|
+
if (parsed.type !== "meta") return null;
|
|
374
|
+
const { type: _t, ...meta } = parsed;
|
|
375
|
+
return meta;
|
|
376
|
+
} catch {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function persistSession(id, messages, title) {
|
|
381
|
+
if (!messages.length) return;
|
|
382
|
+
mkdirSync2(SESSION_DIR, { recursive: true });
|
|
383
|
+
const existing = readMeta(id);
|
|
384
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
385
|
+
const meta = {
|
|
386
|
+
id,
|
|
387
|
+
createdAt: existing?.createdAt ?? now,
|
|
388
|
+
updatedAt: now,
|
|
389
|
+
title: title ?? existing?.title ?? firstUserText(messages),
|
|
390
|
+
messageCount: messages.length
|
|
391
|
+
};
|
|
392
|
+
const lines = [JSON.stringify({ type: "meta", ...meta })];
|
|
393
|
+
for (const message of messages) {
|
|
394
|
+
lines.push(JSON.stringify({ type: "message", message }));
|
|
395
|
+
}
|
|
396
|
+
writeFileSync2(sessionPath(id), lines.join("\n") + "\n", "utf-8");
|
|
397
|
+
}
|
|
398
|
+
function listSessions() {
|
|
399
|
+
if (!existsSync2(SESSION_DIR)) return [];
|
|
400
|
+
const metas = [];
|
|
401
|
+
for (const file of readdirSync(SESSION_DIR)) {
|
|
402
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
403
|
+
const meta = readMeta(file.replace(/\.jsonl$/, ""));
|
|
404
|
+
if (meta) metas.push(meta);
|
|
405
|
+
}
|
|
406
|
+
return metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
407
|
+
}
|
|
408
|
+
function deleteSession(id) {
|
|
409
|
+
try {
|
|
410
|
+
rmSync(sessionPath(id), { force: true });
|
|
411
|
+
} catch {
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function loadSession(id) {
|
|
415
|
+
try {
|
|
416
|
+
const raw = readFileSync2(sessionPath(id), "utf-8");
|
|
417
|
+
const messages = [];
|
|
418
|
+
for (const line of raw.split("\n")) {
|
|
419
|
+
if (!line.trim()) continue;
|
|
420
|
+
const parsed = JSON.parse(line);
|
|
421
|
+
if (parsed.type === "message") messages.push(parsed.message);
|
|
422
|
+
}
|
|
423
|
+
return messages;
|
|
424
|
+
} catch {
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function toDisplayMessages(history) {
|
|
429
|
+
const out = [];
|
|
430
|
+
for (const m of history) {
|
|
431
|
+
if (m.role === "system") continue;
|
|
432
|
+
const blocks = Array.isArray(m.content) ? m.content : [{ type: "text", text: m.content }];
|
|
433
|
+
if (m.role === "user") {
|
|
434
|
+
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
435
|
+
const results = blocks.filter((b) => b.type === "tool_result");
|
|
436
|
+
if (results.length && out.length) {
|
|
437
|
+
const last = out[out.length - 1];
|
|
438
|
+
last.tool_results = [
|
|
439
|
+
...last.tool_results ?? [],
|
|
440
|
+
...results.map((r) => ({
|
|
441
|
+
tool_use_id: r.tool_use_id,
|
|
442
|
+
content: r.content,
|
|
443
|
+
is_error: r.is_error
|
|
444
|
+
}))
|
|
445
|
+
];
|
|
446
|
+
}
|
|
447
|
+
if (text.trim()) out.push({ role: "user", content: text });
|
|
448
|
+
} else {
|
|
449
|
+
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
450
|
+
const uses = blocks.filter((b) => b.type === "tool_use").map((b) => ({ id: b.id, name: b.name, input: b.input }));
|
|
451
|
+
out.push({
|
|
452
|
+
role: "assistant",
|
|
453
|
+
content: text,
|
|
454
|
+
tool_uses: uses.length ? uses : void 0
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return out;
|
|
459
|
+
}
|
|
460
|
+
async function summarizeMessage(model, text) {
|
|
461
|
+
const fallback = text.trim().slice(0, 80) || "untitled";
|
|
462
|
+
const prompt = `Summarize this user request as a short title, 3-6 words, no punctuation. Reply with the title only.
|
|
463
|
+
|
|
464
|
+
Request:
|
|
465
|
+
${text.slice(0, 2e3)}`;
|
|
466
|
+
try {
|
|
467
|
+
let out = "";
|
|
468
|
+
for await (const chunk of chat(
|
|
469
|
+
model,
|
|
470
|
+
[{ role: "user", content: prompt }],
|
|
471
|
+
void 0,
|
|
472
|
+
{ temperature: 0.2, num_predict: 32 }
|
|
473
|
+
)) {
|
|
474
|
+
if (chunk.content) out += chunk.content;
|
|
475
|
+
}
|
|
476
|
+
return out.trim().split("\n").filter(Boolean)[0]?.trim() || fallback;
|
|
477
|
+
} catch {
|
|
478
|
+
return fallback;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
309
482
|
// src/ui/FilePicker.tsx
|
|
310
|
-
import { Box as
|
|
311
|
-
import { readdirSync } from "fs";
|
|
312
|
-
import { join as
|
|
313
|
-
import { jsx as
|
|
483
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
484
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
485
|
+
import { join as join3, relative } from "path";
|
|
486
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
314
487
|
var IGNORE = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage", ".miii"]);
|
|
315
488
|
var MAX_RESULTS = 10;
|
|
316
489
|
var MAX_SCAN = 2e3;
|
|
@@ -323,13 +496,13 @@ function listFiles(cwd) {
|
|
|
323
496
|
const dir = stack.pop();
|
|
324
497
|
let entries;
|
|
325
498
|
try {
|
|
326
|
-
entries =
|
|
499
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
327
500
|
} catch {
|
|
328
501
|
continue;
|
|
329
502
|
}
|
|
330
503
|
for (const e of entries) {
|
|
331
504
|
if (IGNORE.has(e.name) || e.name.startsWith(".")) continue;
|
|
332
|
-
const full =
|
|
505
|
+
const full = join3(dir, e.name);
|
|
333
506
|
if (e.isDirectory()) stack.push(full);
|
|
334
507
|
else if (e.isFile()) out.push(relative(cwd, full));
|
|
335
508
|
if (out.length >= MAX_SCAN) break;
|
|
@@ -363,8 +536,8 @@ function searchFiles(cwd, query) {
|
|
|
363
536
|
}
|
|
364
537
|
function FilePicker({ matches, cursor }) {
|
|
365
538
|
if (matches.length === 0) return null;
|
|
366
|
-
return /* @__PURE__ */
|
|
367
|
-
|
|
539
|
+
return /* @__PURE__ */ jsxs7(
|
|
540
|
+
Box7,
|
|
368
541
|
{
|
|
369
542
|
flexDirection: "column",
|
|
370
543
|
borderStyle: "round",
|
|
@@ -375,24 +548,24 @@ function FilePicker({ matches, cursor }) {
|
|
|
375
548
|
children: [
|
|
376
549
|
matches.map((f, i) => {
|
|
377
550
|
const active = i === cursor;
|
|
378
|
-
return /* @__PURE__ */
|
|
551
|
+
return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { bold: active, color: active ? "blue" : void 0, dimColor: !active, children: [
|
|
379
552
|
active ? "\u276F " : " ",
|
|
380
553
|
f
|
|
381
554
|
] }) }, f);
|
|
382
555
|
}),
|
|
383
|
-
/* @__PURE__ */
|
|
556
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 0, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2191\u2193 navigate tab insert esc dismiss" }) })
|
|
384
557
|
]
|
|
385
558
|
}
|
|
386
559
|
);
|
|
387
560
|
}
|
|
388
561
|
|
|
389
562
|
// src/ui/ChatView.tsx
|
|
390
|
-
import { Box as
|
|
563
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
391
564
|
|
|
392
565
|
// src/ui/ThinkingBlock.tsx
|
|
393
566
|
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
394
|
-
import { Box as
|
|
395
|
-
import { jsx as
|
|
567
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
568
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
396
569
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
397
570
|
var globalThinkingVisible = false;
|
|
398
571
|
var listeners = /* @__PURE__ */ new Set();
|
|
@@ -418,20 +591,20 @@ function ThinkingBlock({ content }) {
|
|
|
418
591
|
const t = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length), 80);
|
|
419
592
|
return () => clearInterval(t);
|
|
420
593
|
}, []);
|
|
421
|
-
return /* @__PURE__ */
|
|
422
|
-
/* @__PURE__ */
|
|
423
|
-
/* @__PURE__ */
|
|
594
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
|
|
595
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
596
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "blue", children: [
|
|
424
597
|
FRAMES[frame],
|
|
425
598
|
" "
|
|
426
599
|
] }),
|
|
427
|
-
/* @__PURE__ */
|
|
428
|
-
/* @__PURE__ */
|
|
600
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: "thinking\u2026" }),
|
|
601
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
429
602
|
" \xB7 ctrl+t to ",
|
|
430
603
|
visible ? "hide" : "show",
|
|
431
604
|
" thoughts"
|
|
432
605
|
] })
|
|
433
606
|
] }),
|
|
434
|
-
visible && content ? /* @__PURE__ */
|
|
607
|
+
visible && content ? /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: content }) }) : null
|
|
435
608
|
] });
|
|
436
609
|
}
|
|
437
610
|
|
|
@@ -439,12 +612,14 @@ function ThinkingBlock({ content }) {
|
|
|
439
612
|
var EMPTY_STATE_HINTS = [
|
|
440
613
|
"\u2022 explain @file \u2014 reference a file with @",
|
|
441
614
|
"\u2022 /models \u2014 switch model or effort",
|
|
442
|
-
"\u2022
|
|
615
|
+
"\u2022 /new \u2014 start a new chat",
|
|
616
|
+
"\u2022 /sessions \u2014 view saved chats",
|
|
617
|
+
"\u2022 ctrl+t \u2014 toggle thinking"
|
|
443
618
|
];
|
|
444
619
|
var EMPTY_STATE_TITLE = "Ask anything, or try:";
|
|
445
620
|
|
|
446
621
|
// src/ui/ChatView.tsx
|
|
447
|
-
import { jsx as
|
|
622
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
448
623
|
function formatTokens(n) {
|
|
449
624
|
if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1) + "k";
|
|
450
625
|
return String(n);
|
|
@@ -470,27 +645,27 @@ function FileEditBlock({
|
|
|
470
645
|
const MAX = 16;
|
|
471
646
|
const shown = previewLines.slice(0, MAX);
|
|
472
647
|
const extra = previewLines.length - shown.length;
|
|
473
|
-
return /* @__PURE__ */
|
|
474
|
-
/* @__PURE__ */
|
|
475
|
-
/* @__PURE__ */
|
|
476
|
-
/* @__PURE__ */
|
|
648
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
|
|
649
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
650
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25CF " }),
|
|
651
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
477
652
|
label,
|
|
478
653
|
" "
|
|
479
654
|
] }),
|
|
480
|
-
/* @__PURE__ */
|
|
481
|
-
/* @__PURE__ */
|
|
482
|
-
/* @__PURE__ */
|
|
655
|
+
/* @__PURE__ */ jsx9(Text9, { children: "(" }),
|
|
656
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: path }),
|
|
657
|
+
/* @__PURE__ */ jsx9(Text9, { children: ")" })
|
|
483
658
|
] }),
|
|
484
|
-
/* @__PURE__ */
|
|
659
|
+
/* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
485
660
|
"\u23BF ",
|
|
486
661
|
removed > 0 ? `Added ${added} lines, removed ${removed} lines` : `Added ${added} lines`
|
|
487
662
|
] }) }),
|
|
488
|
-
shown.map((ln, i) => /* @__PURE__ */
|
|
663
|
+
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
664
|
ln.sign,
|
|
490
665
|
" ",
|
|
491
666
|
ln.text
|
|
492
667
|
] }) }, i)),
|
|
493
|
-
extra > 0 && /* @__PURE__ */
|
|
668
|
+
extra > 0 && /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
494
669
|
"\u2026 ",
|
|
495
670
|
extra,
|
|
496
671
|
" more lines"
|
|
@@ -563,7 +738,7 @@ function ToolResultBlock({ result, toolName }) {
|
|
|
563
738
|
const lines = content.split("\n");
|
|
564
739
|
const showMulti = (toolName === "run_bash" || toolName === "grep" || toolName === "glob" || result.is_error) && lines.length > 1;
|
|
565
740
|
if (!showMulti) {
|
|
566
|
-
return /* @__PURE__ */
|
|
741
|
+
return /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
|
|
567
742
|
"\u23BF ",
|
|
568
743
|
summarizeResult(result, toolName)
|
|
569
744
|
] }) });
|
|
@@ -572,13 +747,13 @@ function ToolResultBlock({ result, toolName }) {
|
|
|
572
747
|
const MAX_LINE_WIDTH = 200;
|
|
573
748
|
const shown = lines.slice(0, MAX_LINES).map((l) => truncate(l, MAX_LINE_WIDTH));
|
|
574
749
|
const extra = lines.length - shown.length;
|
|
575
|
-
return /* @__PURE__ */
|
|
576
|
-
/* @__PURE__ */
|
|
750
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
|
|
751
|
+
/* @__PURE__ */ jsxs9(Text9, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
|
|
577
752
|
"\u23BF ",
|
|
578
753
|
summarizeResult(result, toolName)
|
|
579
754
|
] }),
|
|
580
|
-
shown.map((ln, i) => /* @__PURE__ */
|
|
581
|
-
extra > 0 && /* @__PURE__ */
|
|
755
|
+
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)),
|
|
756
|
+
extra > 0 && /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
582
757
|
"\u2026 ",
|
|
583
758
|
extra,
|
|
584
759
|
" more lines"
|
|
@@ -591,7 +766,7 @@ function ToolUseLine({ use, result }) {
|
|
|
591
766
|
const content = input.content ?? "";
|
|
592
767
|
const added = countLines(content);
|
|
593
768
|
const preview = content.split("\n").map((t) => ({ sign: "+", text: t }));
|
|
594
|
-
return /* @__PURE__ */
|
|
769
|
+
return /* @__PURE__ */ jsx9(FileEditBlock, { label: "Write", path: input.path ?? "", added, removed: 0, previewLines: preview });
|
|
595
770
|
}
|
|
596
771
|
if (use.name === "edit_file" && !result?.is_error) {
|
|
597
772
|
const input = use.input;
|
|
@@ -603,34 +778,34 @@ function ToolUseLine({ use, result }) {
|
|
|
603
778
|
...oldS.split("\n").map((t) => ({ sign: "-", text: t })),
|
|
604
779
|
...newS.split("\n").map((t) => ({ sign: "+", text: t }))
|
|
605
780
|
];
|
|
606
|
-
return /* @__PURE__ */
|
|
781
|
+
return /* @__PURE__ */ jsx9(FileEditBlock, { label: "Update", path: input.path ?? "", added, removed, previewLines: preview });
|
|
607
782
|
}
|
|
608
783
|
const { label, arg } = toolHeader(use);
|
|
609
|
-
return /* @__PURE__ */
|
|
610
|
-
/* @__PURE__ */
|
|
611
|
-
/* @__PURE__ */
|
|
612
|
-
/* @__PURE__ */
|
|
784
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
|
|
785
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
786
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25CF " }),
|
|
787
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
613
788
|
label,
|
|
614
789
|
" "
|
|
615
790
|
] }),
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
/* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
791
|
+
/* @__PURE__ */ jsx9(Text9, { children: "(" }),
|
|
792
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: arg }),
|
|
793
|
+
/* @__PURE__ */ jsx9(Text9, { children: ")" })
|
|
619
794
|
] }),
|
|
620
|
-
result && /* @__PURE__ */
|
|
795
|
+
result && /* @__PURE__ */ jsx9(ToolResultBlock, { result, toolName: use.name })
|
|
621
796
|
] });
|
|
622
797
|
}
|
|
623
798
|
function AssistantMessage({ msg }) {
|
|
624
|
-
return /* @__PURE__ */
|
|
625
|
-
msg.content && /* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
799
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
800
|
+
msg.content && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
|
|
801
|
+
/* @__PURE__ */ jsx9(Text9, { color: "white", children: "\u25CF " }),
|
|
802
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text9, { children: msg.content }) })
|
|
628
803
|
] }),
|
|
629
804
|
msg.tool_uses?.map((u) => {
|
|
630
805
|
const r = msg.tool_results?.find((x) => x.tool_use_id === u.id);
|
|
631
|
-
return /* @__PURE__ */
|
|
806
|
+
return /* @__PURE__ */ jsx9(ToolUseLine, { use: u, result: r }, u.id);
|
|
632
807
|
}),
|
|
633
|
-
msg.tokens && /* @__PURE__ */
|
|
808
|
+
msg.tokens && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
634
809
|
`\u21B3 Completed \xB7 ${formatTokens(msg.tokens.prompt_eval + msg.tokens.eval)} tokens`,
|
|
635
810
|
msg.duration != null ? ` \xB7 ${formatDuration(msg.duration)}` : ""
|
|
636
811
|
] }) })
|
|
@@ -659,15 +834,15 @@ function PermissionPrompt({ req, cursor }) {
|
|
|
659
834
|
{ label: "No", key: "no" }
|
|
660
835
|
];
|
|
661
836
|
const summary = summarizeInput(req.input);
|
|
662
|
-
return /* @__PURE__ */
|
|
663
|
-
/* @__PURE__ */
|
|
664
|
-
/* @__PURE__ */
|
|
837
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
|
|
838
|
+
/* @__PURE__ */ jsx9(Text9, { color: "blue", bold: true, children: "Tool use" }),
|
|
839
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
665
840
|
"Allow ",
|
|
666
|
-
/* @__PURE__ */
|
|
841
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: label }),
|
|
667
842
|
"?"
|
|
668
843
|
] }) }),
|
|
669
|
-
summary && /* @__PURE__ */
|
|
670
|
-
/* @__PURE__ */
|
|
844
|
+
summary && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: summary }) }),
|
|
845
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsxs9(Text9, { color: i === cursor ? "blue" : void 0, children: [
|
|
671
846
|
i === cursor ? "\u276F " : " ",
|
|
672
847
|
i + 1,
|
|
673
848
|
". ",
|
|
@@ -688,33 +863,33 @@ function ChatView({
|
|
|
688
863
|
activeToolResults
|
|
689
864
|
}) {
|
|
690
865
|
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__ */
|
|
866
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 1, marginBottom: 1, children: [
|
|
867
|
+
empty && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
868
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: EMPTY_STATE_TITLE }),
|
|
869
|
+
EMPTY_STATE_HINTS.map((h, i) => /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
695
870
|
" ",
|
|
696
871
|
h
|
|
697
872
|
] }, i))
|
|
698
873
|
] }),
|
|
699
874
|
messages.map(
|
|
700
|
-
(msg, i) => msg.role === "user" ? /* @__PURE__ */
|
|
701
|
-
/* @__PURE__ */
|
|
702
|
-
/* @__PURE__ */
|
|
703
|
-
] }, i) : /* @__PURE__ */
|
|
875
|
+
(msg, i) => msg.role === "user" ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
|
|
876
|
+
/* @__PURE__ */ jsx9(Text9, { color: "blue", children: "\u25CF " }),
|
|
877
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text9, { children: msg.content }) })
|
|
878
|
+
] }, i) : /* @__PURE__ */ jsx9(AssistantMessage, { msg }, i)
|
|
704
879
|
),
|
|
705
|
-
thinking && /* @__PURE__ */
|
|
706
|
-
streaming && streamingContent && /* @__PURE__ */
|
|
707
|
-
/* @__PURE__ */
|
|
708
|
-
/* @__PURE__ */
|
|
880
|
+
thinking && /* @__PURE__ */ jsx9(ThinkingBlock, { content: thinkingContent }),
|
|
881
|
+
streaming && streamingContent && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
|
|
882
|
+
/* @__PURE__ */ jsx9(Text9, { color: "white", children: "\u25CF " }),
|
|
883
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text9, { children: streamingContent }) })
|
|
709
884
|
] }),
|
|
710
885
|
activeToolUses?.map((u) => {
|
|
711
886
|
const r = activeToolResults?.find((x) => x.tool_use_id === u.id);
|
|
712
|
-
return /* @__PURE__ */
|
|
887
|
+
return /* @__PURE__ */ jsx9(ToolUseLine, { use: u, result: r }, u.id);
|
|
713
888
|
}),
|
|
714
|
-
pendingPermission && /* @__PURE__ */
|
|
715
|
-
error && /* @__PURE__ */
|
|
716
|
-
/* @__PURE__ */
|
|
717
|
-
/* @__PURE__ */
|
|
889
|
+
pendingPermission && /* @__PURE__ */ jsx9(PermissionPrompt, { req: pendingPermission, cursor: permissionCursor }),
|
|
890
|
+
error && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
|
|
891
|
+
/* @__PURE__ */ jsx9(Text9, { color: "red", children: "\u25CF " }),
|
|
892
|
+
/* @__PURE__ */ jsx9(Text9, { color: "red", children: error })
|
|
718
893
|
] })
|
|
719
894
|
] });
|
|
720
895
|
}
|
|
@@ -723,7 +898,7 @@ function ChatView({
|
|
|
723
898
|
import { useState as useState3, useRef } from "react";
|
|
724
899
|
|
|
725
900
|
// src/tools/edit_file.ts
|
|
726
|
-
import { readFileSync as
|
|
901
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
727
902
|
var edit_file = {
|
|
728
903
|
name: "edit_file",
|
|
729
904
|
description: "Replace an exact string in a file. old_str must be unique.",
|
|
@@ -737,7 +912,7 @@ var edit_file = {
|
|
|
737
912
|
required: ["path", "old_str", "new_str"]
|
|
738
913
|
},
|
|
739
914
|
handler: ({ path, old_str, new_str }) => {
|
|
740
|
-
const src =
|
|
915
|
+
const src = readFileSync3(path, "utf-8");
|
|
741
916
|
const first = src.indexOf(old_str);
|
|
742
917
|
if (first === -1) {
|
|
743
918
|
return { content: `old_str not found in ${path}`, is_error: true };
|
|
@@ -745,13 +920,13 @@ var edit_file = {
|
|
|
745
920
|
if (src.indexOf(old_str, first + 1) !== -1) {
|
|
746
921
|
return { content: `old_str not unique in ${path}`, is_error: true };
|
|
747
922
|
}
|
|
748
|
-
|
|
923
|
+
writeFileSync3(path, src.slice(0, first) + new_str + src.slice(first + old_str.length), "utf-8");
|
|
749
924
|
return { content: `Edited ${path}` };
|
|
750
925
|
}
|
|
751
926
|
};
|
|
752
927
|
|
|
753
928
|
// src/tools/read_file.ts
|
|
754
|
-
import { readFileSync as
|
|
929
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
755
930
|
var read_file = {
|
|
756
931
|
name: "read_file",
|
|
757
932
|
description: "Read entire file contents as UTF-8 text.",
|
|
@@ -765,7 +940,7 @@ var read_file = {
|
|
|
765
940
|
handler: ({ path }) => {
|
|
766
941
|
try {
|
|
767
942
|
const MAX = 2e5;
|
|
768
|
-
const raw =
|
|
943
|
+
const raw = readFileSync4(path, "utf-8");
|
|
769
944
|
const truncated = raw.length > MAX;
|
|
770
945
|
const body = truncated ? raw.slice(0, MAX) + `
|
|
771
946
|
[truncated: ${raw.length - MAX} more chars]` : raw;
|
|
@@ -777,7 +952,7 @@ var read_file = {
|
|
|
777
952
|
};
|
|
778
953
|
|
|
779
954
|
// src/tools/write_file.ts
|
|
780
|
-
import { writeFileSync as
|
|
955
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
781
956
|
import { dirname } from "path";
|
|
782
957
|
var write_file = {
|
|
783
958
|
name: "write_file",
|
|
@@ -792,8 +967,8 @@ var write_file = {
|
|
|
792
967
|
},
|
|
793
968
|
handler: ({ path, content }) => {
|
|
794
969
|
try {
|
|
795
|
-
|
|
796
|
-
|
|
970
|
+
mkdirSync3(dirname(path), { recursive: true });
|
|
971
|
+
writeFileSync4(path, content, "utf-8");
|
|
797
972
|
return { content: `Wrote ${path} (${content.length} bytes)` };
|
|
798
973
|
} catch (err) {
|
|
799
974
|
return { content: err instanceof Error ? err.message : String(err), is_error: true };
|
|
@@ -1582,19 +1757,28 @@ function useKeyboard(opts) {
|
|
|
1582
1757
|
cfg,
|
|
1583
1758
|
setCfg,
|
|
1584
1759
|
setActiveCtx,
|
|
1585
|
-
|
|
1586
|
-
permissionCursor,
|
|
1587
|
-
setPermissionCursor,
|
|
1588
|
-
resolvePermission,
|
|
1589
|
-
busyRef,
|
|
1590
|
-
abortRef,
|
|
1760
|
+
agent,
|
|
1591
1761
|
input,
|
|
1592
1762
|
setInput,
|
|
1593
1763
|
paletteCursor,
|
|
1594
1764
|
setPaletteCursor,
|
|
1595
1765
|
filePickerCursor,
|
|
1596
1766
|
setFilePickerCursor,
|
|
1767
|
+
sessionId,
|
|
1768
|
+
setSessionId,
|
|
1769
|
+
sessions,
|
|
1770
|
+
setSessions,
|
|
1771
|
+
setNotice
|
|
1772
|
+
} = opts;
|
|
1773
|
+
const {
|
|
1774
|
+
pendingPermissionRef,
|
|
1775
|
+
permissionCursor,
|
|
1776
|
+
setPermissionCursor,
|
|
1777
|
+
resolvePermission,
|
|
1778
|
+
busyRef,
|
|
1779
|
+
abortRef,
|
|
1597
1780
|
sendMessage,
|
|
1781
|
+
agentHistory,
|
|
1598
1782
|
setMessages,
|
|
1599
1783
|
setAgentHistory,
|
|
1600
1784
|
setStreamingContent,
|
|
@@ -1602,7 +1786,17 @@ function useKeyboard(opts) {
|
|
|
1602
1786
|
setActiveToolUses,
|
|
1603
1787
|
setActiveToolResults,
|
|
1604
1788
|
setError
|
|
1605
|
-
} =
|
|
1789
|
+
} = agent;
|
|
1790
|
+
function clearSession() {
|
|
1791
|
+
setMessages(() => []);
|
|
1792
|
+
setAgentHistory([]);
|
|
1793
|
+
setStreamingContent("");
|
|
1794
|
+
setThinkingContent("");
|
|
1795
|
+
setActiveToolUses([]);
|
|
1796
|
+
setActiveToolResults([]);
|
|
1797
|
+
setError(null);
|
|
1798
|
+
setNotice(null);
|
|
1799
|
+
}
|
|
1606
1800
|
const effort = cfg.effort ?? "medium";
|
|
1607
1801
|
useInput((char, key) => {
|
|
1608
1802
|
if (key.ctrl && char === "c") {
|
|
@@ -1649,6 +1843,44 @@ function useKeyboard(opts) {
|
|
|
1649
1843
|
}
|
|
1650
1844
|
return;
|
|
1651
1845
|
}
|
|
1846
|
+
if (state === "sessions") {
|
|
1847
|
+
if (key.upArrow) {
|
|
1848
|
+
setCursor((i) => Math.max(0, i - 1));
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
if (key.downArrow) {
|
|
1852
|
+
setCursor((i) => Math.min(sessions.length - 1, i + 1));
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
if (key.escape) {
|
|
1856
|
+
setState("ready");
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
if ((char === "d" || char === "x" || key.delete || key.backspace) && sessions[cursor]) {
|
|
1860
|
+
const meta = sessions[cursor];
|
|
1861
|
+
deleteSession(meta.id);
|
|
1862
|
+
const next = listSessions();
|
|
1863
|
+
setSessions(next);
|
|
1864
|
+
setCursor((i) => Math.max(0, Math.min(i, next.length - 1)));
|
|
1865
|
+
setNotice(`deleted \xB7 ${meta.title}`);
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
if (key.return && sessions[cursor]) {
|
|
1869
|
+
const meta = sessions[cursor];
|
|
1870
|
+
const history = loadSession(meta.id);
|
|
1871
|
+
setAgentHistory(history);
|
|
1872
|
+
setMessages(toDisplayMessages(history));
|
|
1873
|
+
setStreamingContent("");
|
|
1874
|
+
setThinkingContent("");
|
|
1875
|
+
setActiveToolUses([]);
|
|
1876
|
+
setActiveToolResults([]);
|
|
1877
|
+
setError(null);
|
|
1878
|
+
setSessionId(meta.id);
|
|
1879
|
+
setNotice(`resumed \xB7 ${meta.title}`);
|
|
1880
|
+
setState("ready");
|
|
1881
|
+
}
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1652
1884
|
if (state === "ready" && pendingPermissionRef.current) {
|
|
1653
1885
|
if (key.upArrow) {
|
|
1654
1886
|
setPermissionCursor((i) => Math.max(0, i - 1));
|
|
@@ -1713,16 +1945,30 @@ function useKeyboard(opts) {
|
|
|
1713
1945
|
setCursor(() => Math.max(0, models.findIndex((m) => m === cfg.model)));
|
|
1714
1946
|
setState("models");
|
|
1715
1947
|
} else if (trimmed === "/clear") {
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1948
|
+
clearSession();
|
|
1949
|
+
} else if (trimmed === "/new") {
|
|
1950
|
+
if (agentHistory.length) setNotice("session saved");
|
|
1951
|
+
setSessionId(newSessionId());
|
|
1952
|
+
clearSession();
|
|
1953
|
+
} else if (trimmed === "/sessions") {
|
|
1954
|
+
setSessions(listSessions());
|
|
1955
|
+
setCursor(() => 0);
|
|
1956
|
+
setState("sessions");
|
|
1723
1957
|
} else if (trimmed === "/exit") {
|
|
1724
1958
|
exit();
|
|
1725
1959
|
} else if (trimmed) {
|
|
1960
|
+
setNotice(null);
|
|
1961
|
+
if (!agentHistory.length && cfg.model) {
|
|
1962
|
+
const id = sessionId;
|
|
1963
|
+
const model = cfg.model;
|
|
1964
|
+
void (async () => {
|
|
1965
|
+
try {
|
|
1966
|
+
const title = await summarizeMessage(model, trimmed);
|
|
1967
|
+
persistSession(id, [{ role: "user", content: trimmed }], title);
|
|
1968
|
+
} catch {
|
|
1969
|
+
}
|
|
1970
|
+
})();
|
|
1971
|
+
}
|
|
1726
1972
|
sendMessage(trimmed);
|
|
1727
1973
|
}
|
|
1728
1974
|
setInput(() => "");
|
|
@@ -1782,7 +2028,7 @@ async function checkForUpdate() {
|
|
|
1782
2028
|
}
|
|
1783
2029
|
|
|
1784
2030
|
// src/ui/App.tsx
|
|
1785
|
-
import { Fragment as Fragment2, jsx as
|
|
2031
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1786
2032
|
function App() {
|
|
1787
2033
|
const { exit } = useApp();
|
|
1788
2034
|
const cwd = process.cwd().replace(homedir2(), "~").split(sep).join("/");
|
|
@@ -1793,6 +2039,9 @@ function App() {
|
|
|
1793
2039
|
const [state, setState] = useState4("loading");
|
|
1794
2040
|
const [cursor, setCursor] = useState4(0);
|
|
1795
2041
|
const [updateAvailable, setUpdateAvailable] = useState4(null);
|
|
2042
|
+
const [sessionId, setSessionId] = useState4(() => newSessionId());
|
|
2043
|
+
const [sessions, setSessions] = useState4([]);
|
|
2044
|
+
const [notice, setNotice] = useState4(null);
|
|
1796
2045
|
const [input, setInput] = useState4("");
|
|
1797
2046
|
const [paletteCursor, setPaletteCursor] = useState4(0);
|
|
1798
2047
|
const [filePickerCursor, setFilePickerCursor] = useState4(0);
|
|
@@ -1802,6 +2051,9 @@ function App() {
|
|
|
1802
2051
|
if (v) setUpdateAvailable(v);
|
|
1803
2052
|
});
|
|
1804
2053
|
}, []);
|
|
2054
|
+
useEffect3(() => {
|
|
2055
|
+
if (agent.agentHistory.length) persistSession(sessionId, agent.agentHistory);
|
|
2056
|
+
}, [agent.agentHistory, sessionId]);
|
|
1805
2057
|
useEffect3(() => {
|
|
1806
2058
|
listModels().then((m) => {
|
|
1807
2059
|
setModels(m);
|
|
@@ -1831,26 +2083,18 @@ function App() {
|
|
|
1831
2083
|
cfg,
|
|
1832
2084
|
setCfg,
|
|
1833
2085
|
setActiveCtx,
|
|
1834
|
-
|
|
1835
|
-
permissionCursor: agent.permissionCursor,
|
|
1836
|
-
setPermissionCursor: agent.setPermissionCursor,
|
|
1837
|
-
resolvePermission: agent.resolvePermission,
|
|
1838
|
-
busyRef: agent.busyRef,
|
|
1839
|
-
abortRef: agent.abortRef,
|
|
2086
|
+
agent,
|
|
1840
2087
|
input,
|
|
1841
2088
|
setInput,
|
|
1842
2089
|
paletteCursor,
|
|
1843
2090
|
setPaletteCursor,
|
|
1844
2091
|
filePickerCursor,
|
|
1845
2092
|
setFilePickerCursor,
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
setActiveToolUses: agent.setActiveToolUses,
|
|
1852
|
-
setActiveToolResults: agent.setActiveToolResults,
|
|
1853
|
-
setError: agent.setError
|
|
2093
|
+
sessionId,
|
|
2094
|
+
setSessionId,
|
|
2095
|
+
sessions,
|
|
2096
|
+
setSessions,
|
|
2097
|
+
setNotice
|
|
1854
2098
|
});
|
|
1855
2099
|
const effort = cfg.effort ?? "medium";
|
|
1856
2100
|
const contextWarning = (() => {
|
|
@@ -1860,11 +2104,11 @@ function App() {
|
|
|
1860
2104
|
if (used < activeCtx * 0.7) return null;
|
|
1861
2105
|
return Math.round(used / activeCtx * 100);
|
|
1862
2106
|
})();
|
|
1863
|
-
return /* @__PURE__ */
|
|
1864
|
-
/* @__PURE__ */
|
|
1865
|
-
updateAvailable && /* @__PURE__ */
|
|
1866
|
-
state === "loading" && !agent.error && /* @__PURE__ */
|
|
1867
|
-
agent.error && state !== "ready" && /* @__PURE__ */
|
|
2107
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingX: 1, children: [
|
|
2108
|
+
/* @__PURE__ */ jsx10(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error }),
|
|
2109
|
+
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` }) }),
|
|
2110
|
+
state === "loading" && !agent.error && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "connecting to ollama\u2026" }) }),
|
|
2111
|
+
agent.error && state !== "ready" && /* @__PURE__ */ jsx10(
|
|
1868
2112
|
ChatView,
|
|
1869
2113
|
{
|
|
1870
2114
|
messages: [],
|
|
@@ -1874,12 +2118,12 @@ function App() {
|
|
|
1874
2118
|
error: agent.error
|
|
1875
2119
|
}
|
|
1876
2120
|
),
|
|
1877
|
-
state === "select-model" && /* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1879
|
-
/* @__PURE__ */
|
|
1880
|
-
models.length > 0 && /* @__PURE__ */
|
|
2121
|
+
state === "select-model" && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginLeft: 2, children: [
|
|
2122
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "no model configured \u2014 select one" }),
|
|
2123
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(ModelList, { models, cursor }) }),
|
|
2124
|
+
models.length > 0 && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2191\u2193 navigate enter select ctrl+c quit" }) })
|
|
1881
2125
|
] }),
|
|
1882
|
-
state === "models" && /* @__PURE__ */
|
|
2126
|
+
state === "models" && /* @__PURE__ */ jsx10(
|
|
1883
2127
|
ModelsView,
|
|
1884
2128
|
{
|
|
1885
2129
|
models,
|
|
@@ -1889,8 +2133,10 @@ function App() {
|
|
|
1889
2133
|
effort
|
|
1890
2134
|
}
|
|
1891
2135
|
),
|
|
1892
|
-
state === "
|
|
1893
|
-
|
|
2136
|
+
state === "sessions" && /* @__PURE__ */ jsx10(SessionsView, { sessions, cursor }),
|
|
2137
|
+
state === "ready" && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2138
|
+
notice && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "green", children: `\u2713 ${notice}` }) }),
|
|
2139
|
+
/* @__PURE__ */ jsx10(
|
|
1894
2140
|
ChatView,
|
|
1895
2141
|
{
|
|
1896
2142
|
messages: agent.messages,
|
|
@@ -1905,15 +2151,15 @@ function App() {
|
|
|
1905
2151
|
activeToolResults: agent.activeToolResults
|
|
1906
2152
|
}
|
|
1907
2153
|
),
|
|
1908
|
-
input.startsWith("/") && /* @__PURE__ */
|
|
1909
|
-
contextWarning !== null && /* @__PURE__ */
|
|
2154
|
+
input.startsWith("/") && /* @__PURE__ */ jsx10(CommandPalette, { filter: input, cursor: paletteCursor }),
|
|
2155
|
+
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
2156
|
!input.startsWith("/") && (() => {
|
|
1911
2157
|
const m = parseMention(input);
|
|
1912
2158
|
if (!m) return null;
|
|
1913
|
-
return /* @__PURE__ */
|
|
2159
|
+
return /* @__PURE__ */ jsx10(FilePicker, { matches: searchFiles(process.cwd(), m.query), cursor: filePickerCursor });
|
|
1914
2160
|
})(),
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
!agent.busy && /* @__PURE__ */
|
|
2161
|
+
/* @__PURE__ */ jsx10(InputBar, { input, disabled: agent.busy, processingLabel: agent.processingLabel }),
|
|
2162
|
+
!agent.busy && /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "type / to see commands" }) })
|
|
1917
2163
|
] })
|
|
1918
2164
|
] });
|
|
1919
2165
|
}
|