mini-coder 0.2.2 → 0.3.0
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/README.md +5 -3
- package/dist/mc.js +666 -438
- package/docs/design-decisions.md +31 -0
- package/docs/mini-coder.1.md +1 -1
- package/package.json +2 -2
package/dist/mc.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
|
-
import * as
|
|
5
|
+
import * as c21 from "yoctocolors";
|
|
6
6
|
|
|
7
7
|
// src/agent/agent.ts
|
|
8
|
-
import * as
|
|
8
|
+
import * as c11 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.
|
|
16
|
+
var PACKAGE_VERSION = "0.3.0";
|
|
17
17
|
|
|
18
18
|
// src/mcp/client.ts
|
|
19
19
|
async function connectMcpServer(config) {
|
|
@@ -339,7 +339,7 @@ function deleteMcpServer(name) {
|
|
|
339
339
|
}
|
|
340
340
|
// src/cli/output.ts
|
|
341
341
|
import { homedir as homedir4 } from "os";
|
|
342
|
-
import * as
|
|
342
|
+
import * as c7 from "yoctocolors";
|
|
343
343
|
|
|
344
344
|
// src/agent/context-files.ts
|
|
345
345
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
@@ -577,9 +577,153 @@ var anthropicOAuth = {
|
|
|
577
577
|
refreshToken: refreshAnthropicToken
|
|
578
578
|
};
|
|
579
579
|
|
|
580
|
+
// src/session/oauth/openai.ts
|
|
581
|
+
import { createServer as createServer2 } from "http";
|
|
582
|
+
var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
583
|
+
var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
|
|
584
|
+
var TOKEN_URL2 = "https://auth.openai.com/oauth/token";
|
|
585
|
+
var CALLBACK_HOST2 = "127.0.0.1";
|
|
586
|
+
var CALLBACK_PORT2 = 1455;
|
|
587
|
+
var CALLBACK_PATH2 = "/auth/callback";
|
|
588
|
+
var REDIRECT_URI2 = `http://localhost:${CALLBACK_PORT2}${CALLBACK_PATH2}`;
|
|
589
|
+
var SCOPES2 = "openid profile email offline_access";
|
|
590
|
+
var LOGIN_TIMEOUT_MS2 = 5 * 60 * 1000;
|
|
591
|
+
var SUCCESS_HTML2 = `<!doctype html>
|
|
592
|
+
<html lang="en"><head><meta charset="utf-8"><title>Authenticated</title></head>
|
|
593
|
+
<body><p>OpenAI authentication successful. Return to your terminal.</p></body></html>`;
|
|
594
|
+
function createState() {
|
|
595
|
+
const bytes = new Uint8Array(16);
|
|
596
|
+
crypto.getRandomValues(bytes);
|
|
597
|
+
let hex = "";
|
|
598
|
+
for (const b of bytes)
|
|
599
|
+
hex += b.toString(16).padStart(2, "0");
|
|
600
|
+
return hex;
|
|
601
|
+
}
|
|
602
|
+
function startCallbackServer2(expectedState) {
|
|
603
|
+
return new Promise((resolve2, reject) => {
|
|
604
|
+
let result = null;
|
|
605
|
+
let cancelled = false;
|
|
606
|
+
const server = createServer2((req, res) => {
|
|
607
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
608
|
+
if (url.pathname !== CALLBACK_PATH2) {
|
|
609
|
+
res.writeHead(404).end("Not found");
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const code = url.searchParams.get("code");
|
|
613
|
+
const state = url.searchParams.get("state");
|
|
614
|
+
const error = url.searchParams.get("error");
|
|
615
|
+
if (error || !code || !state || state !== expectedState) {
|
|
616
|
+
res.writeHead(400).end("Authentication failed.");
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
res.writeHead(200, { "Content-Type": "text/html" }).end(SUCCESS_HTML2);
|
|
620
|
+
result = { code };
|
|
621
|
+
});
|
|
622
|
+
server.on("error", reject);
|
|
623
|
+
server.listen(CALLBACK_PORT2, CALLBACK_HOST2, () => {
|
|
624
|
+
resolve2({
|
|
625
|
+
server,
|
|
626
|
+
cancel: () => {
|
|
627
|
+
cancelled = true;
|
|
628
|
+
},
|
|
629
|
+
waitForCode: async () => {
|
|
630
|
+
const deadline = Date.now() + LOGIN_TIMEOUT_MS2;
|
|
631
|
+
while (!result && !cancelled && Date.now() < deadline) {
|
|
632
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
633
|
+
}
|
|
634
|
+
return result;
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
async function postTokenRequest2(body, label) {
|
|
641
|
+
const res = await fetch(TOKEN_URL2, {
|
|
642
|
+
method: "POST",
|
|
643
|
+
headers: {
|
|
644
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
645
|
+
},
|
|
646
|
+
body,
|
|
647
|
+
signal: AbortSignal.timeout(30000)
|
|
648
|
+
});
|
|
649
|
+
if (!res.ok) {
|
|
650
|
+
const text = await res.text();
|
|
651
|
+
throw new Error(`${label} failed (${res.status}): ${text}`);
|
|
652
|
+
}
|
|
653
|
+
const data = await res.json();
|
|
654
|
+
return {
|
|
655
|
+
access: data.access_token,
|
|
656
|
+
refresh: data.refresh_token,
|
|
657
|
+
expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
function exchangeCode2(code, verifier) {
|
|
661
|
+
return postTokenRequest2(new URLSearchParams({
|
|
662
|
+
grant_type: "authorization_code",
|
|
663
|
+
client_id: CLIENT_ID2,
|
|
664
|
+
code,
|
|
665
|
+
code_verifier: verifier,
|
|
666
|
+
redirect_uri: REDIRECT_URI2
|
|
667
|
+
}), "Token exchange");
|
|
668
|
+
}
|
|
669
|
+
function refreshOpenAIToken(refreshToken) {
|
|
670
|
+
return postTokenRequest2(new URLSearchParams({
|
|
671
|
+
grant_type: "refresh_token",
|
|
672
|
+
refresh_token: refreshToken,
|
|
673
|
+
client_id: CLIENT_ID2
|
|
674
|
+
}), "Token refresh");
|
|
675
|
+
}
|
|
676
|
+
async function loginOpenAI(callbacks) {
|
|
677
|
+
const { verifier, challenge } = await generatePKCE();
|
|
678
|
+
const state = createState();
|
|
679
|
+
const cb = await startCallbackServer2(state);
|
|
680
|
+
try {
|
|
681
|
+
const params = new URLSearchParams({
|
|
682
|
+
response_type: "code",
|
|
683
|
+
client_id: CLIENT_ID2,
|
|
684
|
+
redirect_uri: REDIRECT_URI2,
|
|
685
|
+
scope: SCOPES2,
|
|
686
|
+
code_challenge: challenge,
|
|
687
|
+
code_challenge_method: "S256",
|
|
688
|
+
state,
|
|
689
|
+
id_token_add_organizations: "true",
|
|
690
|
+
codex_cli_simplified_flow: "true",
|
|
691
|
+
originator: "mc"
|
|
692
|
+
});
|
|
693
|
+
callbacks.onOpenUrl(`${AUTHORIZE_URL2}?${params}`, "Complete login in your browser.");
|
|
694
|
+
const result = await cb.waitForCode();
|
|
695
|
+
if (!result)
|
|
696
|
+
throw new Error("Login cancelled or no code received");
|
|
697
|
+
callbacks.onProgress("Exchanging authorization code for tokens\u2026");
|
|
698
|
+
return exchangeCode2(result.code, verifier);
|
|
699
|
+
} finally {
|
|
700
|
+
cb.server.close();
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function extractAccountId(accessToken) {
|
|
704
|
+
try {
|
|
705
|
+
const parts = accessToken.split(".");
|
|
706
|
+
const jwt = parts[1];
|
|
707
|
+
if (parts.length !== 3 || !jwt)
|
|
708
|
+
return null;
|
|
709
|
+
const payload = JSON.parse(atob(jwt));
|
|
710
|
+
const auth = payload["https://api.openai.com/auth"];
|
|
711
|
+
return auth?.chatgpt_account_id ?? null;
|
|
712
|
+
} catch {
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
var openaiOAuth = {
|
|
717
|
+
id: "openai",
|
|
718
|
+
name: "OpenAI (ChatGPT Plus/Pro)",
|
|
719
|
+
login: loginOpenAI,
|
|
720
|
+
refreshToken: refreshOpenAIToken
|
|
721
|
+
};
|
|
722
|
+
|
|
580
723
|
// src/session/oauth/auth-storage.ts
|
|
581
724
|
var PROVIDERS = new Map([
|
|
582
|
-
[anthropicOAuth.id, anthropicOAuth]
|
|
725
|
+
[anthropicOAuth.id, anthropicOAuth],
|
|
726
|
+
[openaiOAuth.id, openaiOAuth]
|
|
583
727
|
]);
|
|
584
728
|
function getOAuthProviders() {
|
|
585
729
|
return [...PROVIDERS.values()];
|
|
@@ -1140,15 +1284,42 @@ async function fetchZenModels() {
|
|
|
1140
1284
|
});
|
|
1141
1285
|
}
|
|
1142
1286
|
async function fetchOpenAIModels() {
|
|
1143
|
-
const
|
|
1144
|
-
if (
|
|
1287
|
+
const envKey = process.env.OPENAI_API_KEY;
|
|
1288
|
+
if (envKey) {
|
|
1289
|
+
return fetchPaginatedModelsList(`${OPENAI_BASE}/v1/models`, { headers: { Authorization: `Bearer ${envKey}` } }, 6000, "data", "id", (_item, modelId) => ({
|
|
1290
|
+
providerModelId: modelId,
|
|
1291
|
+
displayName: modelId,
|
|
1292
|
+
contextWindow: null,
|
|
1293
|
+
free: false
|
|
1294
|
+
}));
|
|
1295
|
+
}
|
|
1296
|
+
if (isLoggedIn("openai")) {
|
|
1297
|
+
const token = await getAccessToken("openai");
|
|
1298
|
+
if (!token)
|
|
1299
|
+
return null;
|
|
1300
|
+
return fetchCodexOAuthModels(token);
|
|
1301
|
+
}
|
|
1302
|
+
return null;
|
|
1303
|
+
}
|
|
1304
|
+
var CODEX_MODELS_URL = "https://chatgpt.com/backend-api/codex/models?client_version=1.0.0";
|
|
1305
|
+
async function fetchCodexOAuthModels(token) {
|
|
1306
|
+
try {
|
|
1307
|
+
const res = await fetch(CODEX_MODELS_URL, {
|
|
1308
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1309
|
+
signal: AbortSignal.timeout(6000)
|
|
1310
|
+
});
|
|
1311
|
+
if (!res.ok)
|
|
1312
|
+
return null;
|
|
1313
|
+
const data = await res.json();
|
|
1314
|
+
return (data.models ?? []).filter((m) => m.visibility === "list").map((m) => ({
|
|
1315
|
+
providerModelId: m.slug,
|
|
1316
|
+
displayName: m.display_name || m.slug,
|
|
1317
|
+
contextWindow: m.context_window,
|
|
1318
|
+
free: false
|
|
1319
|
+
}));
|
|
1320
|
+
} catch {
|
|
1145
1321
|
return null;
|
|
1146
|
-
|
|
1147
|
-
providerModelId: modelId,
|
|
1148
|
-
displayName: modelId,
|
|
1149
|
-
contextWindow: null,
|
|
1150
|
-
free: false
|
|
1151
|
-
}));
|
|
1322
|
+
}
|
|
1152
1323
|
}
|
|
1153
1324
|
async function getAnthropicAuth() {
|
|
1154
1325
|
const envKey = process.env.ANTHROPIC_API_KEY;
|
|
@@ -1236,7 +1407,7 @@ async function fetchProviderCandidates(provider) {
|
|
|
1236
1407
|
var MODELS_DEV_SYNC_KEY = "last_models_dev_sync_at";
|
|
1237
1408
|
var PROVIDER_SYNC_KEY_PREFIX = "last_provider_sync_at:";
|
|
1238
1409
|
var CACHE_VERSION_KEY = "model_info_cache_version";
|
|
1239
|
-
var CACHE_VERSION =
|
|
1410
|
+
var CACHE_VERSION = 5;
|
|
1240
1411
|
var MODEL_INFO_TTL_MS = 24 * 60 * 60 * 1000;
|
|
1241
1412
|
var REMOTE_PROVIDER_ENV_KEYS = [
|
|
1242
1413
|
{ provider: "zen", envKeys: ["OPENCODE_API_KEY"] },
|
|
@@ -1281,8 +1452,10 @@ function hasAnyEnvKey(env, keys) {
|
|
|
1281
1452
|
}
|
|
1282
1453
|
function getRemoteProvidersFromEnv(env) {
|
|
1283
1454
|
const providers = REMOTE_PROVIDER_ENV_KEYS.filter((entry) => hasAnyEnvKey(env, entry.envKeys)).map((entry) => entry.provider);
|
|
1284
|
-
|
|
1285
|
-
providers.
|
|
1455
|
+
for (const p of ["anthropic", "openai"]) {
|
|
1456
|
+
if (!providers.includes(p) && isLoggedIn(p)) {
|
|
1457
|
+
providers.push(p);
|
|
1458
|
+
}
|
|
1286
1459
|
}
|
|
1287
1460
|
return providers;
|
|
1288
1461
|
}
|
|
@@ -1586,10 +1759,64 @@ var ZEN_BACKEND_RESOLVERS = {
|
|
|
1586
1759
|
function resolveZenModel(modelId) {
|
|
1587
1760
|
return ZEN_BACKEND_RESOLVERS[getZenBackend(modelId)](modelId);
|
|
1588
1761
|
}
|
|
1589
|
-
function
|
|
1762
|
+
function createOAuthOpenAIProvider(token) {
|
|
1763
|
+
const accountId = extractAccountId(token);
|
|
1764
|
+
return createOpenAI({
|
|
1765
|
+
apiKey: "oauth",
|
|
1766
|
+
baseURL: OPENAI_CODEX_BASE_URL,
|
|
1767
|
+
fetch: (input, init) => {
|
|
1768
|
+
const h = new Headers(init?.headers);
|
|
1769
|
+
h.delete("OpenAI-Organization");
|
|
1770
|
+
h.delete("OpenAI-Project");
|
|
1771
|
+
h.set("Authorization", `Bearer ${token}`);
|
|
1772
|
+
if (accountId)
|
|
1773
|
+
h.set("chatgpt-account-id", accountId);
|
|
1774
|
+
let body = init?.body;
|
|
1775
|
+
if (typeof body === "string") {
|
|
1776
|
+
try {
|
|
1777
|
+
const parsed = JSON.parse(body);
|
|
1778
|
+
if (parsed.input && Array.isArray(parsed.input)) {
|
|
1779
|
+
if (!parsed.instructions) {
|
|
1780
|
+
const sysIdx = parsed.input.findIndex((m) => m.role === "developer" || m.role === "system");
|
|
1781
|
+
if (sysIdx !== -1) {
|
|
1782
|
+
const sysMsg = parsed.input[sysIdx];
|
|
1783
|
+
parsed.instructions = typeof sysMsg.content === "string" ? sysMsg.content : JSON.stringify(sysMsg.content);
|
|
1784
|
+
parsed.input.splice(sysIdx, 1);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
delete parsed.max_output_tokens;
|
|
1788
|
+
parsed.store = false;
|
|
1789
|
+
parsed.stream = true;
|
|
1790
|
+
body = JSON.stringify(parsed);
|
|
1791
|
+
}
|
|
1792
|
+
} catch {}
|
|
1793
|
+
}
|
|
1794
|
+
return fetch(input, {
|
|
1795
|
+
...init,
|
|
1796
|
+
body,
|
|
1797
|
+
headers: Object.fromEntries(h.entries())
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
async function resolveOpenAIModel(modelId) {
|
|
1803
|
+
if (isLoggedIn("openai")) {
|
|
1804
|
+
const token = await getAccessToken("openai");
|
|
1805
|
+
if (token) {
|
|
1806
|
+
if (!oauthOpenAICache || oauthOpenAICache.token !== token) {
|
|
1807
|
+
oauthOpenAICache = {
|
|
1808
|
+
token,
|
|
1809
|
+
provider: createOAuthOpenAIProvider(token)
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
return oauthOpenAICache.provider.responses(modelId);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1590
1815
|
return modelId.startsWith("gpt-") ? directProviders.openai().responses(modelId) : directProviders.openai()(modelId);
|
|
1591
1816
|
}
|
|
1817
|
+
var OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
1592
1818
|
var oauthAnthropicCache = null;
|
|
1819
|
+
var oauthOpenAICache = null;
|
|
1593
1820
|
function createOAuthAnthropicProvider(token) {
|
|
1594
1821
|
return createAnthropic({
|
|
1595
1822
|
apiKey: "",
|
|
@@ -1647,7 +1874,9 @@ function discoverConnectedProviders() {
|
|
|
1647
1874
|
result.push({ name: "anthropic", via: "oauth" });
|
|
1648
1875
|
else if (process.env.ANTHROPIC_API_KEY)
|
|
1649
1876
|
result.push({ name: "anthropic", via: "env" });
|
|
1650
|
-
if (
|
|
1877
|
+
if (isLoggedIn("openai"))
|
|
1878
|
+
result.push({ name: "openai", via: "oauth" });
|
|
1879
|
+
else if (process.env.OPENAI_API_KEY)
|
|
1651
1880
|
result.push({ name: "openai", via: "env" });
|
|
1652
1881
|
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY)
|
|
1653
1882
|
result.push({ name: "google", via: "env" });
|
|
@@ -1660,7 +1889,7 @@ function autoDiscoverModel() {
|
|
|
1660
1889
|
return "zen/claude-sonnet-4-6";
|
|
1661
1890
|
if (process.env.ANTHROPIC_API_KEY || isLoggedIn("anthropic"))
|
|
1662
1891
|
return "anthropic/claude-sonnet-4-6";
|
|
1663
|
-
if (process.env.OPENAI_API_KEY)
|
|
1892
|
+
if (process.env.OPENAI_API_KEY || isLoggedIn("openai"))
|
|
1664
1893
|
return "openai/gpt-5.4";
|
|
1665
1894
|
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY)
|
|
1666
1895
|
return "google/gemini-3.1-pro";
|
|
@@ -2290,9 +2519,6 @@ class Spinner {
|
|
|
2290
2519
|
terminal.setBeforeWriteCallback(null);
|
|
2291
2520
|
terminal.stderrWrite("\r\x1B[2K\x1B[?25h");
|
|
2292
2521
|
}
|
|
2293
|
-
update(label) {
|
|
2294
|
-
this.label = label;
|
|
2295
|
-
}
|
|
2296
2522
|
_tick() {
|
|
2297
2523
|
const f = SPINNER_FRAMES[this.frame++ % SPINNER_FRAMES.length] ?? "\u28FE";
|
|
2298
2524
|
const label = this.label ? c2.dim(` ${this.label}`) : "";
|
|
@@ -2346,19 +2572,6 @@ function buildContextSegment(opts) {
|
|
|
2346
2572
|
function renderStatusLine(segments) {
|
|
2347
2573
|
return segments.join(STATUS_SEP);
|
|
2348
2574
|
}
|
|
2349
|
-
function buildStatusBarSignature(opts) {
|
|
2350
|
-
return JSON.stringify({
|
|
2351
|
-
model: opts.model,
|
|
2352
|
-
cwd: opts.cwd,
|
|
2353
|
-
gitBranch: opts.gitBranch,
|
|
2354
|
-
sessionId: opts.sessionId,
|
|
2355
|
-
inputTokens: opts.inputTokens,
|
|
2356
|
-
outputTokens: opts.outputTokens,
|
|
2357
|
-
contextTokens: opts.contextTokens,
|
|
2358
|
-
contextWindow: opts.contextWindow,
|
|
2359
|
-
thinkingEffort: opts.thinkingEffort ?? null
|
|
2360
|
-
});
|
|
2361
|
-
}
|
|
2362
2575
|
function fitStatusSegments(required, optional, cols) {
|
|
2363
2576
|
const fittedOptional = [...optional];
|
|
2364
2577
|
let line = renderStatusLine([...required, ...fittedOptional]);
|
|
@@ -2400,7 +2613,8 @@ function renderStatusBar(opts) {
|
|
|
2400
2613
|
}
|
|
2401
2614
|
|
|
2402
2615
|
// src/cli/stream-render.ts
|
|
2403
|
-
import * as
|
|
2616
|
+
import * as c6 from "yoctocolors";
|
|
2617
|
+
import { createHighlighter } from "yoctomarkdown";
|
|
2404
2618
|
|
|
2405
2619
|
// src/llm-api/history/gemini.ts
|
|
2406
2620
|
function getGeminiThoughtSignature(part) {
|
|
@@ -2658,60 +2872,6 @@ function extractAssistantText(newMessages) {
|
|
|
2658
2872
|
`);
|
|
2659
2873
|
}
|
|
2660
2874
|
|
|
2661
|
-
// src/cli/live-reasoning.ts
|
|
2662
|
-
import * as c4 from "yoctocolors";
|
|
2663
|
-
function styleReasoningText(text) {
|
|
2664
|
-
return c4.italic(c4.dim(text));
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
class LiveReasoningBlock {
|
|
2668
|
-
blockOpen = false;
|
|
2669
|
-
lineOpen = false;
|
|
2670
|
-
append(delta) {
|
|
2671
|
-
if (!delta)
|
|
2672
|
-
return;
|
|
2673
|
-
this.openBlock();
|
|
2674
|
-
const lines = delta.split(`
|
|
2675
|
-
`);
|
|
2676
|
-
for (const [index, line] of lines.entries()) {
|
|
2677
|
-
if (line)
|
|
2678
|
-
this.writeText(line);
|
|
2679
|
-
if (index < lines.length - 1)
|
|
2680
|
-
this.endLine();
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2683
|
-
isOpen() {
|
|
2684
|
-
return this.blockOpen;
|
|
2685
|
-
}
|
|
2686
|
-
finish() {
|
|
2687
|
-
if (!this.blockOpen)
|
|
2688
|
-
return;
|
|
2689
|
-
if (this.lineOpen)
|
|
2690
|
-
writeln();
|
|
2691
|
-
this.blockOpen = false;
|
|
2692
|
-
this.lineOpen = false;
|
|
2693
|
-
}
|
|
2694
|
-
openBlock() {
|
|
2695
|
-
if (this.blockOpen)
|
|
2696
|
-
return;
|
|
2697
|
-
writeln(`${G.info} ${c4.dim("reasoning")}`);
|
|
2698
|
-
this.blockOpen = true;
|
|
2699
|
-
}
|
|
2700
|
-
writeText(text) {
|
|
2701
|
-
if (!this.lineOpen) {
|
|
2702
|
-
write(" ");
|
|
2703
|
-
this.lineOpen = true;
|
|
2704
|
-
}
|
|
2705
|
-
write(styleReasoningText(text));
|
|
2706
|
-
}
|
|
2707
|
-
endLine() {
|
|
2708
|
-
if (!this.lineOpen)
|
|
2709
|
-
write(" ");
|
|
2710
|
-
writeln();
|
|
2711
|
-
this.lineOpen = false;
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
2875
|
// src/cli/reasoning.ts
|
|
2716
2876
|
function normalizeReasoningDelta(delta) {
|
|
2717
2877
|
return delta.replace(/\r\n?/g, `
|
|
@@ -2745,130 +2905,34 @@ function normalizeReasoningText(text) {
|
|
|
2745
2905
|
`);
|
|
2746
2906
|
}
|
|
2747
2907
|
|
|
2748
|
-
// src/cli/stream-render-content.ts
|
|
2749
|
-
import { createHighlighter } from "yoctomarkdown";
|
|
2750
|
-
class StreamRenderContent {
|
|
2751
|
-
spinner;
|
|
2752
|
-
quiet;
|
|
2753
|
-
inText = false;
|
|
2754
|
-
accumulatedText = "";
|
|
2755
|
-
accumulatedReasoning = "";
|
|
2756
|
-
highlighter;
|
|
2757
|
-
constructor(spinner, quiet = false) {
|
|
2758
|
-
this.spinner = spinner;
|
|
2759
|
-
this.quiet = quiet;
|
|
2760
|
-
}
|
|
2761
|
-
getText() {
|
|
2762
|
-
return this.accumulatedText;
|
|
2763
|
-
}
|
|
2764
|
-
getReasoning() {
|
|
2765
|
-
return this.accumulatedReasoning;
|
|
2766
|
-
}
|
|
2767
|
-
hasOpenContent() {
|
|
2768
|
-
return this.inText;
|
|
2769
|
-
}
|
|
2770
|
-
appendTextDelta(delta, renderedVisibleOutput) {
|
|
2771
|
-
let text = delta ?? "";
|
|
2772
|
-
if (!text)
|
|
2773
|
-
return;
|
|
2774
|
-
if (!this.inText) {
|
|
2775
|
-
text = text.trimStart();
|
|
2776
|
-
if (!text)
|
|
2777
|
-
return;
|
|
2778
|
-
if (!this.quiet) {
|
|
2779
|
-
this.spinner.stop();
|
|
2780
|
-
if (renderedVisibleOutput)
|
|
2781
|
-
writeln();
|
|
2782
|
-
write(`${G.reply} `);
|
|
2783
|
-
}
|
|
2784
|
-
this.inText = true;
|
|
2785
|
-
if (!this.quiet && terminal.isStdoutTTY)
|
|
2786
|
-
this.highlighter = createHighlighter();
|
|
2787
|
-
}
|
|
2788
|
-
const isFirstLine = !this.accumulatedText.includes(`
|
|
2789
|
-
`);
|
|
2790
|
-
this.accumulatedText += text;
|
|
2791
|
-
if (this.quiet)
|
|
2792
|
-
return;
|
|
2793
|
-
this.spinner.stop();
|
|
2794
|
-
if (this.highlighter) {
|
|
2795
|
-
let colored = this.highlighter.write(text);
|
|
2796
|
-
if (colored) {
|
|
2797
|
-
if (isFirstLine && colored.startsWith("\x1B[2K\r")) {
|
|
2798
|
-
colored = `\x1B[2K\r${G.reply} ${colored.slice(5)}`;
|
|
2799
|
-
}
|
|
2800
|
-
write(colored);
|
|
2801
|
-
}
|
|
2802
|
-
} else {
|
|
2803
|
-
write(text);
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
appendReasoningDelta(delta) {
|
|
2807
|
-
const text = delta ?? "";
|
|
2808
|
-
if (!text)
|
|
2809
|
-
return "";
|
|
2810
|
-
let appended = text;
|
|
2811
|
-
if (this.accumulatedReasoning.endsWith("**") && text.startsWith("**") && !this.accumulatedReasoning.endsWith(`
|
|
2812
|
-
`)) {
|
|
2813
|
-
appended = `
|
|
2814
|
-
${text}`;
|
|
2815
|
-
}
|
|
2816
|
-
this.accumulatedReasoning += appended;
|
|
2817
|
-
return appended;
|
|
2818
|
-
}
|
|
2819
|
-
flushOpenContent() {
|
|
2820
|
-
if (!this.inText)
|
|
2821
|
-
return;
|
|
2822
|
-
if (this.quiet) {
|
|
2823
|
-
this.inText = false;
|
|
2824
|
-
return;
|
|
2825
|
-
}
|
|
2826
|
-
if (this.highlighter) {
|
|
2827
|
-
let finalColored = this.highlighter.end();
|
|
2828
|
-
if (finalColored) {
|
|
2829
|
-
const isFirstLine = !this.accumulatedText.includes(`
|
|
2830
|
-
`);
|
|
2831
|
-
if (isFirstLine && finalColored.startsWith("\x1B[2K\r")) {
|
|
2832
|
-
finalColored = `\x1B[2K\r${G.reply} ${finalColored.slice(5)}`;
|
|
2833
|
-
}
|
|
2834
|
-
write(finalColored);
|
|
2835
|
-
}
|
|
2836
|
-
}
|
|
2837
|
-
writeln();
|
|
2838
|
-
this.inText = false;
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2841
|
-
|
|
2842
2908
|
// src/cli/tool-render.ts
|
|
2843
|
-
import * as
|
|
2909
|
+
import * as c5 from "yoctocolors";
|
|
2844
2910
|
|
|
2845
2911
|
// src/cli/tool-result-renderers.ts
|
|
2846
|
-
import * as
|
|
2912
|
+
import * as c4 from "yoctocolors";
|
|
2847
2913
|
function writePreviewLines(opts) {
|
|
2848
2914
|
if (!opts.value.trim())
|
|
2849
2915
|
return;
|
|
2850
2916
|
const lines = opts.value.split(`
|
|
2851
2917
|
`);
|
|
2852
|
-
|
|
2918
|
+
if (opts.label)
|
|
2919
|
+
writeln(`${c4.dim(opts.label)} ${c4.dim(`(${lines.length} lines)`)}`);
|
|
2853
2920
|
if (!Number.isFinite(opts.maxLines) || lines.length <= opts.maxLines) {
|
|
2854
|
-
for (const line of lines)
|
|
2855
|
-
writeln(
|
|
2856
|
-
}
|
|
2921
|
+
for (const line of lines)
|
|
2922
|
+
writeln(line);
|
|
2857
2923
|
return;
|
|
2858
2924
|
}
|
|
2859
2925
|
const headCount = Math.max(1, Math.ceil(opts.maxLines / 2));
|
|
2860
2926
|
const tailCount = Math.max(0, Math.floor(opts.maxLines / 2));
|
|
2861
|
-
for (const line of lines.slice(0, headCount))
|
|
2862
|
-
writeln(
|
|
2863
|
-
}
|
|
2927
|
+
for (const line of lines.slice(0, headCount))
|
|
2928
|
+
writeln(line);
|
|
2864
2929
|
const hiddenLines = Math.max(0, lines.length - (headCount + tailCount));
|
|
2865
2930
|
if (hiddenLines > 0) {
|
|
2866
|
-
writeln(
|
|
2931
|
+
writeln(c4.dim(`\u2026 +${hiddenLines} lines`));
|
|
2867
2932
|
}
|
|
2868
2933
|
if (tailCount > 0) {
|
|
2869
|
-
for (const line of lines.slice(-tailCount))
|
|
2870
|
-
writeln(
|
|
2871
|
-
}
|
|
2934
|
+
for (const line of lines.slice(-tailCount))
|
|
2935
|
+
writeln(line);
|
|
2872
2936
|
}
|
|
2873
2937
|
}
|
|
2874
2938
|
function truncateOneLine(value, max = 100, verboseOutput = false) {
|
|
@@ -2927,11 +2991,11 @@ function renderShellResult(result, opts) {
|
|
|
2927
2991
|
const stdoutLines = countShellLines(r.stdout);
|
|
2928
2992
|
const stderrLines = countShellLines(r.stderr);
|
|
2929
2993
|
const stdoutSingleLine = getSingleShellLine(r.stdout);
|
|
2930
|
-
let badge =
|
|
2994
|
+
let badge = c4.red("error");
|
|
2931
2995
|
if (r.timedOut)
|
|
2932
|
-
badge =
|
|
2996
|
+
badge = c4.yellow("timeout");
|
|
2933
2997
|
else if (r.success)
|
|
2934
|
-
badge =
|
|
2998
|
+
badge = c4.green("done");
|
|
2935
2999
|
const parts = buildShellSummaryParts({
|
|
2936
3000
|
exitCode: r.exitCode,
|
|
2937
3001
|
stdoutLines,
|
|
@@ -2939,11 +3003,10 @@ function renderShellResult(result, opts) {
|
|
|
2939
3003
|
stdoutSingleLine,
|
|
2940
3004
|
verboseOutput
|
|
2941
3005
|
});
|
|
2942
|
-
writeln(
|
|
3006
|
+
writeln(`${badge} ${c4.dim(parts.join(" \xB7 "))}`);
|
|
2943
3007
|
writePreviewLines({
|
|
2944
3008
|
label: "stderr",
|
|
2945
3009
|
value: displayStderr,
|
|
2946
|
-
lineColor: c5.red,
|
|
2947
3010
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 10
|
|
2948
3011
|
});
|
|
2949
3012
|
if (shouldPreviewShellStdout({
|
|
@@ -2955,7 +3018,6 @@ function renderShellResult(result, opts) {
|
|
|
2955
3018
|
writePreviewLines({
|
|
2956
3019
|
label: "stdout",
|
|
2957
3020
|
value: displayStdout,
|
|
2958
|
-
lineColor: c5.dim,
|
|
2959
3021
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 20
|
|
2960
3022
|
});
|
|
2961
3023
|
}
|
|
@@ -2966,21 +3028,21 @@ function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
|
2966
3028
|
if (!trimmed)
|
|
2967
3029
|
return "";
|
|
2968
3030
|
if (verboseOutput)
|
|
2969
|
-
return ` ${
|
|
2970
|
-
return ` ${
|
|
3031
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed)}`;
|
|
3032
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed.length > 60 ? `${trimmed.slice(0, 57)}\u2026` : trimmed)}`;
|
|
2971
3033
|
}
|
|
2972
3034
|
function renderSkillSummaryLine(skill, opts) {
|
|
2973
3035
|
const name = skill.name ?? "(unknown)";
|
|
2974
3036
|
const source = skill.source ?? "unknown";
|
|
2975
|
-
const labelPrefix = opts?.label ? `${
|
|
2976
|
-
writeln(
|
|
3037
|
+
const labelPrefix = opts?.label ? `${c4.dim(opts.label)} ` : "";
|
|
3038
|
+
writeln(`${G.info} ${labelPrefix}${name} ${c4.dim("\xB7")} ${c4.dim(source)}${buildSkillDescriptionPart(skill.description, opts?.verboseOutput === true)}`);
|
|
2977
3039
|
}
|
|
2978
3040
|
function renderListSkillsResult(result, opts) {
|
|
2979
3041
|
const r = result;
|
|
2980
3042
|
if (!Array.isArray(r?.skills))
|
|
2981
3043
|
return false;
|
|
2982
3044
|
if (r.skills.length === 0) {
|
|
2983
|
-
writeln(
|
|
3045
|
+
writeln(`${G.info} ${c4.dim("no skills")}`);
|
|
2984
3046
|
return true;
|
|
2985
3047
|
}
|
|
2986
3048
|
const maxSkills = opts?.verboseOutput ? r.skills.length : 6;
|
|
@@ -2990,7 +3052,7 @@ function renderListSkillsResult(result, opts) {
|
|
|
2990
3052
|
});
|
|
2991
3053
|
}
|
|
2992
3054
|
if (r.skills.length > maxSkills) {
|
|
2993
|
-
writeln(
|
|
3055
|
+
writeln(`${c4.dim(`+${r.skills.length - maxSkills} more skills`)}`);
|
|
2994
3056
|
}
|
|
2995
3057
|
return true;
|
|
2996
3058
|
}
|
|
@@ -2999,7 +3061,7 @@ function renderReadSkillResult(result, _opts) {
|
|
|
2999
3061
|
if (!r || typeof r !== "object")
|
|
3000
3062
|
return false;
|
|
3001
3063
|
if (!r.skill) {
|
|
3002
|
-
writeln(
|
|
3064
|
+
writeln(`${G.info} ${c4.dim("skill")} ${c4.dim("(not found)")}`);
|
|
3003
3065
|
return true;
|
|
3004
3066
|
}
|
|
3005
3067
|
renderSkillSummaryLine(r.skill, {
|
|
@@ -3013,19 +3075,19 @@ function renderWebSearchResult(result, opts) {
|
|
|
3013
3075
|
if (!Array.isArray(r?.results))
|
|
3014
3076
|
return false;
|
|
3015
3077
|
if (r.results.length === 0) {
|
|
3016
|
-
writeln(
|
|
3078
|
+
writeln(`${G.info} ${c4.dim("no results")}`);
|
|
3017
3079
|
return true;
|
|
3018
3080
|
}
|
|
3019
3081
|
const maxResults = opts?.verboseOutput ? r.results.length : 5;
|
|
3020
3082
|
for (const item of r.results.slice(0, maxResults)) {
|
|
3021
3083
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
3022
|
-
const score = typeof item.score === "number" ?
|
|
3023
|
-
writeln(
|
|
3084
|
+
const score = typeof item.score === "number" ? c4.dim(` (${item.score.toFixed(2)})`) : "";
|
|
3085
|
+
writeln(`${c4.dim("\u2022")} ${title}${score}`);
|
|
3024
3086
|
if (item.url)
|
|
3025
|
-
writeln(`
|
|
3087
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
3026
3088
|
}
|
|
3027
3089
|
if (r.results.length > maxResults) {
|
|
3028
|
-
writeln(
|
|
3090
|
+
writeln(`${c4.dim(`+${r.results.length - maxResults} more`)}`);
|
|
3029
3091
|
}
|
|
3030
3092
|
return true;
|
|
3031
3093
|
}
|
|
@@ -3034,23 +3096,23 @@ function renderWebContentResult(result, opts) {
|
|
|
3034
3096
|
if (!Array.isArray(r?.results))
|
|
3035
3097
|
return false;
|
|
3036
3098
|
if (r.results.length === 0) {
|
|
3037
|
-
writeln(
|
|
3099
|
+
writeln(`${G.info} ${c4.dim("no pages")}`);
|
|
3038
3100
|
return true;
|
|
3039
3101
|
}
|
|
3040
3102
|
const maxPages = opts?.verboseOutput ? r.results.length : 3;
|
|
3041
3103
|
for (const item of r.results.slice(0, maxPages)) {
|
|
3042
3104
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
3043
|
-
writeln(
|
|
3105
|
+
writeln(`${c4.dim("\u2022")} ${title}`);
|
|
3044
3106
|
if (item.url)
|
|
3045
|
-
writeln(`
|
|
3107
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
3046
3108
|
const preview = (item.text ?? "").replace(/\s+/g, " ").trim();
|
|
3047
3109
|
if (preview) {
|
|
3048
3110
|
const trimmed = opts?.verboseOutput || preview.length <= 220 ? preview : `${preview.slice(0, 217)}\u2026`;
|
|
3049
|
-
writeln(`
|
|
3111
|
+
writeln(` ${c4.dim(trimmed)}`);
|
|
3050
3112
|
}
|
|
3051
3113
|
}
|
|
3052
3114
|
if (r.results.length > maxPages) {
|
|
3053
|
-
writeln(
|
|
3115
|
+
writeln(`${c4.dim(`+${r.results.length - maxPages} more`)}`);
|
|
3054
3116
|
}
|
|
3055
3117
|
return true;
|
|
3056
3118
|
}
|
|
@@ -3060,11 +3122,11 @@ function renderMcpResult(result, opts) {
|
|
|
3060
3122
|
let rendered = false;
|
|
3061
3123
|
for (const block of content.slice(0, maxBlocks)) {
|
|
3062
3124
|
if (block?.type === "text" && block.text) {
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3125
|
+
writePreviewLines({
|
|
3126
|
+
label: "",
|
|
3127
|
+
value: block.text,
|
|
3128
|
+
maxLines: opts?.verboseOutput ? Number.POSITIVE_INFINITY : 6
|
|
3129
|
+
});
|
|
3068
3130
|
rendered = true;
|
|
3069
3131
|
}
|
|
3070
3132
|
}
|
|
@@ -3152,43 +3214,58 @@ function formatShellCallLine(cmd) {
|
|
|
3152
3214
|
const { cwd, rest } = parseShellCdPrefix(cmd);
|
|
3153
3215
|
const firstCmd = extractFirstCommand(rest);
|
|
3154
3216
|
const glyph = shellCmdGlyph(firstCmd, rest);
|
|
3155
|
-
const cwdSuffix = cwd ? ` ${
|
|
3156
|
-
|
|
3157
|
-
return `${glyph} ${display}${cwdSuffix}`;
|
|
3217
|
+
const cwdSuffix = cwd ? ` ${c5.dim(`in ${cwd}`)}` : "";
|
|
3218
|
+
return `${glyph} ${rest}${cwdSuffix}`;
|
|
3158
3219
|
}
|
|
3159
3220
|
function buildToolCallLine(name, args) {
|
|
3160
3221
|
const a = args && typeof args === "object" ? args : {};
|
|
3161
3222
|
if (name === "shell") {
|
|
3162
3223
|
const cmd = String(a.command ?? "").trim();
|
|
3163
3224
|
if (!cmd)
|
|
3164
|
-
return `${G.run} ${
|
|
3225
|
+
return `${G.run} ${c5.dim("shell")}`;
|
|
3165
3226
|
return formatShellCallLine(cmd);
|
|
3166
3227
|
}
|
|
3167
3228
|
if (name === "listSkills") {
|
|
3168
|
-
return `${G.search} ${
|
|
3229
|
+
return `${G.search} ${c5.dim("list skills")}`;
|
|
3169
3230
|
}
|
|
3170
3231
|
if (name === "readSkill") {
|
|
3171
3232
|
const skillName = typeof a.name === "string" ? a.name : "";
|
|
3172
|
-
return `${G.read} ${
|
|
3233
|
+
return `${G.read} ${c5.dim("read skill")}${skillName ? ` ${skillName}` : ""}`;
|
|
3173
3234
|
}
|
|
3174
3235
|
if (name === "webSearch") {
|
|
3175
3236
|
const query = typeof a.query === "string" ? a.query : "";
|
|
3176
|
-
|
|
3177
|
-
return `${G.search} ${c6.dim("search")}${short ? ` ${short}` : ""}`;
|
|
3237
|
+
return `${G.search} ${c5.dim("search")}${query ? ` ${query}` : ""}`;
|
|
3178
3238
|
}
|
|
3179
3239
|
if (name === "webContent") {
|
|
3180
3240
|
const urls = Array.isArray(a.urls) ? a.urls : [];
|
|
3181
3241
|
const label = urls.length === 1 ? String(urls[0]) : `${urls.length} url${urls.length !== 1 ? "s" : ""}`;
|
|
3182
|
-
|
|
3183
|
-
return `${G.read} ${c6.dim("fetch")} ${short}`;
|
|
3242
|
+
return `${G.read} ${c5.dim("fetch")} ${label}`;
|
|
3184
3243
|
}
|
|
3185
3244
|
if (name.startsWith("mcp_")) {
|
|
3186
|
-
return `${G.mcp} ${
|
|
3245
|
+
return `${G.mcp} ${c5.dim(name)}`;
|
|
3187
3246
|
}
|
|
3188
|
-
return `${toolGlyph(name)} ${
|
|
3247
|
+
return `${toolGlyph(name)} ${c5.dim(name)}`;
|
|
3189
3248
|
}
|
|
3190
|
-
function renderToolCall(toolName, args) {
|
|
3191
|
-
|
|
3249
|
+
function renderToolCall(toolName, args, opts) {
|
|
3250
|
+
const line = buildToolCallLine(toolName, args);
|
|
3251
|
+
if (opts?.verboseOutput) {
|
|
3252
|
+
writeln(line);
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
const lines = line.split(`
|
|
3256
|
+
`);
|
|
3257
|
+
if (lines.length <= 6) {
|
|
3258
|
+
writeln(line);
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
const head = lines.slice(0, 3);
|
|
3262
|
+
const tail = lines.slice(-2);
|
|
3263
|
+
const hidden = lines.length - head.length - tail.length;
|
|
3264
|
+
for (const l of head)
|
|
3265
|
+
writeln(l);
|
|
3266
|
+
writeln(c5.dim(`\u2026 +${hidden} lines`));
|
|
3267
|
+
for (const l of tail)
|
|
3268
|
+
writeln(l);
|
|
3192
3269
|
}
|
|
3193
3270
|
function formatErrorBadge(result) {
|
|
3194
3271
|
let msg;
|
|
@@ -3200,11 +3277,11 @@ function formatErrorBadge(result) {
|
|
|
3200
3277
|
msg = JSON.stringify(result);
|
|
3201
3278
|
const oneLiner = msg.split(`
|
|
3202
3279
|
`)[0] ?? msg;
|
|
3203
|
-
return `${G.err} ${
|
|
3280
|
+
return `${G.err} ${c5.red(oneLiner)}`;
|
|
3204
3281
|
}
|
|
3205
3282
|
function renderToolResult(toolName, result, isError, opts) {
|
|
3206
3283
|
if (isError) {
|
|
3207
|
-
writeln(
|
|
3284
|
+
writeln(formatErrorBadge(result));
|
|
3208
3285
|
return;
|
|
3209
3286
|
}
|
|
3210
3287
|
if (renderToolResultByName(toolName, result, opts)) {
|
|
@@ -3212,32 +3289,130 @@ function renderToolResult(toolName, result, isError, opts) {
|
|
|
3212
3289
|
}
|
|
3213
3290
|
const text = JSON.stringify(result);
|
|
3214
3291
|
if (opts?.verboseOutput || text.length <= 120) {
|
|
3215
|
-
writeln(
|
|
3292
|
+
writeln(c5.dim(text));
|
|
3216
3293
|
return;
|
|
3217
3294
|
}
|
|
3218
|
-
writeln(
|
|
3295
|
+
writeln(c5.dim(`${text.slice(0, 117)}\u2026`));
|
|
3219
3296
|
}
|
|
3220
3297
|
|
|
3221
3298
|
// src/cli/stream-render.ts
|
|
3299
|
+
function styleReasoning(text) {
|
|
3300
|
+
return c6.italic(c6.dim(text));
|
|
3301
|
+
}
|
|
3302
|
+
function writeReasoningDelta(delta, state) {
|
|
3303
|
+
if (!delta)
|
|
3304
|
+
return;
|
|
3305
|
+
if (!state.blockOpen) {
|
|
3306
|
+
writeln(`${G.info} ${c6.dim("reasoning")}`);
|
|
3307
|
+
state.blockOpen = true;
|
|
3308
|
+
}
|
|
3309
|
+
const lines = delta.split(`
|
|
3310
|
+
`);
|
|
3311
|
+
for (const [i, line] of lines.entries()) {
|
|
3312
|
+
if (line) {
|
|
3313
|
+
if (!state.lineOpen) {
|
|
3314
|
+
write(" ");
|
|
3315
|
+
state.lineOpen = true;
|
|
3316
|
+
}
|
|
3317
|
+
write(styleReasoning(line));
|
|
3318
|
+
}
|
|
3319
|
+
if (i < lines.length - 1) {
|
|
3320
|
+
if (!state.lineOpen)
|
|
3321
|
+
write(" ");
|
|
3322
|
+
writeln();
|
|
3323
|
+
state.lineOpen = false;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
function finishReasoning(state) {
|
|
3328
|
+
if (!state.blockOpen)
|
|
3329
|
+
return;
|
|
3330
|
+
if (state.lineOpen)
|
|
3331
|
+
writeln();
|
|
3332
|
+
state.blockOpen = false;
|
|
3333
|
+
state.lineOpen = false;
|
|
3334
|
+
}
|
|
3335
|
+
function appendTextDelta(delta, state, spinner, quiet, renderedVisibleOutput) {
|
|
3336
|
+
let chunk = delta ?? "";
|
|
3337
|
+
if (!chunk)
|
|
3338
|
+
return state.inText;
|
|
3339
|
+
if (!state.inText) {
|
|
3340
|
+
chunk = chunk.trimStart();
|
|
3341
|
+
if (!chunk)
|
|
3342
|
+
return false;
|
|
3343
|
+
if (!quiet) {
|
|
3344
|
+
spinner.stop();
|
|
3345
|
+
if (renderedVisibleOutput)
|
|
3346
|
+
writeln();
|
|
3347
|
+
write(`${G.reply} `);
|
|
3348
|
+
}
|
|
3349
|
+
state.inText = true;
|
|
3350
|
+
if (!quiet && terminal.isStdoutTTY) {
|
|
3351
|
+
state.highlighter = createHighlighter();
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
const isFirstLine = !state.text.includes(`
|
|
3355
|
+
`);
|
|
3356
|
+
state.text += chunk;
|
|
3357
|
+
if (quiet)
|
|
3358
|
+
return state.inText;
|
|
3359
|
+
spinner.stop();
|
|
3360
|
+
if (state.highlighter) {
|
|
3361
|
+
let colored = state.highlighter.write(chunk);
|
|
3362
|
+
if (colored) {
|
|
3363
|
+
if (isFirstLine && colored.startsWith("\x1B[2K\r")) {
|
|
3364
|
+
colored = `\x1B[2K\r${G.reply} ${colored.slice(5)}`;
|
|
3365
|
+
}
|
|
3366
|
+
write(colored);
|
|
3367
|
+
}
|
|
3368
|
+
} else {
|
|
3369
|
+
write(chunk);
|
|
3370
|
+
}
|
|
3371
|
+
return state.inText;
|
|
3372
|
+
}
|
|
3373
|
+
function flushText(state, quiet) {
|
|
3374
|
+
if (!state.inText)
|
|
3375
|
+
return;
|
|
3376
|
+
if (!quiet) {
|
|
3377
|
+
if (state.highlighter) {
|
|
3378
|
+
let finalColored = state.highlighter.end();
|
|
3379
|
+
if (finalColored) {
|
|
3380
|
+
const isFirstLine = !state.text.includes(`
|
|
3381
|
+
`);
|
|
3382
|
+
if (isFirstLine && finalColored.startsWith("\x1B[2K\r")) {
|
|
3383
|
+
finalColored = `\x1B[2K\r${G.reply} ${finalColored.slice(5)}`;
|
|
3384
|
+
}
|
|
3385
|
+
write(finalColored);
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
writeln();
|
|
3389
|
+
}
|
|
3390
|
+
state.inText = false;
|
|
3391
|
+
}
|
|
3222
3392
|
async function renderTurn(events, spinner, opts) {
|
|
3223
3393
|
const quiet = opts?.quiet ?? false;
|
|
3224
3394
|
const showReasoning = !quiet && (opts?.showReasoning ?? true);
|
|
3225
3395
|
const verboseOutput = opts?.verboseOutput ?? false;
|
|
3226
|
-
const
|
|
3227
|
-
|
|
3396
|
+
const textState = {
|
|
3397
|
+
inText: false,
|
|
3398
|
+
text: "",
|
|
3399
|
+
highlighter: undefined
|
|
3400
|
+
};
|
|
3401
|
+
let reasoningRaw = "";
|
|
3402
|
+
let reasoningComputed = false;
|
|
3403
|
+
let reasoningText = "";
|
|
3404
|
+
const reasoningState = { blockOpen: false, lineOpen: false };
|
|
3405
|
+
const startedToolCalls = new Set;
|
|
3406
|
+
const toolCallInfo = new Map;
|
|
3407
|
+
let parallelCallCount = 0;
|
|
3228
3408
|
let inputTokens = 0;
|
|
3229
3409
|
let outputTokens = 0;
|
|
3230
3410
|
let contextTokens = 0;
|
|
3231
3411
|
let newMessages = [];
|
|
3232
|
-
const startedToolCalls = new Set;
|
|
3233
|
-
const toolCallInfo = new Map;
|
|
3234
|
-
let parallelCallCount = 0;
|
|
3235
3412
|
let renderedVisibleOutput = false;
|
|
3236
|
-
let reasoningComputed = false;
|
|
3237
|
-
let reasoningText = "";
|
|
3238
3413
|
const getReasoningText = () => {
|
|
3239
3414
|
if (!reasoningComputed) {
|
|
3240
|
-
reasoningText = normalizeReasoningText(
|
|
3415
|
+
reasoningText = normalizeReasoningText(reasoningRaw);
|
|
3241
3416
|
reasoningComputed = true;
|
|
3242
3417
|
}
|
|
3243
3418
|
return reasoningText;
|
|
@@ -3245,29 +3420,42 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3245
3420
|
for await (const event of events) {
|
|
3246
3421
|
switch (event.type) {
|
|
3247
3422
|
case "text-delta": {
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
if (
|
|
3423
|
+
finishReasoning(reasoningState);
|
|
3424
|
+
textState.inText = appendTextDelta(event.delta, textState, spinner, quiet, renderedVisibleOutput);
|
|
3425
|
+
if (textState.inText)
|
|
3251
3426
|
renderedVisibleOutput = true;
|
|
3252
3427
|
break;
|
|
3253
3428
|
}
|
|
3254
3429
|
case "reasoning-delta": {
|
|
3255
|
-
|
|
3256
|
-
|
|
3430
|
+
flushText(textState, quiet);
|
|
3431
|
+
let appended = normalizeReasoningDelta(event.delta);
|
|
3432
|
+
if (reasoningRaw.endsWith("**") && appended.startsWith("**") && !reasoningRaw.endsWith(`
|
|
3433
|
+
`)) {
|
|
3434
|
+
appended = `
|
|
3435
|
+
${appended}`;
|
|
3436
|
+
}
|
|
3437
|
+
reasoningRaw += appended;
|
|
3257
3438
|
reasoningComputed = false;
|
|
3258
|
-
if (showReasoning &&
|
|
3439
|
+
if (showReasoning && appended) {
|
|
3259
3440
|
spinner.stop();
|
|
3260
|
-
if (renderedVisibleOutput && !
|
|
3441
|
+
if (renderedVisibleOutput && !reasoningState.blockOpen)
|
|
3261
3442
|
writeln();
|
|
3262
|
-
|
|
3443
|
+
writeReasoningDelta(appended, reasoningState);
|
|
3263
3444
|
renderedVisibleOutput = true;
|
|
3264
3445
|
}
|
|
3265
3446
|
break;
|
|
3266
3447
|
}
|
|
3448
|
+
case "tool-input-delta": {
|
|
3449
|
+
if (quiet)
|
|
3450
|
+
break;
|
|
3451
|
+
finishReasoning(reasoningState);
|
|
3452
|
+
flushText(textState, quiet);
|
|
3453
|
+
spinner.start(`composing ${event.toolName}\u2026`);
|
|
3454
|
+
break;
|
|
3455
|
+
}
|
|
3267
3456
|
case "tool-call-start": {
|
|
3268
|
-
if (startedToolCalls.has(event.toolCallId))
|
|
3457
|
+
if (startedToolCalls.has(event.toolCallId))
|
|
3269
3458
|
break;
|
|
3270
|
-
}
|
|
3271
3459
|
const isConsecutiveToolCall = startedToolCalls.size > 0 && toolCallInfo.size > 0;
|
|
3272
3460
|
startedToolCalls.add(event.toolCallId);
|
|
3273
3461
|
toolCallInfo.set(event.toolCallId, {
|
|
@@ -3277,13 +3465,14 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3277
3465
|
if (toolCallInfo.size > 1) {
|
|
3278
3466
|
parallelCallCount = toolCallInfo.size;
|
|
3279
3467
|
}
|
|
3280
|
-
|
|
3281
|
-
|
|
3468
|
+
finishReasoning(reasoningState);
|
|
3469
|
+
flushText(textState, quiet);
|
|
3282
3470
|
if (!quiet) {
|
|
3283
3471
|
spinner.stop();
|
|
3284
|
-
if (renderedVisibleOutput && !isConsecutiveToolCall)
|
|
3472
|
+
if (renderedVisibleOutput && !isConsecutiveToolCall) {
|
|
3285
3473
|
writeln();
|
|
3286
|
-
|
|
3474
|
+
}
|
|
3475
|
+
renderToolCall(event.toolName, event.args, { verboseOutput });
|
|
3287
3476
|
renderedVisibleOutput = true;
|
|
3288
3477
|
spinner.start(event.toolName);
|
|
3289
3478
|
}
|
|
@@ -3293,11 +3482,11 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3293
3482
|
startedToolCalls.delete(event.toolCallId);
|
|
3294
3483
|
const callInfo = toolCallInfo.get(event.toolCallId);
|
|
3295
3484
|
toolCallInfo.delete(event.toolCallId);
|
|
3296
|
-
|
|
3485
|
+
finishReasoning(reasoningState);
|
|
3297
3486
|
if (!quiet) {
|
|
3298
3487
|
spinner.stop();
|
|
3299
3488
|
if (parallelCallCount > 1 && callInfo) {
|
|
3300
|
-
writeln(
|
|
3489
|
+
writeln(`${c6.dim("\u21B3")} ${callInfo.label}`);
|
|
3301
3490
|
}
|
|
3302
3491
|
if (toolCallInfo.size === 0)
|
|
3303
3492
|
parallelCallCount = 0;
|
|
@@ -3313,19 +3502,20 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3313
3502
|
break;
|
|
3314
3503
|
}
|
|
3315
3504
|
case "context-pruned": {
|
|
3316
|
-
|
|
3317
|
-
|
|
3505
|
+
finishReasoning(reasoningState);
|
|
3506
|
+
flushText(textState, quiet);
|
|
3318
3507
|
if (!quiet) {
|
|
3319
3508
|
spinner.stop();
|
|
3320
3509
|
const removedKb = (event.removedBytes / 1024).toFixed(1);
|
|
3321
|
-
writeln(`${G.info} ${
|
|
3510
|
+
writeln(`${G.info} ${c6.dim("context pruned")} ${c6.dim(`\u2013${event.removedMessageCount} messages`)} ${c6.dim(`\u2013${removedKb} KB`)}`);
|
|
3322
3511
|
renderedVisibleOutput = true;
|
|
3512
|
+
spinner.start("thinking");
|
|
3323
3513
|
}
|
|
3324
3514
|
break;
|
|
3325
3515
|
}
|
|
3326
3516
|
case "turn-complete": {
|
|
3327
|
-
|
|
3328
|
-
|
|
3517
|
+
finishReasoning(reasoningState);
|
|
3518
|
+
flushText(textState, quiet);
|
|
3329
3519
|
spinner.stop();
|
|
3330
3520
|
if (!quiet && !renderedVisibleOutput)
|
|
3331
3521
|
writeln();
|
|
@@ -3336,14 +3526,14 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3336
3526
|
break;
|
|
3337
3527
|
}
|
|
3338
3528
|
case "turn-error": {
|
|
3339
|
-
|
|
3340
|
-
|
|
3529
|
+
finishReasoning(reasoningState);
|
|
3530
|
+
flushText(textState, quiet);
|
|
3341
3531
|
spinner.stop();
|
|
3342
3532
|
inputTokens = event.inputTokens;
|
|
3343
3533
|
outputTokens = event.outputTokens;
|
|
3344
3534
|
contextTokens = event.contextTokens;
|
|
3345
3535
|
if (isAbortError(event.error)) {
|
|
3346
|
-
newMessages = buildAbortMessages(event.partialMessages,
|
|
3536
|
+
newMessages = buildAbortMessages(event.partialMessages, textState.text);
|
|
3347
3537
|
} else {
|
|
3348
3538
|
renderError(event.error, "turn");
|
|
3349
3539
|
throw new RenderedError(event.error);
|
|
@@ -3392,17 +3582,17 @@ function renderUserMessage(text) {
|
|
|
3392
3582
|
}
|
|
3393
3583
|
}
|
|
3394
3584
|
var G = {
|
|
3395
|
-
prompt:
|
|
3396
|
-
reply:
|
|
3397
|
-
search:
|
|
3398
|
-
read:
|
|
3399
|
-
write:
|
|
3400
|
-
run:
|
|
3401
|
-
mcp:
|
|
3402
|
-
ok:
|
|
3403
|
-
err:
|
|
3404
|
-
warn:
|
|
3405
|
-
info:
|
|
3585
|
+
prompt: c7.green("\u203A"),
|
|
3586
|
+
reply: c7.cyan("\u25C6"),
|
|
3587
|
+
search: c7.yellow("?"),
|
|
3588
|
+
read: c7.dim("\u2190"),
|
|
3589
|
+
write: c7.green("\u270E"),
|
|
3590
|
+
run: c7.dim("$"),
|
|
3591
|
+
mcp: c7.yellow("\u2699"),
|
|
3592
|
+
ok: c7.green("\u2714"),
|
|
3593
|
+
err: c7.red("\u2716"),
|
|
3594
|
+
warn: c7.yellow("!"),
|
|
3595
|
+
info: c7.dim("\xB7")
|
|
3406
3596
|
};
|
|
3407
3597
|
var PREFIX = {
|
|
3408
3598
|
user: G.prompt,
|
|
@@ -3424,17 +3614,17 @@ class RenderedError extends Error {
|
|
|
3424
3614
|
function renderError(err, context = "render") {
|
|
3425
3615
|
logError(err, context);
|
|
3426
3616
|
const parsed = parseAppError(err);
|
|
3427
|
-
writeln(`${G.err} ${
|
|
3617
|
+
writeln(`${G.err} ${c7.red(parsed.headline)}`);
|
|
3428
3618
|
if (parsed.hint) {
|
|
3429
|
-
writeln(` ${
|
|
3619
|
+
writeln(` ${c7.dim(parsed.hint)}`);
|
|
3430
3620
|
}
|
|
3431
3621
|
}
|
|
3432
3622
|
function renderBanner(model, cwd) {
|
|
3433
3623
|
writeln();
|
|
3434
3624
|
const title = PACKAGE_VERSION ? `mini-coder \xB7 v${PACKAGE_VERSION}` : "mini-coder";
|
|
3435
|
-
writeln(` ${
|
|
3436
|
-
writeln(` ${
|
|
3437
|
-
writeln(` ${
|
|
3625
|
+
writeln(` ${c7.cyan("mc")} ${c7.dim(title)}`);
|
|
3626
|
+
writeln(` ${c7.dim(model)} ${c7.dim("\xB7")} ${c7.dim(tildePath(cwd))}`);
|
|
3627
|
+
writeln(` ${c7.dim("/help for commands \xB7 esc cancel \xB7 ctrl+d exit")}`);
|
|
3438
3628
|
const items = [];
|
|
3439
3629
|
if (getPreferredShowReasoning())
|
|
3440
3630
|
items.push("reasoning: on");
|
|
@@ -3447,7 +3637,7 @@ function renderBanner(model, cwd) {
|
|
|
3447
3637
|
if (skills.size > 0)
|
|
3448
3638
|
items.push(`${skills.size} skill${skills.size > 1 ? "s" : ""}`);
|
|
3449
3639
|
if (items.length > 0) {
|
|
3450
|
-
writeln(` ${
|
|
3640
|
+
writeln(` ${c7.dim(items.join(" \xB7 "))}`);
|
|
3451
3641
|
}
|
|
3452
3642
|
const connParts = [];
|
|
3453
3643
|
for (const p of discoverConnectedProviders()) {
|
|
@@ -3457,7 +3647,7 @@ function renderBanner(model, cwd) {
|
|
|
3457
3647
|
if (mcpCount > 0)
|
|
3458
3648
|
connParts.push(`${mcpCount} mcp`);
|
|
3459
3649
|
if (connParts.length > 0) {
|
|
3460
|
-
writeln(` ${
|
|
3650
|
+
writeln(` ${c7.dim(connParts.join(" \xB7 "))}`);
|
|
3461
3651
|
}
|
|
3462
3652
|
writeln();
|
|
3463
3653
|
}
|
|
@@ -3472,7 +3662,7 @@ class CliReporter {
|
|
|
3472
3662
|
if (this.quiet)
|
|
3473
3663
|
return;
|
|
3474
3664
|
this.spinner.stop();
|
|
3475
|
-
writeln(`${G.info} ${
|
|
3665
|
+
writeln(`${G.info} ${c7.dim(msg)}`);
|
|
3476
3666
|
}
|
|
3477
3667
|
error(msg, hint) {
|
|
3478
3668
|
this.spinner.stop();
|
|
@@ -3676,10 +3866,10 @@ function setPreferredVerboseOutput(verbose) {
|
|
|
3676
3866
|
setSetting("preferred_verbose_output", verbose ? "true" : "false");
|
|
3677
3867
|
}
|
|
3678
3868
|
// src/agent/session-runner.ts
|
|
3679
|
-
import * as
|
|
3869
|
+
import * as c10 from "yoctocolors";
|
|
3680
3870
|
|
|
3681
3871
|
// src/cli/input.ts
|
|
3682
|
-
import * as
|
|
3872
|
+
import * as c8 from "yoctocolors";
|
|
3683
3873
|
|
|
3684
3874
|
// src/cli/input-buffer.ts
|
|
3685
3875
|
var PASTE_TOKEN_START = 57344;
|
|
@@ -4088,7 +4278,7 @@ function watchForCancel(abortController) {
|
|
|
4088
4278
|
process.stdin.on("data", onData);
|
|
4089
4279
|
return cleanup;
|
|
4090
4280
|
}
|
|
4091
|
-
var PROMPT =
|
|
4281
|
+
var PROMPT = c8.green("\u25B6 ");
|
|
4092
4282
|
var PROMPT_RAW_LEN = 2;
|
|
4093
4283
|
async function readline(opts) {
|
|
4094
4284
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -4137,7 +4327,7 @@ async function readline(opts) {
|
|
|
4137
4327
|
process.stdout.write(`${CLEAR_LINE}${prompt}${display}${CSI}${PROMPT_RAW_LEN + displayCursor + 1}G`);
|
|
4138
4328
|
}
|
|
4139
4329
|
function renderSearchPrompt() {
|
|
4140
|
-
process.stdout.write(`${CLEAR_LINE}${
|
|
4330
|
+
process.stdout.write(`${CLEAR_LINE}${c8.cyan("search:")} ${searchQuery}\u2588`);
|
|
4141
4331
|
}
|
|
4142
4332
|
function applyHistory() {
|
|
4143
4333
|
if (histIdx < history.length) {
|
|
@@ -4400,11 +4590,11 @@ import { streamText } from "ai";
|
|
|
4400
4590
|
import { dynamicTool, jsonSchema } from "ai";
|
|
4401
4591
|
|
|
4402
4592
|
// src/llm-api/turn-stream-events.ts
|
|
4403
|
-
function shouldLogStreamChunk(
|
|
4404
|
-
return
|
|
4593
|
+
function shouldLogStreamChunk(c9) {
|
|
4594
|
+
return c9.type !== "text-delta" && c9.type !== "reasoning" && c9.type !== "reasoning-delta" && c9.type !== "tool-input-delta";
|
|
4405
4595
|
}
|
|
4406
|
-
function extractToolArgs(
|
|
4407
|
-
return
|
|
4596
|
+
function extractToolArgs(c9) {
|
|
4597
|
+
return c9.input ?? c9.args;
|
|
4408
4598
|
}
|
|
4409
4599
|
function hasRenderableToolArgs(args) {
|
|
4410
4600
|
if (args === null || args === undefined)
|
|
@@ -4417,10 +4607,10 @@ function hasRenderableToolArgs(args) {
|
|
|
4417
4607
|
return Object.keys(args).length > 0;
|
|
4418
4608
|
return true;
|
|
4419
4609
|
}
|
|
4420
|
-
function mapStreamChunkToTurnEvent(
|
|
4421
|
-
switch (
|
|
4610
|
+
function mapStreamChunkToTurnEvent(c9) {
|
|
4611
|
+
switch (c9.type) {
|
|
4422
4612
|
case "text-delta": {
|
|
4423
|
-
const delta = typeof
|
|
4613
|
+
const delta = typeof c9.text === "string" ? c9.text : "";
|
|
4424
4614
|
return {
|
|
4425
4615
|
type: "text-delta",
|
|
4426
4616
|
delta
|
|
@@ -4428,7 +4618,7 @@ function mapStreamChunkToTurnEvent(c10) {
|
|
|
4428
4618
|
}
|
|
4429
4619
|
case "reasoning-delta":
|
|
4430
4620
|
case "reasoning": {
|
|
4431
|
-
const delta = getReasoningDeltaFromStreamChunk(
|
|
4621
|
+
const delta = getReasoningDeltaFromStreamChunk(c9);
|
|
4432
4622
|
if (delta === null)
|
|
4433
4623
|
return null;
|
|
4434
4624
|
return {
|
|
@@ -4437,49 +4627,65 @@ function mapStreamChunkToTurnEvent(c10) {
|
|
|
4437
4627
|
};
|
|
4438
4628
|
}
|
|
4439
4629
|
case "tool-input-start": {
|
|
4440
|
-
const args = extractToolArgs(
|
|
4441
|
-
const hasStableToolCallId = typeof
|
|
4630
|
+
const args = extractToolArgs(c9);
|
|
4631
|
+
const hasStableToolCallId = typeof c9.toolCallId === "string" && c9.toolCallId.trim().length > 0;
|
|
4442
4632
|
if (hasStableToolCallId && !hasRenderableToolArgs(args))
|
|
4443
4633
|
return null;
|
|
4444
4634
|
return {
|
|
4445
4635
|
type: "tool-call-start",
|
|
4446
|
-
toolCallId: String(
|
|
4447
|
-
toolName: String(
|
|
4636
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4637
|
+
toolName: String(c9.toolName ?? ""),
|
|
4448
4638
|
args
|
|
4449
4639
|
};
|
|
4450
4640
|
}
|
|
4641
|
+
case "tool-input-delta": {
|
|
4642
|
+
let delta = "";
|
|
4643
|
+
if (typeof c9.inputTextDelta === "string") {
|
|
4644
|
+
delta = c9.inputTextDelta;
|
|
4645
|
+
} else if (typeof c9.delta === "string") {
|
|
4646
|
+
delta = c9.delta;
|
|
4647
|
+
}
|
|
4648
|
+
if (!delta)
|
|
4649
|
+
return null;
|
|
4650
|
+
return {
|
|
4651
|
+
type: "tool-input-delta",
|
|
4652
|
+
toolCallId: String(c9.toolCallId ?? c9.id ?? ""),
|
|
4653
|
+
toolName: String(c9.toolName ?? ""),
|
|
4654
|
+
inputTextDelta: delta
|
|
4655
|
+
};
|
|
4656
|
+
}
|
|
4451
4657
|
case "tool-call": {
|
|
4452
4658
|
return {
|
|
4453
4659
|
type: "tool-call-start",
|
|
4454
|
-
toolCallId: String(
|
|
4455
|
-
toolName: String(
|
|
4456
|
-
args: extractToolArgs(
|
|
4660
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4661
|
+
toolName: String(c9.toolName ?? ""),
|
|
4662
|
+
args: extractToolArgs(c9)
|
|
4457
4663
|
};
|
|
4458
4664
|
}
|
|
4459
4665
|
case "tool-result": {
|
|
4460
4666
|
let result;
|
|
4461
|
-
if ("output" in
|
|
4462
|
-
result =
|
|
4463
|
-
else if ("result" in
|
|
4464
|
-
result =
|
|
4667
|
+
if ("output" in c9)
|
|
4668
|
+
result = c9.output;
|
|
4669
|
+
else if ("result" in c9)
|
|
4670
|
+
result = c9.result;
|
|
4465
4671
|
return {
|
|
4466
4672
|
type: "tool-result",
|
|
4467
|
-
toolCallId: String(
|
|
4468
|
-
toolName: String(
|
|
4673
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4674
|
+
toolName: String(c9.toolName ?? ""),
|
|
4469
4675
|
result,
|
|
4470
|
-
isError: "isError" in
|
|
4676
|
+
isError: "isError" in c9 ? Boolean(c9.isError) : false
|
|
4471
4677
|
};
|
|
4472
4678
|
}
|
|
4473
4679
|
case "tool-error":
|
|
4474
4680
|
return {
|
|
4475
4681
|
type: "tool-result",
|
|
4476
|
-
toolCallId: String(
|
|
4477
|
-
toolName: String(
|
|
4478
|
-
result:
|
|
4682
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4683
|
+
toolName: String(c9.toolName ?? ""),
|
|
4684
|
+
result: c9.error ?? "Tool execution failed",
|
|
4479
4685
|
isError: true
|
|
4480
4686
|
};
|
|
4481
4687
|
case "error": {
|
|
4482
|
-
throw normalizeUnknownError(
|
|
4688
|
+
throw normalizeUnknownError(c9.error);
|
|
4483
4689
|
}
|
|
4484
4690
|
default:
|
|
4485
4691
|
return null;
|
|
@@ -4495,9 +4701,9 @@ function toCoreTool(def) {
|
|
|
4495
4701
|
return dynamicTool({
|
|
4496
4702
|
description: def.description,
|
|
4497
4703
|
inputSchema: schema,
|
|
4498
|
-
execute: async (input) => {
|
|
4704
|
+
execute: async (input, { abortSignal }) => {
|
|
4499
4705
|
try {
|
|
4500
|
-
return await def.execute(input);
|
|
4706
|
+
return await def.execute(input, abortSignal ? { signal: abortSignal } : undefined);
|
|
4501
4707
|
} catch (err) {
|
|
4502
4708
|
throw normalizeUnknownError(err);
|
|
4503
4709
|
}
|
|
@@ -4648,6 +4854,7 @@ class StreamToolCallTracker {
|
|
|
4648
4854
|
syntheticCount = 0;
|
|
4649
4855
|
pendingByTool = new Map;
|
|
4650
4856
|
deferredStartsByTool = new Map;
|
|
4857
|
+
toolNameById = new Map;
|
|
4651
4858
|
prepare(chunk) {
|
|
4652
4859
|
const type = chunk.type;
|
|
4653
4860
|
if (!type) {
|
|
@@ -4656,6 +4863,8 @@ class StreamToolCallTracker {
|
|
|
4656
4863
|
if (type === "tool-input-start") {
|
|
4657
4864
|
const toolName = normalizeToolName(chunk.toolName);
|
|
4658
4865
|
const toolCallId = normalizeStringId(chunk.toolCallId);
|
|
4866
|
+
if (toolCallId && toolName)
|
|
4867
|
+
this.toolNameById.set(toolCallId, toolName);
|
|
4659
4868
|
const args = extractToolArgs(chunk);
|
|
4660
4869
|
if (!hasRenderableToolArgs(args)) {
|
|
4661
4870
|
if (!toolCallId) {
|
|
@@ -4690,6 +4899,16 @@ class StreamToolCallTracker {
|
|
|
4690
4899
|
suppressTurnEvent: false
|
|
4691
4900
|
};
|
|
4692
4901
|
}
|
|
4902
|
+
if (type === "tool-input-delta") {
|
|
4903
|
+
const id = normalizeStringId(chunk.id ?? chunk.toolCallId);
|
|
4904
|
+
const toolName = id ? this.toolNameById.get(id) : undefined;
|
|
4905
|
+
if (toolName) {
|
|
4906
|
+
return {
|
|
4907
|
+
chunk: { ...chunk, toolName, toolCallId: id },
|
|
4908
|
+
suppressTurnEvent: false
|
|
4909
|
+
};
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4693
4912
|
return { chunk, suppressTurnEvent: false };
|
|
4694
4913
|
}
|
|
4695
4914
|
trackRenderableStart(chunk, toolName, existingToolCallId) {
|
|
@@ -5349,7 +5568,7 @@ async function* runTurn(options) {
|
|
|
5349
5568
|
}
|
|
5350
5569
|
|
|
5351
5570
|
// src/session/manager.ts
|
|
5352
|
-
import * as
|
|
5571
|
+
import * as c9 from "yoctocolors";
|
|
5353
5572
|
function newSession(model, cwd) {
|
|
5354
5573
|
const id = generateSessionId();
|
|
5355
5574
|
const row = createSession({ id, cwd, model });
|
|
@@ -5384,21 +5603,21 @@ function renderSessionTable(footer) {
|
|
|
5384
5603
|
if (sessions.length === 0)
|
|
5385
5604
|
return false;
|
|
5386
5605
|
writeln(`
|
|
5387
|
-
${
|
|
5606
|
+
${c9.bold("Recent sessions:")}`);
|
|
5388
5607
|
for (const s of sessions) {
|
|
5389
5608
|
const date = new Date(s.updated_at).toLocaleString();
|
|
5390
5609
|
const cwd = tildePath(s.cwd);
|
|
5391
|
-
const title = s.title ||
|
|
5392
|
-
writeln(` ${
|
|
5610
|
+
const title = s.title || c9.dim("(untitled)");
|
|
5611
|
+
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)}`);
|
|
5393
5612
|
}
|
|
5394
5613
|
writeln(`
|
|
5395
5614
|
${footer}`);
|
|
5396
5615
|
return true;
|
|
5397
5616
|
}
|
|
5398
5617
|
function printSessionList() {
|
|
5399
|
-
const shown = renderSessionTable(`${
|
|
5618
|
+
const shown = renderSessionTable(`${c9.dim("Use")} mc --resume <id> ${c9.dim("to continue a session.")}`);
|
|
5400
5619
|
if (!shown)
|
|
5401
|
-
writeln(
|
|
5620
|
+
writeln(c9.dim("No sessions found."));
|
|
5402
5621
|
}
|
|
5403
5622
|
function getMostRecentSession() {
|
|
5404
5623
|
const sessions = listSessions(1);
|
|
@@ -5629,7 +5848,7 @@ class SessionRunner {
|
|
|
5629
5848
|
}
|
|
5630
5849
|
this.session = resumed;
|
|
5631
5850
|
this.currentModel = this.session.model;
|
|
5632
|
-
this.reporter.info(`Resumed session ${this.session.id} (${
|
|
5851
|
+
this.reporter.info(`Resumed session ${this.session.id} (${c10.cyan(this.currentModel)})`);
|
|
5633
5852
|
} else {
|
|
5634
5853
|
this.session = newSession(this.currentModel, this.cwd);
|
|
5635
5854
|
}
|
|
@@ -5875,21 +6094,22 @@ function buildFileEditShellPrelude(command = getFileEditCommand()) {
|
|
|
5875
6094
|
// src/tools/shell.ts
|
|
5876
6095
|
var ShellSchema = z3.object({
|
|
5877
6096
|
command: z3.string().describe("Shell command to execute"),
|
|
5878
|
-
timeout: z3.number().int().min(1000).
|
|
5879
|
-
env: z3.record(z3.string(), z3.string()).
|
|
6097
|
+
timeout: z3.number().int().min(1000).nullable().describe("Timeout in milliseconds. If omitted, the command runs until it exits."),
|
|
6098
|
+
env: z3.record(z3.string(), z3.string()).nullable().describe("Additional environment variables to set")
|
|
5880
6099
|
});
|
|
5881
6100
|
var MAX_OUTPUT_BYTES = 1e4;
|
|
5882
|
-
async function runShellCommand(input) {
|
|
6101
|
+
async function runShellCommand(input, options) {
|
|
5883
6102
|
const cwd = input.cwd ?? process.cwd();
|
|
5884
|
-
const timeout = input.timeout;
|
|
5885
|
-
const
|
|
6103
|
+
const timeout = input.timeout ?? undefined;
|
|
6104
|
+
const inputEnv = input.env ?? undefined;
|
|
6105
|
+
const existingGitCount = Number(inputEnv?.GIT_CONFIG_COUNT ?? process.env.GIT_CONFIG_COUNT ?? "0") || 0;
|
|
5886
6106
|
const gitIdx = String(existingGitCount);
|
|
5887
6107
|
const env = Object.assign({}, process.env, {
|
|
5888
6108
|
FORCE_COLOR: "1",
|
|
5889
6109
|
GIT_CONFIG_COUNT: String(existingGitCount + 1),
|
|
5890
6110
|
[`GIT_CONFIG_KEY_${gitIdx}`]: "color.ui",
|
|
5891
6111
|
[`GIT_CONFIG_VALUE_${gitIdx}`]: "always"
|
|
5892
|
-
},
|
|
6112
|
+
}, inputEnv ?? {});
|
|
5893
6113
|
let timedOut = false;
|
|
5894
6114
|
const readers = [];
|
|
5895
6115
|
const wasRaw = process.stdin.isTTY ? process.stdin.isRaw : false;
|
|
@@ -5900,8 +6120,7 @@ ${input.command}`], {
|
|
|
5900
6120
|
stdout: "pipe",
|
|
5901
6121
|
stderr: "pipe"
|
|
5902
6122
|
});
|
|
5903
|
-
|
|
5904
|
-
timedOut = true;
|
|
6123
|
+
function killProc() {
|
|
5905
6124
|
try {
|
|
5906
6125
|
proc.kill("SIGTERM");
|
|
5907
6126
|
setTimeout(() => {
|
|
@@ -5913,7 +6132,20 @@ ${input.command}`], {
|
|
|
5913
6132
|
for (const reader of readers) {
|
|
5914
6133
|
reader.cancel().catch(() => {});
|
|
5915
6134
|
}
|
|
6135
|
+
}
|
|
6136
|
+
const timer = timeout ? setTimeout(() => {
|
|
6137
|
+
timedOut = true;
|
|
6138
|
+
killProc();
|
|
5916
6139
|
}, timeout) : undefined;
|
|
6140
|
+
const abortSignal = options?.signal;
|
|
6141
|
+
const onAbort = () => {
|
|
6142
|
+
killProc();
|
|
6143
|
+
};
|
|
6144
|
+
if (abortSignal?.aborted) {
|
|
6145
|
+
onAbort();
|
|
6146
|
+
} else {
|
|
6147
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
6148
|
+
}
|
|
5917
6149
|
async function collectStream(stream) {
|
|
5918
6150
|
const reader = stream.getReader();
|
|
5919
6151
|
readers.push(reader);
|
|
@@ -5957,6 +6189,7 @@ ${input.command}`], {
|
|
|
5957
6189
|
} finally {
|
|
5958
6190
|
if (timer)
|
|
5959
6191
|
clearTimeout(timer);
|
|
6192
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
5960
6193
|
restoreTerminal();
|
|
5961
6194
|
if (wasRaw) {
|
|
5962
6195
|
try {
|
|
@@ -5981,7 +6214,7 @@ var shellTool = {
|
|
|
5981
6214
|
name: "shell",
|
|
5982
6215
|
description: "Execute a shell command. Returns stdout, stderr, and exit code. " + "Use this for reading/searching code, running tests, builds, git commands, and invoking `mc-edit` for partial file edits. " + "Prefer non-interactive commands. Avoid commands that run indefinitely.",
|
|
5983
6216
|
schema: ShellSchema,
|
|
5984
|
-
execute: runShellCommand
|
|
6217
|
+
execute: (input, options) => runShellCommand(input, options)
|
|
5985
6218
|
};
|
|
5986
6219
|
|
|
5987
6220
|
// src/agent/tools.ts
|
|
@@ -5989,11 +6222,11 @@ function withCwdDefault(tool, cwd) {
|
|
|
5989
6222
|
const originalExecute = tool.execute;
|
|
5990
6223
|
return {
|
|
5991
6224
|
...tool,
|
|
5992
|
-
execute: async (input) => {
|
|
6225
|
+
execute: async (input, options) => {
|
|
5993
6226
|
const patched = typeof input === "object" && input !== null ? input : {};
|
|
5994
6227
|
if (patched.cwd === undefined)
|
|
5995
6228
|
patched.cwd = cwd;
|
|
5996
|
-
return originalExecute(patched);
|
|
6229
|
+
return originalExecute(patched, options);
|
|
5997
6230
|
}
|
|
5998
6231
|
};
|
|
5999
6232
|
}
|
|
@@ -6036,7 +6269,7 @@ async function initAgent(opts) {
|
|
|
6036
6269
|
for (const row of listMcpServers()) {
|
|
6037
6270
|
try {
|
|
6038
6271
|
await connectAndAddMcp(row.name);
|
|
6039
|
-
opts.reporter.info(`MCP: connected ${
|
|
6272
|
+
opts.reporter.info(`MCP: connected ${c11.cyan(row.name)}`);
|
|
6040
6273
|
} catch (e) {
|
|
6041
6274
|
opts.reporter.error(`MCP: failed to connect ${row.name}: ${String(e)}`);
|
|
6042
6275
|
}
|
|
@@ -6094,7 +6327,7 @@ async function initAgent(opts) {
|
|
|
6094
6327
|
}
|
|
6095
6328
|
|
|
6096
6329
|
// src/cli/args.ts
|
|
6097
|
-
import * as
|
|
6330
|
+
import * as c12 from "yoctocolors";
|
|
6098
6331
|
function parseArgs(argv) {
|
|
6099
6332
|
const args = {
|
|
6100
6333
|
model: null,
|
|
@@ -6143,11 +6376,11 @@ function parseArgs(argv) {
|
|
|
6143
6376
|
return args;
|
|
6144
6377
|
}
|
|
6145
6378
|
function printHelp() {
|
|
6146
|
-
writeln(`${
|
|
6379
|
+
writeln(`${c12.bold("mini-coder")} \u2014 a small, fast CLI coding agent
|
|
6147
6380
|
`);
|
|
6148
|
-
writeln(`${
|
|
6381
|
+
writeln(`${c12.bold("Usage:")} mc [options] [prompt]
|
|
6149
6382
|
`);
|
|
6150
|
-
writeln(`${
|
|
6383
|
+
writeln(`${c12.bold("Options:")}`);
|
|
6151
6384
|
const opts = [
|
|
6152
6385
|
["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
|
|
6153
6386
|
["-c, --continue", "Continue the most recent session"],
|
|
@@ -6157,10 +6390,10 @@ function printHelp() {
|
|
|
6157
6390
|
["-h, --help", "Show this help"]
|
|
6158
6391
|
];
|
|
6159
6392
|
for (const [flag, desc] of opts) {
|
|
6160
|
-
writeln(` ${
|
|
6393
|
+
writeln(` ${c12.cyan((flag ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
|
|
6161
6394
|
}
|
|
6162
6395
|
writeln(`
|
|
6163
|
-
${
|
|
6396
|
+
${c12.bold("Provider env vars:")}`);
|
|
6164
6397
|
const envs = [
|
|
6165
6398
|
["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
|
|
6166
6399
|
["ANTHROPIC_API_KEY", "Anthropic direct"],
|
|
@@ -6171,22 +6404,22 @@ ${c13.bold("Provider env vars:")}`);
|
|
|
6171
6404
|
["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
|
|
6172
6405
|
];
|
|
6173
6406
|
for (const [env, desc] of envs) {
|
|
6174
|
-
writeln(` ${
|
|
6407
|
+
writeln(` ${c12.yellow((env ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
|
|
6175
6408
|
}
|
|
6176
6409
|
writeln(`
|
|
6177
|
-
${
|
|
6178
|
-
writeln(` mc ${
|
|
6179
|
-
writeln(` mc "explain this codebase" ${
|
|
6180
|
-
writeln(` mc -c ${
|
|
6181
|
-
writeln(` mc -m ollama/llama3.2 ${
|
|
6182
|
-
writeln(` mc -l ${
|
|
6410
|
+
${c12.bold("Examples:")}`);
|
|
6411
|
+
writeln(` mc ${c12.dim("# interactive session")}`);
|
|
6412
|
+
writeln(` mc "explain this codebase" ${c12.dim("# one-shot prompt then exit")}`);
|
|
6413
|
+
writeln(` mc -c ${c12.dim("# continue last session")}`);
|
|
6414
|
+
writeln(` mc -m ollama/llama3.2 ${c12.dim("# use local Ollama model")}`);
|
|
6415
|
+
writeln(` mc -l ${c12.dim("# list sessions")}`);
|
|
6183
6416
|
}
|
|
6184
6417
|
|
|
6185
6418
|
// src/cli/bootstrap.ts
|
|
6186
6419
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
6187
6420
|
import { homedir as homedir6 } from "os";
|
|
6188
6421
|
import { join as join7 } from "path";
|
|
6189
|
-
import * as
|
|
6422
|
+
import * as c13 from "yoctocolors";
|
|
6190
6423
|
var REVIEW_SKILL_CONTENT = `---
|
|
6191
6424
|
name: review
|
|
6192
6425
|
description: "Review recent changes for correctness, code quality, and performance. Use when the user asks to review, check, or audit recent code changes, diffs, or pull requests."
|
|
@@ -6221,7 +6454,7 @@ function bootstrapGlobalDefaults() {
|
|
|
6221
6454
|
if (!existsSync5(skillPath)) {
|
|
6222
6455
|
mkdirSync2(skillDir, { recursive: true });
|
|
6223
6456
|
writeFileSync(skillPath, REVIEW_SKILL_CONTENT, "utf-8");
|
|
6224
|
-
writeln(`${
|
|
6457
|
+
writeln(`${c13.green("\u2713")} created ${c13.dim("~/.agents/skills/review/SKILL.md")} ${c13.dim("(edit it to customise your reviews)")}`);
|
|
6225
6458
|
}
|
|
6226
6459
|
}
|
|
6227
6460
|
|
|
@@ -6258,25 +6491,25 @@ ${content}
|
|
|
6258
6491
|
}
|
|
6259
6492
|
|
|
6260
6493
|
// src/cli/input-loop.ts
|
|
6261
|
-
import * as
|
|
6494
|
+
import * as c20 from "yoctocolors";
|
|
6262
6495
|
|
|
6263
6496
|
// src/cli/commands.ts
|
|
6264
6497
|
import { randomBytes } from "crypto";
|
|
6265
6498
|
import { unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
6266
6499
|
import { tmpdir } from "os";
|
|
6267
6500
|
import { join as join9 } from "path";
|
|
6268
|
-
import * as
|
|
6501
|
+
import * as c19 from "yoctocolors";
|
|
6269
6502
|
|
|
6270
6503
|
// src/cli/commands-help.ts
|
|
6271
|
-
import * as
|
|
6504
|
+
import * as c14 from "yoctocolors";
|
|
6272
6505
|
function renderEntries(entries) {
|
|
6273
6506
|
for (const [label, description] of entries) {
|
|
6274
|
-
writeln(` ${
|
|
6507
|
+
writeln(` ${c14.cyan(label.padEnd(28))} ${c14.dim(description)}`);
|
|
6275
6508
|
}
|
|
6276
6509
|
}
|
|
6277
6510
|
function renderHelpCommand(ctx) {
|
|
6278
6511
|
writeln();
|
|
6279
|
-
writeln(` ${
|
|
6512
|
+
writeln(` ${c14.dim("session")}`);
|
|
6280
6513
|
renderEntries([
|
|
6281
6514
|
["/session [id]", "list sessions or switch to one"],
|
|
6282
6515
|
["/new", "start a fresh session"],
|
|
@@ -6284,7 +6517,7 @@ function renderHelpCommand(ctx) {
|
|
|
6284
6517
|
["/exit", "quit"]
|
|
6285
6518
|
]);
|
|
6286
6519
|
writeln();
|
|
6287
|
-
writeln(` ${
|
|
6520
|
+
writeln(` ${c14.dim("model + context")}`);
|
|
6288
6521
|
renderEntries([
|
|
6289
6522
|
["/model [id]", "list or switch models"],
|
|
6290
6523
|
["/reasoning [on|off]", "toggle reasoning display"],
|
|
@@ -6297,7 +6530,7 @@ function renderHelpCommand(ctx) {
|
|
|
6297
6530
|
["/help", "show this help"]
|
|
6298
6531
|
]);
|
|
6299
6532
|
writeln();
|
|
6300
|
-
writeln(` ${
|
|
6533
|
+
writeln(` ${c14.dim("prompt")}`);
|
|
6301
6534
|
renderEntries([
|
|
6302
6535
|
["ask normally", "send a prompt to the current agent"],
|
|
6303
6536
|
["!cmd", "run a shell command and keep the result in context"],
|
|
@@ -6307,44 +6540,44 @@ function renderHelpCommand(ctx) {
|
|
|
6307
6540
|
const skills = loadSkillsIndex(ctx.cwd);
|
|
6308
6541
|
if (skills.size > 0) {
|
|
6309
6542
|
writeln();
|
|
6310
|
-
writeln(` ${
|
|
6543
|
+
writeln(` ${c14.dim("skills")}`);
|
|
6311
6544
|
for (const skill of skills.values()) {
|
|
6312
|
-
const source = skill.source === "local" ?
|
|
6545
|
+
const source = skill.source === "local" ? c14.dim("local") : c14.dim("global");
|
|
6313
6546
|
const desc = truncateText(skill.description, 80);
|
|
6314
|
-
writeln(` ${
|
|
6547
|
+
writeln(` ${c14.green(`/${skill.name}`.padEnd(28))} ${c14.dim(desc)} ${c14.dim("\xB7")} ${source}`);
|
|
6315
6548
|
}
|
|
6316
6549
|
}
|
|
6317
6550
|
writeln();
|
|
6318
|
-
writeln(` ${
|
|
6551
|
+
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`);
|
|
6319
6552
|
writeln();
|
|
6320
6553
|
}
|
|
6321
6554
|
|
|
6322
6555
|
// src/cli/commands-login.ts
|
|
6323
|
-
import * as
|
|
6556
|
+
import * as c15 from "yoctocolors";
|
|
6324
6557
|
function renderLoginHelp() {
|
|
6325
6558
|
writeln();
|
|
6326
|
-
writeln(` ${
|
|
6327
|
-
writeln(` /login ${
|
|
6328
|
-
writeln(` /login <provider> ${
|
|
6329
|
-
writeln(` /logout <provider> ${
|
|
6559
|
+
writeln(` ${c15.dim("usage:")}`);
|
|
6560
|
+
writeln(` /login ${c15.dim("show login status")}`);
|
|
6561
|
+
writeln(` /login <provider> ${c15.dim("login via OAuth")}`);
|
|
6562
|
+
writeln(` /logout <provider> ${c15.dim("clear saved tokens")}`);
|
|
6330
6563
|
writeln();
|
|
6331
|
-
writeln(` ${
|
|
6564
|
+
writeln(` ${c15.dim("providers:")}`);
|
|
6332
6565
|
for (const p of getOAuthProviders()) {
|
|
6333
|
-
const status = isLoggedIn(p.id) ?
|
|
6334
|
-
writeln(` ${
|
|
6566
|
+
const status = isLoggedIn(p.id) ? c15.green("logged in") : c15.dim("not logged in");
|
|
6567
|
+
writeln(` ${c15.cyan(p.id.padEnd(20))} ${p.name} ${c15.dim("\xB7")} ${status}`);
|
|
6335
6568
|
}
|
|
6336
6569
|
writeln();
|
|
6337
6570
|
}
|
|
6338
6571
|
function renderStatus() {
|
|
6339
6572
|
const loggedIn = listLoggedInProviders();
|
|
6340
6573
|
if (loggedIn.length === 0) {
|
|
6341
|
-
writeln(`${PREFIX.info} ${
|
|
6574
|
+
writeln(`${PREFIX.info} ${c15.dim("no OAuth logins \u2014 use")} /login <provider>`);
|
|
6342
6575
|
return;
|
|
6343
6576
|
}
|
|
6344
6577
|
for (const id of loggedIn) {
|
|
6345
6578
|
const provider = getOAuthProvider(id);
|
|
6346
6579
|
const name = provider?.name ?? id;
|
|
6347
|
-
writeln(`${PREFIX.success} ${
|
|
6580
|
+
writeln(`${PREFIX.success} ${c15.cyan(id)} ${c15.dim(name)}`);
|
|
6348
6581
|
}
|
|
6349
6582
|
}
|
|
6350
6583
|
async function handleLoginCommand(ctx, args) {
|
|
@@ -6365,7 +6598,7 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6365
6598
|
if (isLoggedIn(providerId)) {
|
|
6366
6599
|
const token = await getAccessToken(providerId);
|
|
6367
6600
|
if (token) {
|
|
6368
|
-
writeln(`${PREFIX.success} already logged in to ${
|
|
6601
|
+
writeln(`${PREFIX.success} already logged in to ${c15.cyan(provider.name)}`);
|
|
6369
6602
|
return;
|
|
6370
6603
|
}
|
|
6371
6604
|
}
|
|
@@ -6376,7 +6609,7 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6376
6609
|
ctx.stopSpinner();
|
|
6377
6610
|
writeln(`${PREFIX.info} ${instructions}`);
|
|
6378
6611
|
writeln();
|
|
6379
|
-
writeln(` ${
|
|
6612
|
+
writeln(` ${c15.cyan(url)}`);
|
|
6380
6613
|
writeln();
|
|
6381
6614
|
let open = "xdg-open";
|
|
6382
6615
|
if (process.platform === "darwin")
|
|
@@ -6388,12 +6621,12 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6388
6621
|
},
|
|
6389
6622
|
onProgress: (msg) => {
|
|
6390
6623
|
ctx.stopSpinner();
|
|
6391
|
-
writeln(`${PREFIX.info} ${
|
|
6624
|
+
writeln(`${PREFIX.info} ${c15.dim(msg)}`);
|
|
6392
6625
|
ctx.startSpinner("exchanging tokens");
|
|
6393
6626
|
}
|
|
6394
6627
|
});
|
|
6395
6628
|
ctx.stopSpinner();
|
|
6396
|
-
writeln(`${PREFIX.success} logged in to ${
|
|
6629
|
+
writeln(`${PREFIX.success} logged in to ${c15.cyan(provider.name)}`);
|
|
6397
6630
|
} catch (err) {
|
|
6398
6631
|
ctx.stopSpinner();
|
|
6399
6632
|
writeln(`${PREFIX.error} login failed: ${err.message}`);
|
|
@@ -6406,15 +6639,15 @@ function handleLogoutCommand(_ctx, args) {
|
|
|
6406
6639
|
return;
|
|
6407
6640
|
}
|
|
6408
6641
|
if (!isLoggedIn(providerId)) {
|
|
6409
|
-
writeln(`${PREFIX.info} ${
|
|
6642
|
+
writeln(`${PREFIX.info} ${c15.dim("not logged in to")} ${providerId}`);
|
|
6410
6643
|
return;
|
|
6411
6644
|
}
|
|
6412
6645
|
logout(providerId);
|
|
6413
|
-
writeln(`${PREFIX.success} logged out of ${
|
|
6646
|
+
writeln(`${PREFIX.success} logged out of ${c15.cyan(providerId)}`);
|
|
6414
6647
|
}
|
|
6415
6648
|
|
|
6416
6649
|
// src/cli/commands-mcp.ts
|
|
6417
|
-
import * as
|
|
6650
|
+
import * as c16 from "yoctocolors";
|
|
6418
6651
|
async function handleMcpCommand(ctx, args) {
|
|
6419
6652
|
const parts = args.trim().split(/\s+/);
|
|
6420
6653
|
const sub = parts[0] ?? "list";
|
|
@@ -6422,15 +6655,15 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6422
6655
|
case "list": {
|
|
6423
6656
|
const servers = listMcpServers();
|
|
6424
6657
|
if (servers.length === 0) {
|
|
6425
|
-
writeln(
|
|
6426
|
-
writeln(
|
|
6658
|
+
writeln(c16.dim(" no MCP servers configured"));
|
|
6659
|
+
writeln(c16.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
|
|
6427
6660
|
return;
|
|
6428
6661
|
}
|
|
6429
6662
|
writeln();
|
|
6430
6663
|
for (const s of servers) {
|
|
6431
6664
|
const detailText = s.url ?? s.command ?? "";
|
|
6432
|
-
const detail = detailText ?
|
|
6433
|
-
writeln(` ${
|
|
6665
|
+
const detail = detailText ? c16.dim(` ${detailText}`) : "";
|
|
6666
|
+
writeln(` ${c16.yellow("\u2699")} ${c16.bold(s.name)} ${c16.dim(s.transport)}${detail}`);
|
|
6434
6667
|
}
|
|
6435
6668
|
return;
|
|
6436
6669
|
}
|
|
@@ -6475,9 +6708,9 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6475
6708
|
}
|
|
6476
6709
|
try {
|
|
6477
6710
|
await ctx.connectMcpServer(name);
|
|
6478
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6711
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} added and connected`);
|
|
6479
6712
|
} catch (e) {
|
|
6480
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6713
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} saved ${c16.dim(`(connection failed: ${String(e)})`)}`);
|
|
6481
6714
|
}
|
|
6482
6715
|
return;
|
|
6483
6716
|
}
|
|
@@ -6489,17 +6722,17 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6489
6722
|
return;
|
|
6490
6723
|
}
|
|
6491
6724
|
deleteMcpServer(name);
|
|
6492
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6725
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} removed`);
|
|
6493
6726
|
return;
|
|
6494
6727
|
}
|
|
6495
6728
|
default:
|
|
6496
6729
|
writeln(`${PREFIX.error} unknown: /mcp ${sub}`);
|
|
6497
|
-
writeln(
|
|
6730
|
+
writeln(c16.dim(" subcommands: list \xB7 add \xB7 remove"));
|
|
6498
6731
|
}
|
|
6499
6732
|
}
|
|
6500
6733
|
|
|
6501
6734
|
// src/cli/commands-model.ts
|
|
6502
|
-
import * as
|
|
6735
|
+
import * as c17 from "yoctocolors";
|
|
6503
6736
|
import { select } from "yoctoselect";
|
|
6504
6737
|
var THINKING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
6505
6738
|
function parseThinkingEffort(value) {
|
|
@@ -6519,21 +6752,21 @@ function renderModelUpdatedMessage(ctx, modelId, effortArg) {
|
|
|
6519
6752
|
if (effortArg) {
|
|
6520
6753
|
if (effortArg === "off") {
|
|
6521
6754
|
ctx.setThinkingEffort(null);
|
|
6522
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6755
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim("(thinking disabled)")}`);
|
|
6523
6756
|
return;
|
|
6524
6757
|
}
|
|
6525
6758
|
const effort = parseThinkingEffort(effortArg);
|
|
6526
6759
|
if (effort) {
|
|
6527
6760
|
ctx.setThinkingEffort(effort);
|
|
6528
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6761
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim(`(\u2726 ${effort})`)}`);
|
|
6529
6762
|
return;
|
|
6530
6763
|
}
|
|
6531
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6532
|
-
writeln(`${PREFIX.error} unknown effort level ${
|
|
6764
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)}`);
|
|
6765
|
+
writeln(`${PREFIX.error} unknown effort level ${c17.cyan(effortArg)} (use low, medium, high, xhigh, off)`);
|
|
6533
6766
|
return;
|
|
6534
6767
|
}
|
|
6535
|
-
const effortTag = ctx.thinkingEffort ?
|
|
6536
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6768
|
+
const effortTag = ctx.thinkingEffort ? c17.dim(` (\u2726 ${ctx.thinkingEffort})`) : "";
|
|
6769
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)}${effortTag}`);
|
|
6537
6770
|
}
|
|
6538
6771
|
async function handleModelSet(ctx, args) {
|
|
6539
6772
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -6546,7 +6779,7 @@ async function handleModelSet(ctx, args) {
|
|
|
6546
6779
|
const snapshot = await fetchAvailableModels();
|
|
6547
6780
|
const match = findModelIdByAlias(idArg, snapshot.models.map((model) => model.id));
|
|
6548
6781
|
if (!match) {
|
|
6549
|
-
writeln(`${PREFIX.error} unknown model ${
|
|
6782
|
+
writeln(`${PREFIX.error} unknown model ${c17.cyan(idArg)} ${c17.dim("\u2014 run /models for the full list")}`);
|
|
6550
6783
|
return;
|
|
6551
6784
|
}
|
|
6552
6785
|
modelId = match;
|
|
@@ -6566,7 +6799,7 @@ function handleModelEffort(ctx, effortArg) {
|
|
|
6566
6799
|
return;
|
|
6567
6800
|
}
|
|
6568
6801
|
ctx.setThinkingEffort(effort);
|
|
6569
|
-
writeln(`${PREFIX.success} thinking effort \u2192 ${
|
|
6802
|
+
writeln(`${PREFIX.success} thinking effort \u2192 ${c17.cyan(effort)}`);
|
|
6570
6803
|
}
|
|
6571
6804
|
async function handleModelSelect(ctx) {
|
|
6572
6805
|
ctx.startSpinner("fetching models");
|
|
@@ -6574,21 +6807,22 @@ async function handleModelSelect(ctx) {
|
|
|
6574
6807
|
ctx.stopSpinner();
|
|
6575
6808
|
if (snapshot.models.length === 0) {
|
|
6576
6809
|
writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
|
|
6577
|
-
writeln(
|
|
6810
|
+
writeln(c17.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
|
|
6578
6811
|
return;
|
|
6579
6812
|
}
|
|
6580
6813
|
if (snapshot.stale) {
|
|
6581
6814
|
const lastSync = snapshot.lastSyncAt ? new Date(snapshot.lastSyncAt).toLocaleString() : "never";
|
|
6582
6815
|
const refreshTag = snapshot.refreshing ? " (refreshing in background)" : "";
|
|
6583
|
-
writeln(
|
|
6816
|
+
writeln(c17.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
|
|
6584
6817
|
}
|
|
6585
6818
|
const items = snapshot.models.map((model) => {
|
|
6586
6819
|
const isCurrent = ctx.currentModel === model.id;
|
|
6587
|
-
const freeTag = model.free ?
|
|
6588
|
-
const contextTag = model.context ?
|
|
6589
|
-
const currentTag = isCurrent ?
|
|
6820
|
+
const freeTag = model.free ? c17.green(" free") : "";
|
|
6821
|
+
const contextTag = model.context ? c17.dim(` ${Math.round(model.context / 1000)}k`) : "";
|
|
6822
|
+
const currentTag = isCurrent ? c17.cyan(" \u25C0") : "";
|
|
6823
|
+
const providerTag = c17.dim(` [${model.provider}]`);
|
|
6590
6824
|
return {
|
|
6591
|
-
label: `${model.displayName}${freeTag}${contextTag}${currentTag}`,
|
|
6825
|
+
label: `${model.displayName}${freeTag}${contextTag}${currentTag}${providerTag}`,
|
|
6592
6826
|
value: model.id,
|
|
6593
6827
|
filterText: `${model.id} ${model.displayName} ${model.provider}`
|
|
6594
6828
|
};
|
|
@@ -6619,7 +6853,7 @@ async function handleModelCommand(ctx, args) {
|
|
|
6619
6853
|
}
|
|
6620
6854
|
|
|
6621
6855
|
// src/cli/commands-session.ts
|
|
6622
|
-
import * as
|
|
6856
|
+
import * as c18 from "yoctocolors";
|
|
6623
6857
|
import { select as select2 } from "yoctoselect";
|
|
6624
6858
|
async function handleSessionCommand(ctx, args) {
|
|
6625
6859
|
const id = args.trim();
|
|
@@ -6628,15 +6862,15 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6628
6862
|
const ok2 = ctx.switchSession(id);
|
|
6629
6863
|
ctx.stopSpinner();
|
|
6630
6864
|
if (ok2) {
|
|
6631
|
-
writeln(`${PREFIX.success} switched to session ${
|
|
6865
|
+
writeln(`${PREFIX.success} switched to session ${c18.cyan(id)} (${c18.cyan(ctx.currentModel)})`);
|
|
6632
6866
|
} else {
|
|
6633
|
-
writeln(`${PREFIX.error} session ${
|
|
6867
|
+
writeln(`${PREFIX.error} session ${c18.cyan(id)} not found`);
|
|
6634
6868
|
}
|
|
6635
6869
|
return;
|
|
6636
6870
|
}
|
|
6637
6871
|
const sessions = listSessions(50);
|
|
6638
6872
|
if (sessions.length === 0) {
|
|
6639
|
-
writeln(`${PREFIX.info} ${
|
|
6873
|
+
writeln(`${PREFIX.info} ${c18.dim("no sessions found")}`);
|
|
6640
6874
|
return;
|
|
6641
6875
|
}
|
|
6642
6876
|
const items = sessions.map((s) => {
|
|
@@ -6645,7 +6879,7 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6645
6879
|
const cwd = tildePath(s.cwd);
|
|
6646
6880
|
const date = new Date(s.updated_at).toLocaleDateString();
|
|
6647
6881
|
return {
|
|
6648
|
-
label: `${
|
|
6882
|
+
label: `${c18.dim(s.id)} ${title} ${c18.cyan(model)} ${c18.dim(cwd)} ${c18.dim(date)}`,
|
|
6649
6883
|
value: s.id,
|
|
6650
6884
|
filterText: `${s.id} ${s.title} ${s.model} ${s.cwd}`
|
|
6651
6885
|
};
|
|
@@ -6661,9 +6895,9 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6661
6895
|
return;
|
|
6662
6896
|
const ok = ctx.switchSession(picked);
|
|
6663
6897
|
if (ok) {
|
|
6664
|
-
writeln(`${PREFIX.success} switched to session ${
|
|
6898
|
+
writeln(`${PREFIX.success} switched to session ${c18.cyan(picked)} (${c18.cyan(ctx.currentModel)})`);
|
|
6665
6899
|
} else {
|
|
6666
|
-
writeln(`${PREFIX.error} session ${
|
|
6900
|
+
writeln(`${PREFIX.error} session ${c18.cyan(picked)} not found`);
|
|
6667
6901
|
}
|
|
6668
6902
|
}
|
|
6669
6903
|
|
|
@@ -6673,9 +6907,9 @@ async function handleUndo(ctx) {
|
|
|
6673
6907
|
try {
|
|
6674
6908
|
const ok = await ctx.undoLastTurn();
|
|
6675
6909
|
if (ok) {
|
|
6676
|
-
writeln(`${PREFIX.success} ${
|
|
6910
|
+
writeln(`${PREFIX.success} ${c19.dim("last conversation turn removed")}`);
|
|
6677
6911
|
} else {
|
|
6678
|
-
writeln(`${PREFIX.info} ${
|
|
6912
|
+
writeln(`${PREFIX.info} ${c19.dim("nothing to undo")}`);
|
|
6679
6913
|
}
|
|
6680
6914
|
} finally {
|
|
6681
6915
|
ctx.stopSpinner();
|
|
@@ -6733,7 +6967,7 @@ async function handleCommand(command, args, ctx) {
|
|
|
6733
6967
|
if (loaded2) {
|
|
6734
6968
|
const srcPath = skill.source === "local" ? `.agents/skills/${skill.name}/SKILL.md` : `~/.agents/skills/${skill.name}/SKILL.md`;
|
|
6735
6969
|
if (skill.context === "fork") {
|
|
6736
|
-
writeln(`${PREFIX.info} ${
|
|
6970
|
+
writeln(`${PREFIX.info} ${c19.cyan(skill.name)} ${c19.dim(`[${srcPath}] (forked subagent)`)}`);
|
|
6737
6971
|
writeln();
|
|
6738
6972
|
const subagentPrompt = args ? `${loaded2.content}
|
|
6739
6973
|
|
|
@@ -6741,7 +6975,7 @@ ${args}` : loaded2.content;
|
|
|
6741
6975
|
const result = await runForkedSkill(skill.name, subagentPrompt, ctx.cwd);
|
|
6742
6976
|
return { type: "inject-user-message", text: result };
|
|
6743
6977
|
}
|
|
6744
|
-
writeln(`${PREFIX.info} ${
|
|
6978
|
+
writeln(`${PREFIX.info} ${c19.cyan(skill.name)} ${c19.dim(`[${srcPath}]`)}`);
|
|
6745
6979
|
writeln();
|
|
6746
6980
|
const prompt = args ? `${loaded2.content}
|
|
6747
6981
|
|
|
@@ -6749,7 +6983,7 @@ ${args}` : loaded2.content;
|
|
|
6749
6983
|
return { type: "inject-user-message", text: prompt };
|
|
6750
6984
|
}
|
|
6751
6985
|
}
|
|
6752
|
-
writeln(`${PREFIX.error} unknown: /${command} ${
|
|
6986
|
+
writeln(`${PREFIX.error} unknown: /${command} ${c19.dim("\u2014 /help for commands")}`);
|
|
6753
6987
|
return { type: "unknown", command };
|
|
6754
6988
|
}
|
|
6755
6989
|
}
|
|
@@ -6758,7 +6992,7 @@ async function runForkedSkill(skillName, prompt, cwd) {
|
|
|
6758
6992
|
const tmpFile = join9(tmpdir(), `mc-fork-${randomBytes(8).toString("hex")}.md`);
|
|
6759
6993
|
writeFileSync2(tmpFile, prompt, "utf8");
|
|
6760
6994
|
try {
|
|
6761
|
-
writeln(`${PREFIX.info} ${
|
|
6995
|
+
writeln(`${PREFIX.info} ${c19.dim("running subagent\u2026")}`);
|
|
6762
6996
|
const proc = Bun.spawn([process.execPath, Bun.main], {
|
|
6763
6997
|
cwd,
|
|
6764
6998
|
stdin: Bun.file(tmpFile),
|
|
@@ -6790,17 +7024,17 @@ ${output}`;
|
|
|
6790
7024
|
function handleBooleanToggleCommand(opts) {
|
|
6791
7025
|
const mode = opts.args.trim().toLowerCase();
|
|
6792
7026
|
if (!mode) {
|
|
6793
|
-
writeln(`${PREFIX.success} ${opts.label} ${opts.current ?
|
|
7027
|
+
writeln(`${PREFIX.success} ${opts.label} ${opts.current ? c19.green("on") : c19.dim("off")}`);
|
|
6794
7028
|
return;
|
|
6795
7029
|
}
|
|
6796
7030
|
if (mode === "on") {
|
|
6797
7031
|
opts.set(true);
|
|
6798
|
-
writeln(`${PREFIX.success} ${opts.label} ${
|
|
7032
|
+
writeln(`${PREFIX.success} ${opts.label} ${c19.green("on")}`);
|
|
6799
7033
|
return;
|
|
6800
7034
|
}
|
|
6801
7035
|
if (mode === "off") {
|
|
6802
7036
|
opts.set(false);
|
|
6803
|
-
writeln(`${PREFIX.success} ${opts.label} ${
|
|
7037
|
+
writeln(`${PREFIX.success} ${opts.label} ${c19.dim("off")}`);
|
|
6804
7038
|
return;
|
|
6805
7039
|
}
|
|
6806
7040
|
writeln(`${PREFIX.error} usage: ${opts.usage}`);
|
|
@@ -6860,13 +7094,12 @@ async function getGitBranch(cwd) {
|
|
|
6860
7094
|
}
|
|
6861
7095
|
async function runInputLoop(opts) {
|
|
6862
7096
|
const { cwd, reporter, cmdCtx, runner } = opts;
|
|
6863
|
-
let lastStatusSignature = null;
|
|
6864
7097
|
while (true) {
|
|
6865
7098
|
const branch = await getGitBranch(cwd);
|
|
6866
7099
|
const status = runner.getStatusInfo();
|
|
6867
7100
|
const cwdDisplay = tildePath(cwd);
|
|
6868
7101
|
const contextWindow = getContextWindow(status.model);
|
|
6869
|
-
|
|
7102
|
+
reporter.renderStatusBar({
|
|
6870
7103
|
model: status.model,
|
|
6871
7104
|
cwd: cwdDisplay,
|
|
6872
7105
|
gitBranch: branch,
|
|
@@ -6876,12 +7109,7 @@ async function runInputLoop(opts) {
|
|
|
6876
7109
|
contextTokens: status.lastContextTokens,
|
|
6877
7110
|
contextWindow,
|
|
6878
7111
|
thinkingEffort: status.thinkingEffort
|
|
6879
|
-
};
|
|
6880
|
-
const statusSignature = buildStatusBarSignature(statusData);
|
|
6881
|
-
if (statusSignature !== lastStatusSignature) {
|
|
6882
|
-
reporter.renderStatusBar(statusData);
|
|
6883
|
-
lastStatusSignature = statusSignature;
|
|
6884
|
-
}
|
|
7112
|
+
});
|
|
6885
7113
|
let input;
|
|
6886
7114
|
try {
|
|
6887
7115
|
input = await readline({ cwd });
|
|
@@ -6890,14 +7118,14 @@ async function runInputLoop(opts) {
|
|
|
6890
7118
|
}
|
|
6891
7119
|
switch (input.type) {
|
|
6892
7120
|
case "eof":
|
|
6893
|
-
reporter.writeText(
|
|
7121
|
+
reporter.writeText(c20.dim("Goodbye."));
|
|
6894
7122
|
return;
|
|
6895
7123
|
case "interrupt":
|
|
6896
7124
|
continue;
|
|
6897
7125
|
case "command": {
|
|
6898
7126
|
const result = await handleCommand(input.command, input.args, cmdCtx);
|
|
6899
7127
|
if (result.type === "exit") {
|
|
6900
|
-
reporter.writeText(
|
|
7128
|
+
reporter.writeText(c20.dim("Goodbye."));
|
|
6901
7129
|
return;
|
|
6902
7130
|
}
|
|
6903
7131
|
if (result.type === "inject-user-message") {
|
|
@@ -7004,7 +7232,7 @@ async function main() {
|
|
|
7004
7232
|
if (last) {
|
|
7005
7233
|
sessionId = last.id;
|
|
7006
7234
|
} else {
|
|
7007
|
-
writeln(
|
|
7235
|
+
writeln(c21.dim("No previous session found, starting fresh."));
|
|
7008
7236
|
}
|
|
7009
7237
|
} else if (args.sessionId) {
|
|
7010
7238
|
sessionId = args.sessionId;
|