mini-coder 0.2.3 → 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 +2 -2
- package/dist/mc.js +672 -505
- package/docs/design-decisions.md +1 -44
- package/docs/mini-coder.1.md +1 -1
- package/package.json +1 -1
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,8 +2613,8 @@ function renderStatusBar(opts) {
|
|
|
2400
2613
|
}
|
|
2401
2614
|
|
|
2402
2615
|
// src/cli/stream-render.ts
|
|
2403
|
-
import
|
|
2404
|
-
import
|
|
2616
|
+
import * as c6 from "yoctocolors";
|
|
2617
|
+
import { createHighlighter } from "yoctomarkdown";
|
|
2405
2618
|
|
|
2406
2619
|
// src/llm-api/history/gemini.ts
|
|
2407
2620
|
function getGeminiThoughtSignature(part) {
|
|
@@ -2659,60 +2872,6 @@ function extractAssistantText(newMessages) {
|
|
|
2659
2872
|
`);
|
|
2660
2873
|
}
|
|
2661
2874
|
|
|
2662
|
-
// src/cli/live-reasoning.ts
|
|
2663
|
-
import * as c4 from "yoctocolors";
|
|
2664
|
-
function styleReasoningText(text) {
|
|
2665
|
-
return c4.italic(c4.dim(text));
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
class LiveReasoningBlock {
|
|
2669
|
-
blockOpen = false;
|
|
2670
|
-
lineOpen = false;
|
|
2671
|
-
append(delta) {
|
|
2672
|
-
if (!delta)
|
|
2673
|
-
return;
|
|
2674
|
-
this.openBlock();
|
|
2675
|
-
const lines = delta.split(`
|
|
2676
|
-
`);
|
|
2677
|
-
for (const [index, line] of lines.entries()) {
|
|
2678
|
-
if (line)
|
|
2679
|
-
this.writeText(line);
|
|
2680
|
-
if (index < lines.length - 1)
|
|
2681
|
-
this.endLine();
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
isOpen() {
|
|
2685
|
-
return this.blockOpen;
|
|
2686
|
-
}
|
|
2687
|
-
finish() {
|
|
2688
|
-
if (!this.blockOpen)
|
|
2689
|
-
return;
|
|
2690
|
-
if (this.lineOpen)
|
|
2691
|
-
writeln();
|
|
2692
|
-
this.blockOpen = false;
|
|
2693
|
-
this.lineOpen = false;
|
|
2694
|
-
}
|
|
2695
|
-
openBlock() {
|
|
2696
|
-
if (this.blockOpen)
|
|
2697
|
-
return;
|
|
2698
|
-
writeln(`${G.info} ${c4.dim("reasoning")}`);
|
|
2699
|
-
this.blockOpen = true;
|
|
2700
|
-
}
|
|
2701
|
-
writeText(text) {
|
|
2702
|
-
if (!this.lineOpen) {
|
|
2703
|
-
write(" ");
|
|
2704
|
-
this.lineOpen = true;
|
|
2705
|
-
}
|
|
2706
|
-
write(styleReasoningText(text));
|
|
2707
|
-
}
|
|
2708
|
-
endLine() {
|
|
2709
|
-
if (!this.lineOpen)
|
|
2710
|
-
write(" ");
|
|
2711
|
-
writeln();
|
|
2712
|
-
this.lineOpen = false;
|
|
2713
|
-
}
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
2875
|
// src/cli/reasoning.ts
|
|
2717
2876
|
function normalizeReasoningDelta(delta) {
|
|
2718
2877
|
return delta.replace(/\r\n?/g, `
|
|
@@ -2746,130 +2905,34 @@ function normalizeReasoningText(text) {
|
|
|
2746
2905
|
`);
|
|
2747
2906
|
}
|
|
2748
2907
|
|
|
2749
|
-
// src/cli/stream-render-content.ts
|
|
2750
|
-
import { createHighlighter } from "yoctomarkdown";
|
|
2751
|
-
class StreamRenderContent {
|
|
2752
|
-
spinner;
|
|
2753
|
-
quiet;
|
|
2754
|
-
inText = false;
|
|
2755
|
-
accumulatedText = "";
|
|
2756
|
-
accumulatedReasoning = "";
|
|
2757
|
-
highlighter;
|
|
2758
|
-
constructor(spinner, quiet = false) {
|
|
2759
|
-
this.spinner = spinner;
|
|
2760
|
-
this.quiet = quiet;
|
|
2761
|
-
}
|
|
2762
|
-
getText() {
|
|
2763
|
-
return this.accumulatedText;
|
|
2764
|
-
}
|
|
2765
|
-
getReasoning() {
|
|
2766
|
-
return this.accumulatedReasoning;
|
|
2767
|
-
}
|
|
2768
|
-
hasOpenContent() {
|
|
2769
|
-
return this.inText;
|
|
2770
|
-
}
|
|
2771
|
-
appendTextDelta(delta, renderedVisibleOutput) {
|
|
2772
|
-
let text = delta ?? "";
|
|
2773
|
-
if (!text)
|
|
2774
|
-
return;
|
|
2775
|
-
if (!this.inText) {
|
|
2776
|
-
text = text.trimStart();
|
|
2777
|
-
if (!text)
|
|
2778
|
-
return;
|
|
2779
|
-
if (!this.quiet) {
|
|
2780
|
-
this.spinner.stop();
|
|
2781
|
-
if (renderedVisibleOutput)
|
|
2782
|
-
writeln();
|
|
2783
|
-
write(`${G.reply} `);
|
|
2784
|
-
}
|
|
2785
|
-
this.inText = true;
|
|
2786
|
-
if (!this.quiet && terminal.isStdoutTTY)
|
|
2787
|
-
this.highlighter = createHighlighter();
|
|
2788
|
-
}
|
|
2789
|
-
const isFirstLine = !this.accumulatedText.includes(`
|
|
2790
|
-
`);
|
|
2791
|
-
this.accumulatedText += text;
|
|
2792
|
-
if (this.quiet)
|
|
2793
|
-
return;
|
|
2794
|
-
this.spinner.stop();
|
|
2795
|
-
if (this.highlighter) {
|
|
2796
|
-
let colored = this.highlighter.write(text);
|
|
2797
|
-
if (colored) {
|
|
2798
|
-
if (isFirstLine && colored.startsWith("\x1B[2K\r")) {
|
|
2799
|
-
colored = `\x1B[2K\r${G.reply} ${colored.slice(5)}`;
|
|
2800
|
-
}
|
|
2801
|
-
write(colored);
|
|
2802
|
-
}
|
|
2803
|
-
} else {
|
|
2804
|
-
write(text);
|
|
2805
|
-
}
|
|
2806
|
-
}
|
|
2807
|
-
appendReasoningDelta(delta) {
|
|
2808
|
-
const text = delta ?? "";
|
|
2809
|
-
if (!text)
|
|
2810
|
-
return "";
|
|
2811
|
-
let appended = text;
|
|
2812
|
-
if (this.accumulatedReasoning.endsWith("**") && text.startsWith("**") && !this.accumulatedReasoning.endsWith(`
|
|
2813
|
-
`)) {
|
|
2814
|
-
appended = `
|
|
2815
|
-
${text}`;
|
|
2816
|
-
}
|
|
2817
|
-
this.accumulatedReasoning += appended;
|
|
2818
|
-
return appended;
|
|
2819
|
-
}
|
|
2820
|
-
flushOpenContent() {
|
|
2821
|
-
if (!this.inText)
|
|
2822
|
-
return;
|
|
2823
|
-
if (this.quiet) {
|
|
2824
|
-
this.inText = false;
|
|
2825
|
-
return;
|
|
2826
|
-
}
|
|
2827
|
-
if (this.highlighter) {
|
|
2828
|
-
let finalColored = this.highlighter.end();
|
|
2829
|
-
if (finalColored) {
|
|
2830
|
-
const isFirstLine = !this.accumulatedText.includes(`
|
|
2831
|
-
`);
|
|
2832
|
-
if (isFirstLine && finalColored.startsWith("\x1B[2K\r")) {
|
|
2833
|
-
finalColored = `\x1B[2K\r${G.reply} ${finalColored.slice(5)}`;
|
|
2834
|
-
}
|
|
2835
|
-
write(finalColored);
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
writeln();
|
|
2839
|
-
this.inText = false;
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
|
|
2843
2908
|
// src/cli/tool-render.ts
|
|
2844
|
-
import * as
|
|
2909
|
+
import * as c5 from "yoctocolors";
|
|
2845
2910
|
|
|
2846
2911
|
// src/cli/tool-result-renderers.ts
|
|
2847
|
-
import * as
|
|
2912
|
+
import * as c4 from "yoctocolors";
|
|
2848
2913
|
function writePreviewLines(opts) {
|
|
2849
2914
|
if (!opts.value.trim())
|
|
2850
2915
|
return;
|
|
2851
2916
|
const lines = opts.value.split(`
|
|
2852
2917
|
`);
|
|
2853
|
-
|
|
2918
|
+
if (opts.label)
|
|
2919
|
+
writeln(`${c4.dim(opts.label)} ${c4.dim(`(${lines.length} lines)`)}`);
|
|
2854
2920
|
if (!Number.isFinite(opts.maxLines) || lines.length <= opts.maxLines) {
|
|
2855
|
-
for (const line of lines)
|
|
2856
|
-
writeln(
|
|
2857
|
-
}
|
|
2921
|
+
for (const line of lines)
|
|
2922
|
+
writeln(line);
|
|
2858
2923
|
return;
|
|
2859
2924
|
}
|
|
2860
2925
|
const headCount = Math.max(1, Math.ceil(opts.maxLines / 2));
|
|
2861
2926
|
const tailCount = Math.max(0, Math.floor(opts.maxLines / 2));
|
|
2862
|
-
for (const line of lines.slice(0, headCount))
|
|
2863
|
-
writeln(
|
|
2864
|
-
}
|
|
2927
|
+
for (const line of lines.slice(0, headCount))
|
|
2928
|
+
writeln(line);
|
|
2865
2929
|
const hiddenLines = Math.max(0, lines.length - (headCount + tailCount));
|
|
2866
2930
|
if (hiddenLines > 0) {
|
|
2867
|
-
writeln(
|
|
2931
|
+
writeln(c4.dim(`\u2026 +${hiddenLines} lines`));
|
|
2868
2932
|
}
|
|
2869
2933
|
if (tailCount > 0) {
|
|
2870
|
-
for (const line of lines.slice(-tailCount))
|
|
2871
|
-
writeln(
|
|
2872
|
-
}
|
|
2934
|
+
for (const line of lines.slice(-tailCount))
|
|
2935
|
+
writeln(line);
|
|
2873
2936
|
}
|
|
2874
2937
|
}
|
|
2875
2938
|
function truncateOneLine(value, max = 100, verboseOutput = false) {
|
|
@@ -2928,11 +2991,11 @@ function renderShellResult(result, opts) {
|
|
|
2928
2991
|
const stdoutLines = countShellLines(r.stdout);
|
|
2929
2992
|
const stderrLines = countShellLines(r.stderr);
|
|
2930
2993
|
const stdoutSingleLine = getSingleShellLine(r.stdout);
|
|
2931
|
-
let badge =
|
|
2994
|
+
let badge = c4.red("error");
|
|
2932
2995
|
if (r.timedOut)
|
|
2933
|
-
badge =
|
|
2996
|
+
badge = c4.yellow("timeout");
|
|
2934
2997
|
else if (r.success)
|
|
2935
|
-
badge =
|
|
2998
|
+
badge = c4.green("done");
|
|
2936
2999
|
const parts = buildShellSummaryParts({
|
|
2937
3000
|
exitCode: r.exitCode,
|
|
2938
3001
|
stdoutLines,
|
|
@@ -2940,11 +3003,10 @@ function renderShellResult(result, opts) {
|
|
|
2940
3003
|
stdoutSingleLine,
|
|
2941
3004
|
verboseOutput
|
|
2942
3005
|
});
|
|
2943
|
-
writeln(
|
|
3006
|
+
writeln(`${badge} ${c4.dim(parts.join(" \xB7 "))}`);
|
|
2944
3007
|
writePreviewLines({
|
|
2945
3008
|
label: "stderr",
|
|
2946
3009
|
value: displayStderr,
|
|
2947
|
-
lineColor: c5.red,
|
|
2948
3010
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 10
|
|
2949
3011
|
});
|
|
2950
3012
|
if (shouldPreviewShellStdout({
|
|
@@ -2956,7 +3018,6 @@ function renderShellResult(result, opts) {
|
|
|
2956
3018
|
writePreviewLines({
|
|
2957
3019
|
label: "stdout",
|
|
2958
3020
|
value: displayStdout,
|
|
2959
|
-
lineColor: c5.dim,
|
|
2960
3021
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 20
|
|
2961
3022
|
});
|
|
2962
3023
|
}
|
|
@@ -2967,21 +3028,21 @@ function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
|
2967
3028
|
if (!trimmed)
|
|
2968
3029
|
return "";
|
|
2969
3030
|
if (verboseOutput)
|
|
2970
|
-
return ` ${
|
|
2971
|
-
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)}`;
|
|
2972
3033
|
}
|
|
2973
3034
|
function renderSkillSummaryLine(skill, opts) {
|
|
2974
3035
|
const name = skill.name ?? "(unknown)";
|
|
2975
3036
|
const source = skill.source ?? "unknown";
|
|
2976
|
-
const labelPrefix = opts?.label ? `${
|
|
2977
|
-
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)}`);
|
|
2978
3039
|
}
|
|
2979
3040
|
function renderListSkillsResult(result, opts) {
|
|
2980
3041
|
const r = result;
|
|
2981
3042
|
if (!Array.isArray(r?.skills))
|
|
2982
3043
|
return false;
|
|
2983
3044
|
if (r.skills.length === 0) {
|
|
2984
|
-
writeln(
|
|
3045
|
+
writeln(`${G.info} ${c4.dim("no skills")}`);
|
|
2985
3046
|
return true;
|
|
2986
3047
|
}
|
|
2987
3048
|
const maxSkills = opts?.verboseOutput ? r.skills.length : 6;
|
|
@@ -2991,7 +3052,7 @@ function renderListSkillsResult(result, opts) {
|
|
|
2991
3052
|
});
|
|
2992
3053
|
}
|
|
2993
3054
|
if (r.skills.length > maxSkills) {
|
|
2994
|
-
writeln(
|
|
3055
|
+
writeln(`${c4.dim(`+${r.skills.length - maxSkills} more skills`)}`);
|
|
2995
3056
|
}
|
|
2996
3057
|
return true;
|
|
2997
3058
|
}
|
|
@@ -3000,7 +3061,7 @@ function renderReadSkillResult(result, _opts) {
|
|
|
3000
3061
|
if (!r || typeof r !== "object")
|
|
3001
3062
|
return false;
|
|
3002
3063
|
if (!r.skill) {
|
|
3003
|
-
writeln(
|
|
3064
|
+
writeln(`${G.info} ${c4.dim("skill")} ${c4.dim("(not found)")}`);
|
|
3004
3065
|
return true;
|
|
3005
3066
|
}
|
|
3006
3067
|
renderSkillSummaryLine(r.skill, {
|
|
@@ -3014,19 +3075,19 @@ function renderWebSearchResult(result, opts) {
|
|
|
3014
3075
|
if (!Array.isArray(r?.results))
|
|
3015
3076
|
return false;
|
|
3016
3077
|
if (r.results.length === 0) {
|
|
3017
|
-
writeln(
|
|
3078
|
+
writeln(`${G.info} ${c4.dim("no results")}`);
|
|
3018
3079
|
return true;
|
|
3019
3080
|
}
|
|
3020
3081
|
const maxResults = opts?.verboseOutput ? r.results.length : 5;
|
|
3021
3082
|
for (const item of r.results.slice(0, maxResults)) {
|
|
3022
3083
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
3023
|
-
const score = typeof item.score === "number" ?
|
|
3024
|
-
writeln(
|
|
3084
|
+
const score = typeof item.score === "number" ? c4.dim(` (${item.score.toFixed(2)})`) : "";
|
|
3085
|
+
writeln(`${c4.dim("\u2022")} ${title}${score}`);
|
|
3025
3086
|
if (item.url)
|
|
3026
|
-
writeln(`
|
|
3087
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
3027
3088
|
}
|
|
3028
3089
|
if (r.results.length > maxResults) {
|
|
3029
|
-
writeln(
|
|
3090
|
+
writeln(`${c4.dim(`+${r.results.length - maxResults} more`)}`);
|
|
3030
3091
|
}
|
|
3031
3092
|
return true;
|
|
3032
3093
|
}
|
|
@@ -3035,23 +3096,23 @@ function renderWebContentResult(result, opts) {
|
|
|
3035
3096
|
if (!Array.isArray(r?.results))
|
|
3036
3097
|
return false;
|
|
3037
3098
|
if (r.results.length === 0) {
|
|
3038
|
-
writeln(
|
|
3099
|
+
writeln(`${G.info} ${c4.dim("no pages")}`);
|
|
3039
3100
|
return true;
|
|
3040
3101
|
}
|
|
3041
3102
|
const maxPages = opts?.verboseOutput ? r.results.length : 3;
|
|
3042
3103
|
for (const item of r.results.slice(0, maxPages)) {
|
|
3043
3104
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
3044
|
-
writeln(
|
|
3105
|
+
writeln(`${c4.dim("\u2022")} ${title}`);
|
|
3045
3106
|
if (item.url)
|
|
3046
|
-
writeln(`
|
|
3107
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
3047
3108
|
const preview = (item.text ?? "").replace(/\s+/g, " ").trim();
|
|
3048
3109
|
if (preview) {
|
|
3049
3110
|
const trimmed = opts?.verboseOutput || preview.length <= 220 ? preview : `${preview.slice(0, 217)}\u2026`;
|
|
3050
|
-
writeln(`
|
|
3111
|
+
writeln(` ${c4.dim(trimmed)}`);
|
|
3051
3112
|
}
|
|
3052
3113
|
}
|
|
3053
3114
|
if (r.results.length > maxPages) {
|
|
3054
|
-
writeln(
|
|
3115
|
+
writeln(`${c4.dim(`+${r.results.length - maxPages} more`)}`);
|
|
3055
3116
|
}
|
|
3056
3117
|
return true;
|
|
3057
3118
|
}
|
|
@@ -3061,11 +3122,11 @@ function renderMcpResult(result, opts) {
|
|
|
3061
3122
|
let rendered = false;
|
|
3062
3123
|
for (const block of content.slice(0, maxBlocks)) {
|
|
3063
3124
|
if (block?.type === "text" && block.text) {
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3125
|
+
writePreviewLines({
|
|
3126
|
+
label: "",
|
|
3127
|
+
value: block.text,
|
|
3128
|
+
maxLines: opts?.verboseOutput ? Number.POSITIVE_INFINITY : 6
|
|
3129
|
+
});
|
|
3069
3130
|
rendered = true;
|
|
3070
3131
|
}
|
|
3071
3132
|
}
|
|
@@ -3153,43 +3214,58 @@ function formatShellCallLine(cmd) {
|
|
|
3153
3214
|
const { cwd, rest } = parseShellCdPrefix(cmd);
|
|
3154
3215
|
const firstCmd = extractFirstCommand(rest);
|
|
3155
3216
|
const glyph = shellCmdGlyph(firstCmd, rest);
|
|
3156
|
-
const cwdSuffix = cwd ? ` ${
|
|
3157
|
-
|
|
3158
|
-
return `${glyph} ${display}${cwdSuffix}`;
|
|
3217
|
+
const cwdSuffix = cwd ? ` ${c5.dim(`in ${cwd}`)}` : "";
|
|
3218
|
+
return `${glyph} ${rest}${cwdSuffix}`;
|
|
3159
3219
|
}
|
|
3160
3220
|
function buildToolCallLine(name, args) {
|
|
3161
3221
|
const a = args && typeof args === "object" ? args : {};
|
|
3162
3222
|
if (name === "shell") {
|
|
3163
3223
|
const cmd = String(a.command ?? "").trim();
|
|
3164
3224
|
if (!cmd)
|
|
3165
|
-
return `${G.run} ${
|
|
3225
|
+
return `${G.run} ${c5.dim("shell")}`;
|
|
3166
3226
|
return formatShellCallLine(cmd);
|
|
3167
3227
|
}
|
|
3168
3228
|
if (name === "listSkills") {
|
|
3169
|
-
return `${G.search} ${
|
|
3229
|
+
return `${G.search} ${c5.dim("list skills")}`;
|
|
3170
3230
|
}
|
|
3171
3231
|
if (name === "readSkill") {
|
|
3172
3232
|
const skillName = typeof a.name === "string" ? a.name : "";
|
|
3173
|
-
return `${G.read} ${
|
|
3233
|
+
return `${G.read} ${c5.dim("read skill")}${skillName ? ` ${skillName}` : ""}`;
|
|
3174
3234
|
}
|
|
3175
3235
|
if (name === "webSearch") {
|
|
3176
3236
|
const query = typeof a.query === "string" ? a.query : "";
|
|
3177
|
-
|
|
3178
|
-
return `${G.search} ${c6.dim("search")}${short ? ` ${short}` : ""}`;
|
|
3237
|
+
return `${G.search} ${c5.dim("search")}${query ? ` ${query}` : ""}`;
|
|
3179
3238
|
}
|
|
3180
3239
|
if (name === "webContent") {
|
|
3181
3240
|
const urls = Array.isArray(a.urls) ? a.urls : [];
|
|
3182
3241
|
const label = urls.length === 1 ? String(urls[0]) : `${urls.length} url${urls.length !== 1 ? "s" : ""}`;
|
|
3183
|
-
|
|
3184
|
-
return `${G.read} ${c6.dim("fetch")} ${short}`;
|
|
3242
|
+
return `${G.read} ${c5.dim("fetch")} ${label}`;
|
|
3185
3243
|
}
|
|
3186
3244
|
if (name.startsWith("mcp_")) {
|
|
3187
|
-
return `${G.mcp} ${
|
|
3245
|
+
return `${G.mcp} ${c5.dim(name)}`;
|
|
3188
3246
|
}
|
|
3189
|
-
return `${toolGlyph(name)} ${
|
|
3247
|
+
return `${toolGlyph(name)} ${c5.dim(name)}`;
|
|
3190
3248
|
}
|
|
3191
|
-
function renderToolCall(toolName, args) {
|
|
3192
|
-
|
|
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);
|
|
3193
3269
|
}
|
|
3194
3270
|
function formatErrorBadge(result) {
|
|
3195
3271
|
let msg;
|
|
@@ -3201,11 +3277,11 @@ function formatErrorBadge(result) {
|
|
|
3201
3277
|
msg = JSON.stringify(result);
|
|
3202
3278
|
const oneLiner = msg.split(`
|
|
3203
3279
|
`)[0] ?? msg;
|
|
3204
|
-
return `${G.err} ${
|
|
3280
|
+
return `${G.err} ${c5.red(oneLiner)}`;
|
|
3205
3281
|
}
|
|
3206
3282
|
function renderToolResult(toolName, result, isError, opts) {
|
|
3207
3283
|
if (isError) {
|
|
3208
|
-
writeln(
|
|
3284
|
+
writeln(formatErrorBadge(result));
|
|
3209
3285
|
return;
|
|
3210
3286
|
}
|
|
3211
3287
|
if (renderToolResultByName(toolName, result, opts)) {
|
|
@@ -3213,32 +3289,130 @@ function renderToolResult(toolName, result, isError, opts) {
|
|
|
3213
3289
|
}
|
|
3214
3290
|
const text = JSON.stringify(result);
|
|
3215
3291
|
if (opts?.verboseOutput || text.length <= 120) {
|
|
3216
|
-
writeln(
|
|
3292
|
+
writeln(c5.dim(text));
|
|
3217
3293
|
return;
|
|
3218
3294
|
}
|
|
3219
|
-
writeln(
|
|
3295
|
+
writeln(c5.dim(`${text.slice(0, 117)}\u2026`));
|
|
3220
3296
|
}
|
|
3221
3297
|
|
|
3222
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
|
+
}
|
|
3223
3392
|
async function renderTurn(events, spinner, opts) {
|
|
3224
3393
|
const quiet = opts?.quiet ?? false;
|
|
3225
3394
|
const showReasoning = !quiet && (opts?.showReasoning ?? true);
|
|
3226
3395
|
const verboseOutput = opts?.verboseOutput ?? false;
|
|
3227
|
-
const
|
|
3228
|
-
|
|
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;
|
|
3229
3408
|
let inputTokens = 0;
|
|
3230
3409
|
let outputTokens = 0;
|
|
3231
3410
|
let contextTokens = 0;
|
|
3232
3411
|
let newMessages = [];
|
|
3233
|
-
const startedToolCalls = new Set;
|
|
3234
|
-
const toolCallInfo = new Map;
|
|
3235
|
-
let parallelCallCount = 0;
|
|
3236
3412
|
let renderedVisibleOutput = false;
|
|
3237
|
-
let reasoningComputed = false;
|
|
3238
|
-
let reasoningText = "";
|
|
3239
3413
|
const getReasoningText = () => {
|
|
3240
3414
|
if (!reasoningComputed) {
|
|
3241
|
-
reasoningText = normalizeReasoningText(
|
|
3415
|
+
reasoningText = normalizeReasoningText(reasoningRaw);
|
|
3242
3416
|
reasoningComputed = true;
|
|
3243
3417
|
}
|
|
3244
3418
|
return reasoningText;
|
|
@@ -3246,29 +3420,42 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3246
3420
|
for await (const event of events) {
|
|
3247
3421
|
switch (event.type) {
|
|
3248
3422
|
case "text-delta": {
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
if (
|
|
3423
|
+
finishReasoning(reasoningState);
|
|
3424
|
+
textState.inText = appendTextDelta(event.delta, textState, spinner, quiet, renderedVisibleOutput);
|
|
3425
|
+
if (textState.inText)
|
|
3252
3426
|
renderedVisibleOutput = true;
|
|
3253
3427
|
break;
|
|
3254
3428
|
}
|
|
3255
3429
|
case "reasoning-delta": {
|
|
3256
|
-
|
|
3257
|
-
|
|
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;
|
|
3258
3438
|
reasoningComputed = false;
|
|
3259
|
-
if (showReasoning &&
|
|
3439
|
+
if (showReasoning && appended) {
|
|
3260
3440
|
spinner.stop();
|
|
3261
|
-
if (renderedVisibleOutput && !
|
|
3441
|
+
if (renderedVisibleOutput && !reasoningState.blockOpen)
|
|
3262
3442
|
writeln();
|
|
3263
|
-
|
|
3443
|
+
writeReasoningDelta(appended, reasoningState);
|
|
3264
3444
|
renderedVisibleOutput = true;
|
|
3265
3445
|
}
|
|
3266
3446
|
break;
|
|
3267
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
|
+
}
|
|
3268
3456
|
case "tool-call-start": {
|
|
3269
|
-
if (startedToolCalls.has(event.toolCallId))
|
|
3457
|
+
if (startedToolCalls.has(event.toolCallId))
|
|
3270
3458
|
break;
|
|
3271
|
-
}
|
|
3272
3459
|
const isConsecutiveToolCall = startedToolCalls.size > 0 && toolCallInfo.size > 0;
|
|
3273
3460
|
startedToolCalls.add(event.toolCallId);
|
|
3274
3461
|
toolCallInfo.set(event.toolCallId, {
|
|
@@ -3278,13 +3465,14 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3278
3465
|
if (toolCallInfo.size > 1) {
|
|
3279
3466
|
parallelCallCount = toolCallInfo.size;
|
|
3280
3467
|
}
|
|
3281
|
-
|
|
3282
|
-
|
|
3468
|
+
finishReasoning(reasoningState);
|
|
3469
|
+
flushText(textState, quiet);
|
|
3283
3470
|
if (!quiet) {
|
|
3284
3471
|
spinner.stop();
|
|
3285
|
-
if (renderedVisibleOutput && !isConsecutiveToolCall)
|
|
3472
|
+
if (renderedVisibleOutput && !isConsecutiveToolCall) {
|
|
3286
3473
|
writeln();
|
|
3287
|
-
|
|
3474
|
+
}
|
|
3475
|
+
renderToolCall(event.toolName, event.args, { verboseOutput });
|
|
3288
3476
|
renderedVisibleOutput = true;
|
|
3289
3477
|
spinner.start(event.toolName);
|
|
3290
3478
|
}
|
|
@@ -3294,11 +3482,11 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3294
3482
|
startedToolCalls.delete(event.toolCallId);
|
|
3295
3483
|
const callInfo = toolCallInfo.get(event.toolCallId);
|
|
3296
3484
|
toolCallInfo.delete(event.toolCallId);
|
|
3297
|
-
|
|
3485
|
+
finishReasoning(reasoningState);
|
|
3298
3486
|
if (!quiet) {
|
|
3299
3487
|
spinner.stop();
|
|
3300
3488
|
if (parallelCallCount > 1 && callInfo) {
|
|
3301
|
-
writeln(
|
|
3489
|
+
writeln(`${c6.dim("\u21B3")} ${callInfo.label}`);
|
|
3302
3490
|
}
|
|
3303
3491
|
if (toolCallInfo.size === 0)
|
|
3304
3492
|
parallelCallCount = 0;
|
|
@@ -3314,31 +3502,20 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3314
3502
|
break;
|
|
3315
3503
|
}
|
|
3316
3504
|
case "context-pruned": {
|
|
3317
|
-
|
|
3318
|
-
|
|
3505
|
+
finishReasoning(reasoningState);
|
|
3506
|
+
flushText(textState, quiet);
|
|
3319
3507
|
if (!quiet) {
|
|
3320
3508
|
spinner.stop();
|
|
3321
3509
|
const removedKb = (event.removedBytes / 1024).toFixed(1);
|
|
3322
|
-
writeln(`${G.info} ${
|
|
3323
|
-
renderedVisibleOutput = true;
|
|
3324
|
-
}
|
|
3325
|
-
break;
|
|
3326
|
-
}
|
|
3327
|
-
case "file-generated": {
|
|
3328
|
-
liveReasoning.finish();
|
|
3329
|
-
content.flushOpenContent();
|
|
3330
|
-
if (!quiet) {
|
|
3331
|
-
spinner.stop();
|
|
3332
|
-
if (renderedVisibleOutput)
|
|
3333
|
-
writeln();
|
|
3334
|
-
writeln(`${G.info} ${c7.dim("file")} ${c7.dim(event.mediaType)} ${c7.dim("\u2192")} ${basename2(event.filePath)}`);
|
|
3510
|
+
writeln(`${G.info} ${c6.dim("context pruned")} ${c6.dim(`\u2013${event.removedMessageCount} messages`)} ${c6.dim(`\u2013${removedKb} KB`)}`);
|
|
3335
3511
|
renderedVisibleOutput = true;
|
|
3512
|
+
spinner.start("thinking");
|
|
3336
3513
|
}
|
|
3337
3514
|
break;
|
|
3338
3515
|
}
|
|
3339
3516
|
case "turn-complete": {
|
|
3340
|
-
|
|
3341
|
-
|
|
3517
|
+
finishReasoning(reasoningState);
|
|
3518
|
+
flushText(textState, quiet);
|
|
3342
3519
|
spinner.stop();
|
|
3343
3520
|
if (!quiet && !renderedVisibleOutput)
|
|
3344
3521
|
writeln();
|
|
@@ -3349,14 +3526,14 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3349
3526
|
break;
|
|
3350
3527
|
}
|
|
3351
3528
|
case "turn-error": {
|
|
3352
|
-
|
|
3353
|
-
|
|
3529
|
+
finishReasoning(reasoningState);
|
|
3530
|
+
flushText(textState, quiet);
|
|
3354
3531
|
spinner.stop();
|
|
3355
3532
|
inputTokens = event.inputTokens;
|
|
3356
3533
|
outputTokens = event.outputTokens;
|
|
3357
3534
|
contextTokens = event.contextTokens;
|
|
3358
3535
|
if (isAbortError(event.error)) {
|
|
3359
|
-
newMessages = buildAbortMessages(event.partialMessages,
|
|
3536
|
+
newMessages = buildAbortMessages(event.partialMessages, textState.text);
|
|
3360
3537
|
} else {
|
|
3361
3538
|
renderError(event.error, "turn");
|
|
3362
3539
|
throw new RenderedError(event.error);
|
|
@@ -3405,17 +3582,17 @@ function renderUserMessage(text) {
|
|
|
3405
3582
|
}
|
|
3406
3583
|
}
|
|
3407
3584
|
var G = {
|
|
3408
|
-
prompt:
|
|
3409
|
-
reply:
|
|
3410
|
-
search:
|
|
3411
|
-
read:
|
|
3412
|
-
write:
|
|
3413
|
-
run:
|
|
3414
|
-
mcp:
|
|
3415
|
-
ok:
|
|
3416
|
-
err:
|
|
3417
|
-
warn:
|
|
3418
|
-
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")
|
|
3419
3596
|
};
|
|
3420
3597
|
var PREFIX = {
|
|
3421
3598
|
user: G.prompt,
|
|
@@ -3437,17 +3614,17 @@ class RenderedError extends Error {
|
|
|
3437
3614
|
function renderError(err, context = "render") {
|
|
3438
3615
|
logError(err, context);
|
|
3439
3616
|
const parsed = parseAppError(err);
|
|
3440
|
-
writeln(`${G.err} ${
|
|
3617
|
+
writeln(`${G.err} ${c7.red(parsed.headline)}`);
|
|
3441
3618
|
if (parsed.hint) {
|
|
3442
|
-
writeln(` ${
|
|
3619
|
+
writeln(` ${c7.dim(parsed.hint)}`);
|
|
3443
3620
|
}
|
|
3444
3621
|
}
|
|
3445
3622
|
function renderBanner(model, cwd) {
|
|
3446
3623
|
writeln();
|
|
3447
3624
|
const title = PACKAGE_VERSION ? `mini-coder \xB7 v${PACKAGE_VERSION}` : "mini-coder";
|
|
3448
|
-
writeln(` ${
|
|
3449
|
-
writeln(` ${
|
|
3450
|
-
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")}`);
|
|
3451
3628
|
const items = [];
|
|
3452
3629
|
if (getPreferredShowReasoning())
|
|
3453
3630
|
items.push("reasoning: on");
|
|
@@ -3460,7 +3637,7 @@ function renderBanner(model, cwd) {
|
|
|
3460
3637
|
if (skills.size > 0)
|
|
3461
3638
|
items.push(`${skills.size} skill${skills.size > 1 ? "s" : ""}`);
|
|
3462
3639
|
if (items.length > 0) {
|
|
3463
|
-
writeln(` ${
|
|
3640
|
+
writeln(` ${c7.dim(items.join(" \xB7 "))}`);
|
|
3464
3641
|
}
|
|
3465
3642
|
const connParts = [];
|
|
3466
3643
|
for (const p of discoverConnectedProviders()) {
|
|
@@ -3470,7 +3647,7 @@ function renderBanner(model, cwd) {
|
|
|
3470
3647
|
if (mcpCount > 0)
|
|
3471
3648
|
connParts.push(`${mcpCount} mcp`);
|
|
3472
3649
|
if (connParts.length > 0) {
|
|
3473
|
-
writeln(` ${
|
|
3650
|
+
writeln(` ${c7.dim(connParts.join(" \xB7 "))}`);
|
|
3474
3651
|
}
|
|
3475
3652
|
writeln();
|
|
3476
3653
|
}
|
|
@@ -3485,7 +3662,7 @@ class CliReporter {
|
|
|
3485
3662
|
if (this.quiet)
|
|
3486
3663
|
return;
|
|
3487
3664
|
this.spinner.stop();
|
|
3488
|
-
writeln(`${G.info} ${
|
|
3665
|
+
writeln(`${G.info} ${c7.dim(msg)}`);
|
|
3489
3666
|
}
|
|
3490
3667
|
error(msg, hint) {
|
|
3491
3668
|
this.spinner.stop();
|
|
@@ -3689,10 +3866,10 @@ function setPreferredVerboseOutput(verbose) {
|
|
|
3689
3866
|
setSetting("preferred_verbose_output", verbose ? "true" : "false");
|
|
3690
3867
|
}
|
|
3691
3868
|
// src/agent/session-runner.ts
|
|
3692
|
-
import * as
|
|
3869
|
+
import * as c10 from "yoctocolors";
|
|
3693
3870
|
|
|
3694
3871
|
// src/cli/input.ts
|
|
3695
|
-
import * as
|
|
3872
|
+
import * as c8 from "yoctocolors";
|
|
3696
3873
|
|
|
3697
3874
|
// src/cli/input-buffer.ts
|
|
3698
3875
|
var PASTE_TOKEN_START = 57344;
|
|
@@ -4101,7 +4278,7 @@ function watchForCancel(abortController) {
|
|
|
4101
4278
|
process.stdin.on("data", onData);
|
|
4102
4279
|
return cleanup;
|
|
4103
4280
|
}
|
|
4104
|
-
var PROMPT =
|
|
4281
|
+
var PROMPT = c8.green("\u25B6 ");
|
|
4105
4282
|
var PROMPT_RAW_LEN = 2;
|
|
4106
4283
|
async function readline(opts) {
|
|
4107
4284
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -4150,7 +4327,7 @@ async function readline(opts) {
|
|
|
4150
4327
|
process.stdout.write(`${CLEAR_LINE}${prompt}${display}${CSI}${PROMPT_RAW_LEN + displayCursor + 1}G`);
|
|
4151
4328
|
}
|
|
4152
4329
|
function renderSearchPrompt() {
|
|
4153
|
-
process.stdout.write(`${CLEAR_LINE}${
|
|
4330
|
+
process.stdout.write(`${CLEAR_LINE}${c8.cyan("search:")} ${searchQuery}\u2588`);
|
|
4154
4331
|
}
|
|
4155
4332
|
function applyHistory() {
|
|
4156
4333
|
if (histIdx < history.length) {
|
|
@@ -4412,36 +4589,12 @@ import { streamText } from "ai";
|
|
|
4412
4589
|
// src/llm-api/turn-execution.ts
|
|
4413
4590
|
import { dynamicTool, jsonSchema } from "ai";
|
|
4414
4591
|
|
|
4415
|
-
// src/llm-api/generated-files.ts
|
|
4416
|
-
import { join as join6 } from "path";
|
|
4417
|
-
var MEDIA_TYPE_TO_EXT = {
|
|
4418
|
-
"image/jpeg": "jpg",
|
|
4419
|
-
"image/svg+xml": "svg"
|
|
4420
|
-
};
|
|
4421
|
-
function extensionFromMediaType(mediaType) {
|
|
4422
|
-
if (MEDIA_TYPE_TO_EXT[mediaType])
|
|
4423
|
-
return MEDIA_TYPE_TO_EXT[mediaType];
|
|
4424
|
-
const slash = mediaType.indexOf("/");
|
|
4425
|
-
if (slash === -1 || slash === mediaType.length - 1)
|
|
4426
|
-
return "bin";
|
|
4427
|
-
return mediaType.slice(slash + 1);
|
|
4428
|
-
}
|
|
4429
|
-
var counter = 0;
|
|
4430
|
-
async function saveGeneratedFile(file, cwd) {
|
|
4431
|
-
counter += 1;
|
|
4432
|
-
const ext = extensionFromMediaType(file.mediaType);
|
|
4433
|
-
const name = `generated-${counter}.${ext}`;
|
|
4434
|
-
const filePath = join6(cwd, name);
|
|
4435
|
-
await Bun.write(filePath, file.uint8Array);
|
|
4436
|
-
return filePath;
|
|
4437
|
-
}
|
|
4438
|
-
|
|
4439
4592
|
// src/llm-api/turn-stream-events.ts
|
|
4440
|
-
function shouldLogStreamChunk(
|
|
4441
|
-
return
|
|
4593
|
+
function shouldLogStreamChunk(c9) {
|
|
4594
|
+
return c9.type !== "text-delta" && c9.type !== "reasoning" && c9.type !== "reasoning-delta" && c9.type !== "tool-input-delta";
|
|
4442
4595
|
}
|
|
4443
|
-
function extractToolArgs(
|
|
4444
|
-
return
|
|
4596
|
+
function extractToolArgs(c9) {
|
|
4597
|
+
return c9.input ?? c9.args;
|
|
4445
4598
|
}
|
|
4446
4599
|
function hasRenderableToolArgs(args) {
|
|
4447
4600
|
if (args === null || args === undefined)
|
|
@@ -4454,10 +4607,10 @@ function hasRenderableToolArgs(args) {
|
|
|
4454
4607
|
return Object.keys(args).length > 0;
|
|
4455
4608
|
return true;
|
|
4456
4609
|
}
|
|
4457
|
-
function mapStreamChunkToTurnEvent(
|
|
4458
|
-
switch (
|
|
4610
|
+
function mapStreamChunkToTurnEvent(c9) {
|
|
4611
|
+
switch (c9.type) {
|
|
4459
4612
|
case "text-delta": {
|
|
4460
|
-
const delta = typeof
|
|
4613
|
+
const delta = typeof c9.text === "string" ? c9.text : "";
|
|
4461
4614
|
return {
|
|
4462
4615
|
type: "text-delta",
|
|
4463
4616
|
delta
|
|
@@ -4465,7 +4618,7 @@ function mapStreamChunkToTurnEvent(c10) {
|
|
|
4465
4618
|
}
|
|
4466
4619
|
case "reasoning-delta":
|
|
4467
4620
|
case "reasoning": {
|
|
4468
|
-
const delta = getReasoningDeltaFromStreamChunk(
|
|
4621
|
+
const delta = getReasoningDeltaFromStreamChunk(c9);
|
|
4469
4622
|
if (delta === null)
|
|
4470
4623
|
return null;
|
|
4471
4624
|
return {
|
|
@@ -4474,49 +4627,65 @@ function mapStreamChunkToTurnEvent(c10) {
|
|
|
4474
4627
|
};
|
|
4475
4628
|
}
|
|
4476
4629
|
case "tool-input-start": {
|
|
4477
|
-
const args = extractToolArgs(
|
|
4478
|
-
const hasStableToolCallId = typeof
|
|
4630
|
+
const args = extractToolArgs(c9);
|
|
4631
|
+
const hasStableToolCallId = typeof c9.toolCallId === "string" && c9.toolCallId.trim().length > 0;
|
|
4479
4632
|
if (hasStableToolCallId && !hasRenderableToolArgs(args))
|
|
4480
4633
|
return null;
|
|
4481
4634
|
return {
|
|
4482
4635
|
type: "tool-call-start",
|
|
4483
|
-
toolCallId: String(
|
|
4484
|
-
toolName: String(
|
|
4636
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4637
|
+
toolName: String(c9.toolName ?? ""),
|
|
4485
4638
|
args
|
|
4486
4639
|
};
|
|
4487
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
|
+
}
|
|
4488
4657
|
case "tool-call": {
|
|
4489
4658
|
return {
|
|
4490
4659
|
type: "tool-call-start",
|
|
4491
|
-
toolCallId: String(
|
|
4492
|
-
toolName: String(
|
|
4493
|
-
args: extractToolArgs(
|
|
4660
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4661
|
+
toolName: String(c9.toolName ?? ""),
|
|
4662
|
+
args: extractToolArgs(c9)
|
|
4494
4663
|
};
|
|
4495
4664
|
}
|
|
4496
4665
|
case "tool-result": {
|
|
4497
4666
|
let result;
|
|
4498
|
-
if ("output" in
|
|
4499
|
-
result =
|
|
4500
|
-
else if ("result" in
|
|
4501
|
-
result =
|
|
4667
|
+
if ("output" in c9)
|
|
4668
|
+
result = c9.output;
|
|
4669
|
+
else if ("result" in c9)
|
|
4670
|
+
result = c9.result;
|
|
4502
4671
|
return {
|
|
4503
4672
|
type: "tool-result",
|
|
4504
|
-
toolCallId: String(
|
|
4505
|
-
toolName: String(
|
|
4673
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4674
|
+
toolName: String(c9.toolName ?? ""),
|
|
4506
4675
|
result,
|
|
4507
|
-
isError: "isError" in
|
|
4676
|
+
isError: "isError" in c9 ? Boolean(c9.isError) : false
|
|
4508
4677
|
};
|
|
4509
4678
|
}
|
|
4510
4679
|
case "tool-error":
|
|
4511
4680
|
return {
|
|
4512
4681
|
type: "tool-result",
|
|
4513
|
-
toolCallId: String(
|
|
4514
|
-
toolName: String(
|
|
4515
|
-
result:
|
|
4682
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4683
|
+
toolName: String(c9.toolName ?? ""),
|
|
4684
|
+
result: c9.error ?? "Tool execution failed",
|
|
4516
4685
|
isError: true
|
|
4517
4686
|
};
|
|
4518
4687
|
case "error": {
|
|
4519
|
-
throw normalizeUnknownError(
|
|
4688
|
+
throw normalizeUnknownError(c9.error);
|
|
4520
4689
|
}
|
|
4521
4690
|
default:
|
|
4522
4691
|
return null;
|
|
@@ -4532,9 +4701,9 @@ function toCoreTool(def) {
|
|
|
4532
4701
|
return dynamicTool({
|
|
4533
4702
|
description: def.description,
|
|
4534
4703
|
inputSchema: schema,
|
|
4535
|
-
execute: async (input) => {
|
|
4704
|
+
execute: async (input, { abortSignal }) => {
|
|
4536
4705
|
try {
|
|
4537
|
-
return await def.execute(input);
|
|
4706
|
+
return await def.execute(input, abortSignal ? { signal: abortSignal } : undefined);
|
|
4538
4707
|
} catch (err) {
|
|
4539
4708
|
throw normalizeUnknownError(err);
|
|
4540
4709
|
}
|
|
@@ -4685,6 +4854,7 @@ class StreamToolCallTracker {
|
|
|
4685
4854
|
syntheticCount = 0;
|
|
4686
4855
|
pendingByTool = new Map;
|
|
4687
4856
|
deferredStartsByTool = new Map;
|
|
4857
|
+
toolNameById = new Map;
|
|
4688
4858
|
prepare(chunk) {
|
|
4689
4859
|
const type = chunk.type;
|
|
4690
4860
|
if (!type) {
|
|
@@ -4693,6 +4863,8 @@ class StreamToolCallTracker {
|
|
|
4693
4863
|
if (type === "tool-input-start") {
|
|
4694
4864
|
const toolName = normalizeToolName(chunk.toolName);
|
|
4695
4865
|
const toolCallId = normalizeStringId(chunk.toolCallId);
|
|
4866
|
+
if (toolCallId && toolName)
|
|
4867
|
+
this.toolNameById.set(toolCallId, toolName);
|
|
4696
4868
|
const args = extractToolArgs(chunk);
|
|
4697
4869
|
if (!hasRenderableToolArgs(args)) {
|
|
4698
4870
|
if (!toolCallId) {
|
|
@@ -4727,6 +4899,16 @@ class StreamToolCallTracker {
|
|
|
4727
4899
|
suppressTurnEvent: false
|
|
4728
4900
|
};
|
|
4729
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
|
+
}
|
|
4730
4912
|
return { chunk, suppressTurnEvent: false };
|
|
4731
4913
|
}
|
|
4732
4914
|
trackRenderableStart(chunk, toolName, existingToolCallId) {
|
|
@@ -4788,18 +4970,6 @@ async function* mapFullStreamToTurnEvents(stream, opts) {
|
|
|
4788
4970
|
yield { type: "context-pruned", ...rec };
|
|
4789
4971
|
}
|
|
4790
4972
|
}
|
|
4791
|
-
if (originalChunk.type === "file" && opts.cwd) {
|
|
4792
|
-
const fileData = originalChunk.file;
|
|
4793
|
-
if (fileData?.uint8Array) {
|
|
4794
|
-
const filePath = await saveGeneratedFile(fileData, opts.cwd);
|
|
4795
|
-
yield {
|
|
4796
|
-
type: "file-generated",
|
|
4797
|
-
filePath,
|
|
4798
|
-
mediaType: fileData.mediaType
|
|
4799
|
-
};
|
|
4800
|
-
continue;
|
|
4801
|
-
}
|
|
4802
|
-
}
|
|
4803
4973
|
const prepared = toolCallTracker.prepare(originalChunk);
|
|
4804
4974
|
const chunk = prepared.chunk;
|
|
4805
4975
|
const route = textPhaseTracker.route(chunk);
|
|
@@ -5220,12 +5390,6 @@ function buildTurnProviderOptions(input) {
|
|
|
5220
5390
|
const thinkingOpts = thinkingEffort ? getThinkingProviderOptions(modelString, thinkingEffort) : null;
|
|
5221
5391
|
const reasoningSummaryRequested = isRecord(thinkingOpts) && isRecord(thinkingOpts.openai) && typeof thinkingOpts.openai.reasoningSummary === "string";
|
|
5222
5392
|
const cacheFamily = getCacheFamily(modelString);
|
|
5223
|
-
const googleOpts = isGeminiModelFamily(modelString) ? {
|
|
5224
|
-
google: {
|
|
5225
|
-
responseModalities: ["TEXT", "IMAGE"],
|
|
5226
|
-
...isRecord(thinkingOpts?.google) ? thinkingOpts.google : {}
|
|
5227
|
-
}
|
|
5228
|
-
} : {};
|
|
5229
5393
|
const providerOptions = {
|
|
5230
5394
|
...thinkingOpts ?? {},
|
|
5231
5395
|
...isOpenAIGPT(modelString) ? {
|
|
@@ -5233,8 +5397,7 @@ function buildTurnProviderOptions(input) {
|
|
|
5233
5397
|
store: false,
|
|
5234
5398
|
...isRecord(thinkingOpts?.openai) ? thinkingOpts.openai : {}
|
|
5235
5399
|
}
|
|
5236
|
-
} : {}
|
|
5237
|
-
...googleOpts
|
|
5400
|
+
} : {}
|
|
5238
5401
|
};
|
|
5239
5402
|
return {
|
|
5240
5403
|
cacheFamily,
|
|
@@ -5312,8 +5475,7 @@ async function* runTurn(options) {
|
|
|
5312
5475
|
tools,
|
|
5313
5476
|
systemPrompt,
|
|
5314
5477
|
signal,
|
|
5315
|
-
thinkingEffort
|
|
5316
|
-
cwd
|
|
5478
|
+
thinkingEffort
|
|
5317
5479
|
} = options;
|
|
5318
5480
|
const rawToolSet = buildToolSet(tools);
|
|
5319
5481
|
const toolSet = annotateToolCaching(rawToolSet, modelString);
|
|
@@ -5364,7 +5526,6 @@ async function* runTurn(options) {
|
|
|
5364
5526
|
result.response.catch(() => {});
|
|
5365
5527
|
for await (const event of mapFullStreamToTurnEvents(result.fullStream, {
|
|
5366
5528
|
stepPruneQueue,
|
|
5367
|
-
...cwd ? { cwd } : {},
|
|
5368
5529
|
onChunk: (streamChunk) => {
|
|
5369
5530
|
if (streamChunk.type === "tool-call" || streamChunk.type === "tool-result") {
|
|
5370
5531
|
logApiEvent("stream chunk", {
|
|
@@ -5407,7 +5568,7 @@ async function* runTurn(options) {
|
|
|
5407
5568
|
}
|
|
5408
5569
|
|
|
5409
5570
|
// src/session/manager.ts
|
|
5410
|
-
import * as
|
|
5571
|
+
import * as c9 from "yoctocolors";
|
|
5411
5572
|
function newSession(model, cwd) {
|
|
5412
5573
|
const id = generateSessionId();
|
|
5413
5574
|
const row = createSession({ id, cwd, model });
|
|
@@ -5442,21 +5603,21 @@ function renderSessionTable(footer) {
|
|
|
5442
5603
|
if (sessions.length === 0)
|
|
5443
5604
|
return false;
|
|
5444
5605
|
writeln(`
|
|
5445
|
-
${
|
|
5606
|
+
${c9.bold("Recent sessions:")}`);
|
|
5446
5607
|
for (const s of sessions) {
|
|
5447
5608
|
const date = new Date(s.updated_at).toLocaleString();
|
|
5448
5609
|
const cwd = tildePath(s.cwd);
|
|
5449
|
-
const title = s.title ||
|
|
5450
|
-
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)}`);
|
|
5451
5612
|
}
|
|
5452
5613
|
writeln(`
|
|
5453
5614
|
${footer}`);
|
|
5454
5615
|
return true;
|
|
5455
5616
|
}
|
|
5456
5617
|
function printSessionList() {
|
|
5457
|
-
const shown = renderSessionTable(`${
|
|
5618
|
+
const shown = renderSessionTable(`${c9.dim("Use")} mc --resume <id> ${c9.dim("to continue a session.")}`);
|
|
5458
5619
|
if (!shown)
|
|
5459
|
-
writeln(
|
|
5620
|
+
writeln(c9.dim("No sessions found."));
|
|
5460
5621
|
}
|
|
5461
5622
|
function getMostRecentSession() {
|
|
5462
5623
|
const sessions = listSessions(1);
|
|
@@ -5687,7 +5848,7 @@ class SessionRunner {
|
|
|
5687
5848
|
}
|
|
5688
5849
|
this.session = resumed;
|
|
5689
5850
|
this.currentModel = this.session.model;
|
|
5690
|
-
this.reporter.info(`Resumed session ${this.session.id} (${
|
|
5851
|
+
this.reporter.info(`Resumed session ${this.session.id} (${c10.cyan(this.currentModel)})`);
|
|
5691
5852
|
} else {
|
|
5692
5853
|
this.session = newSession(this.currentModel, this.cwd);
|
|
5693
5854
|
}
|
|
@@ -5780,8 +5941,7 @@ ${output}
|
|
|
5780
5941
|
tools: this.tools,
|
|
5781
5942
|
...systemPrompt ? { systemPrompt } : {},
|
|
5782
5943
|
signal: abortController.signal,
|
|
5783
|
-
...this.currentThinkingEffort ? { thinkingEffort: this.currentThinkingEffort } : {}
|
|
5784
|
-
cwd: this.cwd
|
|
5944
|
+
...this.currentThinkingEffort ? { thinkingEffort: this.currentThinkingEffort } : {}
|
|
5785
5945
|
});
|
|
5786
5946
|
const { inputTokens, outputTokens, contextTokens, newMessages } = await this.reporter.renderTurn(events, {
|
|
5787
5947
|
showReasoning: this.showReasoning,
|
|
@@ -5884,7 +6044,7 @@ import { z as z3 } from "zod";
|
|
|
5884
6044
|
|
|
5885
6045
|
// src/internal/file-edit/command.ts
|
|
5886
6046
|
import { existsSync as existsSync4 } from "fs";
|
|
5887
|
-
import { dirname as dirname3, extname, join as
|
|
6047
|
+
import { dirname as dirname3, extname, join as join6 } from "path";
|
|
5888
6048
|
import { fileURLToPath } from "url";
|
|
5889
6049
|
function quoteShellArg(value) {
|
|
5890
6050
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
@@ -5896,7 +6056,7 @@ function resolveSiblingFileEditScript(scriptPath) {
|
|
|
5896
6056
|
const mainDir = dirname3(scriptPath);
|
|
5897
6057
|
const mainBase = scriptPath.slice(mainDir.length + 1);
|
|
5898
6058
|
if (mainBase === `index${ext}` || mainBase === `mc${ext}`) {
|
|
5899
|
-
return
|
|
6059
|
+
return join6(mainDir, `mc-edit${ext}`);
|
|
5900
6060
|
}
|
|
5901
6061
|
return null;
|
|
5902
6062
|
}
|
|
@@ -5905,7 +6065,7 @@ function resolveModuleLocalFileEditScript(moduleUrl) {
|
|
|
5905
6065
|
const ext = extname(modulePath);
|
|
5906
6066
|
if (!ext)
|
|
5907
6067
|
return null;
|
|
5908
|
-
const helperPath =
|
|
6068
|
+
const helperPath = join6(dirname3(modulePath), "..", "..", `mc-edit${ext}`);
|
|
5909
6069
|
return existsSync4(helperPath) ? helperPath : null;
|
|
5910
6070
|
}
|
|
5911
6071
|
function resolveProcessScriptPath(mainModule, argv1) {
|
|
@@ -5938,7 +6098,7 @@ var ShellSchema = z3.object({
|
|
|
5938
6098
|
env: z3.record(z3.string(), z3.string()).nullable().describe("Additional environment variables to set")
|
|
5939
6099
|
});
|
|
5940
6100
|
var MAX_OUTPUT_BYTES = 1e4;
|
|
5941
|
-
async function runShellCommand(input) {
|
|
6101
|
+
async function runShellCommand(input, options) {
|
|
5942
6102
|
const cwd = input.cwd ?? process.cwd();
|
|
5943
6103
|
const timeout = input.timeout ?? undefined;
|
|
5944
6104
|
const inputEnv = input.env ?? undefined;
|
|
@@ -5960,8 +6120,7 @@ ${input.command}`], {
|
|
|
5960
6120
|
stdout: "pipe",
|
|
5961
6121
|
stderr: "pipe"
|
|
5962
6122
|
});
|
|
5963
|
-
|
|
5964
|
-
timedOut = true;
|
|
6123
|
+
function killProc() {
|
|
5965
6124
|
try {
|
|
5966
6125
|
proc.kill("SIGTERM");
|
|
5967
6126
|
setTimeout(() => {
|
|
@@ -5973,7 +6132,20 @@ ${input.command}`], {
|
|
|
5973
6132
|
for (const reader of readers) {
|
|
5974
6133
|
reader.cancel().catch(() => {});
|
|
5975
6134
|
}
|
|
6135
|
+
}
|
|
6136
|
+
const timer = timeout ? setTimeout(() => {
|
|
6137
|
+
timedOut = true;
|
|
6138
|
+
killProc();
|
|
5976
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
|
+
}
|
|
5977
6149
|
async function collectStream(stream) {
|
|
5978
6150
|
const reader = stream.getReader();
|
|
5979
6151
|
readers.push(reader);
|
|
@@ -6017,6 +6189,7 @@ ${input.command}`], {
|
|
|
6017
6189
|
} finally {
|
|
6018
6190
|
if (timer)
|
|
6019
6191
|
clearTimeout(timer);
|
|
6192
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
6020
6193
|
restoreTerminal();
|
|
6021
6194
|
if (wasRaw) {
|
|
6022
6195
|
try {
|
|
@@ -6041,7 +6214,7 @@ var shellTool = {
|
|
|
6041
6214
|
name: "shell",
|
|
6042
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.",
|
|
6043
6216
|
schema: ShellSchema,
|
|
6044
|
-
execute: runShellCommand
|
|
6217
|
+
execute: (input, options) => runShellCommand(input, options)
|
|
6045
6218
|
};
|
|
6046
6219
|
|
|
6047
6220
|
// src/agent/tools.ts
|
|
@@ -6049,11 +6222,11 @@ function withCwdDefault(tool, cwd) {
|
|
|
6049
6222
|
const originalExecute = tool.execute;
|
|
6050
6223
|
return {
|
|
6051
6224
|
...tool,
|
|
6052
|
-
execute: async (input) => {
|
|
6225
|
+
execute: async (input, options) => {
|
|
6053
6226
|
const patched = typeof input === "object" && input !== null ? input : {};
|
|
6054
6227
|
if (patched.cwd === undefined)
|
|
6055
6228
|
patched.cwd = cwd;
|
|
6056
|
-
return originalExecute(patched);
|
|
6229
|
+
return originalExecute(patched, options);
|
|
6057
6230
|
}
|
|
6058
6231
|
};
|
|
6059
6232
|
}
|
|
@@ -6096,7 +6269,7 @@ async function initAgent(opts) {
|
|
|
6096
6269
|
for (const row of listMcpServers()) {
|
|
6097
6270
|
try {
|
|
6098
6271
|
await connectAndAddMcp(row.name);
|
|
6099
|
-
opts.reporter.info(`MCP: connected ${
|
|
6272
|
+
opts.reporter.info(`MCP: connected ${c11.cyan(row.name)}`);
|
|
6100
6273
|
} catch (e) {
|
|
6101
6274
|
opts.reporter.error(`MCP: failed to connect ${row.name}: ${String(e)}`);
|
|
6102
6275
|
}
|
|
@@ -6154,7 +6327,7 @@ async function initAgent(opts) {
|
|
|
6154
6327
|
}
|
|
6155
6328
|
|
|
6156
6329
|
// src/cli/args.ts
|
|
6157
|
-
import * as
|
|
6330
|
+
import * as c12 from "yoctocolors";
|
|
6158
6331
|
function parseArgs(argv) {
|
|
6159
6332
|
const args = {
|
|
6160
6333
|
model: null,
|
|
@@ -6203,11 +6376,11 @@ function parseArgs(argv) {
|
|
|
6203
6376
|
return args;
|
|
6204
6377
|
}
|
|
6205
6378
|
function printHelp() {
|
|
6206
|
-
writeln(`${
|
|
6379
|
+
writeln(`${c12.bold("mini-coder")} \u2014 a small, fast CLI coding agent
|
|
6207
6380
|
`);
|
|
6208
|
-
writeln(`${
|
|
6381
|
+
writeln(`${c12.bold("Usage:")} mc [options] [prompt]
|
|
6209
6382
|
`);
|
|
6210
|
-
writeln(`${
|
|
6383
|
+
writeln(`${c12.bold("Options:")}`);
|
|
6211
6384
|
const opts = [
|
|
6212
6385
|
["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
|
|
6213
6386
|
["-c, --continue", "Continue the most recent session"],
|
|
@@ -6217,10 +6390,10 @@ function printHelp() {
|
|
|
6217
6390
|
["-h, --help", "Show this help"]
|
|
6218
6391
|
];
|
|
6219
6392
|
for (const [flag, desc] of opts) {
|
|
6220
|
-
writeln(` ${
|
|
6393
|
+
writeln(` ${c12.cyan((flag ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
|
|
6221
6394
|
}
|
|
6222
6395
|
writeln(`
|
|
6223
|
-
${
|
|
6396
|
+
${c12.bold("Provider env vars:")}`);
|
|
6224
6397
|
const envs = [
|
|
6225
6398
|
["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
|
|
6226
6399
|
["ANTHROPIC_API_KEY", "Anthropic direct"],
|
|
@@ -6231,22 +6404,22 @@ ${c13.bold("Provider env vars:")}`);
|
|
|
6231
6404
|
["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
|
|
6232
6405
|
];
|
|
6233
6406
|
for (const [env, desc] of envs) {
|
|
6234
|
-
writeln(` ${
|
|
6407
|
+
writeln(` ${c12.yellow((env ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
|
|
6235
6408
|
}
|
|
6236
6409
|
writeln(`
|
|
6237
|
-
${
|
|
6238
|
-
writeln(` mc ${
|
|
6239
|
-
writeln(` mc "explain this codebase" ${
|
|
6240
|
-
writeln(` mc -c ${
|
|
6241
|
-
writeln(` mc -m ollama/llama3.2 ${
|
|
6242
|
-
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")}`);
|
|
6243
6416
|
}
|
|
6244
6417
|
|
|
6245
6418
|
// src/cli/bootstrap.ts
|
|
6246
6419
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
6247
6420
|
import { homedir as homedir6 } from "os";
|
|
6248
|
-
import { join as
|
|
6249
|
-
import * as
|
|
6421
|
+
import { join as join7 } from "path";
|
|
6422
|
+
import * as c13 from "yoctocolors";
|
|
6250
6423
|
var REVIEW_SKILL_CONTENT = `---
|
|
6251
6424
|
name: review
|
|
6252
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."
|
|
@@ -6276,17 +6449,17 @@ Review recent changes and provide actionable feedback.
|
|
|
6276
6449
|
- Keep feedback actionable: say what's wrong and suggest a fix.
|
|
6277
6450
|
`;
|
|
6278
6451
|
function bootstrapGlobalDefaults() {
|
|
6279
|
-
const skillDir =
|
|
6280
|
-
const skillPath =
|
|
6452
|
+
const skillDir = join7(homedir6(), ".agents", "skills", "review");
|
|
6453
|
+
const skillPath = join7(skillDir, "SKILL.md");
|
|
6281
6454
|
if (!existsSync5(skillPath)) {
|
|
6282
6455
|
mkdirSync2(skillDir, { recursive: true });
|
|
6283
6456
|
writeFileSync(skillPath, REVIEW_SKILL_CONTENT, "utf-8");
|
|
6284
|
-
writeln(`${
|
|
6457
|
+
writeln(`${c13.green("\u2713")} created ${c13.dim("~/.agents/skills/review/SKILL.md")} ${c13.dim("(edit it to customise your reviews)")}`);
|
|
6285
6458
|
}
|
|
6286
6459
|
}
|
|
6287
6460
|
|
|
6288
6461
|
// src/cli/file-refs.ts
|
|
6289
|
-
import { join as
|
|
6462
|
+
import { join as join8 } from "path";
|
|
6290
6463
|
async function resolveFileRefs(text, cwd) {
|
|
6291
6464
|
const atPattern = /@([\w./\-_]+)/g;
|
|
6292
6465
|
let result = text;
|
|
@@ -6296,7 +6469,7 @@ async function resolveFileRefs(text, cwd) {
|
|
|
6296
6469
|
const ref = match[1];
|
|
6297
6470
|
if (!ref)
|
|
6298
6471
|
continue;
|
|
6299
|
-
const filePath = ref.startsWith("/") ? ref :
|
|
6472
|
+
const filePath = ref.startsWith("/") ? ref : join8(cwd, ref);
|
|
6300
6473
|
if (isImageFilename(ref)) {
|
|
6301
6474
|
const attachment = await loadImageFile(filePath);
|
|
6302
6475
|
if (attachment) {
|
|
@@ -6318,25 +6491,25 @@ ${content}
|
|
|
6318
6491
|
}
|
|
6319
6492
|
|
|
6320
6493
|
// src/cli/input-loop.ts
|
|
6321
|
-
import * as
|
|
6494
|
+
import * as c20 from "yoctocolors";
|
|
6322
6495
|
|
|
6323
6496
|
// src/cli/commands.ts
|
|
6324
6497
|
import { randomBytes } from "crypto";
|
|
6325
6498
|
import { unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
6326
6499
|
import { tmpdir } from "os";
|
|
6327
|
-
import { join as
|
|
6328
|
-
import * as
|
|
6500
|
+
import { join as join9 } from "path";
|
|
6501
|
+
import * as c19 from "yoctocolors";
|
|
6329
6502
|
|
|
6330
6503
|
// src/cli/commands-help.ts
|
|
6331
|
-
import * as
|
|
6504
|
+
import * as c14 from "yoctocolors";
|
|
6332
6505
|
function renderEntries(entries) {
|
|
6333
6506
|
for (const [label, description] of entries) {
|
|
6334
|
-
writeln(` ${
|
|
6507
|
+
writeln(` ${c14.cyan(label.padEnd(28))} ${c14.dim(description)}`);
|
|
6335
6508
|
}
|
|
6336
6509
|
}
|
|
6337
6510
|
function renderHelpCommand(ctx) {
|
|
6338
6511
|
writeln();
|
|
6339
|
-
writeln(` ${
|
|
6512
|
+
writeln(` ${c14.dim("session")}`);
|
|
6340
6513
|
renderEntries([
|
|
6341
6514
|
["/session [id]", "list sessions or switch to one"],
|
|
6342
6515
|
["/new", "start a fresh session"],
|
|
@@ -6344,7 +6517,7 @@ function renderHelpCommand(ctx) {
|
|
|
6344
6517
|
["/exit", "quit"]
|
|
6345
6518
|
]);
|
|
6346
6519
|
writeln();
|
|
6347
|
-
writeln(` ${
|
|
6520
|
+
writeln(` ${c14.dim("model + context")}`);
|
|
6348
6521
|
renderEntries([
|
|
6349
6522
|
["/model [id]", "list or switch models"],
|
|
6350
6523
|
["/reasoning [on|off]", "toggle reasoning display"],
|
|
@@ -6357,7 +6530,7 @@ function renderHelpCommand(ctx) {
|
|
|
6357
6530
|
["/help", "show this help"]
|
|
6358
6531
|
]);
|
|
6359
6532
|
writeln();
|
|
6360
|
-
writeln(` ${
|
|
6533
|
+
writeln(` ${c14.dim("prompt")}`);
|
|
6361
6534
|
renderEntries([
|
|
6362
6535
|
["ask normally", "send a prompt to the current agent"],
|
|
6363
6536
|
["!cmd", "run a shell command and keep the result in context"],
|
|
@@ -6367,44 +6540,44 @@ function renderHelpCommand(ctx) {
|
|
|
6367
6540
|
const skills = loadSkillsIndex(ctx.cwd);
|
|
6368
6541
|
if (skills.size > 0) {
|
|
6369
6542
|
writeln();
|
|
6370
|
-
writeln(` ${
|
|
6543
|
+
writeln(` ${c14.dim("skills")}`);
|
|
6371
6544
|
for (const skill of skills.values()) {
|
|
6372
|
-
const source = skill.source === "local" ?
|
|
6545
|
+
const source = skill.source === "local" ? c14.dim("local") : c14.dim("global");
|
|
6373
6546
|
const desc = truncateText(skill.description, 80);
|
|
6374
|
-
writeln(` ${
|
|
6547
|
+
writeln(` ${c14.green(`/${skill.name}`.padEnd(28))} ${c14.dim(desc)} ${c14.dim("\xB7")} ${source}`);
|
|
6375
6548
|
}
|
|
6376
6549
|
}
|
|
6377
6550
|
writeln();
|
|
6378
|
-
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`);
|
|
6379
6552
|
writeln();
|
|
6380
6553
|
}
|
|
6381
6554
|
|
|
6382
6555
|
// src/cli/commands-login.ts
|
|
6383
|
-
import * as
|
|
6556
|
+
import * as c15 from "yoctocolors";
|
|
6384
6557
|
function renderLoginHelp() {
|
|
6385
6558
|
writeln();
|
|
6386
|
-
writeln(` ${
|
|
6387
|
-
writeln(` /login ${
|
|
6388
|
-
writeln(` /login <provider> ${
|
|
6389
|
-
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")}`);
|
|
6390
6563
|
writeln();
|
|
6391
|
-
writeln(` ${
|
|
6564
|
+
writeln(` ${c15.dim("providers:")}`);
|
|
6392
6565
|
for (const p of getOAuthProviders()) {
|
|
6393
|
-
const status = isLoggedIn(p.id) ?
|
|
6394
|
-
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}`);
|
|
6395
6568
|
}
|
|
6396
6569
|
writeln();
|
|
6397
6570
|
}
|
|
6398
6571
|
function renderStatus() {
|
|
6399
6572
|
const loggedIn = listLoggedInProviders();
|
|
6400
6573
|
if (loggedIn.length === 0) {
|
|
6401
|
-
writeln(`${PREFIX.info} ${
|
|
6574
|
+
writeln(`${PREFIX.info} ${c15.dim("no OAuth logins \u2014 use")} /login <provider>`);
|
|
6402
6575
|
return;
|
|
6403
6576
|
}
|
|
6404
6577
|
for (const id of loggedIn) {
|
|
6405
6578
|
const provider = getOAuthProvider(id);
|
|
6406
6579
|
const name = provider?.name ?? id;
|
|
6407
|
-
writeln(`${PREFIX.success} ${
|
|
6580
|
+
writeln(`${PREFIX.success} ${c15.cyan(id)} ${c15.dim(name)}`);
|
|
6408
6581
|
}
|
|
6409
6582
|
}
|
|
6410
6583
|
async function handleLoginCommand(ctx, args) {
|
|
@@ -6425,7 +6598,7 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6425
6598
|
if (isLoggedIn(providerId)) {
|
|
6426
6599
|
const token = await getAccessToken(providerId);
|
|
6427
6600
|
if (token) {
|
|
6428
|
-
writeln(`${PREFIX.success} already logged in to ${
|
|
6601
|
+
writeln(`${PREFIX.success} already logged in to ${c15.cyan(provider.name)}`);
|
|
6429
6602
|
return;
|
|
6430
6603
|
}
|
|
6431
6604
|
}
|
|
@@ -6436,7 +6609,7 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6436
6609
|
ctx.stopSpinner();
|
|
6437
6610
|
writeln(`${PREFIX.info} ${instructions}`);
|
|
6438
6611
|
writeln();
|
|
6439
|
-
writeln(` ${
|
|
6612
|
+
writeln(` ${c15.cyan(url)}`);
|
|
6440
6613
|
writeln();
|
|
6441
6614
|
let open = "xdg-open";
|
|
6442
6615
|
if (process.platform === "darwin")
|
|
@@ -6448,12 +6621,12 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6448
6621
|
},
|
|
6449
6622
|
onProgress: (msg) => {
|
|
6450
6623
|
ctx.stopSpinner();
|
|
6451
|
-
writeln(`${PREFIX.info} ${
|
|
6624
|
+
writeln(`${PREFIX.info} ${c15.dim(msg)}`);
|
|
6452
6625
|
ctx.startSpinner("exchanging tokens");
|
|
6453
6626
|
}
|
|
6454
6627
|
});
|
|
6455
6628
|
ctx.stopSpinner();
|
|
6456
|
-
writeln(`${PREFIX.success} logged in to ${
|
|
6629
|
+
writeln(`${PREFIX.success} logged in to ${c15.cyan(provider.name)}`);
|
|
6457
6630
|
} catch (err) {
|
|
6458
6631
|
ctx.stopSpinner();
|
|
6459
6632
|
writeln(`${PREFIX.error} login failed: ${err.message}`);
|
|
@@ -6466,15 +6639,15 @@ function handleLogoutCommand(_ctx, args) {
|
|
|
6466
6639
|
return;
|
|
6467
6640
|
}
|
|
6468
6641
|
if (!isLoggedIn(providerId)) {
|
|
6469
|
-
writeln(`${PREFIX.info} ${
|
|
6642
|
+
writeln(`${PREFIX.info} ${c15.dim("not logged in to")} ${providerId}`);
|
|
6470
6643
|
return;
|
|
6471
6644
|
}
|
|
6472
6645
|
logout(providerId);
|
|
6473
|
-
writeln(`${PREFIX.success} logged out of ${
|
|
6646
|
+
writeln(`${PREFIX.success} logged out of ${c15.cyan(providerId)}`);
|
|
6474
6647
|
}
|
|
6475
6648
|
|
|
6476
6649
|
// src/cli/commands-mcp.ts
|
|
6477
|
-
import * as
|
|
6650
|
+
import * as c16 from "yoctocolors";
|
|
6478
6651
|
async function handleMcpCommand(ctx, args) {
|
|
6479
6652
|
const parts = args.trim().split(/\s+/);
|
|
6480
6653
|
const sub = parts[0] ?? "list";
|
|
@@ -6482,15 +6655,15 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6482
6655
|
case "list": {
|
|
6483
6656
|
const servers = listMcpServers();
|
|
6484
6657
|
if (servers.length === 0) {
|
|
6485
|
-
writeln(
|
|
6486
|
-
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...]"));
|
|
6487
6660
|
return;
|
|
6488
6661
|
}
|
|
6489
6662
|
writeln();
|
|
6490
6663
|
for (const s of servers) {
|
|
6491
6664
|
const detailText = s.url ?? s.command ?? "";
|
|
6492
|
-
const detail = detailText ?
|
|
6493
|
-
writeln(` ${
|
|
6665
|
+
const detail = detailText ? c16.dim(` ${detailText}`) : "";
|
|
6666
|
+
writeln(` ${c16.yellow("\u2699")} ${c16.bold(s.name)} ${c16.dim(s.transport)}${detail}`);
|
|
6494
6667
|
}
|
|
6495
6668
|
return;
|
|
6496
6669
|
}
|
|
@@ -6535,9 +6708,9 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6535
6708
|
}
|
|
6536
6709
|
try {
|
|
6537
6710
|
await ctx.connectMcpServer(name);
|
|
6538
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6711
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} added and connected`);
|
|
6539
6712
|
} catch (e) {
|
|
6540
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6713
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} saved ${c16.dim(`(connection failed: ${String(e)})`)}`);
|
|
6541
6714
|
}
|
|
6542
6715
|
return;
|
|
6543
6716
|
}
|
|
@@ -6549,17 +6722,17 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6549
6722
|
return;
|
|
6550
6723
|
}
|
|
6551
6724
|
deleteMcpServer(name);
|
|
6552
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6725
|
+
writeln(`${PREFIX.success} mcp server ${c16.cyan(name)} removed`);
|
|
6553
6726
|
return;
|
|
6554
6727
|
}
|
|
6555
6728
|
default:
|
|
6556
6729
|
writeln(`${PREFIX.error} unknown: /mcp ${sub}`);
|
|
6557
|
-
writeln(
|
|
6730
|
+
writeln(c16.dim(" subcommands: list \xB7 add \xB7 remove"));
|
|
6558
6731
|
}
|
|
6559
6732
|
}
|
|
6560
6733
|
|
|
6561
6734
|
// src/cli/commands-model.ts
|
|
6562
|
-
import * as
|
|
6735
|
+
import * as c17 from "yoctocolors";
|
|
6563
6736
|
import { select } from "yoctoselect";
|
|
6564
6737
|
var THINKING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
6565
6738
|
function parseThinkingEffort(value) {
|
|
@@ -6579,21 +6752,21 @@ function renderModelUpdatedMessage(ctx, modelId, effortArg) {
|
|
|
6579
6752
|
if (effortArg) {
|
|
6580
6753
|
if (effortArg === "off") {
|
|
6581
6754
|
ctx.setThinkingEffort(null);
|
|
6582
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6755
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim("(thinking disabled)")}`);
|
|
6583
6756
|
return;
|
|
6584
6757
|
}
|
|
6585
6758
|
const effort = parseThinkingEffort(effortArg);
|
|
6586
6759
|
if (effort) {
|
|
6587
6760
|
ctx.setThinkingEffort(effort);
|
|
6588
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6761
|
+
writeln(`${PREFIX.success} model \u2192 ${c17.cyan(modelId)} ${c17.dim(`(\u2726 ${effort})`)}`);
|
|
6589
6762
|
return;
|
|
6590
6763
|
}
|
|
6591
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6592
|
-
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)`);
|
|
6593
6766
|
return;
|
|
6594
6767
|
}
|
|
6595
|
-
const effortTag = ctx.thinkingEffort ?
|
|
6596
|
-
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}`);
|
|
6597
6770
|
}
|
|
6598
6771
|
async function handleModelSet(ctx, args) {
|
|
6599
6772
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -6606,7 +6779,7 @@ async function handleModelSet(ctx, args) {
|
|
|
6606
6779
|
const snapshot = await fetchAvailableModels();
|
|
6607
6780
|
const match = findModelIdByAlias(idArg, snapshot.models.map((model) => model.id));
|
|
6608
6781
|
if (!match) {
|
|
6609
|
-
writeln(`${PREFIX.error} unknown model ${
|
|
6782
|
+
writeln(`${PREFIX.error} unknown model ${c17.cyan(idArg)} ${c17.dim("\u2014 run /models for the full list")}`);
|
|
6610
6783
|
return;
|
|
6611
6784
|
}
|
|
6612
6785
|
modelId = match;
|
|
@@ -6626,7 +6799,7 @@ function handleModelEffort(ctx, effortArg) {
|
|
|
6626
6799
|
return;
|
|
6627
6800
|
}
|
|
6628
6801
|
ctx.setThinkingEffort(effort);
|
|
6629
|
-
writeln(`${PREFIX.success} thinking effort \u2192 ${
|
|
6802
|
+
writeln(`${PREFIX.success} thinking effort \u2192 ${c17.cyan(effort)}`);
|
|
6630
6803
|
}
|
|
6631
6804
|
async function handleModelSelect(ctx) {
|
|
6632
6805
|
ctx.startSpinner("fetching models");
|
|
@@ -6634,20 +6807,20 @@ async function handleModelSelect(ctx) {
|
|
|
6634
6807
|
ctx.stopSpinner();
|
|
6635
6808
|
if (snapshot.models.length === 0) {
|
|
6636
6809
|
writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
|
|
6637
|
-
writeln(
|
|
6810
|
+
writeln(c17.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
|
|
6638
6811
|
return;
|
|
6639
6812
|
}
|
|
6640
6813
|
if (snapshot.stale) {
|
|
6641
6814
|
const lastSync = snapshot.lastSyncAt ? new Date(snapshot.lastSyncAt).toLocaleString() : "never";
|
|
6642
6815
|
const refreshTag = snapshot.refreshing ? " (refreshing in background)" : "";
|
|
6643
|
-
writeln(
|
|
6816
|
+
writeln(c17.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
|
|
6644
6817
|
}
|
|
6645
6818
|
const items = snapshot.models.map((model) => {
|
|
6646
6819
|
const isCurrent = ctx.currentModel === model.id;
|
|
6647
|
-
const freeTag = model.free ?
|
|
6648
|
-
const contextTag = model.context ?
|
|
6649
|
-
const currentTag = isCurrent ?
|
|
6650
|
-
const providerTag =
|
|
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}]`);
|
|
6651
6824
|
return {
|
|
6652
6825
|
label: `${model.displayName}${freeTag}${contextTag}${currentTag}${providerTag}`,
|
|
6653
6826
|
value: model.id,
|
|
@@ -6680,7 +6853,7 @@ async function handleModelCommand(ctx, args) {
|
|
|
6680
6853
|
}
|
|
6681
6854
|
|
|
6682
6855
|
// src/cli/commands-session.ts
|
|
6683
|
-
import * as
|
|
6856
|
+
import * as c18 from "yoctocolors";
|
|
6684
6857
|
import { select as select2 } from "yoctoselect";
|
|
6685
6858
|
async function handleSessionCommand(ctx, args) {
|
|
6686
6859
|
const id = args.trim();
|
|
@@ -6689,15 +6862,15 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6689
6862
|
const ok2 = ctx.switchSession(id);
|
|
6690
6863
|
ctx.stopSpinner();
|
|
6691
6864
|
if (ok2) {
|
|
6692
|
-
writeln(`${PREFIX.success} switched to session ${
|
|
6865
|
+
writeln(`${PREFIX.success} switched to session ${c18.cyan(id)} (${c18.cyan(ctx.currentModel)})`);
|
|
6693
6866
|
} else {
|
|
6694
|
-
writeln(`${PREFIX.error} session ${
|
|
6867
|
+
writeln(`${PREFIX.error} session ${c18.cyan(id)} not found`);
|
|
6695
6868
|
}
|
|
6696
6869
|
return;
|
|
6697
6870
|
}
|
|
6698
6871
|
const sessions = listSessions(50);
|
|
6699
6872
|
if (sessions.length === 0) {
|
|
6700
|
-
writeln(`${PREFIX.info} ${
|
|
6873
|
+
writeln(`${PREFIX.info} ${c18.dim("no sessions found")}`);
|
|
6701
6874
|
return;
|
|
6702
6875
|
}
|
|
6703
6876
|
const items = sessions.map((s) => {
|
|
@@ -6706,7 +6879,7 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6706
6879
|
const cwd = tildePath(s.cwd);
|
|
6707
6880
|
const date = new Date(s.updated_at).toLocaleDateString();
|
|
6708
6881
|
return {
|
|
6709
|
-
label: `${
|
|
6882
|
+
label: `${c18.dim(s.id)} ${title} ${c18.cyan(model)} ${c18.dim(cwd)} ${c18.dim(date)}`,
|
|
6710
6883
|
value: s.id,
|
|
6711
6884
|
filterText: `${s.id} ${s.title} ${s.model} ${s.cwd}`
|
|
6712
6885
|
};
|
|
@@ -6722,9 +6895,9 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6722
6895
|
return;
|
|
6723
6896
|
const ok = ctx.switchSession(picked);
|
|
6724
6897
|
if (ok) {
|
|
6725
|
-
writeln(`${PREFIX.success} switched to session ${
|
|
6898
|
+
writeln(`${PREFIX.success} switched to session ${c18.cyan(picked)} (${c18.cyan(ctx.currentModel)})`);
|
|
6726
6899
|
} else {
|
|
6727
|
-
writeln(`${PREFIX.error} session ${
|
|
6900
|
+
writeln(`${PREFIX.error} session ${c18.cyan(picked)} not found`);
|
|
6728
6901
|
}
|
|
6729
6902
|
}
|
|
6730
6903
|
|
|
@@ -6734,9 +6907,9 @@ async function handleUndo(ctx) {
|
|
|
6734
6907
|
try {
|
|
6735
6908
|
const ok = await ctx.undoLastTurn();
|
|
6736
6909
|
if (ok) {
|
|
6737
|
-
writeln(`${PREFIX.success} ${
|
|
6910
|
+
writeln(`${PREFIX.success} ${c19.dim("last conversation turn removed")}`);
|
|
6738
6911
|
} else {
|
|
6739
|
-
writeln(`${PREFIX.info} ${
|
|
6912
|
+
writeln(`${PREFIX.info} ${c19.dim("nothing to undo")}`);
|
|
6740
6913
|
}
|
|
6741
6914
|
} finally {
|
|
6742
6915
|
ctx.stopSpinner();
|
|
@@ -6794,7 +6967,7 @@ async function handleCommand(command, args, ctx) {
|
|
|
6794
6967
|
if (loaded2) {
|
|
6795
6968
|
const srcPath = skill.source === "local" ? `.agents/skills/${skill.name}/SKILL.md` : `~/.agents/skills/${skill.name}/SKILL.md`;
|
|
6796
6969
|
if (skill.context === "fork") {
|
|
6797
|
-
writeln(`${PREFIX.info} ${
|
|
6970
|
+
writeln(`${PREFIX.info} ${c19.cyan(skill.name)} ${c19.dim(`[${srcPath}] (forked subagent)`)}`);
|
|
6798
6971
|
writeln();
|
|
6799
6972
|
const subagentPrompt = args ? `${loaded2.content}
|
|
6800
6973
|
|
|
@@ -6802,7 +6975,7 @@ ${args}` : loaded2.content;
|
|
|
6802
6975
|
const result = await runForkedSkill(skill.name, subagentPrompt, ctx.cwd);
|
|
6803
6976
|
return { type: "inject-user-message", text: result };
|
|
6804
6977
|
}
|
|
6805
|
-
writeln(`${PREFIX.info} ${
|
|
6978
|
+
writeln(`${PREFIX.info} ${c19.cyan(skill.name)} ${c19.dim(`[${srcPath}]`)}`);
|
|
6806
6979
|
writeln();
|
|
6807
6980
|
const prompt = args ? `${loaded2.content}
|
|
6808
6981
|
|
|
@@ -6810,16 +6983,16 @@ ${args}` : loaded2.content;
|
|
|
6810
6983
|
return { type: "inject-user-message", text: prompt };
|
|
6811
6984
|
}
|
|
6812
6985
|
}
|
|
6813
|
-
writeln(`${PREFIX.error} unknown: /${command} ${
|
|
6986
|
+
writeln(`${PREFIX.error} unknown: /${command} ${c19.dim("\u2014 /help for commands")}`);
|
|
6814
6987
|
return { type: "unknown", command };
|
|
6815
6988
|
}
|
|
6816
6989
|
}
|
|
6817
6990
|
}
|
|
6818
6991
|
async function runForkedSkill(skillName, prompt, cwd) {
|
|
6819
|
-
const tmpFile =
|
|
6992
|
+
const tmpFile = join9(tmpdir(), `mc-fork-${randomBytes(8).toString("hex")}.md`);
|
|
6820
6993
|
writeFileSync2(tmpFile, prompt, "utf8");
|
|
6821
6994
|
try {
|
|
6822
|
-
writeln(`${PREFIX.info} ${
|
|
6995
|
+
writeln(`${PREFIX.info} ${c19.dim("running subagent\u2026")}`);
|
|
6823
6996
|
const proc = Bun.spawn([process.execPath, Bun.main], {
|
|
6824
6997
|
cwd,
|
|
6825
6998
|
stdin: Bun.file(tmpFile),
|
|
@@ -6851,17 +7024,17 @@ ${output}`;
|
|
|
6851
7024
|
function handleBooleanToggleCommand(opts) {
|
|
6852
7025
|
const mode = opts.args.trim().toLowerCase();
|
|
6853
7026
|
if (!mode) {
|
|
6854
|
-
writeln(`${PREFIX.success} ${opts.label} ${opts.current ?
|
|
7027
|
+
writeln(`${PREFIX.success} ${opts.label} ${opts.current ? c19.green("on") : c19.dim("off")}`);
|
|
6855
7028
|
return;
|
|
6856
7029
|
}
|
|
6857
7030
|
if (mode === "on") {
|
|
6858
7031
|
opts.set(true);
|
|
6859
|
-
writeln(`${PREFIX.success} ${opts.label} ${
|
|
7032
|
+
writeln(`${PREFIX.success} ${opts.label} ${c19.green("on")}`);
|
|
6860
7033
|
return;
|
|
6861
7034
|
}
|
|
6862
7035
|
if (mode === "off") {
|
|
6863
7036
|
opts.set(false);
|
|
6864
|
-
writeln(`${PREFIX.success} ${opts.label} ${
|
|
7037
|
+
writeln(`${PREFIX.success} ${opts.label} ${c19.dim("off")}`);
|
|
6865
7038
|
return;
|
|
6866
7039
|
}
|
|
6867
7040
|
writeln(`${PREFIX.error} usage: ${opts.usage}`);
|
|
@@ -6921,13 +7094,12 @@ async function getGitBranch(cwd) {
|
|
|
6921
7094
|
}
|
|
6922
7095
|
async function runInputLoop(opts) {
|
|
6923
7096
|
const { cwd, reporter, cmdCtx, runner } = opts;
|
|
6924
|
-
let lastStatusSignature = null;
|
|
6925
7097
|
while (true) {
|
|
6926
7098
|
const branch = await getGitBranch(cwd);
|
|
6927
7099
|
const status = runner.getStatusInfo();
|
|
6928
7100
|
const cwdDisplay = tildePath(cwd);
|
|
6929
7101
|
const contextWindow = getContextWindow(status.model);
|
|
6930
|
-
|
|
7102
|
+
reporter.renderStatusBar({
|
|
6931
7103
|
model: status.model,
|
|
6932
7104
|
cwd: cwdDisplay,
|
|
6933
7105
|
gitBranch: branch,
|
|
@@ -6937,12 +7109,7 @@ async function runInputLoop(opts) {
|
|
|
6937
7109
|
contextTokens: status.lastContextTokens,
|
|
6938
7110
|
contextWindow,
|
|
6939
7111
|
thinkingEffort: status.thinkingEffort
|
|
6940
|
-
};
|
|
6941
|
-
const statusSignature = buildStatusBarSignature(statusData);
|
|
6942
|
-
if (statusSignature !== lastStatusSignature) {
|
|
6943
|
-
reporter.renderStatusBar(statusData);
|
|
6944
|
-
lastStatusSignature = statusSignature;
|
|
6945
|
-
}
|
|
7112
|
+
});
|
|
6946
7113
|
let input;
|
|
6947
7114
|
try {
|
|
6948
7115
|
input = await readline({ cwd });
|
|
@@ -6951,14 +7118,14 @@ async function runInputLoop(opts) {
|
|
|
6951
7118
|
}
|
|
6952
7119
|
switch (input.type) {
|
|
6953
7120
|
case "eof":
|
|
6954
|
-
reporter.writeText(
|
|
7121
|
+
reporter.writeText(c20.dim("Goodbye."));
|
|
6955
7122
|
return;
|
|
6956
7123
|
case "interrupt":
|
|
6957
7124
|
continue;
|
|
6958
7125
|
case "command": {
|
|
6959
7126
|
const result = await handleCommand(input.command, input.args, cmdCtx);
|
|
6960
7127
|
if (result.type === "exit") {
|
|
6961
|
-
reporter.writeText(
|
|
7128
|
+
reporter.writeText(c20.dim("Goodbye."));
|
|
6962
7129
|
return;
|
|
6963
7130
|
}
|
|
6964
7131
|
if (result.type === "inject-user-message") {
|
|
@@ -7065,7 +7232,7 @@ async function main() {
|
|
|
7065
7232
|
if (last) {
|
|
7066
7233
|
sessionId = last.id;
|
|
7067
7234
|
} else {
|
|
7068
|
-
writeln(
|
|
7235
|
+
writeln(c21.dim("No previous session found, starting fresh."));
|
|
7069
7236
|
}
|
|
7070
7237
|
} else if (args.sessionId) {
|
|
7071
7238
|
sessionId = args.sessionId;
|