mini-coder 0.0.5 → 0.0.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/mc.js CHANGED
@@ -2,12 +2,12 @@
2
2
  // @bun
3
3
 
4
4
  // src/index.ts
5
- import * as c7 from "yoctocolors";
5
+ import * as c8 from "yoctocolors";
6
6
 
7
7
  // src/agent/agent.ts
8
8
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
9
9
  import { join as join14 } from "path";
10
- import * as c6 from "yoctocolors";
10
+ import * as c7 from "yoctocolors";
11
11
 
12
12
  // src/cli/agents.ts
13
13
  import { existsSync, readFileSync, readdirSync } from "fs";
@@ -81,7 +81,7 @@ function loadAgents(cwd) {
81
81
  }
82
82
 
83
83
  // src/cli/commands.ts
84
- import * as c3 from "yoctocolors";
84
+ import * as c4 from "yoctocolors";
85
85
 
86
86
  // src/llm-api/providers.ts
87
87
  import { createAnthropic } from "@ai-sdk/anthropic";
@@ -90,32 +90,15 @@ import { createOpenAI } from "@ai-sdk/openai";
90
90
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
91
91
  import { createOllama } from "ollama-ai-provider";
92
92
  var ZEN_BASE = "https://opencode.ai/zen/v1";
93
- var ZEN_ANTHROPIC_MODELS = new Set([
94
- "claude-opus-4-6",
95
- "claude-opus-4-5",
96
- "claude-opus-4-1",
97
- "claude-sonnet-4-6",
98
- "claude-sonnet-4-5",
99
- "claude-sonnet-4",
100
- "claude-haiku-4-5",
101
- "claude-3-5-haiku"
102
- ]);
103
- var ZEN_OPENAI_MODELS = new Set([
104
- "gpt-5.2",
105
- "gpt-5.2-codex",
106
- "gpt-5.1",
107
- "gpt-5.1-codex",
108
- "gpt-5.1-codex-max",
109
- "gpt-5.1-codex-mini",
110
- "gpt-5",
111
- "gpt-5-codex",
112
- "gpt-5-nano"
113
- ]);
114
- var ZEN_GOOGLE_MODELS = new Set([
115
- "gemini-3.1-pro",
116
- "gemini-3-pro",
117
- "gemini-3-flash"
118
- ]);
93
+ function zenEndpointFor(modelId) {
94
+ if (modelId.startsWith("claude-"))
95
+ return zenAnthropic()(modelId);
96
+ if (modelId.startsWith("gpt-"))
97
+ return zenOpenAI()(modelId);
98
+ if (modelId.startsWith("gemini-"))
99
+ return zenGoogle()(modelId);
100
+ return zenCompat()(modelId);
101
+ }
119
102
  var _zenAnthropic = null;
120
103
  var _zenOpenAI = null;
121
104
  var _zenGoogle = null;
@@ -220,16 +203,7 @@ function resolveModel(modelString) {
220
203
  const modelId = modelString.slice(slashIdx + 1);
221
204
  switch (provider) {
222
205
  case "zen": {
223
- if (ZEN_ANTHROPIC_MODELS.has(modelId)) {
224
- return zenAnthropic()(modelId);
225
- }
226
- if (ZEN_OPENAI_MODELS.has(modelId)) {
227
- return zenOpenAI()(modelId);
228
- }
229
- if (ZEN_GOOGLE_MODELS.has(modelId)) {
230
- return zenGoogle()(modelId);
231
- }
232
- return zenCompat()(modelId);
206
+ return zenEndpointFor(modelId);
233
207
  }
234
208
  case "anthropic":
235
209
  return directAnthropic()(modelId);
@@ -547,85 +521,15 @@ function generateSessionId() {
547
521
 
548
522
  // src/cli/custom-commands.ts
549
523
  import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
550
- import { homedir as homedir3 } from "os";
524
+ import { homedir as homedir4 } from "os";
551
525
  import { basename as basename2, join as join3 } from "path";
552
- function loadFromDir2(dir, source) {
553
- const commands = new Map;
554
- if (!existsSync3(dir))
555
- return commands;
556
- let entries;
557
- try {
558
- entries = readdirSync2(dir);
559
- } catch {
560
- return commands;
561
- }
562
- for (const entry of entries) {
563
- if (!entry.endsWith(".md"))
564
- continue;
565
- const name = basename2(entry, ".md");
566
- const filePath = join3(dir, entry);
567
- let raw;
568
- try {
569
- raw = readFileSync2(filePath, "utf-8");
570
- } catch {
571
- continue;
572
- }
573
- const { meta, body } = parseFrontmatter(raw);
574
- commands.set(name, {
575
- name,
576
- description: meta.description ?? name,
577
- ...meta.model ? { model: meta.model } : {},
578
- template: body,
579
- source
580
- });
581
- }
582
- return commands;
583
- }
584
- function loadCustomCommands(cwd) {
585
- const globalDir = join3(homedir3(), ".agents", "commands");
586
- const localDir = join3(cwd, ".agents", "commands");
587
- const global = loadFromDir2(globalDir, "global");
588
- const local = loadFromDir2(localDir, "local");
589
- return new Map([...global, ...local]);
590
- }
591
- async function expandTemplate(template, args, cwd) {
592
- const tokens = args.match(/("([^"]*)")|('([^']*)')|(\S+)/g)?.map((t) => t.replace(/^["']|["']$/g, "")) ?? [];
593
- let result = template;
594
- for (let i = 9;i >= 1; i--) {
595
- result = result.replaceAll(`$${i}`, tokens[i - 1] ?? "");
596
- }
597
- result = result.replaceAll("$ARGUMENTS", args);
598
- const SHELL_RE = /!`([^`]+)`/g;
599
- const shellMatches = [...result.matchAll(SHELL_RE)];
600
- for (const match of shellMatches) {
601
- const cmd = match[1] ?? "";
602
- let output = "";
603
- try {
604
- const signal = AbortSignal.timeout(1e4);
605
- const proc = Bun.spawn(["bash", "-c", cmd], {
606
- cwd,
607
- stdout: "pipe",
608
- stderr: "pipe"
609
- });
610
- await Promise.race([
611
- proc.exited,
612
- new Promise((_, reject) => signal.addEventListener("abort", () => {
613
- proc.kill();
614
- reject(new Error("timeout"));
615
- }))
616
- ]);
617
- const [stdout, stderr] = await Promise.all([
618
- new Response(proc.stdout).text(),
619
- new Response(proc.stderr).text()
620
- ]);
621
- const exitCode = proc.exitCode ?? 0;
622
- output = exitCode === 0 ? [stdout, stderr].filter(Boolean).join(`
623
- `).trim() : stdout.trim();
624
- } catch {}
625
- result = result.replaceAll(match[0], output);
626
- }
627
- return result;
628
- }
526
+
527
+ // src/cli/config-conflicts.ts
528
+ import * as c3 from "yoctocolors";
529
+
530
+ // src/cli/output.ts
531
+ import { homedir as homedir3 } from "os";
532
+ import * as c2 from "yoctocolors";
629
533
 
630
534
  // src/cli/markdown.ts
631
535
  import * as c from "yoctocolors";
@@ -740,10 +644,8 @@ function renderChunk(text, inFence) {
740
644
  }
741
645
 
742
646
  // src/cli/output.ts
743
- import { homedir as homedir4 } from "os";
744
- import * as c2 from "yoctocolors";
745
- var HOME = homedir4();
746
- var PACKAGE_VERSION = "0.0.4";
647
+ var HOME = homedir3();
648
+ var PACKAGE_VERSION = "0.1.0";
747
649
  function tildePath(p) {
748
650
  return p.startsWith(HOME) ? `~${p.slice(HOME.length)}` : p;
749
651
  }
@@ -1368,6 +1270,112 @@ var PREFIX = {
1368
1270
  success: G.ok
1369
1271
  };
1370
1272
 
1273
+ // src/cli/config-conflicts.ts
1274
+ function warnConventionConflicts(kind, scope, agentsNames, claudeNames) {
1275
+ const agents = new Set(agentsNames);
1276
+ const claude = new Set(claudeNames);
1277
+ const conflicts = [];
1278
+ for (const name of agents) {
1279
+ if (claude.has(name))
1280
+ conflicts.push(name);
1281
+ }
1282
+ if (conflicts.length === 0)
1283
+ return;
1284
+ conflicts.sort((a, b) => a.localeCompare(b));
1285
+ const list = conflicts.map((n) => c3.cyan(n)).join(c3.dim(", "));
1286
+ writeln(`${G.warn} conflicting ${kind} in ${scope} .agents and .claude: ${list} ${c3.dim("\u2014 using .agents version")}`);
1287
+ }
1288
+
1289
+ // src/cli/custom-commands.ts
1290
+ function loadFromDir2(dir, source) {
1291
+ const commands = new Map;
1292
+ if (!existsSync3(dir))
1293
+ return commands;
1294
+ let entries;
1295
+ try {
1296
+ entries = readdirSync2(dir);
1297
+ } catch {
1298
+ return commands;
1299
+ }
1300
+ for (const entry of entries) {
1301
+ if (!entry.endsWith(".md"))
1302
+ continue;
1303
+ const name = basename2(entry, ".md");
1304
+ const filePath = join3(dir, entry);
1305
+ let raw;
1306
+ try {
1307
+ raw = readFileSync2(filePath, "utf-8");
1308
+ } catch {
1309
+ continue;
1310
+ }
1311
+ const { meta, body } = parseFrontmatter(raw);
1312
+ commands.set(name, {
1313
+ name,
1314
+ description: meta.description ?? name,
1315
+ ...meta.model ? { model: meta.model } : {},
1316
+ template: body,
1317
+ source
1318
+ });
1319
+ }
1320
+ return commands;
1321
+ }
1322
+ function loadCustomCommands(cwd) {
1323
+ const globalAgentsDir = join3(homedir4(), ".agents", "commands");
1324
+ const globalClaudeDir = join3(homedir4(), ".claude", "commands");
1325
+ const localAgentsDir = join3(cwd, ".agents", "commands");
1326
+ const localClaudeDir = join3(cwd, ".claude", "commands");
1327
+ const globalAgents = loadFromDir2(globalAgentsDir, "global");
1328
+ const globalClaude = loadFromDir2(globalClaudeDir, "global");
1329
+ const localAgents = loadFromDir2(localAgentsDir, "local");
1330
+ const localClaude = loadFromDir2(localClaudeDir, "local");
1331
+ warnConventionConflicts("commands", "global", globalAgents.keys(), globalClaude.keys());
1332
+ warnConventionConflicts("commands", "local", localAgents.keys(), localClaude.keys());
1333
+ return new Map([
1334
+ ...globalClaude,
1335
+ ...globalAgents,
1336
+ ...localClaude,
1337
+ ...localAgents
1338
+ ]);
1339
+ }
1340
+ async function expandTemplate(template, args, cwd) {
1341
+ const tokens = args.match(/("([^"]*)")|('([^']*)')|(\S+)/g)?.map((t) => t.replace(/^["']|["']$/g, "")) ?? [];
1342
+ let result = template;
1343
+ for (let i = 9;i >= 1; i--) {
1344
+ result = result.replaceAll(`$${i}`, tokens[i - 1] ?? "");
1345
+ }
1346
+ result = result.replaceAll("$ARGUMENTS", args);
1347
+ const SHELL_RE = /!`([^`]+)`/g;
1348
+ const shellMatches = [...result.matchAll(SHELL_RE)];
1349
+ for (const match of shellMatches) {
1350
+ const cmd = match[1] ?? "";
1351
+ let output = "";
1352
+ try {
1353
+ const signal = AbortSignal.timeout(1e4);
1354
+ const proc = Bun.spawn(["bash", "-c", cmd], {
1355
+ cwd,
1356
+ stdout: "pipe",
1357
+ stderr: "pipe"
1358
+ });
1359
+ await Promise.race([
1360
+ proc.exited,
1361
+ new Promise((_, reject) => signal.addEventListener("abort", () => {
1362
+ proc.kill();
1363
+ reject(new Error("timeout"));
1364
+ }))
1365
+ ]);
1366
+ const [stdout, stderr] = await Promise.all([
1367
+ new Response(proc.stdout).text(),
1368
+ new Response(proc.stderr).text()
1369
+ ]);
1370
+ const exitCode = proc.exitCode ?? 0;
1371
+ output = exitCode === 0 ? [stdout, stderr].filter(Boolean).join(`
1372
+ `).trim() : stdout.trim();
1373
+ } catch {}
1374
+ result = result.replaceAll(match[0], output);
1375
+ }
1376
+ return result;
1377
+ }
1378
+
1371
1379
  // src/cli/skills.ts
1372
1380
  import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync } from "fs";
1373
1381
  import { homedir as homedir5 } from "os";
@@ -1403,11 +1411,22 @@ function loadFromDir3(dir, source) {
1403
1411
  return skills;
1404
1412
  }
1405
1413
  function loadSkills(cwd) {
1406
- const globalDir = join4(homedir5(), ".agents", "skills");
1407
- const localDir = join4(cwd, ".agents", "skills");
1408
- const global = loadFromDir3(globalDir, "global");
1409
- const local = loadFromDir3(localDir, "local");
1410
- return new Map([...global, ...local]);
1414
+ const globalAgentsDir = join4(homedir5(), ".agents", "skills");
1415
+ const globalClaudeDir = join4(homedir5(), ".claude", "skills");
1416
+ const localAgentsDir = join4(cwd, ".agents", "skills");
1417
+ const localClaudeDir = join4(cwd, ".claude", "skills");
1418
+ const globalAgents = loadFromDir3(globalAgentsDir, "global");
1419
+ const globalClaude = loadFromDir3(globalClaudeDir, "global");
1420
+ const localAgents = loadFromDir3(localAgentsDir, "local");
1421
+ const localClaude = loadFromDir3(localClaudeDir, "local");
1422
+ warnConventionConflicts("skills", "global", globalAgents.keys(), globalClaude.keys());
1423
+ warnConventionConflicts("skills", "local", localAgents.keys(), localClaude.keys());
1424
+ return new Map([
1425
+ ...globalClaude,
1426
+ ...globalAgents,
1427
+ ...localClaude,
1428
+ ...localAgents
1429
+ ]);
1411
1430
  }
1412
1431
 
1413
1432
  // src/cli/commands.ts
@@ -1420,20 +1439,20 @@ async function handleModel(ctx, args) {
1420
1439
  if (match) {
1421
1440
  modelId = match.id;
1422
1441
  } else {
1423
- writeln(`${PREFIX.error} unknown model ${c3.cyan(args)} ${c3.dim("\u2014 run /models for the full list")}`);
1442
+ writeln(`${PREFIX.error} unknown model ${c4.cyan(args)} ${c4.dim("\u2014 run /models for the full list")}`);
1424
1443
  return;
1425
1444
  }
1426
1445
  }
1427
1446
  ctx.setModel(modelId);
1428
- writeln(`${PREFIX.success} model \u2192 ${c3.cyan(modelId)}`);
1447
+ writeln(`${PREFIX.success} model \u2192 ${c4.cyan(modelId)}`);
1429
1448
  return;
1430
1449
  }
1431
- writeln(`${c3.dim(" fetching models\u2026")}`);
1450
+ writeln(`${c4.dim(" fetching models\u2026")}`);
1432
1451
  const models = await fetchAvailableModels();
1433
1452
  process.stdout.write("\x1B[1A\r\x1B[2K");
1434
1453
  if (models.length === 0) {
1435
1454
  writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
1436
- writeln(c3.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
1455
+ writeln(c4.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
1437
1456
  return;
1438
1457
  }
1439
1458
  const byProvider = new Map;
@@ -1447,27 +1466,27 @@ async function handleModel(ctx, args) {
1447
1466
  }
1448
1467
  writeln();
1449
1468
  for (const [provider, list] of byProvider) {
1450
- writeln(c3.bold(` ${provider}`));
1469
+ writeln(c4.bold(` ${provider}`));
1451
1470
  for (const m of list) {
1452
1471
  const isCurrent = ctx.currentModel === m.id;
1453
- const freeTag = m.free ? c3.green(" free") : "";
1454
- const ctxTag = m.context ? c3.dim(` ${Math.round(m.context / 1000)}k`) : "";
1455
- const cur = isCurrent ? c3.cyan(" \u25C0") : "";
1456
- writeln(` ${c3.dim("\xB7")} ${m.displayName}${freeTag}${ctxTag}${cur}`);
1457
- writeln(` ${c3.dim(m.id)}`);
1472
+ const freeTag = m.free ? c4.green(" free") : "";
1473
+ const ctxTag = m.context ? c4.dim(` ${Math.round(m.context / 1000)}k`) : "";
1474
+ const cur = isCurrent ? c4.cyan(" \u25C0") : "";
1475
+ writeln(` ${c4.dim("\xB7")} ${m.displayName}${freeTag}${ctxTag}${cur}`);
1476
+ writeln(` ${c4.dim(m.id)}`);
1458
1477
  }
1459
1478
  }
1460
1479
  writeln();
1461
- writeln(c3.dim(" /model <id> to switch \xB7 e.g. /model zen/claude-sonnet-4-6"));
1480
+ writeln(c4.dim(" /model <id> to switch \xB7 e.g. /model zen/claude-sonnet-4-6"));
1462
1481
  }
1463
1482
  function handlePlan(ctx) {
1464
1483
  ctx.setPlanMode(!ctx.planMode);
1465
1484
  if (ctx.planMode) {
1466
1485
  if (ctx.ralphMode)
1467
1486
  ctx.setRalphMode(false);
1468
- writeln(`${PREFIX.info} ${c3.yellow("plan mode")} ${c3.dim("\u2014 read-only tools + MCP, no writes or shell")}`);
1487
+ writeln(`${PREFIX.info} ${c4.yellow("plan mode")} ${c4.dim("\u2014 read-only tools + MCP, no writes or shell")}`);
1469
1488
  } else {
1470
- writeln(`${PREFIX.info} ${c3.dim("plan mode off")}`);
1489
+ writeln(`${PREFIX.info} ${c4.dim("plan mode off")}`);
1471
1490
  }
1472
1491
  }
1473
1492
  function handleRalph(ctx) {
@@ -1475,17 +1494,17 @@ function handleRalph(ctx) {
1475
1494
  if (ctx.ralphMode) {
1476
1495
  if (ctx.planMode)
1477
1496
  ctx.setPlanMode(false);
1478
- writeln(`${PREFIX.info} ${c3.magenta("ralph mode")} ${c3.dim("\u2014 loops until done, fresh context each iteration")}`);
1497
+ writeln(`${PREFIX.info} ${c4.magenta("ralph mode")} ${c4.dim("\u2014 loops until done, fresh context each iteration")}`);
1479
1498
  } else {
1480
- writeln(`${PREFIX.info} ${c3.dim("ralph mode off")}`);
1499
+ writeln(`${PREFIX.info} ${c4.dim("ralph mode off")}`);
1481
1500
  }
1482
1501
  }
1483
1502
  async function handleUndo(ctx) {
1484
1503
  const ok = await ctx.undoLastTurn();
1485
1504
  if (ok) {
1486
- writeln(`${PREFIX.success} ${c3.dim("last turn undone \u2014 history and files restored")}`);
1505
+ writeln(`${PREFIX.success} ${c4.dim("last turn undone \u2014 history and files restored")}`);
1487
1506
  } else {
1488
- writeln(`${PREFIX.info} ${c3.dim("nothing to undo")}`);
1507
+ writeln(`${PREFIX.info} ${c4.dim("nothing to undo")}`);
1489
1508
  }
1490
1509
  }
1491
1510
  async function handleMcp(ctx, args) {
@@ -1495,28 +1514,28 @@ async function handleMcp(ctx, args) {
1495
1514
  case "list": {
1496
1515
  const servers = listMcpServers();
1497
1516
  if (servers.length === 0) {
1498
- writeln(c3.dim(" no MCP servers configured"));
1499
- writeln(c3.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
1517
+ writeln(c4.dim(" no MCP servers configured"));
1518
+ writeln(c4.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
1500
1519
  return;
1501
1520
  }
1502
1521
  writeln();
1503
1522
  for (const s of servers) {
1504
- const detail = s.url ? c3.dim(` ${s.url}`) : s.command ? c3.dim(` ${s.command}`) : "";
1505
- writeln(` ${c3.yellow("\u2699")} ${c3.bold(s.name)} ${c3.dim(s.transport)}${detail}`);
1523
+ const detail = s.url ? c4.dim(` ${s.url}`) : s.command ? c4.dim(` ${s.command}`) : "";
1524
+ writeln(` ${c4.yellow("\u2699")} ${c4.bold(s.name)} ${c4.dim(s.transport)}${detail}`);
1506
1525
  }
1507
1526
  return;
1508
1527
  }
1509
1528
  case "add": {
1510
1529
  const [, name, transport, ...rest] = parts;
1511
1530
  if (!name || !transport || rest.length === 0) {
1512
- writeln(c3.red(" usage: /mcp add <name> http <url>"));
1513
- writeln(c3.red(" /mcp add <name> stdio <cmd> [args...]"));
1531
+ writeln(c4.red(" usage: /mcp add <name> http <url>"));
1532
+ writeln(c4.red(" /mcp add <name> stdio <cmd> [args...]"));
1514
1533
  return;
1515
1534
  }
1516
1535
  if (transport === "http") {
1517
1536
  const url = rest[0];
1518
1537
  if (!url) {
1519
- writeln(c3.red(" usage: /mcp add <name> http <url>"));
1538
+ writeln(c4.red(" usage: /mcp add <name> http <url>"));
1520
1539
  return;
1521
1540
  }
1522
1541
  upsertMcpServer({
@@ -1530,7 +1549,7 @@ async function handleMcp(ctx, args) {
1530
1549
  } else if (transport === "stdio") {
1531
1550
  const [command, ...cmdArgs] = rest;
1532
1551
  if (!command) {
1533
- writeln(c3.red(" usage: /mcp add <name> stdio <cmd> [args...]"));
1552
+ writeln(c4.red(" usage: /mcp add <name> stdio <cmd> [args...]"));
1534
1553
  return;
1535
1554
  }
1536
1555
  upsertMcpServer({
@@ -1542,14 +1561,14 @@ async function handleMcp(ctx, args) {
1542
1561
  env: null
1543
1562
  });
1544
1563
  } else {
1545
- writeln(c3.red(` unknown transport: ${transport} (use http or stdio)`));
1564
+ writeln(c4.red(` unknown transport: ${transport} (use http or stdio)`));
1546
1565
  return;
1547
1566
  }
1548
1567
  try {
1549
1568
  await ctx.connectMcpServer(name);
1550
- writeln(`${PREFIX.success} mcp server ${c3.cyan(name)} added and connected`);
1569
+ writeln(`${PREFIX.success} mcp server ${c4.cyan(name)} added and connected`);
1551
1570
  } catch (e) {
1552
- writeln(`${PREFIX.success} mcp server ${c3.cyan(name)} saved ${c3.dim(`(connection failed: ${String(e)})`)}`);
1571
+ writeln(`${PREFIX.success} mcp server ${c4.cyan(name)} saved ${c4.dim(`(connection failed: ${String(e)})`)}`);
1553
1572
  }
1554
1573
  return;
1555
1574
  }
@@ -1557,50 +1576,36 @@ async function handleMcp(ctx, args) {
1557
1576
  case "rm": {
1558
1577
  const [, name] = parts;
1559
1578
  if (!name) {
1560
- writeln(c3.red(" usage: /mcp remove <name>"));
1579
+ writeln(c4.red(" usage: /mcp remove <name>"));
1561
1580
  return;
1562
1581
  }
1563
1582
  deleteMcpServer(name);
1564
- writeln(`${PREFIX.success} mcp server ${c3.cyan(name)} removed`);
1583
+ writeln(`${PREFIX.success} mcp server ${c4.cyan(name)} removed`);
1565
1584
  return;
1566
1585
  }
1567
1586
  default:
1568
- writeln(c3.red(` unknown: /mcp ${sub}`));
1569
- writeln(c3.dim(" subcommands: list \xB7 add \xB7 remove"));
1587
+ writeln(c4.red(` unknown: /mcp ${sub}`));
1588
+ writeln(c4.dim(" subcommands: list \xB7 add \xB7 remove"));
1570
1589
  }
1571
1590
  }
1572
1591
  var REVIEW_PROMPT = (cwd, focus) => `You are a code reviewer. Review recent changes and provide actionable feedback.
1573
1592
 
1574
1593
  Working directory: ${cwd}
1575
- ${focus ? `Focus: ${focus}` : ""}
1576
-
1577
- ## What to review
1578
- - No args: \`git diff\` (unstaged) + \`git diff --cached\` (staged) + \`git status --short\` (untracked)
1579
- - Commit hash: \`git show <hash>\`
1580
- - Branch: \`git diff <branch>...HEAD\`
1581
- - PR number/URL: \`gh pr view\` + \`gh pr diff\`
1582
-
1583
- ## How to review
1584
- After getting the diff, read the full files changed \u2014 diffs alone miss context.
1585
- Check for AGENTS.md or CONVENTIONS.md for project conventions.
1586
-
1587
- ## What to flag (priority order)
1588
- 1. **Bugs** \u2014 logic errors, missing edge cases, unhandled errors, race conditions, security issues. Be certain before flagging; investigate first.
1589
- 2. **Structure** \u2014 wrong abstraction, established patterns ignored, excessive nesting.
1590
- 3. **Performance** \u2014 only if obviously problematic (O(n\xB2) on unbounded data, N+1, blocking hot paths).
1591
- 4. **Style** \u2014 only clear violations of project conventions. Don't be a zealot.
1592
-
1593
- Only review the changed code, not pre-existing code.
1594
-
1595
- ## Output
1596
- - Be direct and specific: quote code, cite file and line number.
1597
- - State the scenario/input that triggers a bug \u2014 severity depends on this.
1598
- - No flattery, no filler. Matter-of-fact tone.
1599
- - End with a short **Summary** of the most important items.
1594
+ ${focus ? `Review: ${focus}` : "Review the current changes"}
1595
+
1596
+ Perform a sensible code review:
1597
+
1598
+ - Correctness: Are the changes in alignment with the goal?
1599
+ - Code quality: Is there duplicate, dead or bad code patterns introduced or as a result of the changes?
1600
+ - Is the code performant?
1601
+ - Never flag style choices as bugs, don't be a zeolot.
1602
+ - Never flag false positives, if you think something is wrong, check before saying it's an issue.
1603
+
1604
+ Output a small summary with only the issues found. If nothing of note was found reply saying that.
1600
1605
  `;
1601
1606
  async function handleReview(ctx, args) {
1602
1607
  const focus = args.trim();
1603
- writeln(`${PREFIX.info} ${c3.cyan("review")} ${c3.dim("\u2014 spawning review subagent\u2026")}`);
1608
+ writeln(`${PREFIX.info} ${c4.cyan("review")} ${c4.dim("\u2014 spawning review subagent\u2026")}`);
1604
1609
  writeln();
1605
1610
  try {
1606
1611
  const output = await ctx.runSubagent(REVIEW_PROMPT(ctx.cwd, focus));
@@ -1625,13 +1630,13 @@ ${output.result}
1625
1630
  }
1626
1631
  function handleNew(ctx) {
1627
1632
  ctx.startNewSession();
1628
- writeln(`${PREFIX.success} ${c3.dim("new session started \u2014 context cleared")}`);
1633
+ writeln(`${PREFIX.success} ${c4.dim("new session started \u2014 context cleared")}`);
1629
1634
  }
1630
1635
  async function handleCustomCommand(cmd, args, ctx) {
1631
1636
  const prompt = await expandTemplate(cmd.template, args, ctx.cwd);
1632
- const label = c3.cyan(cmd.name);
1637
+ const label = c4.cyan(cmd.name);
1633
1638
  const srcPath = cmd.source === "local" ? `.agents/commands/${cmd.name}.md` : `~/.agents/commands/${cmd.name}.md`;
1634
- const src = c3.dim(`[${srcPath}]`);
1639
+ const src = c4.dim(`[${srcPath}]`);
1635
1640
  writeln(`${PREFIX.info} ${label} ${src}`);
1636
1641
  writeln();
1637
1642
  try {
@@ -1674,41 +1679,41 @@ function handleHelp(ctx, custom) {
1674
1679
  ["/exit", "quit"]
1675
1680
  ];
1676
1681
  for (const [cmd, desc] of cmds) {
1677
- writeln(` ${c3.cyan(cmd.padEnd(26))} ${c3.dim(desc)}`);
1682
+ writeln(` ${c4.cyan(cmd.padEnd(26))} ${c4.dim(desc)}`);
1678
1683
  }
1679
1684
  if (custom.size > 0) {
1680
1685
  writeln();
1681
- writeln(c3.dim(" custom commands:"));
1686
+ writeln(c4.dim(" custom commands:"));
1682
1687
  for (const cmd of custom.values()) {
1683
- const tag = cmd.source === "local" ? c3.dim(" (local)") : c3.dim(" (global)");
1684
- writeln(` ${c3.green(`/${cmd.name}`.padEnd(26))} ${c3.dim(cmd.description)}${tag}`);
1688
+ const tag = cmd.source === "local" ? c4.dim(" (local)") : c4.dim(" (global)");
1689
+ writeln(` ${c4.green(`/${cmd.name}`.padEnd(26))} ${c4.dim(cmd.description)}${tag}`);
1685
1690
  }
1686
1691
  }
1687
1692
  const agents = loadAgents(ctx.cwd);
1688
1693
  if (agents.size > 0) {
1689
1694
  writeln();
1690
- writeln(c3.dim(" agents (~/.agents/agents/ or .agents/agents/):"));
1695
+ writeln(c4.dim(" agents (~/.agents/agents/ or .agents/agents/):"));
1691
1696
  for (const agent of agents.values()) {
1692
- const tag = agent.source === "local" ? c3.dim(" (local)") : c3.dim(" (global)");
1693
- writeln(` ${c3.magenta(`@${agent.name}`.padEnd(26))} ${c3.dim(agent.description)}${tag}`);
1697
+ const tag = agent.source === "local" ? c4.dim(" (local)") : c4.dim(" (global)");
1698
+ writeln(` ${c4.magenta(`@${agent.name}`.padEnd(26))} ${c4.dim(agent.description)}${tag}`);
1694
1699
  }
1695
1700
  }
1696
1701
  const skills = loadSkills(ctx.cwd);
1697
1702
  if (skills.size > 0) {
1698
1703
  writeln();
1699
- writeln(c3.dim(" skills (~/.agents/skills/ or .agents/skills/):"));
1704
+ writeln(c4.dim(" skills (~/.agents/skills/ or .agents/skills/):"));
1700
1705
  for (const skill of skills.values()) {
1701
- const tag = skill.source === "local" ? c3.dim(" (local)") : c3.dim(" (global)");
1702
- writeln(` ${c3.yellow(`@${skill.name}`.padEnd(26))} ${c3.dim(skill.description)}${tag}`);
1706
+ const tag = skill.source === "local" ? c4.dim(" (local)") : c4.dim(" (global)");
1707
+ writeln(` ${c4.yellow(`@${skill.name}`.padEnd(26))} ${c4.dim(skill.description)}${tag}`);
1703
1708
  }
1704
1709
  }
1705
1710
  writeln();
1706
- writeln(` ${c3.green("@agent".padEnd(26))} ${c3.dim("run prompt through a custom agent (Tab to complete)")}`);
1707
- writeln(` ${c3.green("@skill".padEnd(26))} ${c3.dim("inject skill instructions into prompt (Tab to complete)")}`);
1708
- writeln(` ${c3.green("@file".padEnd(26))} ${c3.dim("inject file contents into prompt (Tab to complete)")}`);
1709
- writeln(` ${c3.green("!cmd".padEnd(26))} ${c3.dim("run shell command, output added as context")}`);
1711
+ writeln(` ${c4.green("@agent".padEnd(26))} ${c4.dim("run prompt through a custom agent (Tab to complete)")}`);
1712
+ writeln(` ${c4.green("@skill".padEnd(26))} ${c4.dim("inject skill instructions into prompt (Tab to complete)")}`);
1713
+ writeln(` ${c4.green("@file".padEnd(26))} ${c4.dim("inject file contents into prompt (Tab to complete)")}`);
1714
+ writeln(` ${c4.green("!cmd".padEnd(26))} ${c4.dim("run shell command, output added as context")}`);
1710
1715
  writeln();
1711
- writeln(` ${c3.dim("ctrl+c")} cancel ${c3.dim("\xB7")} ${c3.dim("ctrl+d")} exit ${c3.dim("\xB7")} ${c3.dim("ctrl+r")} history search ${c3.dim("\xB7")} ${c3.dim("\u2191\u2193")} history`);
1716
+ writeln(` ${c4.dim("ctrl+c")} cancel ${c4.dim("\xB7")} ${c4.dim("ctrl+d")} exit ${c4.dim("\xB7")} ${c4.dim("ctrl+r")} history search ${c4.dim("\xB7")} ${c4.dim("\u2191\u2193")} history`);
1712
1717
  writeln();
1713
1718
  }
1714
1719
  async function handleCommand(command, args, ctx) {
@@ -1748,7 +1753,7 @@ async function handleCommand(command, args, ctx) {
1748
1753
  case "q":
1749
1754
  return { type: "exit" };
1750
1755
  default: {
1751
- writeln(`${PREFIX.error} unknown: /${command} ${c3.dim("\u2014 /help for commands")}`);
1756
+ writeln(`${PREFIX.error} unknown: /${command} ${c4.dim("\u2014 /help for commands")}`);
1752
1757
  return { type: "unknown", command };
1753
1758
  }
1754
1759
  }
@@ -1791,7 +1796,7 @@ async function loadImageFile(filePath) {
1791
1796
 
1792
1797
  // src/cli/input.ts
1793
1798
  import { join as join5, relative } from "path";
1794
- import * as c4 from "yoctocolors";
1799
+ import * as c5 from "yoctocolors";
1795
1800
  var ESC = "\x1B";
1796
1801
  var CSI = `${ESC}[`;
1797
1802
  var CLEAR_LINE = `\r${CSI}2K`;
@@ -1912,9 +1917,9 @@ function pasteLabel(text) {
1912
1917
  const more = extra > 0 ? ` +${extra} more line${extra === 1 ? "" : "s"}` : "";
1913
1918
  return `[pasted: "${preview}"${more}]`;
1914
1919
  }
1915
- var PROMPT = c4.green("\u25B6 ");
1916
- var PROMPT_PLAN = c4.yellow("\u2B22 ");
1917
- var PROMPT_RALPH = c4.magenta("\u21BB ");
1920
+ var PROMPT = c5.green("\u25B6 ");
1921
+ var PROMPT_PLAN = c5.yellow("\u2B22 ");
1922
+ var PROMPT_RALPH = c5.magenta("\u21BB ");
1918
1923
  var PROMPT_RAW_LEN = 2;
1919
1924
  async function readline(opts) {
1920
1925
  const cwd = opts.cwd ?? process.cwd();
@@ -1933,7 +1938,7 @@ async function readline(opts) {
1933
1938
  const reader = getStdinReader();
1934
1939
  function renderPrompt() {
1935
1940
  const cols = process.stdout.columns ?? 80;
1936
- const visualBuf = (pasteBuffer ? buf.replace(PASTE_SENTINEL, c4.dim(pasteLabel(pasteBuffer))) : buf).replace(/\[image: [^\]]+\]/g, (m) => c4.dim(c4.cyan(m)));
1941
+ const visualBuf = (pasteBuffer ? buf.replace(PASTE_SENTINEL, c5.dim(pasteLabel(pasteBuffer))) : buf).replace(/\[image: [^\]]+\]/g, (m) => c5.dim(c5.cyan(m)));
1937
1942
  const visualCursor = pasteBuffer ? (() => {
1938
1943
  const sentinelPos = buf.indexOf(PASTE_SENTINEL);
1939
1944
  if (sentinelPos === -1 || cursor <= sentinelPos)
@@ -1945,7 +1950,7 @@ async function readline(opts) {
1945
1950
  process.stdout.write(`${CLEAR_LINE}${prompt}${display}${CSI}${PROMPT_RAW_LEN + visualCursor + 1}G`);
1946
1951
  }
1947
1952
  function renderSearchPrompt() {
1948
- process.stdout.write(`${CLEAR_LINE}${c4.cyan("search:")} ${searchQuery}\u2588`);
1953
+ process.stdout.write(`${CLEAR_LINE}${c5.cyan("search:")} ${searchQuery}\u2588`);
1949
1954
  }
1950
1955
  function applyHistory() {
1951
1956
  if (histIdx < history.length) {
@@ -2061,6 +2066,18 @@ async function readline(opts) {
2061
2066
  renderPrompt();
2062
2067
  continue;
2063
2068
  }
2069
+ if (raw === `${ESC}${BACKSPACE}`) {
2070
+ const end = cursor;
2071
+ while (cursor > 0 && buf[cursor - 1] === " ")
2072
+ cursor--;
2073
+ while (cursor > 0 && buf[cursor - 1] !== " ")
2074
+ cursor--;
2075
+ buf = buf.slice(0, cursor) + buf.slice(end);
2076
+ if (pasteBuffer && !buf.includes(PASTE_SENTINEL))
2077
+ pasteBuffer = null;
2078
+ renderPrompt();
2079
+ continue;
2080
+ }
2064
2081
  if (raw === ESC) {
2065
2082
  process.stdout.write(`
2066
2083
  `);
@@ -2149,8 +2166,8 @@ async function readline(opts) {
2149
2166
  } else if (completions.length > 1) {
2150
2167
  process.stdout.write(`
2151
2168
  `);
2152
- for (const c5 of completions)
2153
- process.stdout.write(` ${c5}
2169
+ for (const c6 of completions)
2170
+ process.stdout.write(` ${c6}
2154
2171
  `);
2155
2172
  renderPrompt();
2156
2173
  }
@@ -2264,10 +2281,10 @@ async function* runTurn(options) {
2264
2281
  for await (const chunk of result.fullStream) {
2265
2282
  if (signal?.aborted)
2266
2283
  break;
2267
- const c5 = chunk;
2268
- switch (c5.type) {
2284
+ const c6 = chunk;
2285
+ switch (c6.type) {
2269
2286
  case "text-delta": {
2270
- const delta = typeof c5.text === "string" ? c5.text : typeof c5.textDelta === "string" ? c5.textDelta : "";
2287
+ const delta = typeof c6.text === "string" ? c6.text : typeof c6.textDelta === "string" ? c6.textDelta : "";
2271
2288
  yield {
2272
2289
  type: "text-delta",
2273
2290
  delta
@@ -2277,18 +2294,18 @@ async function* runTurn(options) {
2277
2294
  case "tool-call": {
2278
2295
  yield {
2279
2296
  type: "tool-call-start",
2280
- toolCallId: String(c5.toolCallId ?? ""),
2281
- toolName: String(c5.toolName ?? ""),
2282
- args: c5.input ?? c5.args
2297
+ toolCallId: String(c6.toolCallId ?? ""),
2298
+ toolName: String(c6.toolName ?? ""),
2299
+ args: c6.input ?? c6.args
2283
2300
  };
2284
2301
  break;
2285
2302
  }
2286
2303
  case "tool-result": {
2287
2304
  yield {
2288
2305
  type: "tool-result",
2289
- toolCallId: String(c5.toolCallId ?? ""),
2290
- toolName: String(c5.toolName ?? ""),
2291
- result: "output" in c5 ? c5.output : ("result" in c5) ? c5.result : undefined,
2306
+ toolCallId: String(c6.toolCallId ?? ""),
2307
+ toolName: String(c6.toolName ?? ""),
2308
+ result: "output" in c6 ? c6.output : ("result" in c6) ? c6.result : undefined,
2292
2309
  isError: false
2293
2310
  };
2294
2311
  break;
@@ -2296,15 +2313,15 @@ async function* runTurn(options) {
2296
2313
  case "tool-error": {
2297
2314
  yield {
2298
2315
  type: "tool-result",
2299
- toolCallId: String(c5.toolCallId ?? ""),
2300
- toolName: String(c5.toolName ?? ""),
2301
- result: c5.error ?? "Tool execution failed",
2316
+ toolCallId: String(c6.toolCallId ?? ""),
2317
+ toolName: String(c6.toolName ?? ""),
2318
+ result: c6.error ?? "Tool execution failed",
2302
2319
  isError: true
2303
2320
  };
2304
2321
  break;
2305
2322
  }
2306
2323
  case "error": {
2307
- const err = c5.error;
2324
+ const err = c6.error;
2308
2325
  throw err instanceof Error ? err : new Error(String(err));
2309
2326
  }
2310
2327
  }
@@ -2554,7 +2571,7 @@ async function restoreSnapshot(cwd, sessionId, turnIndex) {
2554
2571
  }
2555
2572
 
2556
2573
  // src/session/manager.ts
2557
- import * as c5 from "yoctocolors";
2574
+ import * as c6 from "yoctocolors";
2558
2575
  function newSession(model, cwd) {
2559
2576
  const id = generateSessionId();
2560
2577
  createSession({ id, cwd, model });
@@ -2573,19 +2590,19 @@ function touchActiveSession(session) {
2573
2590
  function printSessionList() {
2574
2591
  const sessions = listSessions(20);
2575
2592
  if (sessions.length === 0) {
2576
- writeln(c5.dim("No sessions found."));
2593
+ writeln(c6.dim("No sessions found."));
2577
2594
  return;
2578
2595
  }
2579
2596
  writeln(`
2580
- ${c5.bold("Recent sessions:")}`);
2597
+ ${c6.bold("Recent sessions:")}`);
2581
2598
  for (const s of sessions) {
2582
2599
  const date = new Date(s.updated_at).toLocaleString();
2583
2600
  const cwd = tildePath(s.cwd);
2584
- const title = s.title || c5.dim("(untitled)");
2585
- writeln(` ${c5.dim(s.id.padEnd(14))} ${title.padEnd(30)} ${c5.cyan(s.model.split("/").pop() ?? s.model).padEnd(20)} ${c5.dim(cwd)} ${c5.dim(date)}`);
2601
+ const title = s.title || c6.dim("(untitled)");
2602
+ writeln(` ${c6.dim(s.id.padEnd(14))} ${title.padEnd(30)} ${c6.cyan(s.model.split("/").pop() ?? s.model).padEnd(20)} ${c6.dim(cwd)} ${c6.dim(date)}`);
2586
2603
  }
2587
2604
  writeln(`
2588
- ${c5.dim("Use")} mc --resume <id> ${c5.dim("to continue a session.")}`);
2605
+ ${c6.dim("Use")} mc --resume <id> ${c6.dim("to continue a session.")}`);
2589
2606
  }
2590
2607
  function getMostRecentSession() {
2591
2608
  const sessions = listSessions(1);
@@ -2905,11 +2922,11 @@ var grepTool = {
2905
2922
  const ctxStart = Math.max(0, i - contextLines);
2906
2923
  const ctxEnd = Math.min(lines.length - 1, i + contextLines);
2907
2924
  const context = [];
2908
- for (let c6 = ctxStart;c6 <= ctxEnd; c6++) {
2925
+ for (let c7 = ctxStart;c7 <= ctxEnd; c7++) {
2909
2926
  context.push({
2910
- line: c6 + 1,
2911
- text: formatHashLine(c6 + 1, lines[c6] ?? ""),
2912
- isMatch: c6 === i
2927
+ line: c7 + 1,
2928
+ text: formatHashLine(c7 + 1, lines[c7] ?? ""),
2929
+ isMatch: c7 === i
2913
2930
  });
2914
2931
  }
2915
2932
  allMatches.push({
@@ -3463,7 +3480,7 @@ async function runShellPassthrough(command, cwd) {
3463
3480
  const out = [stdout, stderr].filter(Boolean).join(`
3464
3481
  `).trim();
3465
3482
  if (out)
3466
- writeln(c6.dim(out));
3483
+ writeln(c7.dim(out));
3467
3484
  return out;
3468
3485
  } finally {
3469
3486
  restoreTerminal();
@@ -3482,7 +3499,7 @@ async function runAgent(opts) {
3482
3499
  session = resumed;
3483
3500
  currentModel = session.model;
3484
3501
  deleteAllSnapshots(session.id);
3485
- renderInfo(`Resumed session ${session.id} (${c6.cyan(currentModel)})`);
3502
+ renderInfo(`Resumed session ${session.id} (${c7.cyan(currentModel)})`);
3486
3503
  } else {
3487
3504
  session = newSession(currentModel, cwd);
3488
3505
  }
@@ -3573,7 +3590,7 @@ async function runAgent(opts) {
3573
3590
  for (const row of listMcpServers()) {
3574
3591
  try {
3575
3592
  await connectAndAddMcp(row.name);
3576
- renderInfo(`MCP: connected ${c6.cyan(row.name)}`);
3593
+ renderInfo(`MCP: connected ${c7.cyan(row.name)}`);
3577
3594
  } catch (e) {
3578
3595
  renderError(`MCP: failed to connect ${row.name}: ${String(e)}`);
3579
3596
  }
@@ -3667,14 +3684,14 @@ async function runAgent(opts) {
3667
3684
  }
3668
3685
  switch (input.type) {
3669
3686
  case "eof":
3670
- writeln(c6.dim("Goodbye."));
3687
+ writeln(c7.dim("Goodbye."));
3671
3688
  return;
3672
3689
  case "interrupt":
3673
3690
  continue;
3674
3691
  case "command": {
3675
3692
  const result = await handleCommand(input.command, input.args, cmdCtx);
3676
3693
  if (result.type === "exit") {
3677
- writeln(c6.dim("Goodbye."));
3694
+ writeln(c7.dim("Goodbye."));
3678
3695
  return;
3679
3696
  }
3680
3697
  if (result.type === "inject-user-message") {
@@ -3709,11 +3726,11 @@ ${out}
3709
3726
  while (ralphMode) {
3710
3727
  if (hasRalphSignal(lastText)) {
3711
3728
  ralphMode = false;
3712
- writeln(`${PREFIX.info} ${c6.dim("ralph mode off")}`);
3729
+ writeln(`${PREFIX.info} ${c7.dim("ralph mode off")}`);
3713
3730
  break;
3714
3731
  }
3715
3732
  if (ralphIteration >= RALPH_MAX_ITERATIONS) {
3716
- writeln(`${PREFIX.info} ${c6.yellow("ralph")} ${c6.dim("\u2014 max iterations reached, stopping")}`);
3733
+ writeln(`${PREFIX.info} ${c7.yellow("ralph")} ${c7.dim("\u2014 max iterations reached, stopping")}`);
3717
3734
  ralphMode = false;
3718
3735
  break;
3719
3736
  }
@@ -3739,13 +3756,6 @@ ${out}
3739
3756
  const allImages = [...pastedImages, ...refImages];
3740
3757
  const thisTurn = turnIndex++;
3741
3758
  const snapped = await takeSnapshot(cwd, session.id, thisTurn);
3742
- if (wasAborted) {
3743
- process.removeListener("SIGINT", onSigInt);
3744
- if (snapped)
3745
- deleteSnapshot(session.id, thisTurn);
3746
- turnIndex--;
3747
- return "";
3748
- }
3749
3759
  const coreContent = planMode ? `${resolvedText}
3750
3760
 
3751
3761
  <system-message>PLAN MODE ACTIVE: Help the user gather context for the plan -- READ ONLY</system-message>` : ralphMode ? `${resolvedText}
@@ -3762,6 +3772,19 @@ ${out}
3762
3772
  }))
3763
3773
  ]
3764
3774
  } : { role: "user", content: coreContent };
3775
+ if (wasAborted) {
3776
+ process.removeListener("SIGINT", onSigInt);
3777
+ const stubMsg = {
3778
+ role: "assistant",
3779
+ content: "[interrupted]"
3780
+ };
3781
+ session.messages.push(userMsg, stubMsg);
3782
+ saveMessages(session.id, [userMsg, stubMsg], thisTurn);
3783
+ coreHistory.push(userMsg, stubMsg);
3784
+ snapshotStack.push(snapped ? thisTurn : null);
3785
+ touchActiveSession(session);
3786
+ return "";
3787
+ }
3765
3788
  session.messages.push(userMsg);
3766
3789
  saveMessages(session.id, [userMsg], thisTurn);
3767
3790
  coreHistory.push(userMsg);
@@ -3796,6 +3819,14 @@ ${out}
3796
3819
  coreHistory.push(...newMessages);
3797
3820
  session.messages.push(...newMessages);
3798
3821
  saveMessages(session.id, newMessages, thisTurn);
3822
+ } else if (wasAborted) {
3823
+ const stubMsg = {
3824
+ role: "assistant",
3825
+ content: "[interrupted]"
3826
+ };
3827
+ coreHistory.push(stubMsg);
3828
+ session.messages.push(stubMsg);
3829
+ saveMessages(session.id, [stubMsg], thisTurn);
3799
3830
  } else {
3800
3831
  rollbackTurn();
3801
3832
  }
@@ -3948,11 +3979,11 @@ function parseArgs(argv) {
3948
3979
  return args;
3949
3980
  }
3950
3981
  function printHelp() {
3951
- writeln(`${c7.bold("mini-coder")} \u2014 a small, fast CLI coding agent
3982
+ writeln(`${c8.bold("mini-coder")} \u2014 a small, fast CLI coding agent
3952
3983
  `);
3953
- writeln(`${c7.bold("Usage:")} mc [options] [prompt]
3984
+ writeln(`${c8.bold("Usage:")} mc [options] [prompt]
3954
3985
  `);
3955
- writeln(`${c7.bold("Options:")}`);
3986
+ writeln(`${c8.bold("Options:")}`);
3956
3987
  const opts = [
3957
3988
  ["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
3958
3989
  ["-c, --continue", "Continue the most recent session"],
@@ -3962,10 +3993,10 @@ function printHelp() {
3962
3993
  ["-h, --help", "Show this help"]
3963
3994
  ];
3964
3995
  for (const [flag, desc] of opts) {
3965
- writeln(` ${c7.cyan((flag ?? "").padEnd(22))} ${c7.dim(desc ?? "")}`);
3996
+ writeln(` ${c8.cyan((flag ?? "").padEnd(22))} ${c8.dim(desc ?? "")}`);
3966
3997
  }
3967
3998
  writeln(`
3968
- ${c7.bold("Provider env vars:")}`);
3999
+ ${c8.bold("Provider env vars:")}`);
3969
4000
  const envs = [
3970
4001
  ["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
3971
4002
  ["ANTHROPIC_API_KEY", "Anthropic direct"],
@@ -3974,15 +4005,15 @@ ${c7.bold("Provider env vars:")}`);
3974
4005
  ["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
3975
4006
  ];
3976
4007
  for (const [env, desc] of envs) {
3977
- writeln(` ${c7.yellow((env ?? "").padEnd(22))} ${c7.dim(desc ?? "")}`);
4008
+ writeln(` ${c8.yellow((env ?? "").padEnd(22))} ${c8.dim(desc ?? "")}`);
3978
4009
  }
3979
4010
  writeln(`
3980
- ${c7.bold("Examples:")}`);
3981
- writeln(` mc ${c7.dim("# interactive session")}`);
3982
- writeln(` mc "explain this codebase" ${c7.dim("# one-shot prompt then interactive")}`);
3983
- writeln(` mc -c ${c7.dim("# continue last session")}`);
3984
- writeln(` mc -m ollama/llama3.2 ${c7.dim("# use local Ollama model")}`);
3985
- writeln(` mc -l ${c7.dim("# list sessions")}`);
4011
+ ${c8.bold("Examples:")}`);
4012
+ writeln(` mc ${c8.dim("# interactive session")}`);
4013
+ writeln(` mc "explain this codebase" ${c8.dim("# one-shot prompt then interactive")}`);
4014
+ writeln(` mc -c ${c8.dim("# continue last session")}`);
4015
+ writeln(` mc -m ollama/llama3.2 ${c8.dim("# use local Ollama model")}`);
4016
+ writeln(` mc -l ${c8.dim("# list sessions")}`);
3986
4017
  }
3987
4018
  async function main() {
3988
4019
  const argv = process.argv.slice(2);
@@ -4001,7 +4032,7 @@ async function main() {
4001
4032
  if (last) {
4002
4033
  sessionId = last.id;
4003
4034
  } else {
4004
- writeln(c7.dim("No previous session found, starting fresh."));
4035
+ writeln(c8.dim("No previous session found, starting fresh."));
4005
4036
  }
4006
4037
  } else if (args.sessionId) {
4007
4038
  sessionId = args.sessionId;