mini-coder 0.4.0 → 0.4.1

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/mc.js +193 -105
  2. package/package.json +1 -1
package/dist/mc.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import * as c20 from "yoctocolors";
6
6
 
7
7
  // src/agent/agent.ts
8
- import * as c11 from "yoctocolors";
8
+ import * as c12 from "yoctocolors";
9
9
 
10
10
  // src/mcp/client.ts
11
11
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -13,7 +13,7 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
13
13
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
14
14
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
15
15
  // src/internal/version.ts
16
- var PACKAGE_VERSION = "0.4.0";
16
+ var PACKAGE_VERSION = "0.4.1";
17
17
 
18
18
  // src/mcp/client.ts
19
19
  async function connectMcpServer(config) {
@@ -616,7 +616,7 @@ function setPreferredVerboseOutput(verbose) {
616
616
  setSetting("preferred_verbose_output", verbose ? "true" : "false");
617
617
  }
618
618
  // src/agent/session-runner.ts
619
- import * as c10 from "yoctocolors";
619
+ import * as c11 from "yoctocolors";
620
620
 
621
621
  // src/cli/input.ts
622
622
  import * as c8 from "yoctocolors";
@@ -1780,6 +1780,68 @@ import * as c7 from "yoctocolors";
1780
1780
 
1781
1781
  // src/cli/frontmatter.ts
1782
1782
  var FM_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
1783
+ function trimQuotedValue(value) {
1784
+ return value.replace(/^["']|["']$/g, "");
1785
+ }
1786
+ function lineIndent(line) {
1787
+ return line.length - line.trimStart().length;
1788
+ }
1789
+ function parseBlockScalarStyle(value) {
1790
+ if (/^>[+-]?$/.test(value))
1791
+ return ">";
1792
+ if (/^\|[+-]?$/.test(value))
1793
+ return "|";
1794
+ return null;
1795
+ }
1796
+ function collectBlockScalarLines(lines, startIndex, parentIndent) {
1797
+ const blockLines = [];
1798
+ let minIndent = Number.POSITIVE_INFINITY;
1799
+ let index = startIndex;
1800
+ for (;index < lines.length; index++) {
1801
+ const line = lines[index]?.replace(/\r$/, "") ?? "";
1802
+ if (line.trim() === "") {
1803
+ blockLines.push("");
1804
+ continue;
1805
+ }
1806
+ const indent = lineIndent(line);
1807
+ if (indent <= parentIndent)
1808
+ break;
1809
+ if (indent < minIndent)
1810
+ minIndent = indent;
1811
+ blockLines.push(line);
1812
+ }
1813
+ if (!Number.isFinite(minIndent)) {
1814
+ return { lines: blockLines, nextIndex: index };
1815
+ }
1816
+ return {
1817
+ lines: blockLines.map((line) => line === "" ? "" : line.slice(Math.min(minIndent, line.length))),
1818
+ nextIndex: index
1819
+ };
1820
+ }
1821
+ function foldBlockScalarLines(lines) {
1822
+ let result = "";
1823
+ let pendingNewlines = 0;
1824
+ for (const line of lines) {
1825
+ if (line === "") {
1826
+ pendingNewlines++;
1827
+ continue;
1828
+ }
1829
+ if (result !== "") {
1830
+ if (pendingNewlines > 0)
1831
+ result += `
1832
+ `.repeat(pendingNewlines);
1833
+ else
1834
+ result += " ";
1835
+ }
1836
+ result += line;
1837
+ pendingNewlines = 0;
1838
+ }
1839
+ return result;
1840
+ }
1841
+ function blockScalarValue(style, lines) {
1842
+ return style === ">" ? foldBlockScalarLines(lines) : lines.join(`
1843
+ `);
1844
+ }
1783
1845
  function parseFrontmatter(raw) {
1784
1846
  const m = raw.match(FM_RE);
1785
1847
  if (!m)
@@ -1788,37 +1850,51 @@ function parseFrontmatter(raw) {
1788
1850
  const lines = (m[1] ?? "").split(`
1789
1851
  `);
1790
1852
  let parentKey = "";
1791
- for (const line of lines) {
1792
- const stripped = line.replace(/\r$/, "");
1793
- const indent = stripped.length - stripped.trimStart().length;
1853
+ for (let index = 0;index < lines.length; index++) {
1854
+ const stripped = (lines[index] ?? "").replace(/\r$/, "");
1855
+ const indent = lineIndent(stripped);
1794
1856
  if (indent > 0 && parentKey) {
1795
1857
  const colon2 = stripped.indexOf(":");
1796
1858
  if (colon2 === -1)
1797
1859
  continue;
1798
1860
  const key2 = stripped.slice(0, colon2).trim();
1799
- const val2 = stripped.slice(colon2 + 1).trim().replace(/^["']|["']$/g, "");
1861
+ const rawValue2 = stripped.slice(colon2 + 1).trim();
1800
1862
  if (!key2)
1801
1863
  continue;
1864
+ const style2 = parseBlockScalarStyle(rawValue2);
1802
1865
  const parent = meta[parentKey];
1803
- if (typeof parent === "object")
1804
- parent[key2] = val2;
1805
- else
1806
- meta[parentKey] = { [key2]: val2 };
1866
+ if (typeof parent !== "object")
1867
+ continue;
1868
+ if (style2) {
1869
+ const block = collectBlockScalarLines(lines, index + 1, indent);
1870
+ parent[key2] = blockScalarValue(style2, block.lines);
1871
+ index = block.nextIndex - 1;
1872
+ continue;
1873
+ }
1874
+ parent[key2] = trimQuotedValue(rawValue2);
1807
1875
  continue;
1808
1876
  }
1809
1877
  const colon = stripped.indexOf(":");
1810
1878
  if (colon === -1)
1811
1879
  continue;
1812
1880
  const key = stripped.slice(0, colon).trim();
1813
- const val = stripped.slice(colon + 1).trim().replace(/^["']|["']$/g, "");
1881
+ const rawValue = stripped.slice(colon + 1).trim();
1814
1882
  if (!key)
1815
1883
  continue;
1816
- if (val === "") {
1884
+ const style = parseBlockScalarStyle(rawValue);
1885
+ if (style) {
1886
+ const block = collectBlockScalarLines(lines, index + 1, indent);
1887
+ parentKey = "";
1888
+ meta[key] = blockScalarValue(style, block.lines);
1889
+ index = block.nextIndex - 1;
1890
+ continue;
1891
+ }
1892
+ if (rawValue === "") {
1817
1893
  parentKey = key;
1818
1894
  meta[key] = {};
1819
1895
  } else {
1820
1896
  parentKey = "";
1821
- meta[key] = val;
1897
+ meta[key] = trimQuotedValue(rawValue);
1822
1898
  }
1823
1899
  }
1824
1900
  return { meta, body: (m[2] ?? "").trim() };
@@ -5533,7 +5609,22 @@ async function* runTurn(options) {
5533
5609
  }
5534
5610
 
5535
5611
  // src/session/manager.ts
5612
+ import * as c10 from "yoctocolors";
5613
+
5614
+ // src/session/resume-command.ts
5536
5615
  import * as c9 from "yoctocolors";
5616
+ function buildResumeSessionCommand(sessionId) {
5617
+ return `mc -r ${sessionId}`;
5618
+ }
5619
+ function buildResumeSessionHint(sessionId, detail = "to continue this session.") {
5620
+ return `${c9.dim("Use")} ${c9.cyan(buildResumeSessionCommand(sessionId))} ${c9.dim(detail)}`;
5621
+ }
5622
+ function buildSessionExitMessage(sessionId) {
5623
+ return `${buildResumeSessionHint(sessionId)}
5624
+ ${c9.dim("Goodbye.")}`;
5625
+ }
5626
+
5627
+ // src/session/manager.ts
5537
5628
  function newSession(model, cwd) {
5538
5629
  const id = generateSessionId();
5539
5630
  const row = createSession({ id, cwd, model });
@@ -5568,21 +5659,21 @@ function renderSessionTable(footer) {
5568
5659
  if (sessions.length === 0)
5569
5660
  return false;
5570
5661
  writeln(`
5571
- ${c9.bold("Recent sessions:")}`);
5662
+ ${c10.bold("Recent sessions:")}`);
5572
5663
  for (const s of sessions) {
5573
5664
  const date = new Date(s.updated_at).toLocaleString();
5574
5665
  const cwd = tildePath(s.cwd);
5575
- const title = s.title || c9.dim("(untitled)");
5576
- writeln(` ${c9.dim(s.id.padEnd(14))} ${title.padEnd(30)} ${c9.cyan(s.model.split("/").pop() ?? s.model).padEnd(20)} ${c9.dim(cwd)} ${c9.dim(date)}`);
5666
+ const title = s.title || c10.dim("(untitled)");
5667
+ writeln(` ${c10.dim(s.id.padEnd(14))} ${title.padEnd(30)} ${c10.cyan(s.model.split("/").pop() ?? s.model).padEnd(20)} ${c10.dim(cwd)} ${c10.dim(date)}`);
5577
5668
  }
5578
5669
  writeln(`
5579
5670
  ${footer}`);
5580
5671
  return true;
5581
5672
  }
5582
5673
  function printSessionList() {
5583
- const shown = renderSessionTable(`${c9.dim("Use")} mc --resume <id> ${c9.dim("to continue a session.")}`);
5674
+ const shown = renderSessionTable(buildResumeSessionHint("<id>", "to continue a session."));
5584
5675
  if (!shown)
5585
- writeln(c9.dim("No sessions found."));
5676
+ writeln(c10.dim("No sessions found."));
5586
5677
  }
5587
5678
  function getMostRecentSession() {
5588
5679
  const sessions = listSessions(1);
@@ -5807,7 +5898,7 @@ class SessionRunner {
5807
5898
  }
5808
5899
  this.session = resumed;
5809
5900
  this.currentModel = this.session.model;
5810
- this.reporter.info(`Resumed session ${this.session.id} (${c10.cyan(this.currentModel)})`);
5901
+ this.reporter.info(`Resumed session ${this.session.id} (${c11.cyan(this.currentModel)})`);
5811
5902
  } else {
5812
5903
  this.session = newSession(this.currentModel, this.cwd);
5813
5904
  }
@@ -6249,7 +6340,7 @@ async function initAgent(opts) {
6249
6340
  for (const row of listMcpServers()) {
6250
6341
  try {
6251
6342
  await connectAndAddMcp(row.name);
6252
- opts.reporter.info(`MCP: connected ${c11.cyan(row.name)}`);
6343
+ opts.reporter.info(`MCP: connected ${c12.cyan(row.name)}`);
6253
6344
  } catch (e) {
6254
6345
  opts.reporter.error(`MCP: failed to connect ${row.name}: ${String(e)}`);
6255
6346
  }
@@ -6344,7 +6435,7 @@ async function runWithTeardown(opts) {
6344
6435
  }
6345
6436
 
6346
6437
  // src/cli/args.ts
6347
- import * as c12 from "yoctocolors";
6438
+ import * as c13 from "yoctocolors";
6348
6439
  function parseArgs(argv) {
6349
6440
  const args = {
6350
6441
  model: null,
@@ -6393,11 +6484,11 @@ function parseArgs(argv) {
6393
6484
  return args;
6394
6485
  }
6395
6486
  function printHelp() {
6396
- writeln(`${c12.bold("mini-coder")} \u2014 a small, fast CLI coding agent
6487
+ writeln(`${c13.bold("mini-coder")} \u2014 a small, fast CLI coding agent
6397
6488
  `);
6398
- writeln(`${c12.bold("Usage:")} mc [options] [prompt]
6489
+ writeln(`${c13.bold("Usage:")} mc [options] [prompt]
6399
6490
  `);
6400
- writeln(`${c12.bold("Options:")}`);
6491
+ writeln(`${c13.bold("Options:")}`);
6401
6492
  const opts = [
6402
6493
  ["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
6403
6494
  ["-c, --continue", "Continue the most recent session"],
@@ -6407,10 +6498,10 @@ function printHelp() {
6407
6498
  ["-h, --help", "Show this help"]
6408
6499
  ];
6409
6500
  for (const [flag, desc] of opts) {
6410
- writeln(` ${c12.cyan((flag ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
6501
+ writeln(` ${c13.cyan((flag ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
6411
6502
  }
6412
6503
  writeln(`
6413
- ${c12.bold("Provider env vars:")}`);
6504
+ ${c13.bold("Provider env vars:")}`);
6414
6505
  const envs = [
6415
6506
  ["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
6416
6507
  ["ANTHROPIC_API_KEY", "Anthropic direct"],
@@ -6421,15 +6512,15 @@ ${c12.bold("Provider env vars:")}`);
6421
6512
  ["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
6422
6513
  ];
6423
6514
  for (const [env, desc] of envs) {
6424
- writeln(` ${c12.yellow((env ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
6515
+ writeln(` ${c13.yellow((env ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
6425
6516
  }
6426
6517
  writeln(`
6427
- ${c12.bold("Examples:")}`);
6428
- writeln(` mc ${c12.dim("# interactive session")}`);
6429
- writeln(` mc "explain this codebase" ${c12.dim("# one-shot prompt then exit")}`);
6430
- writeln(` mc -c ${c12.dim("# continue last session")}`);
6431
- writeln(` mc -m ollama/llama3.2 ${c12.dim("# use local Ollama model")}`);
6432
- writeln(` mc -l ${c12.dim("# list sessions")}`);
6518
+ ${c13.bold("Examples:")}`);
6519
+ writeln(` mc ${c13.dim("# interactive session")}`);
6520
+ writeln(` mc "explain this codebase" ${c13.dim("# one-shot prompt then exit")}`);
6521
+ writeln(` mc -c ${c13.dim("# continue last session")}`);
6522
+ writeln(` mc -m ollama/llama3.2 ${c13.dim("# use local Ollama model")}`);
6523
+ writeln(` mc -l ${c13.dim("# list sessions")}`);
6433
6524
  }
6434
6525
 
6435
6526
  // src/cli/file-refs.ts
@@ -6464,26 +6555,23 @@ ${content}
6464
6555
  return { text: result, images };
6465
6556
  }
6466
6557
 
6467
- // src/cli/input-loop.ts
6468
- import * as c19 from "yoctocolors";
6469
-
6470
6558
  // src/cli/commands.ts
6471
6559
  import { randomBytes } from "crypto";
6472
6560
  import { unlinkSync, writeFileSync } from "fs";
6473
6561
  import { tmpdir } from "os";
6474
6562
  import { join as join8 } from "path";
6475
- import * as c18 from "yoctocolors";
6563
+ import * as c19 from "yoctocolors";
6476
6564
 
6477
6565
  // src/cli/commands-help.ts
6478
- import * as c13 from "yoctocolors";
6566
+ import * as c14 from "yoctocolors";
6479
6567
  function renderEntries(entries) {
6480
6568
  for (const [label, description] of entries) {
6481
- writeln(` ${c13.cyan(label.padEnd(28))} ${c13.dim(description)}`);
6569
+ writeln(` ${c14.cyan(label.padEnd(28))} ${c14.dim(description)}`);
6482
6570
  }
6483
6571
  }
6484
6572
  function renderHelpCommand(ctx) {
6485
6573
  writeln();
6486
- writeln(` ${c13.dim("session")}`);
6574
+ writeln(` ${c14.dim("session")}`);
6487
6575
  renderEntries([
6488
6576
  ["/session [id]", "list sessions or switch to one"],
6489
6577
  ["/new", "start a fresh session"],
@@ -6491,7 +6579,7 @@ function renderHelpCommand(ctx) {
6491
6579
  ["/exit", "quit"]
6492
6580
  ]);
6493
6581
  writeln();
6494
- writeln(` ${c13.dim("model + context")}`);
6582
+ writeln(` ${c14.dim("model + context")}`);
6495
6583
  renderEntries([
6496
6584
  ["/model [id]", "list or switch models"],
6497
6585
  ["/models [id]", "alias for /model"],
@@ -6505,7 +6593,7 @@ function renderHelpCommand(ctx) {
6505
6593
  ["/help", "show this help"]
6506
6594
  ]);
6507
6595
  writeln();
6508
- writeln(` ${c13.dim("prompt")}`);
6596
+ writeln(` ${c14.dim("prompt")}`);
6509
6597
  renderEntries([
6510
6598
  ["ask normally", "send a prompt to the current agent"],
6511
6599
  ["!cmd", "run a shell command and keep the result in context"],
@@ -6515,44 +6603,44 @@ function renderHelpCommand(ctx) {
6515
6603
  const skills = loadSkillsIndex(ctx.cwd);
6516
6604
  if (skills.size > 0) {
6517
6605
  writeln();
6518
- writeln(` ${c13.dim("skills")}`);
6606
+ writeln(` ${c14.dim("skills")}`);
6519
6607
  for (const skill of skills.values()) {
6520
- const source = skill.source === "local" ? c13.dim("local") : c13.dim("global");
6608
+ const source = skill.source === "local" ? c14.dim("local") : c14.dim("global");
6521
6609
  const desc = truncateText(skill.description, 80);
6522
- writeln(` ${c13.green(`/${skill.name}`.padEnd(28))} ${c13.dim(desc)} ${c13.dim("\xB7")} ${source}`);
6610
+ writeln(` ${c14.green(`/${skill.name}`.padEnd(28))} ${c14.dim(desc)} ${c14.dim("\xB7")} ${source}`);
6523
6611
  }
6524
6612
  }
6525
6613
  writeln();
6526
- writeln(` ${c13.dim("keys")} ${c13.dim("esc")} cancel response ${c13.dim("\xB7")} ${c13.dim("ctrl+c / ctrl+d")} exit ${c13.dim("\xB7")} ${c13.dim("ctrl+r")} history search ${c13.dim("\xB7")} ${c13.dim("\u2191\u2193")} history`);
6614
+ writeln(` ${c14.dim("keys")} ${c14.dim("esc")} cancel response ${c14.dim("\xB7")} ${c14.dim("ctrl+c / ctrl+d")} exit ${c14.dim("\xB7")} ${c14.dim("ctrl+r")} history search ${c14.dim("\xB7")} ${c14.dim("\u2191\u2193")} history`);
6527
6615
  writeln();
6528
6616
  }
6529
6617
 
6530
6618
  // src/cli/commands-login.ts
6531
- import * as c14 from "yoctocolors";
6619
+ import * as c15 from "yoctocolors";
6532
6620
  function renderLoginHelp() {
6533
6621
  writeln();
6534
- writeln(` ${c14.dim("usage:")}`);
6535
- writeln(` /login ${c14.dim("show login status")}`);
6536
- writeln(` /login <provider> ${c14.dim("login via OAuth")}`);
6537
- writeln(` /logout <provider> ${c14.dim("clear saved tokens")}`);
6622
+ writeln(` ${c15.dim("usage:")}`);
6623
+ writeln(` /login ${c15.dim("show login status")}`);
6624
+ writeln(` /login <provider> ${c15.dim("login via OAuth")}`);
6625
+ writeln(` /logout <provider> ${c15.dim("clear saved tokens")}`);
6538
6626
  writeln();
6539
- writeln(` ${c14.dim("providers:")}`);
6627
+ writeln(` ${c15.dim("providers:")}`);
6540
6628
  for (const p of getOAuthProviders()) {
6541
- const status = isLoggedIn(p.id) ? c14.green("logged in") : c14.dim("not logged in");
6542
- writeln(` ${c14.cyan(p.id.padEnd(20))} ${p.name} ${c14.dim("\xB7")} ${status}`);
6629
+ const status = isLoggedIn(p.id) ? c15.green("logged in") : c15.dim("not logged in");
6630
+ writeln(` ${c15.cyan(p.id.padEnd(20))} ${p.name} ${c15.dim("\xB7")} ${status}`);
6543
6631
  }
6544
6632
  writeln();
6545
6633
  }
6546
6634
  function renderStatus() {
6547
6635
  const loggedIn = listLoggedInProviders();
6548
6636
  if (loggedIn.length === 0) {
6549
- writeln(`${PREFIX.info} ${c14.dim("no OAuth logins \u2014 use")} /login <provider>`);
6637
+ writeln(`${PREFIX.info} ${c15.dim("no OAuth logins \u2014 use")} /login <provider>`);
6550
6638
  return;
6551
6639
  }
6552
6640
  for (const id of loggedIn) {
6553
6641
  const provider = getOAuthProvider(id);
6554
6642
  const name = provider?.name ?? id;
6555
- writeln(`${PREFIX.success} ${c14.cyan(id)} ${c14.dim(name)}`);
6643
+ writeln(`${PREFIX.success} ${c15.cyan(id)} ${c15.dim(name)}`);
6556
6644
  }
6557
6645
  }
6558
6646
  async function handleLoginCommand(ctx, args) {
@@ -6573,7 +6661,7 @@ async function handleLoginCommand(ctx, args) {
6573
6661
  if (isLoggedIn(providerId)) {
6574
6662
  const token = await getAccessToken(providerId);
6575
6663
  if (token) {
6576
- writeln(`${PREFIX.success} already logged in to ${c14.cyan(provider.name)}`);
6664
+ writeln(`${PREFIX.success} already logged in to ${c15.cyan(provider.name)}`);
6577
6665
  return;
6578
6666
  }
6579
6667
  }
@@ -6584,7 +6672,7 @@ async function handleLoginCommand(ctx, args) {
6584
6672
  ctx.stopSpinner();
6585
6673
  writeln(`${PREFIX.info} ${instructions}`);
6586
6674
  writeln();
6587
- writeln(` ${c14.cyan(url)}`);
6675
+ writeln(` ${c15.cyan(url)}`);
6588
6676
  writeln();
6589
6677
  let open = "xdg-open";
6590
6678
  if (process.platform === "darwin")
@@ -6596,12 +6684,12 @@ async function handleLoginCommand(ctx, args) {
6596
6684
  },
6597
6685
  onProgress: (msg) => {
6598
6686
  ctx.stopSpinner();
6599
- writeln(`${PREFIX.info} ${c14.dim(msg)}`);
6687
+ writeln(`${PREFIX.info} ${c15.dim(msg)}`);
6600
6688
  ctx.startSpinner("exchanging tokens");
6601
6689
  }
6602
6690
  });
6603
6691
  ctx.stopSpinner();
6604
- writeln(`${PREFIX.success} logged in to ${c14.cyan(provider.name)}`);
6692
+ writeln(`${PREFIX.success} logged in to ${c15.cyan(provider.name)}`);
6605
6693
  } catch (err) {
6606
6694
  ctx.stopSpinner();
6607
6695
  writeln(`${PREFIX.error} login failed: ${err.message}`);
@@ -6619,15 +6707,15 @@ function handleLogoutCommand(_ctx, args) {
6619
6707
  return;
6620
6708
  }
6621
6709
  if (!isLoggedIn(providerId)) {
6622
- writeln(`${PREFIX.info} ${c14.dim("not logged in to")} ${providerId}`);
6710
+ writeln(`${PREFIX.info} ${c15.dim("not logged in to")} ${providerId}`);
6623
6711
  return;
6624
6712
  }
6625
6713
  logout(providerId);
6626
- writeln(`${PREFIX.success} logged out of ${c14.cyan(provider.id)}`);
6714
+ writeln(`${PREFIX.success} logged out of ${c15.cyan(provider.id)}`);
6627
6715
  }
6628
6716
 
6629
6717
  // src/cli/commands-mcp.ts
6630
- import * as c15 from "yoctocolors";
6718
+ import * as c16 from "yoctocolors";
6631
6719
  async function handleMcpCommand(ctx, args) {
6632
6720
  const parts = args.trim().split(/\s+/);
6633
6721
  const sub = parts[0] ?? "list";
@@ -6635,15 +6723,15 @@ async function handleMcpCommand(ctx, args) {
6635
6723
  case "list": {
6636
6724
  const servers = listMcpServers();
6637
6725
  if (servers.length === 0) {
6638
- writeln(c15.dim(" no MCP servers configured"));
6639
- writeln(c15.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
6726
+ writeln(c16.dim(" no MCP servers configured"));
6727
+ writeln(c16.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
6640
6728
  return;
6641
6729
  }
6642
6730
  writeln();
6643
6731
  for (const s of servers) {
6644
6732
  const detailText = s.url ?? s.command ?? "";
6645
- const detail = detailText ? c15.dim(` ${detailText}`) : "";
6646
- writeln(` ${c15.yellow("\u2699")} ${c15.bold(s.name)} ${c15.dim(s.transport)}${detail}`);
6733
+ const detail = detailText ? c16.dim(` ${detailText}`) : "";
6734
+ writeln(` ${c16.yellow("\u2699")} ${c16.bold(s.name)} ${c16.dim(s.transport)}${detail}`);
6647
6735
  }
6648
6736
  return;
6649
6737
  }
@@ -6688,9 +6776,9 @@ async function handleMcpCommand(ctx, args) {
6688
6776
  }
6689
6777
  try {
6690
6778
  await ctx.connectMcpServer(name);
6691
- writeln(`${PREFIX.success} mcp server ${c15.cyan(name)} added and connected`);
6779
+ writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} added and connected`);
6692
6780
  } catch (e) {
6693
- writeln(`${PREFIX.success} mcp server ${c15.cyan(name)} saved ${c15.dim(`(connection failed: ${String(e)})`)}`);
6781
+ writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} saved ${c16.dim(`(connection failed: ${String(e)})`)}`);
6694
6782
  }
6695
6783
  return;
6696
6784
  }
@@ -6702,17 +6790,17 @@ async function handleMcpCommand(ctx, args) {
6702
6790
  return;
6703
6791
  }
6704
6792
  deleteMcpServer(name);
6705
- writeln(`${PREFIX.success} mcp server ${c15.cyan(name)} removed`);
6793
+ writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} removed`);
6706
6794
  return;
6707
6795
  }
6708
6796
  default:
6709
6797
  writeln(`${PREFIX.error} unknown: /mcp ${sub}`);
6710
- writeln(c15.dim(" subcommands: list \xB7 add \xB7 remove"));
6798
+ writeln(c16.dim(" subcommands: list \xB7 add \xB7 remove"));
6711
6799
  }
6712
6800
  }
6713
6801
 
6714
6802
  // src/cli/commands-model.ts
6715
- import * as c16 from "yoctocolors";
6803
+ import * as c17 from "yoctocolors";
6716
6804
  import { select } from "yoctoselect";
6717
6805
  var THINKING_EFFORTS = ["low", "medium", "high", "xhigh"];
6718
6806
  function parseThinkingEffort(value) {
@@ -6732,21 +6820,21 @@ function renderModelUpdatedMessage(ctx, modelId, effortArg) {
6732
6820
  if (effortArg) {
6733
6821
  if (effortArg === "off") {
6734
6822
  ctx.setThinkingEffort(null);
6735
- writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)} ${c16.dim("(thinking disabled)")}`);
6823
+ writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim("(thinking disabled)")}`);
6736
6824
  return;
6737
6825
  }
6738
6826
  const effort = parseThinkingEffort(effortArg);
6739
6827
  if (effort) {
6740
6828
  ctx.setThinkingEffort(effort);
6741
- writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)} ${c16.dim(`(\u2726 ${effort})`)}`);
6829
+ writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim(`(\u2726 ${effort})`)}`);
6742
6830
  return;
6743
6831
  }
6744
- writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)}`);
6745
- writeln(`${PREFIX.error} unknown effort level ${c16.cyan(effortArg)} (use low, medium, high, xhigh, off)`);
6832
+ writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)}`);
6833
+ writeln(`${PREFIX.error} unknown effort level ${c17.cyan(effortArg)} (use low, medium, high, xhigh, off)`);
6746
6834
  return;
6747
6835
  }
6748
- const effortTag = ctx.thinkingEffort ? c16.dim(` (\u2726 ${ctx.thinkingEffort})`) : "";
6749
- writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)}${effortTag}`);
6836
+ const effortTag = ctx.thinkingEffort ? c17.dim(` (\u2726 ${ctx.thinkingEffort})`) : "";
6837
+ writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)}${effortTag}`);
6750
6838
  }
6751
6839
  async function handleModelSet(ctx, args) {
6752
6840
  const parts = args.trim().split(/\s+/).filter(Boolean);
@@ -6759,7 +6847,7 @@ async function handleModelSet(ctx, args) {
6759
6847
  const snapshot = await fetchAvailableModels();
6760
6848
  const match = findModelIdByAlias(idArg, snapshot.models.map((model) => model.id));
6761
6849
  if (!match) {
6762
- writeln(`${PREFIX.error} unknown model ${c16.cyan(idArg)} ${c16.dim("\u2014 run /models for the full list")}`);
6850
+ writeln(`${PREFIX.error} unknown model ${c17.cyan(idArg)} ${c17.dim("\u2014 run /models for the full list")}`);
6763
6851
  return;
6764
6852
  }
6765
6853
  modelId = match;
@@ -6779,7 +6867,7 @@ function handleModelEffort(ctx, effortArg) {
6779
6867
  return;
6780
6868
  }
6781
6869
  ctx.setThinkingEffort(effort);
6782
- writeln(`${PREFIX.success} thinking effort \u2192 ${c16.cyan(effort)}`);
6870
+ writeln(`${PREFIX.success} thinking effort \u2192 ${c17.cyan(effort)}`);
6783
6871
  }
6784
6872
  async function handleModelSelect(ctx) {
6785
6873
  ctx.startSpinner("fetching models");
@@ -6787,20 +6875,20 @@ async function handleModelSelect(ctx) {
6787
6875
  ctx.stopSpinner();
6788
6876
  if (snapshot.models.length === 0) {
6789
6877
  writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
6790
- writeln(c16.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
6878
+ writeln(c17.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
6791
6879
  return;
6792
6880
  }
6793
6881
  if (snapshot.stale) {
6794
6882
  const lastSync = snapshot.lastSyncAt ? new Date(snapshot.lastSyncAt).toLocaleString() : "never";
6795
6883
  const refreshTag = snapshot.refreshing ? " (refreshing in background)" : "";
6796
- writeln(c16.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
6884
+ writeln(c17.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
6797
6885
  }
6798
6886
  const items = snapshot.models.map((model) => {
6799
6887
  const isCurrent = ctx.currentModel === model.id;
6800
- const freeTag = model.free ? c16.green(" free") : "";
6801
- const contextTag = model.context ? c16.dim(` ${Math.round(model.context / 1000)}k`) : "";
6802
- const currentTag = isCurrent ? c16.cyan(" \u25C0") : "";
6803
- const providerTag = c16.dim(` [${model.provider}]`);
6888
+ const freeTag = model.free ? c17.green(" free") : "";
6889
+ const contextTag = model.context ? c17.dim(` ${Math.round(model.context / 1000)}k`) : "";
6890
+ const currentTag = isCurrent ? c17.cyan(" \u25C0") : "";
6891
+ const providerTag = c17.dim(` [${model.provider}]`);
6804
6892
  return {
6805
6893
  label: `${model.displayName}${freeTag}${contextTag}${currentTag}${providerTag}`,
6806
6894
  value: model.id,
@@ -6833,7 +6921,7 @@ async function handleModelCommand(ctx, args) {
6833
6921
  }
6834
6922
 
6835
6923
  // src/cli/commands-session.ts
6836
- import * as c17 from "yoctocolors";
6924
+ import * as c18 from "yoctocolors";
6837
6925
  import { select as select2 } from "yoctoselect";
6838
6926
  async function handleSessionCommand(ctx, args) {
6839
6927
  const id = args.trim();
@@ -6842,15 +6930,15 @@ async function handleSessionCommand(ctx, args) {
6842
6930
  const ok2 = await ctx.switchSession(id);
6843
6931
  ctx.stopSpinner();
6844
6932
  if (ok2) {
6845
- writeln(`${PREFIX.success} switched to session ${c17.cyan(id)} (${c17.cyan(ctx.currentModel)})`);
6933
+ writeln(`${PREFIX.success} switched to session ${c18.cyan(id)} (${c18.cyan(ctx.currentModel)})`);
6846
6934
  } else {
6847
- writeln(`${PREFIX.error} session ${c17.cyan(id)} not found`);
6935
+ writeln(`${PREFIX.error} session ${c18.cyan(id)} not found`);
6848
6936
  }
6849
6937
  return;
6850
6938
  }
6851
6939
  const sessions = listSessions(50);
6852
6940
  if (sessions.length === 0) {
6853
- writeln(`${PREFIX.info} ${c17.dim("no sessions found")}`);
6941
+ writeln(`${PREFIX.info} ${c18.dim("no sessions found")}`);
6854
6942
  return;
6855
6943
  }
6856
6944
  const items = sessions.map((s) => {
@@ -6859,7 +6947,7 @@ async function handleSessionCommand(ctx, args) {
6859
6947
  const cwd = tildePath(s.cwd);
6860
6948
  const date = new Date(s.updated_at).toLocaleDateString();
6861
6949
  return {
6862
- label: `${c17.dim(s.id)} ${title} ${c17.cyan(model)} ${c17.dim(cwd)} ${c17.dim(date)}`,
6950
+ label: `${c18.dim(s.id)} ${title} ${c18.cyan(model)} ${c18.dim(cwd)} ${c18.dim(date)}`,
6863
6951
  value: s.id,
6864
6952
  filterText: `${s.id} ${s.title} ${s.model} ${s.cwd}`
6865
6953
  };
@@ -6875,9 +6963,9 @@ async function handleSessionCommand(ctx, args) {
6875
6963
  return;
6876
6964
  const ok = await ctx.switchSession(picked);
6877
6965
  if (ok) {
6878
- writeln(`${PREFIX.success} switched to session ${c17.cyan(picked)} (${c17.cyan(ctx.currentModel)})`);
6966
+ writeln(`${PREFIX.success} switched to session ${c18.cyan(picked)} (${c18.cyan(ctx.currentModel)})`);
6879
6967
  } else {
6880
- writeln(`${PREFIX.error} session ${c17.cyan(picked)} not found`);
6968
+ writeln(`${PREFIX.error} session ${c18.cyan(picked)} not found`);
6881
6969
  }
6882
6970
  }
6883
6971
 
@@ -6887,9 +6975,9 @@ async function handleUndo(ctx) {
6887
6975
  try {
6888
6976
  const ok = await ctx.undoLastTurn();
6889
6977
  if (ok) {
6890
- writeln(`${PREFIX.success} ${c18.dim("last conversation turn removed")}`);
6978
+ writeln(`${PREFIX.success} ${c19.dim("last conversation turn removed")}`);
6891
6979
  } else {
6892
- writeln(`${PREFIX.info} ${c18.dim("nothing to undo")}`);
6980
+ writeln(`${PREFIX.info} ${c19.dim("nothing to undo")}`);
6893
6981
  }
6894
6982
  } finally {
6895
6983
  ctx.stopSpinner();
@@ -6947,7 +7035,7 @@ async function handleCommand(command, args, ctx) {
6947
7035
  if (loaded2) {
6948
7036
  const srcPath = skill.source === "local" ? `.agents/skills/${skill.name}/SKILL.md` : `~/.agents/skills/${skill.name}/SKILL.md`;
6949
7037
  if (skill.context === "fork") {
6950
- writeln(`${PREFIX.info} ${c18.cyan(skill.name)} ${c18.dim(`[${srcPath}] (forked subagent)`)}`);
7038
+ writeln(`${PREFIX.info} ${c19.cyan(skill.name)} ${c19.dim(`[${srcPath}] (forked subagent)`)}`);
6951
7039
  writeln();
6952
7040
  const subagentPrompt = args ? `${loaded2.content}
6953
7041
 
@@ -6955,7 +7043,7 @@ ${args}` : loaded2.content;
6955
7043
  const result = await runForkedSkill(skill.name, subagentPrompt, ctx.cwd);
6956
7044
  return { type: "inject-user-message", text: result };
6957
7045
  }
6958
- writeln(`${PREFIX.info} ${c18.cyan(skill.name)} ${c18.dim(`[${srcPath}]`)}`);
7046
+ writeln(`${PREFIX.info} ${c19.cyan(skill.name)} ${c19.dim(`[${srcPath}]`)}`);
6959
7047
  writeln();
6960
7048
  const prompt = args ? `${loaded2.content}
6961
7049
 
@@ -6963,7 +7051,7 @@ ${args}` : loaded2.content;
6963
7051
  return { type: "inject-user-message", text: prompt };
6964
7052
  }
6965
7053
  }
6966
- writeln(`${PREFIX.error} unknown: /${command} ${c18.dim("\u2014 /help for commands")}`);
7054
+ writeln(`${PREFIX.error} unknown: /${command} ${c19.dim("\u2014 /help for commands")}`);
6967
7055
  return { type: "unknown", command };
6968
7056
  }
6969
7057
  }
@@ -6972,7 +7060,7 @@ async function runForkedSkill(skillName, prompt, cwd) {
6972
7060
  const tmpFile = join8(tmpdir(), `mc-fork-${randomBytes(8).toString("hex")}.md`);
6973
7061
  writeFileSync(tmpFile, prompt, "utf8");
6974
7062
  try {
6975
- writeln(`${PREFIX.info} ${c18.dim("running subagent\u2026")}`);
7063
+ writeln(`${PREFIX.info} ${c19.dim("running subagent\u2026")}`);
6976
7064
  const proc = Bun.spawn([process.execPath, Bun.main], {
6977
7065
  cwd,
6978
7066
  stdin: Bun.file(tmpFile),
@@ -7004,17 +7092,17 @@ ${output}`;
7004
7092
  function handleBooleanToggleCommand(opts) {
7005
7093
  const mode = opts.args.trim().toLowerCase();
7006
7094
  if (!mode) {
7007
- writeln(`${PREFIX.success} ${opts.label} ${opts.current ? c18.green("on") : c18.dim("off")}`);
7095
+ writeln(`${PREFIX.success} ${opts.label} ${opts.current ? c19.green("on") : c19.dim("off")}`);
7008
7096
  return;
7009
7097
  }
7010
7098
  if (mode === "on") {
7011
7099
  opts.set(true);
7012
- writeln(`${PREFIX.success} ${opts.label} ${c18.green("on")}`);
7100
+ writeln(`${PREFIX.success} ${opts.label} ${c19.green("on")}`);
7013
7101
  return;
7014
7102
  }
7015
7103
  if (mode === "off") {
7016
7104
  opts.set(false);
7017
- writeln(`${PREFIX.success} ${opts.label} ${c18.dim("off")}`);
7105
+ writeln(`${PREFIX.success} ${opts.label} ${c19.dim("off")}`);
7018
7106
  return;
7019
7107
  }
7020
7108
  writeln(`${PREFIX.error} usage: ${opts.usage}`);
@@ -7098,14 +7186,14 @@ async function runInputLoop(opts) {
7098
7186
  }
7099
7187
  switch (input.type) {
7100
7188
  case "eof":
7101
- reporter.writeText(c19.dim("Goodbye."));
7189
+ reporter.writeText(buildSessionExitMessage(runner.session.id));
7102
7190
  return;
7103
7191
  case "interrupt":
7104
7192
  continue;
7105
7193
  case "command": {
7106
7194
  const result = await handleCommand(input.command, input.args, cmdCtx);
7107
7195
  if (result.type === "exit") {
7108
- reporter.writeText(c19.dim("Goodbye."));
7196
+ reporter.writeText(buildSessionExitMessage(runner.session.id));
7109
7197
  return;
7110
7198
  }
7111
7199
  if (result.type === "inject-user-message") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mini-coder",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "A small, fast CLI coding agent",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",