miii-agent 0.1.1 → 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.
Files changed (2) hide show
  1. package/dist/cli.js +407 -141
  2. 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 Box9, Text as Text9, useApp } from "ink";
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
 
@@ -195,8 +195,8 @@ function ModelList({ models, cursor, activeModel, showActive }) {
195
195
  "<model>"
196
196
  ] });
197
197
  }
198
- return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: models.map((m, i) => /* @__PURE__ */ jsxs2(Text2, { color: i === cursor ? "white" : void 0, dimColor: i !== cursor, children: [
199
- i === cursor ? "\u25B6 " : " ",
198
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: models.map((m, i) => /* @__PURE__ */ jsxs2(Text2, { color: i === cursor ? "blue" : void 0, dimColor: i !== cursor, children: [
199
+ i === cursor ? "\u276F " : " ",
200
200
  m,
201
201
  showActive && m === activeModel ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " (active)" }) : null
202
202
  ] }, m)) });
@@ -266,35 +266,72 @@ function ModelsView({ models, cursor, model, ollamaHost, effort }) {
266
266
  ] });
267
267
  }
268
268
 
269
- // src/ui/CommandPalette.tsx
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
- return /* @__PURE__ */ jsxs5(
281
- Box5,
313
+ const nameWidth = Math.max(...filtered.map((c) => c.name.length));
314
+ return /* @__PURE__ */ jsxs6(
315
+ Box6,
282
316
  {
283
317
  flexDirection: "column",
284
- borderStyle: "single",
285
- borderColor: "white dim",
318
+ borderStyle: "round",
319
+ borderColor: "gray",
286
320
  marginX: 1,
287
321
  marginBottom: 0,
288
322
  paddingX: 1,
289
323
  children: [
290
324
  filtered.map((cmd, i) => {
291
325
  const active = i === cursor;
292
- return /* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
293
- /* @__PURE__ */ jsx5(Text5, { bold: active, color: active ? "white" : void 0, dimColor: !active, children: cmd.name }),
294
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: cmd.description })
326
+ return /* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
327
+ /* @__PURE__ */ jsxs6(Text6, { bold: active, color: active ? "blue" : void 0, dimColor: !active, children: [
328
+ active ? "\u276F " : " ",
329
+ cmd.name.padEnd(nameWidth)
330
+ ] }),
331
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: cmd.description })
295
332
  ] }, cmd.name);
296
333
  }),
297
- /* @__PURE__ */ jsx5(Box5, { marginTop: 0, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191\u2193 navigate tab/enter autocomplete esc dismiss" }) })
334
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 0, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2191\u2193 navigate tab/enter autocomplete esc dismiss" }) })
298
335
  ]
299
336
  }
300
337
  );
@@ -303,11 +340,150 @@ function filteredCommands(filter) {
303
340
  return COMMANDS.filter((c) => c.name.startsWith(filter));
304
341
  }
305
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
+
306
482
  // src/ui/FilePicker.tsx
307
- import { Box as Box6, Text as Text6 } from "ink";
308
- import { readdirSync } from "fs";
309
- import { join as join2, relative } from "path";
310
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
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";
311
487
  var IGNORE = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage", ".miii"]);
312
488
  var MAX_RESULTS = 10;
313
489
  var MAX_SCAN = 2e3;
@@ -320,13 +496,13 @@ function listFiles(cwd) {
320
496
  const dir = stack.pop();
321
497
  let entries;
322
498
  try {
323
- entries = readdirSync(dir, { withFileTypes: true });
499
+ entries = readdirSync2(dir, { withFileTypes: true });
324
500
  } catch {
325
501
  continue;
326
502
  }
327
503
  for (const e of entries) {
328
504
  if (IGNORE.has(e.name) || e.name.startsWith(".")) continue;
329
- const full = join2(dir, e.name);
505
+ const full = join3(dir, e.name);
330
506
  if (e.isDirectory()) stack.push(full);
331
507
  else if (e.isFile()) out.push(relative(cwd, full));
332
508
  if (out.length >= MAX_SCAN) break;
@@ -360,36 +536,36 @@ function searchFiles(cwd, query) {
360
536
  }
361
537
  function FilePicker({ matches, cursor }) {
362
538
  if (matches.length === 0) return null;
363
- return /* @__PURE__ */ jsxs6(
364
- Box6,
539
+ return /* @__PURE__ */ jsxs7(
540
+ Box7,
365
541
  {
366
542
  flexDirection: "column",
367
- borderStyle: "single",
368
- borderColor: "white dim",
543
+ borderStyle: "round",
544
+ borderColor: "gray",
369
545
  marginX: 1,
370
546
  marginBottom: 0,
371
547
  paddingX: 1,
372
548
  children: [
373
549
  matches.map((f, i) => {
374
550
  const active = i === cursor;
375
- return /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { bold: active, color: active ? "white" : void 0, dimColor: !active, children: [
376
- active ? "\u203A " : " ",
551
+ return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs7(Text7, { bold: active, color: active ? "blue" : void 0, dimColor: !active, children: [
552
+ active ? "\u276F " : " ",
377
553
  f
378
554
  ] }) }, f);
379
555
  }),
380
- /* @__PURE__ */ jsx6(Box6, { marginTop: 0, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2191\u2193 navigate tab insert esc dismiss" }) })
556
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 0, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2191\u2193 navigate tab insert esc dismiss" }) })
381
557
  ]
382
558
  }
383
559
  );
384
560
  }
385
561
 
386
562
  // src/ui/ChatView.tsx
387
- import { Box as Box8, Text as Text8 } from "ink";
563
+ import { Box as Box9, Text as Text9 } from "ink";
388
564
 
389
565
  // src/ui/ThinkingBlock.tsx
390
566
  import { useState as useState2, useEffect as useEffect2 } from "react";
391
- import { Box as Box7, Text as Text7 } from "ink";
392
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
567
+ import { Box as Box8, Text as Text8 } from "ink";
568
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
393
569
  var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
394
570
  var globalThinkingVisible = false;
395
571
  var listeners = /* @__PURE__ */ new Set();
@@ -415,25 +591,35 @@ function ThinkingBlock({ content }) {
415
591
  const t = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length), 80);
416
592
  return () => clearInterval(t);
417
593
  }, []);
418
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
419
- /* @__PURE__ */ jsxs7(Box7, { children: [
420
- /* @__PURE__ */ jsxs7(Text7, { color: "blue", children: [
594
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
595
+ /* @__PURE__ */ jsxs8(Box8, { children: [
596
+ /* @__PURE__ */ jsxs8(Text8, { color: "blue", children: [
421
597
  FRAMES[frame],
422
598
  " "
423
599
  ] }),
424
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, italic: true, children: "thinking\u2026" }),
425
- /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
600
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: "thinking\u2026" }),
601
+ /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
426
602
  " \xB7 ctrl+t to ",
427
603
  visible ? "hide" : "show",
428
604
  " thoughts"
429
605
  ] })
430
606
  ] }),
431
- visible && content ? /* @__PURE__ */ jsx7(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, italic: true, children: content }) }) : null
607
+ visible && content ? /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: content }) }) : null
432
608
  ] });
433
609
  }
434
610
 
611
+ // src/ui/constants.ts
612
+ var EMPTY_STATE_HINTS = [
613
+ "\u2022 explain @file \u2014 reference a file with @",
614
+ "\u2022 /models \u2014 switch model or effort",
615
+ "\u2022 /new \u2014 start a new chat",
616
+ "\u2022 /sessions \u2014 view saved chats",
617
+ "\u2022 ctrl+t \u2014 toggle thinking"
618
+ ];
619
+ var EMPTY_STATE_TITLE = "Ask anything, or try:";
620
+
435
621
  // src/ui/ChatView.tsx
436
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
622
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
437
623
  function formatTokens(n) {
438
624
  if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1) + "k";
439
625
  return String(n);
@@ -459,27 +645,27 @@ function FileEditBlock({
459
645
  const MAX = 16;
460
646
  const shown = previewLines.slice(0, MAX);
461
647
  const extra = previewLines.length - shown.length;
462
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, children: [
463
- /* @__PURE__ */ jsxs8(Box8, { children: [
464
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25CF " }),
465
- /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
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: [
466
652
  label,
467
653
  " "
468
654
  ] }),
469
- /* @__PURE__ */ jsx8(Text8, { children: "(" }),
470
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: path }),
471
- /* @__PURE__ */ jsx8(Text8, { children: ")" })
655
+ /* @__PURE__ */ jsx9(Text9, { children: "(" }),
656
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: path }),
657
+ /* @__PURE__ */ jsx9(Text9, { children: ")" })
472
658
  ] }),
473
- /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
659
+ /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
474
660
  "\u23BF ",
475
661
  removed > 0 ? `Added ${added} lines, removed ${removed} lines` : `Added ${added} lines`
476
662
  ] }) }),
477
- shown.map((ln, i) => /* @__PURE__ */ jsx8(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsxs8(Text8, { color: ln.sign === "+" ? "green" : ln.sign === "-" ? "red" : void 0, dimColor: ln.sign === " ", children: [
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: [
478
664
  ln.sign,
479
665
  " ",
480
666
  ln.text
481
667
  ] }) }, i)),
482
- extra > 0 && /* @__PURE__ */ jsx8(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
668
+ extra > 0 && /* @__PURE__ */ jsx9(Box9, { marginLeft: 4, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
483
669
  "\u2026 ",
484
670
  extra,
485
671
  " more lines"
@@ -552,7 +738,7 @@ function ToolResultBlock({ result, toolName }) {
552
738
  const lines = content.split("\n");
553
739
  const showMulti = (toolName === "run_bash" || toolName === "grep" || toolName === "glob" || result.is_error) && lines.length > 1;
554
740
  if (!showMulti) {
555
- return /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsxs8(Text8, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
741
+ return /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
556
742
  "\u23BF ",
557
743
  summarizeResult(result, toolName)
558
744
  ] }) });
@@ -561,13 +747,13 @@ function ToolResultBlock({ result, toolName }) {
561
747
  const MAX_LINE_WIDTH = 200;
562
748
  const shown = lines.slice(0, MAX_LINES).map((l) => truncate(l, MAX_LINE_WIDTH));
563
749
  const extra = lines.length - shown.length;
564
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, children: [
565
- /* @__PURE__ */ jsxs8(Text8, { color: result.is_error ? "red" : void 0, dimColor: !result.is_error, children: [
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: [
566
752
  "\u23BF ",
567
753
  summarizeResult(result, toolName)
568
754
  ] }),
569
- shown.map((ln, i) => /* @__PURE__ */ jsx8(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsx8(Text8, { color: result.is_error ? "red" : void 0, dimColor: true, children: ln || " " }) }, i)),
570
- extra > 0 && /* @__PURE__ */ jsx8(Box8, { marginLeft: 4, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
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: [
571
757
  "\u2026 ",
572
758
  extra,
573
759
  " more lines"
@@ -580,7 +766,7 @@ function ToolUseLine({ use, result }) {
580
766
  const content = input.content ?? "";
581
767
  const added = countLines(content);
582
768
  const preview = content.split("\n").map((t) => ({ sign: "+", text: t }));
583
- return /* @__PURE__ */ jsx8(FileEditBlock, { label: "Write", path: input.path ?? "", added, removed: 0, previewLines: preview });
769
+ return /* @__PURE__ */ jsx9(FileEditBlock, { label: "Write", path: input.path ?? "", added, removed: 0, previewLines: preview });
584
770
  }
585
771
  if (use.name === "edit_file" && !result?.is_error) {
586
772
  const input = use.input;
@@ -592,34 +778,34 @@ function ToolUseLine({ use, result }) {
592
778
  ...oldS.split("\n").map((t) => ({ sign: "-", text: t })),
593
779
  ...newS.split("\n").map((t) => ({ sign: "+", text: t }))
594
780
  ];
595
- return /* @__PURE__ */ jsx8(FileEditBlock, { label: "Update", path: input.path ?? "", added, removed, previewLines: preview });
781
+ return /* @__PURE__ */ jsx9(FileEditBlock, { label: "Update", path: input.path ?? "", added, removed, previewLines: preview });
596
782
  }
597
783
  const { label, arg } = toolHeader(use);
598
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, children: [
599
- /* @__PURE__ */ jsxs8(Box8, { children: [
600
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25CF " }),
601
- /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
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: [
602
788
  label,
603
789
  " "
604
790
  ] }),
605
- /* @__PURE__ */ jsx8(Text8, { children: "(" }),
606
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: arg }),
607
- /* @__PURE__ */ jsx8(Text8, { children: ")" })
791
+ /* @__PURE__ */ jsx9(Text9, { children: "(" }),
792
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: arg }),
793
+ /* @__PURE__ */ jsx9(Text9, { children: ")" })
608
794
  ] }),
609
- result && /* @__PURE__ */ jsx8(ToolResultBlock, { result, toolName: use.name })
795
+ result && /* @__PURE__ */ jsx9(ToolResultBlock, { result, toolName: use.name })
610
796
  ] });
611
797
  }
612
798
  function AssistantMessage({ msg }) {
613
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
614
- msg.content && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", children: [
615
- /* @__PURE__ */ jsx8(Text8, { color: "white", children: "\u25CF " }),
616
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx8(Text8, { children: msg.content }) })
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 }) })
617
803
  ] }),
618
804
  msg.tool_uses?.map((u) => {
619
805
  const r = msg.tool_results?.find((x) => x.tool_use_id === u.id);
620
- return /* @__PURE__ */ jsx8(ToolUseLine, { use: u, result: r }, u.id);
806
+ return /* @__PURE__ */ jsx9(ToolUseLine, { use: u, result: r }, u.id);
621
807
  }),
622
- msg.tokens && /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
808
+ msg.tokens && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
623
809
  `\u21B3 Completed \xB7 ${formatTokens(msg.tokens.prompt_eval + msg.tokens.eval)} tokens`,
624
810
  msg.duration != null ? ` \xB7 ${formatDuration(msg.duration)}` : ""
625
811
  ] }) })
@@ -642,20 +828,21 @@ function summarizeInput(input) {
642
828
  return "";
643
829
  }
644
830
  function PermissionPrompt({ req, cursor }) {
831
+ const label = TOOL_LABEL[req.toolName] ?? req.toolName;
645
832
  const options = [
646
833
  { label: "Yes", key: "yes" },
647
- { label: "No, and tell me what to do differently", key: "no" }
834
+ { label: "No", key: "no" }
648
835
  ];
649
836
  const summary = summarizeInput(req.input);
650
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
651
- /* @__PURE__ */ jsx8(Text8, { color: "blue", bold: true, children: "Tool use" }),
652
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
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: [
653
840
  "Allow ",
654
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: req.toolName }),
841
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: label }),
655
842
  "?"
656
843
  ] }) }),
657
- summary && /* @__PURE__ */ jsx8(Box8, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: summary }) }),
658
- /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsxs8(Text8, { color: i === cursor ? "blue" : void 0, children: [
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: [
659
846
  i === cursor ? "\u276F " : " ",
660
847
  i + 1,
661
848
  ". ",
@@ -675,26 +862,34 @@ function ChatView({
675
862
  activeToolUses,
676
863
  activeToolResults
677
864
  }) {
678
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 1, marginBottom: 1, children: [
865
+ const empty = messages.length === 0 && !streaming && !thinking && !pendingPermission && !error;
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: [
870
+ " ",
871
+ h
872
+ ] }, i))
873
+ ] }),
679
874
  messages.map(
680
- (msg, i) => msg.role === "user" ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginBottom: 1, children: [
681
- /* @__PURE__ */ jsx8(Text8, { color: "blue", children: "\u25CF " }),
682
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx8(Text8, { children: msg.content }) })
683
- ] }, i) : /* @__PURE__ */ jsx8(AssistantMessage, { msg }, i)
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)
684
879
  ),
685
- thinking && /* @__PURE__ */ jsx8(ThinkingBlock, { content: thinkingContent }),
686
- streaming && streamingContent && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginBottom: 1, children: [
687
- /* @__PURE__ */ jsx8(Text8, { color: "white", children: "\u25CF " }),
688
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, children: /* @__PURE__ */ jsx8(Text8, { children: streamingContent }) })
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 }) })
689
884
  ] }),
690
885
  activeToolUses?.map((u) => {
691
886
  const r = activeToolResults?.find((x) => x.tool_use_id === u.id);
692
- return /* @__PURE__ */ jsx8(ToolUseLine, { use: u, result: r }, u.id);
887
+ return /* @__PURE__ */ jsx9(ToolUseLine, { use: u, result: r }, u.id);
693
888
  }),
694
- pendingPermission && /* @__PURE__ */ jsx8(PermissionPrompt, { req: pendingPermission, cursor: permissionCursor }),
695
- error && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", marginBottom: 1, children: [
696
- /* @__PURE__ */ jsx8(Text8, { color: "red", children: "\u25CF " }),
697
- /* @__PURE__ */ jsx8(Text8, { color: "red", children: error })
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 })
698
893
  ] })
699
894
  ] });
700
895
  }
@@ -703,7 +898,7 @@ function ChatView({
703
898
  import { useState as useState3, useRef } from "react";
704
899
 
705
900
  // src/tools/edit_file.ts
706
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
901
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
707
902
  var edit_file = {
708
903
  name: "edit_file",
709
904
  description: "Replace an exact string in a file. old_str must be unique.",
@@ -717,7 +912,7 @@ var edit_file = {
717
912
  required: ["path", "old_str", "new_str"]
718
913
  },
719
914
  handler: ({ path, old_str, new_str }) => {
720
- const src = readFileSync2(path, "utf-8");
915
+ const src = readFileSync3(path, "utf-8");
721
916
  const first = src.indexOf(old_str);
722
917
  if (first === -1) {
723
918
  return { content: `old_str not found in ${path}`, is_error: true };
@@ -725,13 +920,13 @@ var edit_file = {
725
920
  if (src.indexOf(old_str, first + 1) !== -1) {
726
921
  return { content: `old_str not unique in ${path}`, is_error: true };
727
922
  }
728
- writeFileSync2(path, src.slice(0, first) + new_str + src.slice(first + old_str.length), "utf-8");
923
+ writeFileSync3(path, src.slice(0, first) + new_str + src.slice(first + old_str.length), "utf-8");
729
924
  return { content: `Edited ${path}` };
730
925
  }
731
926
  };
732
927
 
733
928
  // src/tools/read_file.ts
734
- import { readFileSync as readFileSync3 } from "fs";
929
+ import { readFileSync as readFileSync4 } from "fs";
735
930
  var read_file = {
736
931
  name: "read_file",
737
932
  description: "Read entire file contents as UTF-8 text.",
@@ -745,7 +940,7 @@ var read_file = {
745
940
  handler: ({ path }) => {
746
941
  try {
747
942
  const MAX = 2e5;
748
- const raw = readFileSync3(path, "utf-8");
943
+ const raw = readFileSync4(path, "utf-8");
749
944
  const truncated = raw.length > MAX;
750
945
  const body = truncated ? raw.slice(0, MAX) + `
751
946
  [truncated: ${raw.length - MAX} more chars]` : raw;
@@ -757,7 +952,7 @@ var read_file = {
757
952
  };
758
953
 
759
954
  // src/tools/write_file.ts
760
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
955
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
761
956
  import { dirname } from "path";
762
957
  var write_file = {
763
958
  name: "write_file",
@@ -772,8 +967,8 @@ var write_file = {
772
967
  },
773
968
  handler: ({ path, content }) => {
774
969
  try {
775
- mkdirSync2(dirname(path), { recursive: true });
776
- writeFileSync3(path, content, "utf-8");
970
+ mkdirSync3(dirname(path), { recursive: true });
971
+ writeFileSync4(path, content, "utf-8");
777
972
  return { content: `Wrote ${path} (${content.length} bytes)` };
778
973
  } catch (err) {
779
974
  return { content: err instanceof Error ? err.message : String(err), is_error: true };
@@ -1562,19 +1757,28 @@ function useKeyboard(opts) {
1562
1757
  cfg,
1563
1758
  setCfg,
1564
1759
  setActiveCtx,
1565
- pendingPermissionRef,
1566
- permissionCursor,
1567
- setPermissionCursor,
1568
- resolvePermission,
1569
- busyRef,
1570
- abortRef,
1760
+ agent,
1571
1761
  input,
1572
1762
  setInput,
1573
1763
  paletteCursor,
1574
1764
  setPaletteCursor,
1575
1765
  filePickerCursor,
1576
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,
1577
1780
  sendMessage,
1781
+ agentHistory,
1578
1782
  setMessages,
1579
1783
  setAgentHistory,
1580
1784
  setStreamingContent,
@@ -1582,7 +1786,17 @@ function useKeyboard(opts) {
1582
1786
  setActiveToolUses,
1583
1787
  setActiveToolResults,
1584
1788
  setError
1585
- } = opts;
1789
+ } = agent;
1790
+ function clearSession() {
1791
+ setMessages(() => []);
1792
+ setAgentHistory([]);
1793
+ setStreamingContent("");
1794
+ setThinkingContent("");
1795
+ setActiveToolUses([]);
1796
+ setActiveToolResults([]);
1797
+ setError(null);
1798
+ setNotice(null);
1799
+ }
1586
1800
  const effort = cfg.effort ?? "medium";
1587
1801
  useInput((char, key) => {
1588
1802
  if (key.ctrl && char === "c") {
@@ -1629,6 +1843,44 @@ function useKeyboard(opts) {
1629
1843
  }
1630
1844
  return;
1631
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
+ }
1632
1884
  if (state === "ready" && pendingPermissionRef.current) {
1633
1885
  if (key.upArrow) {
1634
1886
  setPermissionCursor((i) => Math.max(0, i - 1));
@@ -1693,16 +1945,30 @@ function useKeyboard(opts) {
1693
1945
  setCursor(() => Math.max(0, models.findIndex((m) => m === cfg.model)));
1694
1946
  setState("models");
1695
1947
  } else if (trimmed === "/clear") {
1696
- setMessages(() => []);
1697
- setAgentHistory([]);
1698
- setStreamingContent("");
1699
- setThinkingContent("");
1700
- setActiveToolUses([]);
1701
- setActiveToolResults([]);
1702
- setError(null);
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");
1703
1957
  } else if (trimmed === "/exit") {
1704
1958
  exit();
1705
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
+ }
1706
1972
  sendMessage(trimmed);
1707
1973
  }
1708
1974
  setInput(() => "");
@@ -1762,7 +2028,7 @@ async function checkForUpdate() {
1762
2028
  }
1763
2029
 
1764
2030
  // src/ui/App.tsx
1765
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2031
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1766
2032
  function App() {
1767
2033
  const { exit } = useApp();
1768
2034
  const cwd = process.cwd().replace(homedir2(), "~").split(sep).join("/");
@@ -1773,6 +2039,9 @@ function App() {
1773
2039
  const [state, setState] = useState4("loading");
1774
2040
  const [cursor, setCursor] = useState4(0);
1775
2041
  const [updateAvailable, setUpdateAvailable] = useState4(null);
2042
+ const [sessionId, setSessionId] = useState4(() => newSessionId());
2043
+ const [sessions, setSessions] = useState4([]);
2044
+ const [notice, setNotice] = useState4(null);
1776
2045
  const [input, setInput] = useState4("");
1777
2046
  const [paletteCursor, setPaletteCursor] = useState4(0);
1778
2047
  const [filePickerCursor, setFilePickerCursor] = useState4(0);
@@ -1782,6 +2051,9 @@ function App() {
1782
2051
  if (v) setUpdateAvailable(v);
1783
2052
  });
1784
2053
  }, []);
2054
+ useEffect3(() => {
2055
+ if (agent.agentHistory.length) persistSession(sessionId, agent.agentHistory);
2056
+ }, [agent.agentHistory, sessionId]);
1785
2057
  useEffect3(() => {
1786
2058
  listModels().then((m) => {
1787
2059
  setModels(m);
@@ -1811,26 +2083,18 @@ function App() {
1811
2083
  cfg,
1812
2084
  setCfg,
1813
2085
  setActiveCtx,
1814
- pendingPermissionRef: agent.pendingPermissionRef,
1815
- permissionCursor: agent.permissionCursor,
1816
- setPermissionCursor: agent.setPermissionCursor,
1817
- resolvePermission: agent.resolvePermission,
1818
- busyRef: agent.busyRef,
1819
- abortRef: agent.abortRef,
2086
+ agent,
1820
2087
  input,
1821
2088
  setInput,
1822
2089
  paletteCursor,
1823
2090
  setPaletteCursor,
1824
2091
  filePickerCursor,
1825
2092
  setFilePickerCursor,
1826
- sendMessage: agent.sendMessage,
1827
- setMessages: agent.setMessages,
1828
- setAgentHistory: agent.setAgentHistory,
1829
- setStreamingContent: agent.setStreamingContent,
1830
- setThinkingContent: agent.setThinkingContent,
1831
- setActiveToolUses: agent.setActiveToolUses,
1832
- setActiveToolResults: agent.setActiveToolResults,
1833
- setError: agent.setError
2093
+ sessionId,
2094
+ setSessionId,
2095
+ sessions,
2096
+ setSessions,
2097
+ setNotice
1834
2098
  });
1835
2099
  const effort = cfg.effort ?? "medium";
1836
2100
  const contextWarning = (() => {
@@ -1840,11 +2104,11 @@ function App() {
1840
2104
  if (used < activeCtx * 0.7) return null;
1841
2105
  return Math.round(used / activeCtx * 100);
1842
2106
  })();
1843
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingX: 1, children: [
1844
- /* @__PURE__ */ jsx9(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error }),
1845
- updateAvailable && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: npm i -g miii-agent` }) }),
1846
- state === "loading" && !agent.error && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "connecting to ollama\u2026" }) }),
1847
- agent.error && state !== "ready" && /* @__PURE__ */ jsx9(
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(
1848
2112
  ChatView,
1849
2113
  {
1850
2114
  messages: [],
@@ -1854,12 +2118,12 @@ function App() {
1854
2118
  error: agent.error
1855
2119
  }
1856
2120
  ),
1857
- state === "select-model" && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
1858
- /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "no model configured \u2014 select one" }),
1859
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(ModelList, { models, cursor }) }),
1860
- models.length > 0 && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2191\u2193 navigate enter select ctrl+c quit" }) })
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" }) })
1861
2125
  ] }),
1862
- state === "models" && /* @__PURE__ */ jsx9(
2126
+ state === "models" && /* @__PURE__ */ jsx10(
1863
2127
  ModelsView,
1864
2128
  {
1865
2129
  models,
@@ -1869,8 +2133,10 @@ function App() {
1869
2133
  effort
1870
2134
  }
1871
2135
  ),
1872
- state === "ready" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1873
- /* @__PURE__ */ jsx9(
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(
1874
2140
  ChatView,
1875
2141
  {
1876
2142
  messages: agent.messages,
@@ -1885,15 +2151,15 @@ function App() {
1885
2151
  activeToolResults: agent.activeToolResults
1886
2152
  }
1887
2153
  ),
1888
- input.startsWith("/") && /* @__PURE__ */ jsx9(CommandPalette, { filter: input, cursor: paletteCursor }),
1889
- contextWarning !== null && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: `\u26A0 context ${contextWarning}% full \u2014 run /clear and start fresh` }) }),
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` }) }),
1890
2156
  !input.startsWith("/") && (() => {
1891
2157
  const m = parseMention(input);
1892
2158
  if (!m) return null;
1893
- return /* @__PURE__ */ jsx9(FilePicker, { matches: searchFiles(process.cwd(), m.query), cursor: filePickerCursor });
2159
+ return /* @__PURE__ */ jsx10(FilePicker, { matches: searchFiles(process.cwd(), m.query), cursor: filePickerCursor });
1894
2160
  })(),
1895
- /* @__PURE__ */ jsx9(InputBar, { input, disabled: agent.busy, processingLabel: agent.processingLabel }),
1896
- !agent.busy && /* @__PURE__ */ jsx9(Box9, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "type / to see commands" }) })
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" }) })
1897
2163
  ] })
1898
2164
  ] });
1899
2165
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miii-agent",
3
- "version": "0.1.1",
3
+ "version": "0.1.5",
4
4
  "description": "Terminal AI coding agent powered by Ollama",
5
5
  "type": "module",
6
6
  "bin": {