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.
- package/dist/mc.js +193 -105
- 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
|
|
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.
|
|
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
|
|
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 (
|
|
1792
|
-
const stripped =
|
|
1793
|
-
const indent = stripped
|
|
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
|
|
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
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
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
|
|
1881
|
+
const rawValue = stripped.slice(colon + 1).trim();
|
|
1814
1882
|
if (!key)
|
|
1815
1883
|
continue;
|
|
1816
|
-
|
|
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] =
|
|
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
|
-
${
|
|
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 ||
|
|
5576
|
-
writeln(` ${
|
|
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(
|
|
5674
|
+
const shown = renderSessionTable(buildResumeSessionHint("<id>", "to continue a session."));
|
|
5584
5675
|
if (!shown)
|
|
5585
|
-
writeln(
|
|
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} (${
|
|
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 ${
|
|
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
|
|
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(`${
|
|
6487
|
+
writeln(`${c13.bold("mini-coder")} \u2014 a small, fast CLI coding agent
|
|
6397
6488
|
`);
|
|
6398
|
-
writeln(`${
|
|
6489
|
+
writeln(`${c13.bold("Usage:")} mc [options] [prompt]
|
|
6399
6490
|
`);
|
|
6400
|
-
writeln(`${
|
|
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(` ${
|
|
6501
|
+
writeln(` ${c13.cyan((flag ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
|
|
6411
6502
|
}
|
|
6412
6503
|
writeln(`
|
|
6413
|
-
${
|
|
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(` ${
|
|
6515
|
+
writeln(` ${c13.yellow((env ?? "").padEnd(22))} ${c13.dim(desc ?? "")}`);
|
|
6425
6516
|
}
|
|
6426
6517
|
writeln(`
|
|
6427
|
-
${
|
|
6428
|
-
writeln(` mc ${
|
|
6429
|
-
writeln(` mc "explain this codebase" ${
|
|
6430
|
-
writeln(` mc -c ${
|
|
6431
|
-
writeln(` mc -m ollama/llama3.2 ${
|
|
6432
|
-
writeln(` mc -l ${
|
|
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
|
|
6563
|
+
import * as c19 from "yoctocolors";
|
|
6476
6564
|
|
|
6477
6565
|
// src/cli/commands-help.ts
|
|
6478
|
-
import * as
|
|
6566
|
+
import * as c14 from "yoctocolors";
|
|
6479
6567
|
function renderEntries(entries) {
|
|
6480
6568
|
for (const [label, description] of entries) {
|
|
6481
|
-
writeln(` ${
|
|
6569
|
+
writeln(` ${c14.cyan(label.padEnd(28))} ${c14.dim(description)}`);
|
|
6482
6570
|
}
|
|
6483
6571
|
}
|
|
6484
6572
|
function renderHelpCommand(ctx) {
|
|
6485
6573
|
writeln();
|
|
6486
|
-
writeln(` ${
|
|
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(` ${
|
|
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(` ${
|
|
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(` ${
|
|
6606
|
+
writeln(` ${c14.dim("skills")}`);
|
|
6519
6607
|
for (const skill of skills.values()) {
|
|
6520
|
-
const source = skill.source === "local" ?
|
|
6608
|
+
const source = skill.source === "local" ? c14.dim("local") : c14.dim("global");
|
|
6521
6609
|
const desc = truncateText(skill.description, 80);
|
|
6522
|
-
writeln(` ${
|
|
6610
|
+
writeln(` ${c14.green(`/${skill.name}`.padEnd(28))} ${c14.dim(desc)} ${c14.dim("\xB7")} ${source}`);
|
|
6523
6611
|
}
|
|
6524
6612
|
}
|
|
6525
6613
|
writeln();
|
|
6526
|
-
writeln(` ${
|
|
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
|
|
6619
|
+
import * as c15 from "yoctocolors";
|
|
6532
6620
|
function renderLoginHelp() {
|
|
6533
6621
|
writeln();
|
|
6534
|
-
writeln(` ${
|
|
6535
|
-
writeln(` /login ${
|
|
6536
|
-
writeln(` /login <provider> ${
|
|
6537
|
-
writeln(` /logout <provider> ${
|
|
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(` ${
|
|
6627
|
+
writeln(` ${c15.dim("providers:")}`);
|
|
6540
6628
|
for (const p of getOAuthProviders()) {
|
|
6541
|
-
const status = isLoggedIn(p.id) ?
|
|
6542
|
-
writeln(` ${
|
|
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} ${
|
|
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} ${
|
|
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 ${
|
|
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(` ${
|
|
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} ${
|
|
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 ${
|
|
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} ${
|
|
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 ${
|
|
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
|
|
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(
|
|
6639
|
-
writeln(
|
|
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 ?
|
|
6646
|
-
writeln(` ${
|
|
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 ${
|
|
6779
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} added and connected`);
|
|
6692
6780
|
} catch (e) {
|
|
6693
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
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 ${
|
|
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(
|
|
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
|
|
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 ${
|
|
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 ${
|
|
6829
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim(`(\u2726 ${effort})`)}`);
|
|
6742
6830
|
return;
|
|
6743
6831
|
}
|
|
6744
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6745
|
-
writeln(`${PREFIX.error} unknown effort level ${
|
|
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 ?
|
|
6749
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
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(
|
|
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 ?
|
|
6801
|
-
const contextTag = model.context ?
|
|
6802
|
-
const currentTag = isCurrent ?
|
|
6803
|
-
const providerTag =
|
|
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
|
|
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 ${
|
|
6933
|
+
writeln(`${PREFIX.success} switched to session ${c18.cyan(id)} (${c18.cyan(ctx.currentModel)})`);
|
|
6846
6934
|
} else {
|
|
6847
|
-
writeln(`${PREFIX.error} session ${
|
|
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} ${
|
|
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: `${
|
|
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 ${
|
|
6966
|
+
writeln(`${PREFIX.success} switched to session ${c18.cyan(picked)} (${c18.cyan(ctx.currentModel)})`);
|
|
6879
6967
|
} else {
|
|
6880
|
-
writeln(`${PREFIX.error} session ${
|
|
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} ${
|
|
6978
|
+
writeln(`${PREFIX.success} ${c19.dim("last conversation turn removed")}`);
|
|
6891
6979
|
} else {
|
|
6892
|
-
writeln(`${PREFIX.info} ${
|
|
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} ${
|
|
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} ${
|
|
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} ${
|
|
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} ${
|
|
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 ?
|
|
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} ${
|
|
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} ${
|
|
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(
|
|
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(
|
|
7196
|
+
reporter.writeText(buildSessionExitMessage(runner.session.id));
|
|
7109
7197
|
return;
|
|
7110
7198
|
}
|
|
7111
7199
|
if (result.type === "inject-user-message") {
|