mini-coder 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/mc.js +648 -682
- package/docs/KNOWN_ISSUES.md +1 -0
- package/docs/design-decisions.md +2 -45
- package/docs/mini-coder.1.md +3 -8
- package/docs/superpowers/plans/2026-03-30-anthropic-oauth-removal.md +61 -0
- package/docs/superpowers/specs/2026-03-30-anthropic-oauth-removal-design.md +47 -0
- 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 c20 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.1";
|
|
17
17
|
|
|
18
18
|
// src/mcp/client.ts
|
|
19
19
|
async function connectMcpServer(config) {
|
|
@@ -70,7 +70,7 @@ async function connectMcpServer(config) {
|
|
|
70
70
|
|
|
71
71
|
// src/session/db/connection.ts
|
|
72
72
|
import { Database } from "bun:sqlite";
|
|
73
|
-
import { existsSync, mkdirSync,
|
|
73
|
+
import { existsSync, mkdirSync, renameSync } from "fs";
|
|
74
74
|
import { homedir } from "os";
|
|
75
75
|
import { join } from "path";
|
|
76
76
|
function getConfigDir() {
|
|
@@ -201,27 +201,32 @@ function configureDb(db) {
|
|
|
201
201
|
db.exec("PRAGMA foreign_keys=ON;");
|
|
202
202
|
db.exec("PRAGMA busy_timeout=1000;");
|
|
203
203
|
}
|
|
204
|
+
function rotateDbFiles(dbPath, version) {
|
|
205
|
+
const backupBase = `${dbPath}.bak-v${version}-${Date.now()}`;
|
|
206
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
207
|
+
const path = `${dbPath}${suffix}`;
|
|
208
|
+
if (!existsSync(path))
|
|
209
|
+
continue;
|
|
210
|
+
renameSync(path, `${backupBase}${suffix}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
204
213
|
function getDb() {
|
|
205
214
|
if (!_db) {
|
|
206
215
|
const dbPath = getDbPath();
|
|
216
|
+
const dbExists = existsSync(dbPath);
|
|
207
217
|
let db = new Database(dbPath, { create: true });
|
|
208
218
|
configureDb(db);
|
|
209
219
|
const version = db.query("PRAGMA user_version").get()?.user_version ?? 0;
|
|
210
|
-
if (version !== DB_VERSION) {
|
|
220
|
+
if (dbExists && version !== DB_VERSION) {
|
|
211
221
|
try {
|
|
212
222
|
db.close();
|
|
213
223
|
} catch {}
|
|
214
|
-
|
|
215
|
-
if (existsSync(path))
|
|
216
|
-
unlinkSync(path);
|
|
217
|
-
}
|
|
224
|
+
rotateDbFiles(dbPath, version);
|
|
218
225
|
db = new Database(dbPath, { create: true });
|
|
219
226
|
configureDb(db);
|
|
220
|
-
db.exec(SCHEMA);
|
|
221
|
-
db.exec(`PRAGMA user_version = ${DB_VERSION};`);
|
|
222
|
-
} else {
|
|
223
|
-
db.exec(SCHEMA);
|
|
224
227
|
}
|
|
228
|
+
db.exec(SCHEMA);
|
|
229
|
+
db.exec(`PRAGMA user_version = ${DB_VERSION};`);
|
|
225
230
|
_db = db;
|
|
226
231
|
}
|
|
227
232
|
return _db;
|
|
@@ -339,7 +344,7 @@ function deleteMcpServer(name) {
|
|
|
339
344
|
}
|
|
340
345
|
// src/cli/output.ts
|
|
341
346
|
import { homedir as homedir4 } from "os";
|
|
342
|
-
import * as
|
|
347
|
+
import * as c7 from "yoctocolors";
|
|
343
348
|
|
|
344
349
|
// src/agent/context-files.ts
|
|
345
350
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
@@ -436,7 +441,7 @@ import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
|
436
441
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
437
442
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
438
443
|
|
|
439
|
-
// src/session/oauth/
|
|
444
|
+
// src/session/oauth/openai.ts
|
|
440
445
|
import { createServer } from "http";
|
|
441
446
|
|
|
442
447
|
// src/session/oauth/pkce.ts
|
|
@@ -457,19 +462,27 @@ async function generatePKCE() {
|
|
|
457
462
|
return { verifier, challenge };
|
|
458
463
|
}
|
|
459
464
|
|
|
460
|
-
// src/session/oauth/
|
|
461
|
-
var CLIENT_ID = "
|
|
462
|
-
var AUTHORIZE_URL = "https://
|
|
463
|
-
var TOKEN_URL = "https://
|
|
465
|
+
// src/session/oauth/openai.ts
|
|
466
|
+
var CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
467
|
+
var AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
|
|
468
|
+
var TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
464
469
|
var CALLBACK_HOST = "127.0.0.1";
|
|
465
|
-
var CALLBACK_PORT =
|
|
466
|
-
var CALLBACK_PATH = "/callback";
|
|
470
|
+
var CALLBACK_PORT = 1455;
|
|
471
|
+
var CALLBACK_PATH = "/auth/callback";
|
|
467
472
|
var REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
468
|
-
var SCOPES = "
|
|
473
|
+
var SCOPES = "openid profile email offline_access";
|
|
469
474
|
var LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
470
475
|
var SUCCESS_HTML = `<!doctype html>
|
|
471
476
|
<html lang="en"><head><meta charset="utf-8"><title>Authenticated</title></head>
|
|
472
|
-
<body><p>
|
|
477
|
+
<body><p>OpenAI authentication successful. Return to your terminal.</p></body></html>`;
|
|
478
|
+
function createState() {
|
|
479
|
+
const bytes = new Uint8Array(16);
|
|
480
|
+
crypto.getRandomValues(bytes);
|
|
481
|
+
let hex = "";
|
|
482
|
+
for (const b of bytes)
|
|
483
|
+
hex += b.toString(16).padStart(2, "0");
|
|
484
|
+
return hex;
|
|
485
|
+
}
|
|
473
486
|
function startCallbackServer(expectedState) {
|
|
474
487
|
return new Promise((resolve2, reject) => {
|
|
475
488
|
let result = null;
|
|
@@ -488,7 +501,7 @@ function startCallbackServer(expectedState) {
|
|
|
488
501
|
return;
|
|
489
502
|
}
|
|
490
503
|
res.writeHead(200, { "Content-Type": "text/html" }).end(SUCCESS_HTML);
|
|
491
|
-
result = { code
|
|
504
|
+
result = { code };
|
|
492
505
|
});
|
|
493
506
|
server.on("error", reject);
|
|
494
507
|
server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
|
|
@@ -512,10 +525,9 @@ async function postTokenRequest(body, label) {
|
|
|
512
525
|
const res = await fetch(TOKEN_URL, {
|
|
513
526
|
method: "POST",
|
|
514
527
|
headers: {
|
|
515
|
-
"Content-Type": "application/
|
|
516
|
-
Accept: "application/json"
|
|
528
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
517
529
|
},
|
|
518
|
-
body
|
|
530
|
+
body,
|
|
519
531
|
signal: AbortSignal.timeout(30000)
|
|
520
532
|
});
|
|
521
533
|
if (!res.ok) {
|
|
@@ -529,57 +541,72 @@ async function postTokenRequest(body, label) {
|
|
|
529
541
|
expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000
|
|
530
542
|
};
|
|
531
543
|
}
|
|
532
|
-
function exchangeCode(code,
|
|
533
|
-
return postTokenRequest({
|
|
544
|
+
function exchangeCode(code, verifier) {
|
|
545
|
+
return postTokenRequest(new URLSearchParams({
|
|
534
546
|
grant_type: "authorization_code",
|
|
535
547
|
client_id: CLIENT_ID,
|
|
536
548
|
code,
|
|
537
|
-
|
|
538
|
-
redirect_uri: REDIRECT_URI
|
|
539
|
-
|
|
540
|
-
}, "Token exchange");
|
|
549
|
+
code_verifier: verifier,
|
|
550
|
+
redirect_uri: REDIRECT_URI
|
|
551
|
+
}), "Token exchange");
|
|
541
552
|
}
|
|
542
|
-
function
|
|
543
|
-
return postTokenRequest({
|
|
553
|
+
function refreshOpenAIToken(refreshToken) {
|
|
554
|
+
return postTokenRequest(new URLSearchParams({
|
|
544
555
|
grant_type: "refresh_token",
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}, "Token refresh");
|
|
556
|
+
refresh_token: refreshToken,
|
|
557
|
+
client_id: CLIENT_ID
|
|
558
|
+
}), "Token refresh");
|
|
548
559
|
}
|
|
549
|
-
async function
|
|
560
|
+
async function loginOpenAI(callbacks) {
|
|
550
561
|
const { verifier, challenge } = await generatePKCE();
|
|
551
|
-
const
|
|
562
|
+
const state = createState();
|
|
563
|
+
const cb = await startCallbackServer(state);
|
|
552
564
|
try {
|
|
553
565
|
const params = new URLSearchParams({
|
|
554
|
-
code: "true",
|
|
555
|
-
client_id: CLIENT_ID,
|
|
556
566
|
response_type: "code",
|
|
567
|
+
client_id: CLIENT_ID,
|
|
557
568
|
redirect_uri: REDIRECT_URI,
|
|
558
569
|
scope: SCOPES,
|
|
559
570
|
code_challenge: challenge,
|
|
560
571
|
code_challenge_method: "S256",
|
|
561
|
-
state
|
|
572
|
+
state,
|
|
573
|
+
id_token_add_organizations: "true",
|
|
574
|
+
codex_cli_simplified_flow: "true",
|
|
575
|
+
originator: "mc"
|
|
562
576
|
});
|
|
563
577
|
callbacks.onOpenUrl(`${AUTHORIZE_URL}?${params}`, "Complete login in your browser.");
|
|
564
578
|
const result = await cb.waitForCode();
|
|
565
579
|
if (!result)
|
|
566
580
|
throw new Error("Login cancelled or no code received");
|
|
567
581
|
callbacks.onProgress("Exchanging authorization code for tokens\u2026");
|
|
568
|
-
return exchangeCode(result.code,
|
|
582
|
+
return exchangeCode(result.code, verifier);
|
|
569
583
|
} finally {
|
|
570
584
|
cb.server.close();
|
|
571
585
|
}
|
|
572
586
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
587
|
+
function extractAccountId(accessToken) {
|
|
588
|
+
try {
|
|
589
|
+
const parts = accessToken.split(".");
|
|
590
|
+
const jwt = parts[1];
|
|
591
|
+
if (parts.length !== 3 || !jwt)
|
|
592
|
+
return null;
|
|
593
|
+
const payload = JSON.parse(atob(jwt));
|
|
594
|
+
const auth = payload["https://api.openai.com/auth"];
|
|
595
|
+
return auth?.chatgpt_account_id ?? null;
|
|
596
|
+
} catch {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
var openaiOAuth = {
|
|
601
|
+
id: "openai",
|
|
602
|
+
name: "OpenAI (ChatGPT Plus/Pro)",
|
|
603
|
+
login: loginOpenAI,
|
|
604
|
+
refreshToken: refreshOpenAIToken
|
|
578
605
|
};
|
|
579
606
|
|
|
580
607
|
// src/session/oauth/auth-storage.ts
|
|
581
608
|
var PROVIDERS = new Map([
|
|
582
|
-
[
|
|
609
|
+
[openaiOAuth.id, openaiOAuth]
|
|
583
610
|
]);
|
|
584
611
|
function getOAuthProviders() {
|
|
585
612
|
return [...PROVIDERS.values()];
|
|
@@ -608,10 +635,10 @@ function isAuthError(err) {
|
|
|
608
635
|
return /\b(401|403)\b/.test(err.message);
|
|
609
636
|
}
|
|
610
637
|
function isLoggedIn(provider) {
|
|
611
|
-
return getStoredToken(provider) !== null;
|
|
638
|
+
return PROVIDERS.has(provider) && getStoredToken(provider) !== null;
|
|
612
639
|
}
|
|
613
640
|
function listLoggedInProviders() {
|
|
614
|
-
return getDb().query("SELECT provider FROM oauth_tokens ORDER BY provider").all().map((r) => r.provider);
|
|
641
|
+
return getDb().query("SELECT provider FROM oauth_tokens ORDER BY provider").all().map((r) => r.provider).filter((provider) => PROVIDERS.has(provider));
|
|
615
642
|
}
|
|
616
643
|
async function login(providerId, callbacks) {
|
|
617
644
|
const provider = PROVIDERS.get(providerId);
|
|
@@ -1140,41 +1167,53 @@ async function fetchZenModels() {
|
|
|
1140
1167
|
});
|
|
1141
1168
|
}
|
|
1142
1169
|
async function fetchOpenAIModels() {
|
|
1143
|
-
const
|
|
1144
|
-
if (
|
|
1145
|
-
return
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
return
|
|
1157
|
-
if (isLoggedIn("anthropic")) {
|
|
1158
|
-
const token = await getAccessToken("anthropic");
|
|
1159
|
-
if (token)
|
|
1160
|
-
return { key: token, oauth: true };
|
|
1170
|
+
const envKey = process.env.OPENAI_API_KEY;
|
|
1171
|
+
if (envKey) {
|
|
1172
|
+
return fetchPaginatedModelsList(`${OPENAI_BASE}/v1/models`, { headers: { Authorization: `Bearer ${envKey}` } }, 6000, "data", "id", (_item, modelId) => ({
|
|
1173
|
+
providerModelId: modelId,
|
|
1174
|
+
displayName: modelId,
|
|
1175
|
+
contextWindow: null,
|
|
1176
|
+
free: false
|
|
1177
|
+
}));
|
|
1178
|
+
}
|
|
1179
|
+
if (isLoggedIn("openai")) {
|
|
1180
|
+
const token = await getAccessToken("openai");
|
|
1181
|
+
if (!token)
|
|
1182
|
+
return null;
|
|
1183
|
+
return fetchCodexOAuthModels(token);
|
|
1161
1184
|
}
|
|
1162
1185
|
return null;
|
|
1163
1186
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1187
|
+
var CODEX_MODELS_URL = "https://chatgpt.com/backend-api/codex/models?client_version=1.0.0";
|
|
1188
|
+
async function fetchCodexOAuthModels(token) {
|
|
1189
|
+
try {
|
|
1190
|
+
const res = await fetch(CODEX_MODELS_URL, {
|
|
1191
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1192
|
+
signal: AbortSignal.timeout(6000)
|
|
1193
|
+
});
|
|
1194
|
+
if (!res.ok)
|
|
1195
|
+
return null;
|
|
1196
|
+
const data = await res.json();
|
|
1197
|
+
return (data.models ?? []).filter((m) => m.visibility === "list").map((m) => ({
|
|
1198
|
+
providerModelId: m.slug,
|
|
1199
|
+
displayName: m.display_name || m.slug,
|
|
1200
|
+
contextWindow: m.context_window,
|
|
1201
|
+
free: false
|
|
1202
|
+
}));
|
|
1203
|
+
} catch {
|
|
1167
1204
|
return null;
|
|
1168
|
-
const headers = {
|
|
1169
|
-
"anthropic-version": "2023-06-01"
|
|
1170
|
-
};
|
|
1171
|
-
if (auth.oauth) {
|
|
1172
|
-
headers.Authorization = `Bearer ${auth.key}`;
|
|
1173
|
-
headers["anthropic-beta"] = "oauth-2025-04-20";
|
|
1174
|
-
} else {
|
|
1175
|
-
headers["x-api-key"] = auth.key;
|
|
1176
1205
|
}
|
|
1177
|
-
|
|
1206
|
+
}
|
|
1207
|
+
async function fetchAnthropicModels() {
|
|
1208
|
+
const key = process.env.ANTHROPIC_API_KEY;
|
|
1209
|
+
if (!key)
|
|
1210
|
+
return null;
|
|
1211
|
+
const payload = await fetchJson(`${ANTHROPIC_BASE}/v1/models`, {
|
|
1212
|
+
headers: {
|
|
1213
|
+
"anthropic-version": "2023-06-01",
|
|
1214
|
+
"x-api-key": key
|
|
1215
|
+
}
|
|
1216
|
+
}, 6000);
|
|
1178
1217
|
return processModelsList(payload, "data", "id", (item, modelId) => {
|
|
1179
1218
|
const displayName = typeof item.display_name === "string" && item.display_name.trim().length > 0 ? item.display_name : modelId;
|
|
1180
1219
|
return {
|
|
@@ -1236,7 +1275,7 @@ async function fetchProviderCandidates(provider) {
|
|
|
1236
1275
|
var MODELS_DEV_SYNC_KEY = "last_models_dev_sync_at";
|
|
1237
1276
|
var PROVIDER_SYNC_KEY_PREFIX = "last_provider_sync_at:";
|
|
1238
1277
|
var CACHE_VERSION_KEY = "model_info_cache_version";
|
|
1239
|
-
var CACHE_VERSION =
|
|
1278
|
+
var CACHE_VERSION = 5;
|
|
1240
1279
|
var MODEL_INFO_TTL_MS = 24 * 60 * 60 * 1000;
|
|
1241
1280
|
var REMOTE_PROVIDER_ENV_KEYS = [
|
|
1242
1281
|
{ provider: "zen", envKeys: ["OPENCODE_API_KEY"] },
|
|
@@ -1281,8 +1320,8 @@ function hasAnyEnvKey(env, keys) {
|
|
|
1281
1320
|
}
|
|
1282
1321
|
function getRemoteProvidersFromEnv(env) {
|
|
1283
1322
|
const providers = REMOTE_PROVIDER_ENV_KEYS.filter((entry) => hasAnyEnvKey(env, entry.envKeys)).map((entry) => entry.provider);
|
|
1284
|
-
if (!providers.includes("
|
|
1285
|
-
providers.push("
|
|
1323
|
+
if (!providers.includes("openai") && isLoggedIn("openai")) {
|
|
1324
|
+
providers.push("openai");
|
|
1286
1325
|
}
|
|
1287
1326
|
return providers;
|
|
1288
1327
|
}
|
|
@@ -1487,27 +1526,6 @@ var SUPPORTED_PROVIDERS = [
|
|
|
1487
1526
|
"ollama"
|
|
1488
1527
|
];
|
|
1489
1528
|
var ZEN_BASE2 = "https://opencode.ai/zen/v1";
|
|
1490
|
-
var OAUTH_STRIP_BETAS = new Set;
|
|
1491
|
-
function createOAuthFetch(accessToken) {
|
|
1492
|
-
const oauthFetch = async (input, init) => {
|
|
1493
|
-
let opts = init;
|
|
1494
|
-
if (opts?.headers) {
|
|
1495
|
-
const h = new Headers(opts.headers);
|
|
1496
|
-
const beta = h.get("anthropic-beta");
|
|
1497
|
-
if (beta) {
|
|
1498
|
-
const filtered = beta.split(",").filter((b) => !OAUTH_STRIP_BETAS.has(b)).join(",");
|
|
1499
|
-
h.set("anthropic-beta", filtered);
|
|
1500
|
-
}
|
|
1501
|
-
h.delete("x-api-key");
|
|
1502
|
-
h.set("authorization", `Bearer ${accessToken}`);
|
|
1503
|
-
h.set("user-agent", "claude-cli/2.1.75");
|
|
1504
|
-
h.set("x-app", "cli");
|
|
1505
|
-
opts = { ...opts, headers: Object.fromEntries(h.entries()) };
|
|
1506
|
-
}
|
|
1507
|
-
return fetch(input, opts);
|
|
1508
|
-
};
|
|
1509
|
-
return oauthFetch;
|
|
1510
|
-
}
|
|
1511
1529
|
function requireEnv(name) {
|
|
1512
1530
|
const value = process.env[name];
|
|
1513
1531
|
if (!value)
|
|
@@ -1586,32 +1604,64 @@ var ZEN_BACKEND_RESOLVERS = {
|
|
|
1586
1604
|
function resolveZenModel(modelId) {
|
|
1587
1605
|
return ZEN_BACKEND_RESOLVERS[getZenBackend(modelId)](modelId);
|
|
1588
1606
|
}
|
|
1589
|
-
function
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
"
|
|
1607
|
+
function createOAuthOpenAIProvider(token) {
|
|
1608
|
+
const accountId = extractAccountId(token);
|
|
1609
|
+
return createOpenAI({
|
|
1610
|
+
apiKey: "oauth",
|
|
1611
|
+
baseURL: OPENAI_CODEX_BASE_URL,
|
|
1612
|
+
fetch: (input, init) => {
|
|
1613
|
+
const h = new Headers(init?.headers);
|
|
1614
|
+
h.delete("OpenAI-Organization");
|
|
1615
|
+
h.delete("OpenAI-Project");
|
|
1616
|
+
h.set("Authorization", `Bearer ${token}`);
|
|
1617
|
+
if (accountId)
|
|
1618
|
+
h.set("chatgpt-account-id", accountId);
|
|
1619
|
+
let body = init?.body;
|
|
1620
|
+
if (typeof body === "string") {
|
|
1621
|
+
try {
|
|
1622
|
+
const parsed = JSON.parse(body);
|
|
1623
|
+
if (parsed.input && Array.isArray(parsed.input)) {
|
|
1624
|
+
if (!parsed.instructions) {
|
|
1625
|
+
const sysIdx = parsed.input.findIndex((m) => m.role === "developer" || m.role === "system");
|
|
1626
|
+
if (sysIdx !== -1) {
|
|
1627
|
+
const sysMsg = parsed.input[sysIdx];
|
|
1628
|
+
parsed.instructions = typeof sysMsg.content === "string" ? sysMsg.content : JSON.stringify(sysMsg.content);
|
|
1629
|
+
parsed.input.splice(sysIdx, 1);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
delete parsed.max_output_tokens;
|
|
1633
|
+
parsed.store = false;
|
|
1634
|
+
parsed.stream = true;
|
|
1635
|
+
body = JSON.stringify(parsed);
|
|
1636
|
+
}
|
|
1637
|
+
} catch {}
|
|
1638
|
+
}
|
|
1639
|
+
return fetch(input, {
|
|
1640
|
+
...init,
|
|
1641
|
+
body,
|
|
1642
|
+
headers: Object.fromEntries(h.entries())
|
|
1643
|
+
});
|
|
1599
1644
|
}
|
|
1600
1645
|
});
|
|
1601
1646
|
}
|
|
1602
|
-
async function
|
|
1603
|
-
if (isLoggedIn("
|
|
1604
|
-
const token = await getAccessToken("
|
|
1647
|
+
async function resolveOpenAIModel(modelId) {
|
|
1648
|
+
if (isLoggedIn("openai")) {
|
|
1649
|
+
const token = await getAccessToken("openai");
|
|
1605
1650
|
if (token) {
|
|
1606
|
-
if (!
|
|
1607
|
-
|
|
1651
|
+
if (!oauthOpenAICache || oauthOpenAICache.token !== token) {
|
|
1652
|
+
oauthOpenAICache = {
|
|
1608
1653
|
token,
|
|
1609
|
-
provider:
|
|
1654
|
+
provider: createOAuthOpenAIProvider(token)
|
|
1610
1655
|
};
|
|
1611
1656
|
}
|
|
1612
|
-
return
|
|
1657
|
+
return oauthOpenAICache.provider.responses(modelId);
|
|
1613
1658
|
}
|
|
1614
1659
|
}
|
|
1660
|
+
return modelId.startsWith("gpt-") ? directProviders.openai().responses(modelId) : directProviders.openai()(modelId);
|
|
1661
|
+
}
|
|
1662
|
+
var OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
1663
|
+
var oauthOpenAICache = null;
|
|
1664
|
+
async function resolveAnthropicModel(modelId) {
|
|
1615
1665
|
return directProviders.anthropic()(modelId);
|
|
1616
1666
|
}
|
|
1617
1667
|
var PROVIDER_MODEL_RESOLVERS = {
|
|
@@ -1636,18 +1686,15 @@ async function resolveModel(modelString) {
|
|
|
1636
1686
|
}
|
|
1637
1687
|
return PROVIDER_MODEL_RESOLVERS[provider](modelId);
|
|
1638
1688
|
}
|
|
1639
|
-
function isAnthropicOAuth() {
|
|
1640
|
-
return isLoggedIn("anthropic");
|
|
1641
|
-
}
|
|
1642
1689
|
function discoverConnectedProviders() {
|
|
1643
1690
|
const result = [];
|
|
1644
1691
|
if (process.env.OPENCODE_API_KEY)
|
|
1645
1692
|
result.push({ name: "zen", via: "env" });
|
|
1646
|
-
if (
|
|
1647
|
-
result.push({ name: "anthropic", via: "oauth" });
|
|
1648
|
-
else if (process.env.ANTHROPIC_API_KEY)
|
|
1693
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
1649
1694
|
result.push({ name: "anthropic", via: "env" });
|
|
1650
|
-
if (
|
|
1695
|
+
if (isLoggedIn("openai"))
|
|
1696
|
+
result.push({ name: "openai", via: "oauth" });
|
|
1697
|
+
else if (process.env.OPENAI_API_KEY)
|
|
1651
1698
|
result.push({ name: "openai", via: "env" });
|
|
1652
1699
|
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY)
|
|
1653
1700
|
result.push({ name: "google", via: "env" });
|
|
@@ -1658,9 +1705,9 @@ function discoverConnectedProviders() {
|
|
|
1658
1705
|
function autoDiscoverModel() {
|
|
1659
1706
|
if (process.env.OPENCODE_API_KEY)
|
|
1660
1707
|
return "zen/claude-sonnet-4-6";
|
|
1661
|
-
if (process.env.ANTHROPIC_API_KEY
|
|
1708
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
1662
1709
|
return "anthropic/claude-sonnet-4-6";
|
|
1663
|
-
if (process.env.OPENAI_API_KEY)
|
|
1710
|
+
if (process.env.OPENAI_API_KEY || isLoggedIn("openai"))
|
|
1664
1711
|
return "openai/gpt-5.4";
|
|
1665
1712
|
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY)
|
|
1666
1713
|
return "google/gemini-3.1-pro";
|
|
@@ -2290,9 +2337,6 @@ class Spinner {
|
|
|
2290
2337
|
terminal.setBeforeWriteCallback(null);
|
|
2291
2338
|
terminal.stderrWrite("\r\x1B[2K\x1B[?25h");
|
|
2292
2339
|
}
|
|
2293
|
-
update(label) {
|
|
2294
|
-
this.label = label;
|
|
2295
|
-
}
|
|
2296
2340
|
_tick() {
|
|
2297
2341
|
const f = SPINNER_FRAMES[this.frame++ % SPINNER_FRAMES.length] ?? "\u28FE";
|
|
2298
2342
|
const label = this.label ? c2.dim(` ${this.label}`) : "";
|
|
@@ -2346,19 +2390,6 @@ function buildContextSegment(opts) {
|
|
|
2346
2390
|
function renderStatusLine(segments) {
|
|
2347
2391
|
return segments.join(STATUS_SEP);
|
|
2348
2392
|
}
|
|
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
2393
|
function fitStatusSegments(required, optional, cols) {
|
|
2363
2394
|
const fittedOptional = [...optional];
|
|
2364
2395
|
let line = renderStatusLine([...required, ...fittedOptional]);
|
|
@@ -2400,8 +2431,8 @@ function renderStatusBar(opts) {
|
|
|
2400
2431
|
}
|
|
2401
2432
|
|
|
2402
2433
|
// src/cli/stream-render.ts
|
|
2403
|
-
import
|
|
2404
|
-
import
|
|
2434
|
+
import * as c6 from "yoctocolors";
|
|
2435
|
+
import { createHighlighter } from "yoctomarkdown";
|
|
2405
2436
|
|
|
2406
2437
|
// src/llm-api/history/gemini.ts
|
|
2407
2438
|
function getGeminiThoughtSignature(part) {
|
|
@@ -2659,60 +2690,6 @@ function extractAssistantText(newMessages) {
|
|
|
2659
2690
|
`);
|
|
2660
2691
|
}
|
|
2661
2692
|
|
|
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
2693
|
// src/cli/reasoning.ts
|
|
2717
2694
|
function normalizeReasoningDelta(delta) {
|
|
2718
2695
|
return delta.replace(/\r\n?/g, `
|
|
@@ -2746,130 +2723,34 @@ function normalizeReasoningText(text) {
|
|
|
2746
2723
|
`);
|
|
2747
2724
|
}
|
|
2748
2725
|
|
|
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
2726
|
// src/cli/tool-render.ts
|
|
2844
|
-
import * as
|
|
2727
|
+
import * as c5 from "yoctocolors";
|
|
2845
2728
|
|
|
2846
2729
|
// src/cli/tool-result-renderers.ts
|
|
2847
|
-
import * as
|
|
2730
|
+
import * as c4 from "yoctocolors";
|
|
2848
2731
|
function writePreviewLines(opts) {
|
|
2849
2732
|
if (!opts.value.trim())
|
|
2850
2733
|
return;
|
|
2851
2734
|
const lines = opts.value.split(`
|
|
2852
2735
|
`);
|
|
2853
|
-
|
|
2736
|
+
if (opts.label)
|
|
2737
|
+
writeln(`${c4.dim(opts.label)} ${c4.dim(`(${lines.length} lines)`)}`);
|
|
2854
2738
|
if (!Number.isFinite(opts.maxLines) || lines.length <= opts.maxLines) {
|
|
2855
|
-
for (const line of lines)
|
|
2856
|
-
writeln(
|
|
2857
|
-
}
|
|
2739
|
+
for (const line of lines)
|
|
2740
|
+
writeln(line);
|
|
2858
2741
|
return;
|
|
2859
2742
|
}
|
|
2860
2743
|
const headCount = Math.max(1, Math.ceil(opts.maxLines / 2));
|
|
2861
2744
|
const tailCount = Math.max(0, Math.floor(opts.maxLines / 2));
|
|
2862
|
-
for (const line of lines.slice(0, headCount))
|
|
2863
|
-
writeln(
|
|
2864
|
-
}
|
|
2745
|
+
for (const line of lines.slice(0, headCount))
|
|
2746
|
+
writeln(line);
|
|
2865
2747
|
const hiddenLines = Math.max(0, lines.length - (headCount + tailCount));
|
|
2866
2748
|
if (hiddenLines > 0) {
|
|
2867
|
-
writeln(
|
|
2749
|
+
writeln(c4.dim(`\u2026 +${hiddenLines} lines`));
|
|
2868
2750
|
}
|
|
2869
2751
|
if (tailCount > 0) {
|
|
2870
|
-
for (const line of lines.slice(-tailCount))
|
|
2871
|
-
writeln(
|
|
2872
|
-
}
|
|
2752
|
+
for (const line of lines.slice(-tailCount))
|
|
2753
|
+
writeln(line);
|
|
2873
2754
|
}
|
|
2874
2755
|
}
|
|
2875
2756
|
function truncateOneLine(value, max = 100, verboseOutput = false) {
|
|
@@ -2928,11 +2809,11 @@ function renderShellResult(result, opts) {
|
|
|
2928
2809
|
const stdoutLines = countShellLines(r.stdout);
|
|
2929
2810
|
const stderrLines = countShellLines(r.stderr);
|
|
2930
2811
|
const stdoutSingleLine = getSingleShellLine(r.stdout);
|
|
2931
|
-
let badge =
|
|
2812
|
+
let badge = c4.red("error");
|
|
2932
2813
|
if (r.timedOut)
|
|
2933
|
-
badge =
|
|
2814
|
+
badge = c4.yellow("timeout");
|
|
2934
2815
|
else if (r.success)
|
|
2935
|
-
badge =
|
|
2816
|
+
badge = c4.green("done");
|
|
2936
2817
|
const parts = buildShellSummaryParts({
|
|
2937
2818
|
exitCode: r.exitCode,
|
|
2938
2819
|
stdoutLines,
|
|
@@ -2940,11 +2821,10 @@ function renderShellResult(result, opts) {
|
|
|
2940
2821
|
stdoutSingleLine,
|
|
2941
2822
|
verboseOutput
|
|
2942
2823
|
});
|
|
2943
|
-
writeln(
|
|
2824
|
+
writeln(`${badge} ${c4.dim(parts.join(" \xB7 "))}`);
|
|
2944
2825
|
writePreviewLines({
|
|
2945
2826
|
label: "stderr",
|
|
2946
2827
|
value: displayStderr,
|
|
2947
|
-
lineColor: c5.red,
|
|
2948
2828
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 10
|
|
2949
2829
|
});
|
|
2950
2830
|
if (shouldPreviewShellStdout({
|
|
@@ -2956,7 +2836,6 @@ function renderShellResult(result, opts) {
|
|
|
2956
2836
|
writePreviewLines({
|
|
2957
2837
|
label: "stdout",
|
|
2958
2838
|
value: displayStdout,
|
|
2959
|
-
lineColor: c5.dim,
|
|
2960
2839
|
maxLines: verboseOutput ? Number.POSITIVE_INFINITY : 20
|
|
2961
2840
|
});
|
|
2962
2841
|
}
|
|
@@ -2967,21 +2846,21 @@ function buildSkillDescriptionPart(description, verboseOutput = false) {
|
|
|
2967
2846
|
if (!trimmed)
|
|
2968
2847
|
return "";
|
|
2969
2848
|
if (verboseOutput)
|
|
2970
|
-
return ` ${
|
|
2971
|
-
return ` ${
|
|
2849
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed)}`;
|
|
2850
|
+
return ` ${c4.dim("\xB7")} ${c4.dim(trimmed.length > 60 ? `${trimmed.slice(0, 57)}\u2026` : trimmed)}`;
|
|
2972
2851
|
}
|
|
2973
2852
|
function renderSkillSummaryLine(skill, opts) {
|
|
2974
2853
|
const name = skill.name ?? "(unknown)";
|
|
2975
2854
|
const source = skill.source ?? "unknown";
|
|
2976
|
-
const labelPrefix = opts?.label ? `${
|
|
2977
|
-
writeln(
|
|
2855
|
+
const labelPrefix = opts?.label ? `${c4.dim(opts.label)} ` : "";
|
|
2856
|
+
writeln(`${G.info} ${labelPrefix}${name} ${c4.dim("\xB7")} ${c4.dim(source)}${buildSkillDescriptionPart(skill.description, opts?.verboseOutput === true)}`);
|
|
2978
2857
|
}
|
|
2979
2858
|
function renderListSkillsResult(result, opts) {
|
|
2980
2859
|
const r = result;
|
|
2981
2860
|
if (!Array.isArray(r?.skills))
|
|
2982
2861
|
return false;
|
|
2983
2862
|
if (r.skills.length === 0) {
|
|
2984
|
-
writeln(
|
|
2863
|
+
writeln(`${G.info} ${c4.dim("no skills")}`);
|
|
2985
2864
|
return true;
|
|
2986
2865
|
}
|
|
2987
2866
|
const maxSkills = opts?.verboseOutput ? r.skills.length : 6;
|
|
@@ -2991,7 +2870,7 @@ function renderListSkillsResult(result, opts) {
|
|
|
2991
2870
|
});
|
|
2992
2871
|
}
|
|
2993
2872
|
if (r.skills.length > maxSkills) {
|
|
2994
|
-
writeln(
|
|
2873
|
+
writeln(`${c4.dim(`+${r.skills.length - maxSkills} more skills`)}`);
|
|
2995
2874
|
}
|
|
2996
2875
|
return true;
|
|
2997
2876
|
}
|
|
@@ -3000,13 +2879,20 @@ function renderReadSkillResult(result, _opts) {
|
|
|
3000
2879
|
if (!r || typeof r !== "object")
|
|
3001
2880
|
return false;
|
|
3002
2881
|
if (!r.skill) {
|
|
3003
|
-
|
|
2882
|
+
if (typeof r.note === "string" && r.note.trim().length > 0) {
|
|
2883
|
+
writeln(`${G.info} ${c4.dim("skill")} ${c4.dim(r.note.trim())}`);
|
|
2884
|
+
return true;
|
|
2885
|
+
}
|
|
2886
|
+
writeln(`${G.info} ${c4.dim("skill")} ${c4.dim("(not found)")}`);
|
|
3004
2887
|
return true;
|
|
3005
2888
|
}
|
|
3006
2889
|
renderSkillSummaryLine(r.skill, {
|
|
3007
2890
|
label: "skill",
|
|
3008
2891
|
verboseOutput: _opts?.verboseOutput === true
|
|
3009
2892
|
});
|
|
2893
|
+
if (typeof r.note === "string" && r.note.trim().length > 0) {
|
|
2894
|
+
writeln(c4.dim(r.note.trim()));
|
|
2895
|
+
}
|
|
3010
2896
|
return true;
|
|
3011
2897
|
}
|
|
3012
2898
|
function renderWebSearchResult(result, opts) {
|
|
@@ -3014,19 +2900,19 @@ function renderWebSearchResult(result, opts) {
|
|
|
3014
2900
|
if (!Array.isArray(r?.results))
|
|
3015
2901
|
return false;
|
|
3016
2902
|
if (r.results.length === 0) {
|
|
3017
|
-
writeln(
|
|
2903
|
+
writeln(`${G.info} ${c4.dim("no results")}`);
|
|
3018
2904
|
return true;
|
|
3019
2905
|
}
|
|
3020
2906
|
const maxResults = opts?.verboseOutput ? r.results.length : 5;
|
|
3021
2907
|
for (const item of r.results.slice(0, maxResults)) {
|
|
3022
2908
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
3023
|
-
const score = typeof item.score === "number" ?
|
|
3024
|
-
writeln(
|
|
2909
|
+
const score = typeof item.score === "number" ? c4.dim(` (${item.score.toFixed(2)})`) : "";
|
|
2910
|
+
writeln(`${c4.dim("\u2022")} ${title}${score}`);
|
|
3025
2911
|
if (item.url)
|
|
3026
|
-
writeln(`
|
|
2912
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
3027
2913
|
}
|
|
3028
2914
|
if (r.results.length > maxResults) {
|
|
3029
|
-
writeln(
|
|
2915
|
+
writeln(`${c4.dim(`+${r.results.length - maxResults} more`)}`);
|
|
3030
2916
|
}
|
|
3031
2917
|
return true;
|
|
3032
2918
|
}
|
|
@@ -3035,23 +2921,23 @@ function renderWebContentResult(result, opts) {
|
|
|
3035
2921
|
if (!Array.isArray(r?.results))
|
|
3036
2922
|
return false;
|
|
3037
2923
|
if (r.results.length === 0) {
|
|
3038
|
-
writeln(
|
|
2924
|
+
writeln(`${G.info} ${c4.dim("no pages")}`);
|
|
3039
2925
|
return true;
|
|
3040
2926
|
}
|
|
3041
2927
|
const maxPages = opts?.verboseOutput ? r.results.length : 3;
|
|
3042
2928
|
for (const item of r.results.slice(0, maxPages)) {
|
|
3043
2929
|
const title = (item.title?.trim() || item.url || "(untitled)").replace(/\s+/g, " ");
|
|
3044
|
-
writeln(
|
|
2930
|
+
writeln(`${c4.dim("\u2022")} ${title}`);
|
|
3045
2931
|
if (item.url)
|
|
3046
|
-
writeln(`
|
|
2932
|
+
writeln(` ${c4.dim(item.url)}`);
|
|
3047
2933
|
const preview = (item.text ?? "").replace(/\s+/g, " ").trim();
|
|
3048
2934
|
if (preview) {
|
|
3049
2935
|
const trimmed = opts?.verboseOutput || preview.length <= 220 ? preview : `${preview.slice(0, 217)}\u2026`;
|
|
3050
|
-
writeln(`
|
|
2936
|
+
writeln(` ${c4.dim(trimmed)}`);
|
|
3051
2937
|
}
|
|
3052
2938
|
}
|
|
3053
2939
|
if (r.results.length > maxPages) {
|
|
3054
|
-
writeln(
|
|
2940
|
+
writeln(`${c4.dim(`+${r.results.length - maxPages} more`)}`);
|
|
3055
2941
|
}
|
|
3056
2942
|
return true;
|
|
3057
2943
|
}
|
|
@@ -3061,11 +2947,11 @@ function renderMcpResult(result, opts) {
|
|
|
3061
2947
|
let rendered = false;
|
|
3062
2948
|
for (const block of content.slice(0, maxBlocks)) {
|
|
3063
2949
|
if (block?.type === "text" && block.text) {
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
2950
|
+
writePreviewLines({
|
|
2951
|
+
label: "",
|
|
2952
|
+
value: block.text,
|
|
2953
|
+
maxLines: opts?.verboseOutput ? Number.POSITIVE_INFINITY : 6
|
|
2954
|
+
});
|
|
3069
2955
|
rendered = true;
|
|
3070
2956
|
}
|
|
3071
2957
|
}
|
|
@@ -3153,43 +3039,58 @@ function formatShellCallLine(cmd) {
|
|
|
3153
3039
|
const { cwd, rest } = parseShellCdPrefix(cmd);
|
|
3154
3040
|
const firstCmd = extractFirstCommand(rest);
|
|
3155
3041
|
const glyph = shellCmdGlyph(firstCmd, rest);
|
|
3156
|
-
const cwdSuffix = cwd ? ` ${
|
|
3157
|
-
|
|
3158
|
-
return `${glyph} ${display}${cwdSuffix}`;
|
|
3042
|
+
const cwdSuffix = cwd ? ` ${c5.dim(`in ${cwd}`)}` : "";
|
|
3043
|
+
return `${glyph} ${rest}${cwdSuffix}`;
|
|
3159
3044
|
}
|
|
3160
3045
|
function buildToolCallLine(name, args) {
|
|
3161
3046
|
const a = args && typeof args === "object" ? args : {};
|
|
3162
3047
|
if (name === "shell") {
|
|
3163
3048
|
const cmd = String(a.command ?? "").trim();
|
|
3164
3049
|
if (!cmd)
|
|
3165
|
-
return `${G.run} ${
|
|
3050
|
+
return `${G.run} ${c5.dim("shell")}`;
|
|
3166
3051
|
return formatShellCallLine(cmd);
|
|
3167
3052
|
}
|
|
3168
3053
|
if (name === "listSkills") {
|
|
3169
|
-
return `${G.search} ${
|
|
3054
|
+
return `${G.search} ${c5.dim("list skills")}`;
|
|
3170
3055
|
}
|
|
3171
3056
|
if (name === "readSkill") {
|
|
3172
3057
|
const skillName = typeof a.name === "string" ? a.name : "";
|
|
3173
|
-
return `${G.read} ${
|
|
3058
|
+
return `${G.read} ${c5.dim("read skill")}${skillName ? ` ${skillName}` : ""}`;
|
|
3174
3059
|
}
|
|
3175
3060
|
if (name === "webSearch") {
|
|
3176
3061
|
const query = typeof a.query === "string" ? a.query : "";
|
|
3177
|
-
|
|
3178
|
-
return `${G.search} ${c6.dim("search")}${short ? ` ${short}` : ""}`;
|
|
3062
|
+
return `${G.search} ${c5.dim("search")}${query ? ` ${query}` : ""}`;
|
|
3179
3063
|
}
|
|
3180
3064
|
if (name === "webContent") {
|
|
3181
3065
|
const urls = Array.isArray(a.urls) ? a.urls : [];
|
|
3182
3066
|
const label = urls.length === 1 ? String(urls[0]) : `${urls.length} url${urls.length !== 1 ? "s" : ""}`;
|
|
3183
|
-
|
|
3184
|
-
return `${G.read} ${c6.dim("fetch")} ${short}`;
|
|
3067
|
+
return `${G.read} ${c5.dim("fetch")} ${label}`;
|
|
3185
3068
|
}
|
|
3186
3069
|
if (name.startsWith("mcp_")) {
|
|
3187
|
-
return `${G.mcp} ${
|
|
3070
|
+
return `${G.mcp} ${c5.dim(name)}`;
|
|
3188
3071
|
}
|
|
3189
|
-
return `${toolGlyph(name)} ${
|
|
3072
|
+
return `${toolGlyph(name)} ${c5.dim(name)}`;
|
|
3190
3073
|
}
|
|
3191
|
-
function renderToolCall(toolName, args) {
|
|
3192
|
-
|
|
3074
|
+
function renderToolCall(toolName, args, opts) {
|
|
3075
|
+
const line = buildToolCallLine(toolName, args);
|
|
3076
|
+
if (opts?.verboseOutput) {
|
|
3077
|
+
writeln(line);
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
const lines = line.split(`
|
|
3081
|
+
`);
|
|
3082
|
+
if (lines.length <= 6) {
|
|
3083
|
+
writeln(line);
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
const head = lines.slice(0, 3);
|
|
3087
|
+
const tail = lines.slice(-2);
|
|
3088
|
+
const hidden = lines.length - head.length - tail.length;
|
|
3089
|
+
for (const l of head)
|
|
3090
|
+
writeln(l);
|
|
3091
|
+
writeln(c5.dim(`\u2026 +${hidden} lines`));
|
|
3092
|
+
for (const l of tail)
|
|
3093
|
+
writeln(l);
|
|
3193
3094
|
}
|
|
3194
3095
|
function formatErrorBadge(result) {
|
|
3195
3096
|
let msg;
|
|
@@ -3201,11 +3102,11 @@ function formatErrorBadge(result) {
|
|
|
3201
3102
|
msg = JSON.stringify(result);
|
|
3202
3103
|
const oneLiner = msg.split(`
|
|
3203
3104
|
`)[0] ?? msg;
|
|
3204
|
-
return `${G.err} ${
|
|
3105
|
+
return `${G.err} ${c5.red(oneLiner)}`;
|
|
3205
3106
|
}
|
|
3206
3107
|
function renderToolResult(toolName, result, isError, opts) {
|
|
3207
3108
|
if (isError) {
|
|
3208
|
-
writeln(
|
|
3109
|
+
writeln(formatErrorBadge(result));
|
|
3209
3110
|
return;
|
|
3210
3111
|
}
|
|
3211
3112
|
if (renderToolResultByName(toolName, result, opts)) {
|
|
@@ -3213,32 +3114,130 @@ function renderToolResult(toolName, result, isError, opts) {
|
|
|
3213
3114
|
}
|
|
3214
3115
|
const text = JSON.stringify(result);
|
|
3215
3116
|
if (opts?.verboseOutput || text.length <= 120) {
|
|
3216
|
-
writeln(
|
|
3117
|
+
writeln(c5.dim(text));
|
|
3217
3118
|
return;
|
|
3218
3119
|
}
|
|
3219
|
-
writeln(
|
|
3120
|
+
writeln(c5.dim(`${text.slice(0, 117)}\u2026`));
|
|
3220
3121
|
}
|
|
3221
3122
|
|
|
3222
3123
|
// src/cli/stream-render.ts
|
|
3124
|
+
function styleReasoning(text) {
|
|
3125
|
+
return c6.italic(c6.dim(text));
|
|
3126
|
+
}
|
|
3127
|
+
function writeReasoningDelta(delta, state) {
|
|
3128
|
+
if (!delta)
|
|
3129
|
+
return;
|
|
3130
|
+
if (!state.blockOpen) {
|
|
3131
|
+
writeln(`${G.info} ${c6.dim("reasoning")}`);
|
|
3132
|
+
state.blockOpen = true;
|
|
3133
|
+
}
|
|
3134
|
+
const lines = delta.split(`
|
|
3135
|
+
`);
|
|
3136
|
+
for (const [i, line] of lines.entries()) {
|
|
3137
|
+
if (line) {
|
|
3138
|
+
if (!state.lineOpen) {
|
|
3139
|
+
write(" ");
|
|
3140
|
+
state.lineOpen = true;
|
|
3141
|
+
}
|
|
3142
|
+
write(styleReasoning(line));
|
|
3143
|
+
}
|
|
3144
|
+
if (i < lines.length - 1) {
|
|
3145
|
+
if (!state.lineOpen)
|
|
3146
|
+
write(" ");
|
|
3147
|
+
writeln();
|
|
3148
|
+
state.lineOpen = false;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
function finishReasoning(state) {
|
|
3153
|
+
if (!state.blockOpen)
|
|
3154
|
+
return;
|
|
3155
|
+
if (state.lineOpen)
|
|
3156
|
+
writeln();
|
|
3157
|
+
state.blockOpen = false;
|
|
3158
|
+
state.lineOpen = false;
|
|
3159
|
+
}
|
|
3160
|
+
function appendTextDelta(delta, state, spinner, quiet, renderedVisibleOutput) {
|
|
3161
|
+
let chunk = delta ?? "";
|
|
3162
|
+
if (!chunk)
|
|
3163
|
+
return state.inText;
|
|
3164
|
+
if (!state.inText) {
|
|
3165
|
+
chunk = chunk.trimStart();
|
|
3166
|
+
if (!chunk)
|
|
3167
|
+
return false;
|
|
3168
|
+
if (!quiet) {
|
|
3169
|
+
spinner.stop();
|
|
3170
|
+
if (renderedVisibleOutput)
|
|
3171
|
+
writeln();
|
|
3172
|
+
write(`${G.reply} `);
|
|
3173
|
+
}
|
|
3174
|
+
state.inText = true;
|
|
3175
|
+
if (!quiet && terminal.isStdoutTTY) {
|
|
3176
|
+
state.highlighter = createHighlighter();
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
const isFirstLine = !state.text.includes(`
|
|
3180
|
+
`);
|
|
3181
|
+
state.text += chunk;
|
|
3182
|
+
if (quiet)
|
|
3183
|
+
return state.inText;
|
|
3184
|
+
spinner.stop();
|
|
3185
|
+
if (state.highlighter) {
|
|
3186
|
+
let colored = state.highlighter.write(chunk);
|
|
3187
|
+
if (colored) {
|
|
3188
|
+
if (isFirstLine && colored.startsWith("\x1B[2K\r")) {
|
|
3189
|
+
colored = `\x1B[2K\r${G.reply} ${colored.slice(5)}`;
|
|
3190
|
+
}
|
|
3191
|
+
write(colored);
|
|
3192
|
+
}
|
|
3193
|
+
} else {
|
|
3194
|
+
write(chunk);
|
|
3195
|
+
}
|
|
3196
|
+
return state.inText;
|
|
3197
|
+
}
|
|
3198
|
+
function flushText(state, quiet) {
|
|
3199
|
+
if (!state.inText)
|
|
3200
|
+
return;
|
|
3201
|
+
if (!quiet) {
|
|
3202
|
+
if (state.highlighter) {
|
|
3203
|
+
let finalColored = state.highlighter.end();
|
|
3204
|
+
if (finalColored) {
|
|
3205
|
+
const isFirstLine = !state.text.includes(`
|
|
3206
|
+
`);
|
|
3207
|
+
if (isFirstLine && finalColored.startsWith("\x1B[2K\r")) {
|
|
3208
|
+
finalColored = `\x1B[2K\r${G.reply} ${finalColored.slice(5)}`;
|
|
3209
|
+
}
|
|
3210
|
+
write(finalColored);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
writeln();
|
|
3214
|
+
}
|
|
3215
|
+
state.inText = false;
|
|
3216
|
+
}
|
|
3223
3217
|
async function renderTurn(events, spinner, opts) {
|
|
3224
3218
|
const quiet = opts?.quiet ?? false;
|
|
3225
3219
|
const showReasoning = !quiet && (opts?.showReasoning ?? true);
|
|
3226
3220
|
const verboseOutput = opts?.verboseOutput ?? false;
|
|
3227
|
-
const
|
|
3228
|
-
|
|
3221
|
+
const textState = {
|
|
3222
|
+
inText: false,
|
|
3223
|
+
text: "",
|
|
3224
|
+
highlighter: undefined
|
|
3225
|
+
};
|
|
3226
|
+
let reasoningRaw = "";
|
|
3227
|
+
let reasoningComputed = false;
|
|
3228
|
+
let reasoningText = "";
|
|
3229
|
+
const reasoningState = { blockOpen: false, lineOpen: false };
|
|
3230
|
+
const startedToolCalls = new Set;
|
|
3231
|
+
const toolCallInfo = new Map;
|
|
3232
|
+
let parallelCallCount = 0;
|
|
3229
3233
|
let inputTokens = 0;
|
|
3230
3234
|
let outputTokens = 0;
|
|
3231
3235
|
let contextTokens = 0;
|
|
3232
3236
|
let newMessages = [];
|
|
3233
|
-
const startedToolCalls = new Set;
|
|
3234
|
-
const toolCallInfo = new Map;
|
|
3235
|
-
let parallelCallCount = 0;
|
|
3236
3237
|
let renderedVisibleOutput = false;
|
|
3237
|
-
let reasoningComputed = false;
|
|
3238
|
-
let reasoningText = "";
|
|
3239
3238
|
const getReasoningText = () => {
|
|
3240
3239
|
if (!reasoningComputed) {
|
|
3241
|
-
reasoningText = normalizeReasoningText(
|
|
3240
|
+
reasoningText = normalizeReasoningText(reasoningRaw);
|
|
3242
3241
|
reasoningComputed = true;
|
|
3243
3242
|
}
|
|
3244
3243
|
return reasoningText;
|
|
@@ -3246,29 +3245,42 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3246
3245
|
for await (const event of events) {
|
|
3247
3246
|
switch (event.type) {
|
|
3248
3247
|
case "text-delta": {
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
if (
|
|
3248
|
+
finishReasoning(reasoningState);
|
|
3249
|
+
textState.inText = appendTextDelta(event.delta, textState, spinner, quiet, renderedVisibleOutput);
|
|
3250
|
+
if (textState.inText)
|
|
3252
3251
|
renderedVisibleOutput = true;
|
|
3253
3252
|
break;
|
|
3254
3253
|
}
|
|
3255
3254
|
case "reasoning-delta": {
|
|
3256
|
-
|
|
3257
|
-
|
|
3255
|
+
flushText(textState, quiet);
|
|
3256
|
+
let appended = normalizeReasoningDelta(event.delta);
|
|
3257
|
+
if (reasoningRaw.endsWith("**") && appended.startsWith("**") && !reasoningRaw.endsWith(`
|
|
3258
|
+
`)) {
|
|
3259
|
+
appended = `
|
|
3260
|
+
${appended}`;
|
|
3261
|
+
}
|
|
3262
|
+
reasoningRaw += appended;
|
|
3258
3263
|
reasoningComputed = false;
|
|
3259
|
-
if (showReasoning &&
|
|
3264
|
+
if (showReasoning && appended) {
|
|
3260
3265
|
spinner.stop();
|
|
3261
|
-
if (renderedVisibleOutput && !
|
|
3266
|
+
if (renderedVisibleOutput && !reasoningState.blockOpen)
|
|
3262
3267
|
writeln();
|
|
3263
|
-
|
|
3268
|
+
writeReasoningDelta(appended, reasoningState);
|
|
3264
3269
|
renderedVisibleOutput = true;
|
|
3265
3270
|
}
|
|
3266
3271
|
break;
|
|
3267
3272
|
}
|
|
3273
|
+
case "tool-input-delta": {
|
|
3274
|
+
if (quiet)
|
|
3275
|
+
break;
|
|
3276
|
+
finishReasoning(reasoningState);
|
|
3277
|
+
flushText(textState, quiet);
|
|
3278
|
+
spinner.start(`composing ${event.toolName}\u2026`);
|
|
3279
|
+
break;
|
|
3280
|
+
}
|
|
3268
3281
|
case "tool-call-start": {
|
|
3269
|
-
if (startedToolCalls.has(event.toolCallId))
|
|
3282
|
+
if (startedToolCalls.has(event.toolCallId))
|
|
3270
3283
|
break;
|
|
3271
|
-
}
|
|
3272
3284
|
const isConsecutiveToolCall = startedToolCalls.size > 0 && toolCallInfo.size > 0;
|
|
3273
3285
|
startedToolCalls.add(event.toolCallId);
|
|
3274
3286
|
toolCallInfo.set(event.toolCallId, {
|
|
@@ -3278,13 +3290,14 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3278
3290
|
if (toolCallInfo.size > 1) {
|
|
3279
3291
|
parallelCallCount = toolCallInfo.size;
|
|
3280
3292
|
}
|
|
3281
|
-
|
|
3282
|
-
|
|
3293
|
+
finishReasoning(reasoningState);
|
|
3294
|
+
flushText(textState, quiet);
|
|
3283
3295
|
if (!quiet) {
|
|
3284
3296
|
spinner.stop();
|
|
3285
|
-
if (renderedVisibleOutput && !isConsecutiveToolCall)
|
|
3297
|
+
if (renderedVisibleOutput && !isConsecutiveToolCall) {
|
|
3286
3298
|
writeln();
|
|
3287
|
-
|
|
3299
|
+
}
|
|
3300
|
+
renderToolCall(event.toolName, event.args, { verboseOutput });
|
|
3288
3301
|
renderedVisibleOutput = true;
|
|
3289
3302
|
spinner.start(event.toolName);
|
|
3290
3303
|
}
|
|
@@ -3294,11 +3307,11 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3294
3307
|
startedToolCalls.delete(event.toolCallId);
|
|
3295
3308
|
const callInfo = toolCallInfo.get(event.toolCallId);
|
|
3296
3309
|
toolCallInfo.delete(event.toolCallId);
|
|
3297
|
-
|
|
3310
|
+
finishReasoning(reasoningState);
|
|
3298
3311
|
if (!quiet) {
|
|
3299
3312
|
spinner.stop();
|
|
3300
3313
|
if (parallelCallCount > 1 && callInfo) {
|
|
3301
|
-
writeln(
|
|
3314
|
+
writeln(`${c6.dim("\u21B3")} ${callInfo.label}`);
|
|
3302
3315
|
}
|
|
3303
3316
|
if (toolCallInfo.size === 0)
|
|
3304
3317
|
parallelCallCount = 0;
|
|
@@ -3314,31 +3327,20 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3314
3327
|
break;
|
|
3315
3328
|
}
|
|
3316
3329
|
case "context-pruned": {
|
|
3317
|
-
|
|
3318
|
-
|
|
3330
|
+
finishReasoning(reasoningState);
|
|
3331
|
+
flushText(textState, quiet);
|
|
3319
3332
|
if (!quiet) {
|
|
3320
3333
|
spinner.stop();
|
|
3321
3334
|
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)}`);
|
|
3335
|
+
writeln(`${G.info} ${c6.dim("context pruned")} ${c6.dim(`\u2013${event.removedMessageCount} messages`)} ${c6.dim(`\u2013${removedKb} KB`)}`);
|
|
3335
3336
|
renderedVisibleOutput = true;
|
|
3337
|
+
spinner.start("thinking");
|
|
3336
3338
|
}
|
|
3337
3339
|
break;
|
|
3338
3340
|
}
|
|
3339
3341
|
case "turn-complete": {
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
+
finishReasoning(reasoningState);
|
|
3343
|
+
flushText(textState, quiet);
|
|
3342
3344
|
spinner.stop();
|
|
3343
3345
|
if (!quiet && !renderedVisibleOutput)
|
|
3344
3346
|
writeln();
|
|
@@ -3349,14 +3351,14 @@ async function renderTurn(events, spinner, opts) {
|
|
|
3349
3351
|
break;
|
|
3350
3352
|
}
|
|
3351
3353
|
case "turn-error": {
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
+
finishReasoning(reasoningState);
|
|
3355
|
+
flushText(textState, quiet);
|
|
3354
3356
|
spinner.stop();
|
|
3355
3357
|
inputTokens = event.inputTokens;
|
|
3356
3358
|
outputTokens = event.outputTokens;
|
|
3357
3359
|
contextTokens = event.contextTokens;
|
|
3358
3360
|
if (isAbortError(event.error)) {
|
|
3359
|
-
newMessages = buildAbortMessages(event.partialMessages,
|
|
3361
|
+
newMessages = buildAbortMessages(event.partialMessages, textState.text);
|
|
3360
3362
|
} else {
|
|
3361
3363
|
renderError(event.error, "turn");
|
|
3362
3364
|
throw new RenderedError(event.error);
|
|
@@ -3405,17 +3407,17 @@ function renderUserMessage(text) {
|
|
|
3405
3407
|
}
|
|
3406
3408
|
}
|
|
3407
3409
|
var G = {
|
|
3408
|
-
prompt:
|
|
3409
|
-
reply:
|
|
3410
|
-
search:
|
|
3411
|
-
read:
|
|
3412
|
-
write:
|
|
3413
|
-
run:
|
|
3414
|
-
mcp:
|
|
3415
|
-
ok:
|
|
3416
|
-
err:
|
|
3417
|
-
warn:
|
|
3418
|
-
info:
|
|
3410
|
+
prompt: c7.green("\u203A"),
|
|
3411
|
+
reply: c7.cyan("\u25C6"),
|
|
3412
|
+
search: c7.yellow("?"),
|
|
3413
|
+
read: c7.dim("\u2190"),
|
|
3414
|
+
write: c7.green("\u270E"),
|
|
3415
|
+
run: c7.dim("$"),
|
|
3416
|
+
mcp: c7.yellow("\u2699"),
|
|
3417
|
+
ok: c7.green("\u2714"),
|
|
3418
|
+
err: c7.red("\u2716"),
|
|
3419
|
+
warn: c7.yellow("!"),
|
|
3420
|
+
info: c7.dim("\xB7")
|
|
3419
3421
|
};
|
|
3420
3422
|
var PREFIX = {
|
|
3421
3423
|
user: G.prompt,
|
|
@@ -3437,17 +3439,17 @@ class RenderedError extends Error {
|
|
|
3437
3439
|
function renderError(err, context = "render") {
|
|
3438
3440
|
logError(err, context);
|
|
3439
3441
|
const parsed = parseAppError(err);
|
|
3440
|
-
writeln(`${G.err} ${
|
|
3442
|
+
writeln(`${G.err} ${c7.red(parsed.headline)}`);
|
|
3441
3443
|
if (parsed.hint) {
|
|
3442
|
-
writeln(` ${
|
|
3444
|
+
writeln(` ${c7.dim(parsed.hint)}`);
|
|
3443
3445
|
}
|
|
3444
3446
|
}
|
|
3445
3447
|
function renderBanner(model, cwd) {
|
|
3446
3448
|
writeln();
|
|
3447
3449
|
const title = PACKAGE_VERSION ? `mini-coder \xB7 v${PACKAGE_VERSION}` : "mini-coder";
|
|
3448
|
-
writeln(` ${
|
|
3449
|
-
writeln(` ${
|
|
3450
|
-
writeln(` ${
|
|
3450
|
+
writeln(` ${c7.cyan("mc")} ${c7.dim(title)}`);
|
|
3451
|
+
writeln(` ${c7.dim(model)} ${c7.dim("\xB7")} ${c7.dim(tildePath(cwd))}`);
|
|
3452
|
+
writeln(` ${c7.dim("/help for commands \xB7 esc cancel \xB7 ctrl+d exit")}`);
|
|
3451
3453
|
const items = [];
|
|
3452
3454
|
if (getPreferredShowReasoning())
|
|
3453
3455
|
items.push("reasoning: on");
|
|
@@ -3460,7 +3462,7 @@ function renderBanner(model, cwd) {
|
|
|
3460
3462
|
if (skills.size > 0)
|
|
3461
3463
|
items.push(`${skills.size} skill${skills.size > 1 ? "s" : ""}`);
|
|
3462
3464
|
if (items.length > 0) {
|
|
3463
|
-
writeln(` ${
|
|
3465
|
+
writeln(` ${c7.dim(items.join(" \xB7 "))}`);
|
|
3464
3466
|
}
|
|
3465
3467
|
const connParts = [];
|
|
3466
3468
|
for (const p of discoverConnectedProviders()) {
|
|
@@ -3470,7 +3472,7 @@ function renderBanner(model, cwd) {
|
|
|
3470
3472
|
if (mcpCount > 0)
|
|
3471
3473
|
connParts.push(`${mcpCount} mcp`);
|
|
3472
3474
|
if (connParts.length > 0) {
|
|
3473
|
-
writeln(` ${
|
|
3475
|
+
writeln(` ${c7.dim(connParts.join(" \xB7 "))}`);
|
|
3474
3476
|
}
|
|
3475
3477
|
writeln();
|
|
3476
3478
|
}
|
|
@@ -3485,7 +3487,7 @@ class CliReporter {
|
|
|
3485
3487
|
if (this.quiet)
|
|
3486
3488
|
return;
|
|
3487
3489
|
this.spinner.stop();
|
|
3488
|
-
writeln(`${G.info} ${
|
|
3490
|
+
writeln(`${G.info} ${c7.dim(msg)}`);
|
|
3489
3491
|
}
|
|
3490
3492
|
error(msg, hint) {
|
|
3491
3493
|
this.spinner.stop();
|
|
@@ -3689,10 +3691,10 @@ function setPreferredVerboseOutput(verbose) {
|
|
|
3689
3691
|
setSetting("preferred_verbose_output", verbose ? "true" : "false");
|
|
3690
3692
|
}
|
|
3691
3693
|
// src/agent/session-runner.ts
|
|
3692
|
-
import * as
|
|
3694
|
+
import * as c10 from "yoctocolors";
|
|
3693
3695
|
|
|
3694
3696
|
// src/cli/input.ts
|
|
3695
|
-
import * as
|
|
3697
|
+
import * as c8 from "yoctocolors";
|
|
3696
3698
|
|
|
3697
3699
|
// src/cli/input-buffer.ts
|
|
3698
3700
|
var PASTE_TOKEN_START = 57344;
|
|
@@ -4101,7 +4103,7 @@ function watchForCancel(abortController) {
|
|
|
4101
4103
|
process.stdin.on("data", onData);
|
|
4102
4104
|
return cleanup;
|
|
4103
4105
|
}
|
|
4104
|
-
var PROMPT =
|
|
4106
|
+
var PROMPT = c8.green("\u25B6 ");
|
|
4105
4107
|
var PROMPT_RAW_LEN = 2;
|
|
4106
4108
|
async function readline(opts) {
|
|
4107
4109
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -4150,7 +4152,7 @@ async function readline(opts) {
|
|
|
4150
4152
|
process.stdout.write(`${CLEAR_LINE}${prompt}${display}${CSI}${PROMPT_RAW_LEN + displayCursor + 1}G`);
|
|
4151
4153
|
}
|
|
4152
4154
|
function renderSearchPrompt() {
|
|
4153
|
-
process.stdout.write(`${CLEAR_LINE}${
|
|
4155
|
+
process.stdout.write(`${CLEAR_LINE}${c8.cyan("search:")} ${searchQuery}\u2588`);
|
|
4154
4156
|
}
|
|
4155
4157
|
function applyHistory() {
|
|
4156
4158
|
if (histIdx < history.length) {
|
|
@@ -4412,36 +4414,12 @@ import { streamText } from "ai";
|
|
|
4412
4414
|
// src/llm-api/turn-execution.ts
|
|
4413
4415
|
import { dynamicTool, jsonSchema } from "ai";
|
|
4414
4416
|
|
|
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
4417
|
// src/llm-api/turn-stream-events.ts
|
|
4440
|
-
function shouldLogStreamChunk(
|
|
4441
|
-
return
|
|
4418
|
+
function shouldLogStreamChunk(c9) {
|
|
4419
|
+
return c9.type !== "text-delta" && c9.type !== "reasoning" && c9.type !== "reasoning-delta" && c9.type !== "tool-input-delta";
|
|
4442
4420
|
}
|
|
4443
|
-
function extractToolArgs(
|
|
4444
|
-
return
|
|
4421
|
+
function extractToolArgs(c9) {
|
|
4422
|
+
return c9.input ?? c9.args;
|
|
4445
4423
|
}
|
|
4446
4424
|
function hasRenderableToolArgs(args) {
|
|
4447
4425
|
if (args === null || args === undefined)
|
|
@@ -4454,10 +4432,10 @@ function hasRenderableToolArgs(args) {
|
|
|
4454
4432
|
return Object.keys(args).length > 0;
|
|
4455
4433
|
return true;
|
|
4456
4434
|
}
|
|
4457
|
-
function mapStreamChunkToTurnEvent(
|
|
4458
|
-
switch (
|
|
4435
|
+
function mapStreamChunkToTurnEvent(c9) {
|
|
4436
|
+
switch (c9.type) {
|
|
4459
4437
|
case "text-delta": {
|
|
4460
|
-
const delta = typeof
|
|
4438
|
+
const delta = typeof c9.text === "string" ? c9.text : "";
|
|
4461
4439
|
return {
|
|
4462
4440
|
type: "text-delta",
|
|
4463
4441
|
delta
|
|
@@ -4465,7 +4443,7 @@ function mapStreamChunkToTurnEvent(c10) {
|
|
|
4465
4443
|
}
|
|
4466
4444
|
case "reasoning-delta":
|
|
4467
4445
|
case "reasoning": {
|
|
4468
|
-
const delta = getReasoningDeltaFromStreamChunk(
|
|
4446
|
+
const delta = getReasoningDeltaFromStreamChunk(c9);
|
|
4469
4447
|
if (delta === null)
|
|
4470
4448
|
return null;
|
|
4471
4449
|
return {
|
|
@@ -4474,49 +4452,65 @@ function mapStreamChunkToTurnEvent(c10) {
|
|
|
4474
4452
|
};
|
|
4475
4453
|
}
|
|
4476
4454
|
case "tool-input-start": {
|
|
4477
|
-
const args = extractToolArgs(
|
|
4478
|
-
const hasStableToolCallId = typeof
|
|
4455
|
+
const args = extractToolArgs(c9);
|
|
4456
|
+
const hasStableToolCallId = typeof c9.toolCallId === "string" && c9.toolCallId.trim().length > 0;
|
|
4479
4457
|
if (hasStableToolCallId && !hasRenderableToolArgs(args))
|
|
4480
4458
|
return null;
|
|
4481
4459
|
return {
|
|
4482
4460
|
type: "tool-call-start",
|
|
4483
|
-
toolCallId: String(
|
|
4484
|
-
toolName: String(
|
|
4461
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4462
|
+
toolName: String(c9.toolName ?? ""),
|
|
4485
4463
|
args
|
|
4486
4464
|
};
|
|
4487
4465
|
}
|
|
4466
|
+
case "tool-input-delta": {
|
|
4467
|
+
let delta = "";
|
|
4468
|
+
if (typeof c9.inputTextDelta === "string") {
|
|
4469
|
+
delta = c9.inputTextDelta;
|
|
4470
|
+
} else if (typeof c9.delta === "string") {
|
|
4471
|
+
delta = c9.delta;
|
|
4472
|
+
}
|
|
4473
|
+
if (!delta)
|
|
4474
|
+
return null;
|
|
4475
|
+
return {
|
|
4476
|
+
type: "tool-input-delta",
|
|
4477
|
+
toolCallId: String(c9.toolCallId ?? c9.id ?? ""),
|
|
4478
|
+
toolName: String(c9.toolName ?? ""),
|
|
4479
|
+
inputTextDelta: delta
|
|
4480
|
+
};
|
|
4481
|
+
}
|
|
4488
4482
|
case "tool-call": {
|
|
4489
4483
|
return {
|
|
4490
4484
|
type: "tool-call-start",
|
|
4491
|
-
toolCallId: String(
|
|
4492
|
-
toolName: String(
|
|
4493
|
-
args: extractToolArgs(
|
|
4485
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4486
|
+
toolName: String(c9.toolName ?? ""),
|
|
4487
|
+
args: extractToolArgs(c9)
|
|
4494
4488
|
};
|
|
4495
4489
|
}
|
|
4496
4490
|
case "tool-result": {
|
|
4497
4491
|
let result;
|
|
4498
|
-
if ("output" in
|
|
4499
|
-
result =
|
|
4500
|
-
else if ("result" in
|
|
4501
|
-
result =
|
|
4492
|
+
if ("output" in c9)
|
|
4493
|
+
result = c9.output;
|
|
4494
|
+
else if ("result" in c9)
|
|
4495
|
+
result = c9.result;
|
|
4502
4496
|
return {
|
|
4503
4497
|
type: "tool-result",
|
|
4504
|
-
toolCallId: String(
|
|
4505
|
-
toolName: String(
|
|
4498
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4499
|
+
toolName: String(c9.toolName ?? ""),
|
|
4506
4500
|
result,
|
|
4507
|
-
isError: "isError" in
|
|
4501
|
+
isError: "isError" in c9 ? Boolean(c9.isError) : false
|
|
4508
4502
|
};
|
|
4509
4503
|
}
|
|
4510
4504
|
case "tool-error":
|
|
4511
4505
|
return {
|
|
4512
4506
|
type: "tool-result",
|
|
4513
|
-
toolCallId: String(
|
|
4514
|
-
toolName: String(
|
|
4515
|
-
result:
|
|
4507
|
+
toolCallId: String(c9.toolCallId ?? ""),
|
|
4508
|
+
toolName: String(c9.toolName ?? ""),
|
|
4509
|
+
result: c9.error ?? "Tool execution failed",
|
|
4516
4510
|
isError: true
|
|
4517
4511
|
};
|
|
4518
4512
|
case "error": {
|
|
4519
|
-
throw normalizeUnknownError(
|
|
4513
|
+
throw normalizeUnknownError(c9.error);
|
|
4520
4514
|
}
|
|
4521
4515
|
default:
|
|
4522
4516
|
return null;
|
|
@@ -4532,9 +4526,9 @@ function toCoreTool(def) {
|
|
|
4532
4526
|
return dynamicTool({
|
|
4533
4527
|
description: def.description,
|
|
4534
4528
|
inputSchema: schema,
|
|
4535
|
-
execute: async (input) => {
|
|
4529
|
+
execute: async (input, { abortSignal }) => {
|
|
4536
4530
|
try {
|
|
4537
|
-
return await def.execute(input);
|
|
4531
|
+
return await def.execute(input, abortSignal ? { signal: abortSignal } : undefined);
|
|
4538
4532
|
} catch (err) {
|
|
4539
4533
|
throw normalizeUnknownError(err);
|
|
4540
4534
|
}
|
|
@@ -4685,6 +4679,7 @@ class StreamToolCallTracker {
|
|
|
4685
4679
|
syntheticCount = 0;
|
|
4686
4680
|
pendingByTool = new Map;
|
|
4687
4681
|
deferredStartsByTool = new Map;
|
|
4682
|
+
toolNameById = new Map;
|
|
4688
4683
|
prepare(chunk) {
|
|
4689
4684
|
const type = chunk.type;
|
|
4690
4685
|
if (!type) {
|
|
@@ -4693,6 +4688,8 @@ class StreamToolCallTracker {
|
|
|
4693
4688
|
if (type === "tool-input-start") {
|
|
4694
4689
|
const toolName = normalizeToolName(chunk.toolName);
|
|
4695
4690
|
const toolCallId = normalizeStringId(chunk.toolCallId);
|
|
4691
|
+
if (toolCallId && toolName)
|
|
4692
|
+
this.toolNameById.set(toolCallId, toolName);
|
|
4696
4693
|
const args = extractToolArgs(chunk);
|
|
4697
4694
|
if (!hasRenderableToolArgs(args)) {
|
|
4698
4695
|
if (!toolCallId) {
|
|
@@ -4727,6 +4724,16 @@ class StreamToolCallTracker {
|
|
|
4727
4724
|
suppressTurnEvent: false
|
|
4728
4725
|
};
|
|
4729
4726
|
}
|
|
4727
|
+
if (type === "tool-input-delta") {
|
|
4728
|
+
const id = normalizeStringId(chunk.id ?? chunk.toolCallId);
|
|
4729
|
+
const toolName = id ? this.toolNameById.get(id) : undefined;
|
|
4730
|
+
if (toolName) {
|
|
4731
|
+
return {
|
|
4732
|
+
chunk: { ...chunk, toolName, toolCallId: id },
|
|
4733
|
+
suppressTurnEvent: false
|
|
4734
|
+
};
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4730
4737
|
return { chunk, suppressTurnEvent: false };
|
|
4731
4738
|
}
|
|
4732
4739
|
trackRenderableStart(chunk, toolName, existingToolCallId) {
|
|
@@ -4788,18 +4795,6 @@ async function* mapFullStreamToTurnEvents(stream, opts) {
|
|
|
4788
4795
|
yield { type: "context-pruned", ...rec };
|
|
4789
4796
|
}
|
|
4790
4797
|
}
|
|
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
4798
|
const prepared = toolCallTracker.prepare(originalChunk);
|
|
4804
4799
|
const chunk = prepared.chunk;
|
|
4805
4800
|
const route = textPhaseTracker.route(chunk);
|
|
@@ -4892,11 +4887,34 @@ function getMessageDiagnostics(messages) {
|
|
|
4892
4887
|
}
|
|
4893
4888
|
};
|
|
4894
4889
|
}
|
|
4890
|
+
function collectPrunableToolNames(messages) {
|
|
4891
|
+
const names = new Set;
|
|
4892
|
+
for (const message of messages) {
|
|
4893
|
+
if (!Array.isArray(message.content))
|
|
4894
|
+
continue;
|
|
4895
|
+
for (const part of message.content) {
|
|
4896
|
+
if (!isRecord(part))
|
|
4897
|
+
continue;
|
|
4898
|
+
const partRecord = part;
|
|
4899
|
+
const toolName = partRecord.toolName;
|
|
4900
|
+
if (typeof toolName !== "string" || toolName.length === 0)
|
|
4901
|
+
continue;
|
|
4902
|
+
if (toolName === "readSkill")
|
|
4903
|
+
continue;
|
|
4904
|
+
names.add(toolName);
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
return [...names].sort((a, b) => a.localeCompare(b));
|
|
4908
|
+
}
|
|
4909
|
+
function buildToolCallPruning(messages, type) {
|
|
4910
|
+
const tools = collectPrunableToolNames(messages);
|
|
4911
|
+
return tools.length === 0 ? "none" : [{ type, tools }];
|
|
4912
|
+
}
|
|
4895
4913
|
function applyContextPruning(messages) {
|
|
4896
4914
|
return pruneMessages({
|
|
4897
4915
|
messages,
|
|
4898
4916
|
reasoning: "before-last-message",
|
|
4899
|
-
toolCalls: "before-last-40-messages",
|
|
4917
|
+
toolCalls: buildToolCallPruning(messages, "before-last-40-messages"),
|
|
4900
4918
|
emptyMessages: "remove"
|
|
4901
4919
|
});
|
|
4902
4920
|
}
|
|
@@ -4905,7 +4923,7 @@ function applyStepPruning(messages, initialMessageCount) {
|
|
|
4905
4923
|
return pruneMessages({
|
|
4906
4924
|
messages,
|
|
4907
4925
|
reasoning: "none",
|
|
4908
|
-
toolCalls: `before-last-${40 + newMessageCount}-messages
|
|
4926
|
+
toolCalls: buildToolCallPruning(messages, `before-last-${40 + newMessageCount}-messages`),
|
|
4909
4927
|
emptyMessages: "remove"
|
|
4910
4928
|
});
|
|
4911
4929
|
}
|
|
@@ -5086,26 +5104,10 @@ function prepareTurnMessages(input) {
|
|
|
5086
5104
|
diagnostics: getMessageDiagnostics(compacted)
|
|
5087
5105
|
});
|
|
5088
5106
|
}
|
|
5089
|
-
let finalMessages = compacted;
|
|
5090
|
-
let finalSystemPrompt = systemPrompt;
|
|
5091
|
-
if (isAnthropicModelFamily(modelString) && isAnthropicOAuth()) {
|
|
5092
|
-
const ccIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
5093
|
-
const systemMessages = [
|
|
5094
|
-
{ role: "system", content: ccIdentity }
|
|
5095
|
-
];
|
|
5096
|
-
if (finalSystemPrompt) {
|
|
5097
|
-
systemMessages.push({
|
|
5098
|
-
role: "system",
|
|
5099
|
-
content: finalSystemPrompt
|
|
5100
|
-
});
|
|
5101
|
-
finalSystemPrompt = undefined;
|
|
5102
|
-
}
|
|
5103
|
-
finalMessages = [...systemMessages, ...finalMessages];
|
|
5104
|
-
}
|
|
5105
5107
|
const wasPruned = postStats.messageCount < preStats.messageCount;
|
|
5106
5108
|
return {
|
|
5107
|
-
messages:
|
|
5108
|
-
systemPrompt
|
|
5109
|
+
messages: compacted,
|
|
5110
|
+
systemPrompt,
|
|
5109
5111
|
pruned: wasPruned,
|
|
5110
5112
|
prePruneMessageCount: preStats.messageCount,
|
|
5111
5113
|
prePruneTotalBytes: preStats.totalBytes,
|
|
@@ -5220,12 +5222,6 @@ function buildTurnProviderOptions(input) {
|
|
|
5220
5222
|
const thinkingOpts = thinkingEffort ? getThinkingProviderOptions(modelString, thinkingEffort) : null;
|
|
5221
5223
|
const reasoningSummaryRequested = isRecord(thinkingOpts) && isRecord(thinkingOpts.openai) && typeof thinkingOpts.openai.reasoningSummary === "string";
|
|
5222
5224
|
const cacheFamily = getCacheFamily(modelString);
|
|
5223
|
-
const googleOpts = isGeminiModelFamily(modelString) ? {
|
|
5224
|
-
google: {
|
|
5225
|
-
responseModalities: ["TEXT", "IMAGE"],
|
|
5226
|
-
...isRecord(thinkingOpts?.google) ? thinkingOpts.google : {}
|
|
5227
|
-
}
|
|
5228
|
-
} : {};
|
|
5229
5225
|
const providerOptions = {
|
|
5230
5226
|
...thinkingOpts ?? {},
|
|
5231
5227
|
...isOpenAIGPT(modelString) ? {
|
|
@@ -5233,8 +5229,7 @@ function buildTurnProviderOptions(input) {
|
|
|
5233
5229
|
store: false,
|
|
5234
5230
|
...isRecord(thinkingOpts?.openai) ? thinkingOpts.openai : {}
|
|
5235
5231
|
}
|
|
5236
|
-
} : {}
|
|
5237
|
-
...googleOpts
|
|
5232
|
+
} : {}
|
|
5238
5233
|
};
|
|
5239
5234
|
return {
|
|
5240
5235
|
cacheFamily,
|
|
@@ -5312,8 +5307,7 @@ async function* runTurn(options) {
|
|
|
5312
5307
|
tools,
|
|
5313
5308
|
systemPrompt,
|
|
5314
5309
|
signal,
|
|
5315
|
-
thinkingEffort
|
|
5316
|
-
cwd
|
|
5310
|
+
thinkingEffort
|
|
5317
5311
|
} = options;
|
|
5318
5312
|
const rawToolSet = buildToolSet(tools);
|
|
5319
5313
|
const toolSet = annotateToolCaching(rawToolSet, modelString);
|
|
@@ -5364,7 +5358,6 @@ async function* runTurn(options) {
|
|
|
5364
5358
|
result.response.catch(() => {});
|
|
5365
5359
|
for await (const event of mapFullStreamToTurnEvents(result.fullStream, {
|
|
5366
5360
|
stepPruneQueue,
|
|
5367
|
-
...cwd ? { cwd } : {},
|
|
5368
5361
|
onChunk: (streamChunk) => {
|
|
5369
5362
|
if (streamChunk.type === "tool-call" || streamChunk.type === "tool-result") {
|
|
5370
5363
|
logApiEvent("stream chunk", {
|
|
@@ -5407,7 +5400,7 @@ async function* runTurn(options) {
|
|
|
5407
5400
|
}
|
|
5408
5401
|
|
|
5409
5402
|
// src/session/manager.ts
|
|
5410
|
-
import * as
|
|
5403
|
+
import * as c9 from "yoctocolors";
|
|
5411
5404
|
function newSession(model, cwd) {
|
|
5412
5405
|
const id = generateSessionId();
|
|
5413
5406
|
const row = createSession({ id, cwd, model });
|
|
@@ -5442,21 +5435,21 @@ function renderSessionTable(footer) {
|
|
|
5442
5435
|
if (sessions.length === 0)
|
|
5443
5436
|
return false;
|
|
5444
5437
|
writeln(`
|
|
5445
|
-
${
|
|
5438
|
+
${c9.bold("Recent sessions:")}`);
|
|
5446
5439
|
for (const s of sessions) {
|
|
5447
5440
|
const date = new Date(s.updated_at).toLocaleString();
|
|
5448
5441
|
const cwd = tildePath(s.cwd);
|
|
5449
|
-
const title = s.title ||
|
|
5450
|
-
writeln(` ${
|
|
5442
|
+
const title = s.title || c9.dim("(untitled)");
|
|
5443
|
+
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
5444
|
}
|
|
5452
5445
|
writeln(`
|
|
5453
5446
|
${footer}`);
|
|
5454
5447
|
return true;
|
|
5455
5448
|
}
|
|
5456
5449
|
function printSessionList() {
|
|
5457
|
-
const shown = renderSessionTable(`${
|
|
5450
|
+
const shown = renderSessionTable(`${c9.dim("Use")} mc --resume <id> ${c9.dim("to continue a session.")}`);
|
|
5458
5451
|
if (!shown)
|
|
5459
|
-
writeln(
|
|
5452
|
+
writeln(c9.dim("No sessions found."));
|
|
5460
5453
|
}
|
|
5461
5454
|
function getMostRecentSession() {
|
|
5462
5455
|
const sessions = listSessions(1);
|
|
@@ -5564,6 +5557,12 @@ Guidelines:
|
|
|
5564
5557
|
- Make parallel tool calls when the lookups are independent \u2014 this speeds up multi-file investigation.
|
|
5565
5558
|
- Before starting work, scan the skills list below. If there is even a small chance a skill applies to your task, load it with \`readSkill\` and follow its instructions before writing code or responding. Skills are mandatory when they match \u2014 not optional references.
|
|
5566
5559
|
- Keep it simple: DRY, KISS, YAGNI. Avoid unnecessary complexity.
|
|
5560
|
+
- Apply Rob Pike's 5 Rules of Programming:
|
|
5561
|
+
1. You can't tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don't try to second guess and put in a speed hack until you've proven that's where the bottleneck is.
|
|
5562
|
+
2. Measure. Don't tune for speed until you've measured, and even then don't unless one part of the code overwhelms the rest.
|
|
5563
|
+
3. Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don't get fancy. (Even if n does get big, use Rule 2 first.)
|
|
5564
|
+
4. Fancy algorithms are buggier than simple ones, and they're much harder to implement. Use simple algorithms as well as simple data structures.
|
|
5565
|
+
5. Data dominates. If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
|
|
5567
5566
|
|
|
5568
5567
|
# File editing with mc-edit
|
|
5569
5568
|
\`mc-edit\` applies one exact-text replacement per invocation. It fails deterministically if the old text is missing or matches more than once.
|
|
@@ -5687,7 +5686,7 @@ class SessionRunner {
|
|
|
5687
5686
|
}
|
|
5688
5687
|
this.session = resumed;
|
|
5689
5688
|
this.currentModel = this.session.model;
|
|
5690
|
-
this.reporter.info(`Resumed session ${this.session.id} (${
|
|
5689
|
+
this.reporter.info(`Resumed session ${this.session.id} (${c10.cyan(this.currentModel)})`);
|
|
5691
5690
|
} else {
|
|
5692
5691
|
this.session = newSession(this.currentModel, this.cwd);
|
|
5693
5692
|
}
|
|
@@ -5780,8 +5779,7 @@ ${output}
|
|
|
5780
5779
|
tools: this.tools,
|
|
5781
5780
|
...systemPrompt ? { systemPrompt } : {},
|
|
5782
5781
|
signal: abortController.signal,
|
|
5783
|
-
...this.currentThinkingEffort ? { thinkingEffort: this.currentThinkingEffort } : {}
|
|
5784
|
-
cwd: this.cwd
|
|
5782
|
+
...this.currentThinkingEffort ? { thinkingEffort: this.currentThinkingEffort } : {}
|
|
5785
5783
|
});
|
|
5786
5784
|
const { inputTokens, outputTokens, contextTokens, newMessages } = await this.reporter.renderTurn(events, {
|
|
5787
5785
|
showReasoning: this.showReasoning,
|
|
@@ -5884,7 +5882,7 @@ import { z as z3 } from "zod";
|
|
|
5884
5882
|
|
|
5885
5883
|
// src/internal/file-edit/command.ts
|
|
5886
5884
|
import { existsSync as existsSync4 } from "fs";
|
|
5887
|
-
import { dirname as dirname3, extname, join as
|
|
5885
|
+
import { dirname as dirname3, extname, join as join6 } from "path";
|
|
5888
5886
|
import { fileURLToPath } from "url";
|
|
5889
5887
|
function quoteShellArg(value) {
|
|
5890
5888
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
@@ -5896,7 +5894,7 @@ function resolveSiblingFileEditScript(scriptPath) {
|
|
|
5896
5894
|
const mainDir = dirname3(scriptPath);
|
|
5897
5895
|
const mainBase = scriptPath.slice(mainDir.length + 1);
|
|
5898
5896
|
if (mainBase === `index${ext}` || mainBase === `mc${ext}`) {
|
|
5899
|
-
return
|
|
5897
|
+
return join6(mainDir, `mc-edit${ext}`);
|
|
5900
5898
|
}
|
|
5901
5899
|
return null;
|
|
5902
5900
|
}
|
|
@@ -5905,7 +5903,7 @@ function resolveModuleLocalFileEditScript(moduleUrl) {
|
|
|
5905
5903
|
const ext = extname(modulePath);
|
|
5906
5904
|
if (!ext)
|
|
5907
5905
|
return null;
|
|
5908
|
-
const helperPath =
|
|
5906
|
+
const helperPath = join6(dirname3(modulePath), "..", "..", `mc-edit${ext}`);
|
|
5909
5907
|
return existsSync4(helperPath) ? helperPath : null;
|
|
5910
5908
|
}
|
|
5911
5909
|
function resolveProcessScriptPath(mainModule, argv1) {
|
|
@@ -5937,8 +5935,8 @@ var ShellSchema = z3.object({
|
|
|
5937
5935
|
timeout: z3.number().int().min(1000).nullable().describe("Timeout in milliseconds. If omitted, the command runs until it exits."),
|
|
5938
5936
|
env: z3.record(z3.string(), z3.string()).nullable().describe("Additional environment variables to set")
|
|
5939
5937
|
});
|
|
5940
|
-
var MAX_OUTPUT_BYTES =
|
|
5941
|
-
async function runShellCommand(input) {
|
|
5938
|
+
var MAX_OUTPUT_BYTES = 24000;
|
|
5939
|
+
async function runShellCommand(input, options) {
|
|
5942
5940
|
const cwd = input.cwd ?? process.cwd();
|
|
5943
5941
|
const timeout = input.timeout ?? undefined;
|
|
5944
5942
|
const inputEnv = input.env ?? undefined;
|
|
@@ -5960,8 +5958,7 @@ ${input.command}`], {
|
|
|
5960
5958
|
stdout: "pipe",
|
|
5961
5959
|
stderr: "pipe"
|
|
5962
5960
|
});
|
|
5963
|
-
|
|
5964
|
-
timedOut = true;
|
|
5961
|
+
function killProc() {
|
|
5965
5962
|
try {
|
|
5966
5963
|
proc.kill("SIGTERM");
|
|
5967
5964
|
setTimeout(() => {
|
|
@@ -5973,7 +5970,20 @@ ${input.command}`], {
|
|
|
5973
5970
|
for (const reader of readers) {
|
|
5974
5971
|
reader.cancel().catch(() => {});
|
|
5975
5972
|
}
|
|
5973
|
+
}
|
|
5974
|
+
const timer = timeout ? setTimeout(() => {
|
|
5975
|
+
timedOut = true;
|
|
5976
|
+
killProc();
|
|
5976
5977
|
}, timeout) : undefined;
|
|
5978
|
+
const abortSignal = options?.signal;
|
|
5979
|
+
const onAbort = () => {
|
|
5980
|
+
killProc();
|
|
5981
|
+
};
|
|
5982
|
+
if (abortSignal?.aborted) {
|
|
5983
|
+
onAbort();
|
|
5984
|
+
} else {
|
|
5985
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
5986
|
+
}
|
|
5977
5987
|
async function collectStream(stream) {
|
|
5978
5988
|
const reader = stream.getReader();
|
|
5979
5989
|
readers.push(reader);
|
|
@@ -6017,6 +6027,7 @@ ${input.command}`], {
|
|
|
6017
6027
|
} finally {
|
|
6018
6028
|
if (timer)
|
|
6019
6029
|
clearTimeout(timer);
|
|
6030
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
6020
6031
|
restoreTerminal();
|
|
6021
6032
|
if (wasRaw) {
|
|
6022
6033
|
try {
|
|
@@ -6041,7 +6052,7 @@ var shellTool = {
|
|
|
6041
6052
|
name: "shell",
|
|
6042
6053
|
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
6054
|
schema: ShellSchema,
|
|
6044
|
-
execute: runShellCommand
|
|
6055
|
+
execute: (input, options) => runShellCommand(input, options)
|
|
6045
6056
|
};
|
|
6046
6057
|
|
|
6047
6058
|
// src/agent/tools.ts
|
|
@@ -6049,11 +6060,11 @@ function withCwdDefault(tool, cwd) {
|
|
|
6049
6060
|
const originalExecute = tool.execute;
|
|
6050
6061
|
return {
|
|
6051
6062
|
...tool,
|
|
6052
|
-
execute: async (input) => {
|
|
6063
|
+
execute: async (input, options) => {
|
|
6053
6064
|
const patched = typeof input === "object" && input !== null ? input : {};
|
|
6054
6065
|
if (patched.cwd === undefined)
|
|
6055
6066
|
patched.cwd = cwd;
|
|
6056
|
-
return originalExecute(patched);
|
|
6067
|
+
return originalExecute(patched, options);
|
|
6057
6068
|
}
|
|
6058
6069
|
};
|
|
6059
6070
|
}
|
|
@@ -6096,7 +6107,7 @@ async function initAgent(opts) {
|
|
|
6096
6107
|
for (const row of listMcpServers()) {
|
|
6097
6108
|
try {
|
|
6098
6109
|
await connectAndAddMcp(row.name);
|
|
6099
|
-
opts.reporter.info(`MCP: connected ${
|
|
6110
|
+
opts.reporter.info(`MCP: connected ${c11.cyan(row.name)}`);
|
|
6100
6111
|
} catch (e) {
|
|
6101
6112
|
opts.reporter.error(`MCP: failed to connect ${row.name}: ${String(e)}`);
|
|
6102
6113
|
}
|
|
@@ -6154,7 +6165,7 @@ async function initAgent(opts) {
|
|
|
6154
6165
|
}
|
|
6155
6166
|
|
|
6156
6167
|
// src/cli/args.ts
|
|
6157
|
-
import * as
|
|
6168
|
+
import * as c12 from "yoctocolors";
|
|
6158
6169
|
function parseArgs(argv) {
|
|
6159
6170
|
const args = {
|
|
6160
6171
|
model: null,
|
|
@@ -6203,11 +6214,11 @@ function parseArgs(argv) {
|
|
|
6203
6214
|
return args;
|
|
6204
6215
|
}
|
|
6205
6216
|
function printHelp() {
|
|
6206
|
-
writeln(`${
|
|
6217
|
+
writeln(`${c12.bold("mini-coder")} \u2014 a small, fast CLI coding agent
|
|
6207
6218
|
`);
|
|
6208
|
-
writeln(`${
|
|
6219
|
+
writeln(`${c12.bold("Usage:")} mc [options] [prompt]
|
|
6209
6220
|
`);
|
|
6210
|
-
writeln(`${
|
|
6221
|
+
writeln(`${c12.bold("Options:")}`);
|
|
6211
6222
|
const opts = [
|
|
6212
6223
|
["-m, --model <id>", "Model to use (e.g. zen/claude-sonnet-4-6)"],
|
|
6213
6224
|
["-c, --continue", "Continue the most recent session"],
|
|
@@ -6217,10 +6228,10 @@ function printHelp() {
|
|
|
6217
6228
|
["-h, --help", "Show this help"]
|
|
6218
6229
|
];
|
|
6219
6230
|
for (const [flag, desc] of opts) {
|
|
6220
|
-
writeln(` ${
|
|
6231
|
+
writeln(` ${c12.cyan((flag ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
|
|
6221
6232
|
}
|
|
6222
6233
|
writeln(`
|
|
6223
|
-
${
|
|
6234
|
+
${c12.bold("Provider env vars:")}`);
|
|
6224
6235
|
const envs = [
|
|
6225
6236
|
["OPENCODE_API_KEY", "OpenCode Zen (recommended)"],
|
|
6226
6237
|
["ANTHROPIC_API_KEY", "Anthropic direct"],
|
|
@@ -6231,62 +6242,19 @@ ${c13.bold("Provider env vars:")}`);
|
|
|
6231
6242
|
["OLLAMA_BASE_URL", "Ollama base URL (default: http://localhost:11434)"]
|
|
6232
6243
|
];
|
|
6233
6244
|
for (const [env, desc] of envs) {
|
|
6234
|
-
writeln(` ${
|
|
6245
|
+
writeln(` ${c12.yellow((env ?? "").padEnd(22))} ${c12.dim(desc ?? "")}`);
|
|
6235
6246
|
}
|
|
6236
6247
|
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 ${
|
|
6243
|
-
}
|
|
6244
|
-
|
|
6245
|
-
// src/cli/bootstrap.ts
|
|
6246
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
6247
|
-
import { homedir as homedir6 } from "os";
|
|
6248
|
-
import { join as join8 } from "path";
|
|
6249
|
-
import * as c14 from "yoctocolors";
|
|
6250
|
-
var REVIEW_SKILL_CONTENT = `---
|
|
6251
|
-
name: review
|
|
6252
|
-
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."
|
|
6253
|
-
context: fork
|
|
6254
|
-
---
|
|
6255
|
-
|
|
6256
|
-
Review recent changes and provide actionable feedback.
|
|
6257
|
-
|
|
6258
|
-
## Steps
|
|
6259
|
-
|
|
6260
|
-
1. Identify the changes to review \u2014 check \`git diff\`, \`git log\`, and staged files.
|
|
6261
|
-
2. Read the changed files and understand the intent behind each change.
|
|
6262
|
-
3. Evaluate each change against the criteria below.
|
|
6263
|
-
4. Output a concise summary with only the issues found. If nothing is wrong, say so.
|
|
6264
|
-
|
|
6265
|
-
## Review criteria
|
|
6266
|
-
|
|
6267
|
-
- **Correctness** \u2014 Are the changes aligned with their stated goal? Do they introduce bugs or regressions?
|
|
6268
|
-
- **Code quality** \u2014 Is there duplicate, dead, or overly complex code? Are abstractions appropriate?
|
|
6269
|
-
- **Performance** \u2014 Are there unnecessary allocations, redundant I/O, or algorithmic concerns?
|
|
6270
|
-
- **Edge cases** \u2014 Are boundary conditions and error paths handled?
|
|
6271
|
-
|
|
6272
|
-
## Guidelines
|
|
6273
|
-
|
|
6274
|
-
- Never flag style choices as bugs \u2014 don't be a zealot.
|
|
6275
|
-
- Never flag false positives \u2014 verify before raising an issue.
|
|
6276
|
-
- Keep feedback actionable: say what's wrong and suggest a fix.
|
|
6277
|
-
`;
|
|
6278
|
-
function bootstrapGlobalDefaults() {
|
|
6279
|
-
const skillDir = join8(homedir6(), ".agents", "skills", "review");
|
|
6280
|
-
const skillPath = join8(skillDir, "SKILL.md");
|
|
6281
|
-
if (!existsSync5(skillPath)) {
|
|
6282
|
-
mkdirSync2(skillDir, { recursive: true });
|
|
6283
|
-
writeFileSync(skillPath, REVIEW_SKILL_CONTENT, "utf-8");
|
|
6284
|
-
writeln(`${c14.green("\u2713")} created ${c14.dim("~/.agents/skills/review/SKILL.md")} ${c14.dim("(edit it to customise your reviews)")}`);
|
|
6285
|
-
}
|
|
6248
|
+
${c12.bold("Examples:")}`);
|
|
6249
|
+
writeln(` mc ${c12.dim("# interactive session")}`);
|
|
6250
|
+
writeln(` mc "explain this codebase" ${c12.dim("# one-shot prompt then exit")}`);
|
|
6251
|
+
writeln(` mc -c ${c12.dim("# continue last session")}`);
|
|
6252
|
+
writeln(` mc -m ollama/llama3.2 ${c12.dim("# use local Ollama model")}`);
|
|
6253
|
+
writeln(` mc -l ${c12.dim("# list sessions")}`);
|
|
6286
6254
|
}
|
|
6287
6255
|
|
|
6288
6256
|
// src/cli/file-refs.ts
|
|
6289
|
-
import { join as
|
|
6257
|
+
import { join as join7 } from "path";
|
|
6290
6258
|
async function resolveFileRefs(text, cwd) {
|
|
6291
6259
|
const atPattern = /@([\w./\-_]+)/g;
|
|
6292
6260
|
let result = text;
|
|
@@ -6296,7 +6264,7 @@ async function resolveFileRefs(text, cwd) {
|
|
|
6296
6264
|
const ref = match[1];
|
|
6297
6265
|
if (!ref)
|
|
6298
6266
|
continue;
|
|
6299
|
-
const filePath = ref.startsWith("/") ? ref :
|
|
6267
|
+
const filePath = ref.startsWith("/") ? ref : join7(cwd, ref);
|
|
6300
6268
|
if (isImageFilename(ref)) {
|
|
6301
6269
|
const attachment = await loadImageFile(filePath);
|
|
6302
6270
|
if (attachment) {
|
|
@@ -6318,25 +6286,25 @@ ${content}
|
|
|
6318
6286
|
}
|
|
6319
6287
|
|
|
6320
6288
|
// src/cli/input-loop.ts
|
|
6321
|
-
import * as
|
|
6289
|
+
import * as c19 from "yoctocolors";
|
|
6322
6290
|
|
|
6323
6291
|
// src/cli/commands.ts
|
|
6324
6292
|
import { randomBytes } from "crypto";
|
|
6325
|
-
import { unlinkSync
|
|
6293
|
+
import { unlinkSync, writeFileSync } from "fs";
|
|
6326
6294
|
import { tmpdir } from "os";
|
|
6327
|
-
import { join as
|
|
6328
|
-
import * as
|
|
6295
|
+
import { join as join8 } from "path";
|
|
6296
|
+
import * as c18 from "yoctocolors";
|
|
6329
6297
|
|
|
6330
6298
|
// src/cli/commands-help.ts
|
|
6331
|
-
import * as
|
|
6299
|
+
import * as c13 from "yoctocolors";
|
|
6332
6300
|
function renderEntries(entries) {
|
|
6333
6301
|
for (const [label, description] of entries) {
|
|
6334
|
-
writeln(` ${
|
|
6302
|
+
writeln(` ${c13.cyan(label.padEnd(28))} ${c13.dim(description)}`);
|
|
6335
6303
|
}
|
|
6336
6304
|
}
|
|
6337
6305
|
function renderHelpCommand(ctx) {
|
|
6338
6306
|
writeln();
|
|
6339
|
-
writeln(` ${
|
|
6307
|
+
writeln(` ${c13.dim("session")}`);
|
|
6340
6308
|
renderEntries([
|
|
6341
6309
|
["/session [id]", "list sessions or switch to one"],
|
|
6342
6310
|
["/new", "start a fresh session"],
|
|
@@ -6344,7 +6312,7 @@ function renderHelpCommand(ctx) {
|
|
|
6344
6312
|
["/exit", "quit"]
|
|
6345
6313
|
]);
|
|
6346
6314
|
writeln();
|
|
6347
|
-
writeln(` ${
|
|
6315
|
+
writeln(` ${c13.dim("model + context")}`);
|
|
6348
6316
|
renderEntries([
|
|
6349
6317
|
["/model [id]", "list or switch models"],
|
|
6350
6318
|
["/reasoning [on|off]", "toggle reasoning display"],
|
|
@@ -6352,12 +6320,12 @@ function renderHelpCommand(ctx) {
|
|
|
6352
6320
|
["/mcp list", "list MCP servers"],
|
|
6353
6321
|
["/mcp add <n> <t> [u]", "add an MCP server"],
|
|
6354
6322
|
["/mcp remove <name>", "remove an MCP server"],
|
|
6355
|
-
["/login [provider]", "login via OAuth (e.g.
|
|
6323
|
+
["/login [provider]", "login via OAuth (e.g. openai)"],
|
|
6356
6324
|
["/logout <provider>", "clear OAuth tokens"],
|
|
6357
6325
|
["/help", "show this help"]
|
|
6358
6326
|
]);
|
|
6359
6327
|
writeln();
|
|
6360
|
-
writeln(` ${
|
|
6328
|
+
writeln(` ${c13.dim("prompt")}`);
|
|
6361
6329
|
renderEntries([
|
|
6362
6330
|
["ask normally", "send a prompt to the current agent"],
|
|
6363
6331
|
["!cmd", "run a shell command and keep the result in context"],
|
|
@@ -6367,44 +6335,44 @@ function renderHelpCommand(ctx) {
|
|
|
6367
6335
|
const skills = loadSkillsIndex(ctx.cwd);
|
|
6368
6336
|
if (skills.size > 0) {
|
|
6369
6337
|
writeln();
|
|
6370
|
-
writeln(` ${
|
|
6338
|
+
writeln(` ${c13.dim("skills")}`);
|
|
6371
6339
|
for (const skill of skills.values()) {
|
|
6372
|
-
const source = skill.source === "local" ?
|
|
6340
|
+
const source = skill.source === "local" ? c13.dim("local") : c13.dim("global");
|
|
6373
6341
|
const desc = truncateText(skill.description, 80);
|
|
6374
|
-
writeln(` ${
|
|
6342
|
+
writeln(` ${c13.green(`/${skill.name}`.padEnd(28))} ${c13.dim(desc)} ${c13.dim("\xB7")} ${source}`);
|
|
6375
6343
|
}
|
|
6376
6344
|
}
|
|
6377
6345
|
writeln();
|
|
6378
|
-
writeln(` ${
|
|
6346
|
+
writeln(` ${c13.dim("keys")} ${c13.dim("esc")} cancel response ${c13.dim("\xB7")} ${c13.dim("ctrl+c / ctrl+d")} exit ${c13.dim("\xB7")} ${c13.dim("ctrl+r")} history search ${c13.dim("\xB7")} ${c13.dim("\u2191\u2193")} history`);
|
|
6379
6347
|
writeln();
|
|
6380
6348
|
}
|
|
6381
6349
|
|
|
6382
6350
|
// src/cli/commands-login.ts
|
|
6383
|
-
import * as
|
|
6351
|
+
import * as c14 from "yoctocolors";
|
|
6384
6352
|
function renderLoginHelp() {
|
|
6385
6353
|
writeln();
|
|
6386
|
-
writeln(` ${
|
|
6387
|
-
writeln(` /login ${
|
|
6388
|
-
writeln(` /login <provider> ${
|
|
6389
|
-
writeln(` /logout <provider> ${
|
|
6354
|
+
writeln(` ${c14.dim("usage:")}`);
|
|
6355
|
+
writeln(` /login ${c14.dim("show login status")}`);
|
|
6356
|
+
writeln(` /login <provider> ${c14.dim("login via OAuth")}`);
|
|
6357
|
+
writeln(` /logout <provider> ${c14.dim("clear saved tokens")}`);
|
|
6390
6358
|
writeln();
|
|
6391
|
-
writeln(` ${
|
|
6359
|
+
writeln(` ${c14.dim("providers:")}`);
|
|
6392
6360
|
for (const p of getOAuthProviders()) {
|
|
6393
|
-
const status = isLoggedIn(p.id) ?
|
|
6394
|
-
writeln(` ${
|
|
6361
|
+
const status = isLoggedIn(p.id) ? c14.green("logged in") : c14.dim("not logged in");
|
|
6362
|
+
writeln(` ${c14.cyan(p.id.padEnd(20))} ${p.name} ${c14.dim("\xB7")} ${status}`);
|
|
6395
6363
|
}
|
|
6396
6364
|
writeln();
|
|
6397
6365
|
}
|
|
6398
6366
|
function renderStatus() {
|
|
6399
6367
|
const loggedIn = listLoggedInProviders();
|
|
6400
6368
|
if (loggedIn.length === 0) {
|
|
6401
|
-
writeln(`${PREFIX.info} ${
|
|
6369
|
+
writeln(`${PREFIX.info} ${c14.dim("no OAuth logins \u2014 use")} /login <provider>`);
|
|
6402
6370
|
return;
|
|
6403
6371
|
}
|
|
6404
6372
|
for (const id of loggedIn) {
|
|
6405
6373
|
const provider = getOAuthProvider(id);
|
|
6406
6374
|
const name = provider?.name ?? id;
|
|
6407
|
-
writeln(`${PREFIX.success} ${
|
|
6375
|
+
writeln(`${PREFIX.success} ${c14.cyan(id)} ${c14.dim(name)}`);
|
|
6408
6376
|
}
|
|
6409
6377
|
}
|
|
6410
6378
|
async function handleLoginCommand(ctx, args) {
|
|
@@ -6425,7 +6393,7 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6425
6393
|
if (isLoggedIn(providerId)) {
|
|
6426
6394
|
const token = await getAccessToken(providerId);
|
|
6427
6395
|
if (token) {
|
|
6428
|
-
writeln(`${PREFIX.success} already logged in to ${
|
|
6396
|
+
writeln(`${PREFIX.success} already logged in to ${c14.cyan(provider.name)}`);
|
|
6429
6397
|
return;
|
|
6430
6398
|
}
|
|
6431
6399
|
}
|
|
@@ -6436,7 +6404,7 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6436
6404
|
ctx.stopSpinner();
|
|
6437
6405
|
writeln(`${PREFIX.info} ${instructions}`);
|
|
6438
6406
|
writeln();
|
|
6439
|
-
writeln(` ${
|
|
6407
|
+
writeln(` ${c14.cyan(url)}`);
|
|
6440
6408
|
writeln();
|
|
6441
6409
|
let open = "xdg-open";
|
|
6442
6410
|
if (process.platform === "darwin")
|
|
@@ -6448,12 +6416,12 @@ async function handleLoginCommand(ctx, args) {
|
|
|
6448
6416
|
},
|
|
6449
6417
|
onProgress: (msg) => {
|
|
6450
6418
|
ctx.stopSpinner();
|
|
6451
|
-
writeln(`${PREFIX.info} ${
|
|
6419
|
+
writeln(`${PREFIX.info} ${c14.dim(msg)}`);
|
|
6452
6420
|
ctx.startSpinner("exchanging tokens");
|
|
6453
6421
|
}
|
|
6454
6422
|
});
|
|
6455
6423
|
ctx.stopSpinner();
|
|
6456
|
-
writeln(`${PREFIX.success} logged in to ${
|
|
6424
|
+
writeln(`${PREFIX.success} logged in to ${c14.cyan(provider.name)}`);
|
|
6457
6425
|
} catch (err) {
|
|
6458
6426
|
ctx.stopSpinner();
|
|
6459
6427
|
writeln(`${PREFIX.error} login failed: ${err.message}`);
|
|
@@ -6465,16 +6433,21 @@ function handleLogoutCommand(_ctx, args) {
|
|
|
6465
6433
|
writeln(`${PREFIX.error} usage: /logout <provider>`);
|
|
6466
6434
|
return;
|
|
6467
6435
|
}
|
|
6436
|
+
const provider = getOAuthProvider(providerId);
|
|
6437
|
+
if (!provider) {
|
|
6438
|
+
writeln(`${PREFIX.error} unknown provider "${providerId}" \u2014 available: ${getOAuthProviders().map((p) => p.id).join(", ")}`);
|
|
6439
|
+
return;
|
|
6440
|
+
}
|
|
6468
6441
|
if (!isLoggedIn(providerId)) {
|
|
6469
|
-
writeln(`${PREFIX.info} ${
|
|
6442
|
+
writeln(`${PREFIX.info} ${c14.dim("not logged in to")} ${providerId}`);
|
|
6470
6443
|
return;
|
|
6471
6444
|
}
|
|
6472
6445
|
logout(providerId);
|
|
6473
|
-
writeln(`${PREFIX.success} logged out of ${
|
|
6446
|
+
writeln(`${PREFIX.success} logged out of ${c14.cyan(provider.id)}`);
|
|
6474
6447
|
}
|
|
6475
6448
|
|
|
6476
6449
|
// src/cli/commands-mcp.ts
|
|
6477
|
-
import * as
|
|
6450
|
+
import * as c15 from "yoctocolors";
|
|
6478
6451
|
async function handleMcpCommand(ctx, args) {
|
|
6479
6452
|
const parts = args.trim().split(/\s+/);
|
|
6480
6453
|
const sub = parts[0] ?? "list";
|
|
@@ -6482,15 +6455,15 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6482
6455
|
case "list": {
|
|
6483
6456
|
const servers = listMcpServers();
|
|
6484
6457
|
if (servers.length === 0) {
|
|
6485
|
-
writeln(
|
|
6486
|
-
writeln(
|
|
6458
|
+
writeln(c15.dim(" no MCP servers configured"));
|
|
6459
|
+
writeln(c15.dim(" /mcp add <name> http <url> \xB7 /mcp add <name> stdio <cmd> [args...]"));
|
|
6487
6460
|
return;
|
|
6488
6461
|
}
|
|
6489
6462
|
writeln();
|
|
6490
6463
|
for (const s of servers) {
|
|
6491
6464
|
const detailText = s.url ?? s.command ?? "";
|
|
6492
|
-
const detail = detailText ?
|
|
6493
|
-
writeln(` ${
|
|
6465
|
+
const detail = detailText ? c15.dim(` ${detailText}`) : "";
|
|
6466
|
+
writeln(` ${c15.yellow("\u2699")} ${c15.bold(s.name)} ${c15.dim(s.transport)}${detail}`);
|
|
6494
6467
|
}
|
|
6495
6468
|
return;
|
|
6496
6469
|
}
|
|
@@ -6535,9 +6508,9 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6535
6508
|
}
|
|
6536
6509
|
try {
|
|
6537
6510
|
await ctx.connectMcpServer(name);
|
|
6538
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6511
|
+
writeln(`${PREFIX.success} mcp server ${c15.cyan(name)} added and connected`);
|
|
6539
6512
|
} catch (e) {
|
|
6540
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6513
|
+
writeln(`${PREFIX.success} mcp server ${c15.cyan(name)} saved ${c15.dim(`(connection failed: ${String(e)})`)}`);
|
|
6541
6514
|
}
|
|
6542
6515
|
return;
|
|
6543
6516
|
}
|
|
@@ -6549,17 +6522,17 @@ async function handleMcpCommand(ctx, args) {
|
|
|
6549
6522
|
return;
|
|
6550
6523
|
}
|
|
6551
6524
|
deleteMcpServer(name);
|
|
6552
|
-
writeln(`${PREFIX.success} mcp server ${
|
|
6525
|
+
writeln(`${PREFIX.success} mcp server ${c15.cyan(name)} removed`);
|
|
6553
6526
|
return;
|
|
6554
6527
|
}
|
|
6555
6528
|
default:
|
|
6556
6529
|
writeln(`${PREFIX.error} unknown: /mcp ${sub}`);
|
|
6557
|
-
writeln(
|
|
6530
|
+
writeln(c15.dim(" subcommands: list \xB7 add \xB7 remove"));
|
|
6558
6531
|
}
|
|
6559
6532
|
}
|
|
6560
6533
|
|
|
6561
6534
|
// src/cli/commands-model.ts
|
|
6562
|
-
import * as
|
|
6535
|
+
import * as c16 from "yoctocolors";
|
|
6563
6536
|
import { select } from "yoctoselect";
|
|
6564
6537
|
var THINKING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
6565
6538
|
function parseThinkingEffort(value) {
|
|
@@ -6579,21 +6552,21 @@ function renderModelUpdatedMessage(ctx, modelId, effortArg) {
|
|
|
6579
6552
|
if (effortArg) {
|
|
6580
6553
|
if (effortArg === "off") {
|
|
6581
6554
|
ctx.setThinkingEffort(null);
|
|
6582
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6555
|
+
writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)} ${c16.dim("(thinking disabled)")}`);
|
|
6583
6556
|
return;
|
|
6584
6557
|
}
|
|
6585
6558
|
const effort = parseThinkingEffort(effortArg);
|
|
6586
6559
|
if (effort) {
|
|
6587
6560
|
ctx.setThinkingEffort(effort);
|
|
6588
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6561
|
+
writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)} ${c16.dim(`(\u2726 ${effort})`)}`);
|
|
6589
6562
|
return;
|
|
6590
6563
|
}
|
|
6591
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6592
|
-
writeln(`${PREFIX.error} unknown effort level ${
|
|
6564
|
+
writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)}`);
|
|
6565
|
+
writeln(`${PREFIX.error} unknown effort level ${c16.cyan(effortArg)} (use low, medium, high, xhigh, off)`);
|
|
6593
6566
|
return;
|
|
6594
6567
|
}
|
|
6595
|
-
const effortTag = ctx.thinkingEffort ?
|
|
6596
|
-
writeln(`${PREFIX.success} model \u2192 ${
|
|
6568
|
+
const effortTag = ctx.thinkingEffort ? c16.dim(` (\u2726 ${ctx.thinkingEffort})`) : "";
|
|
6569
|
+
writeln(`${PREFIX.success} model \u2192 ${c16.cyan(modelId)}${effortTag}`);
|
|
6597
6570
|
}
|
|
6598
6571
|
async function handleModelSet(ctx, args) {
|
|
6599
6572
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -6606,7 +6579,7 @@ async function handleModelSet(ctx, args) {
|
|
|
6606
6579
|
const snapshot = await fetchAvailableModels();
|
|
6607
6580
|
const match = findModelIdByAlias(idArg, snapshot.models.map((model) => model.id));
|
|
6608
6581
|
if (!match) {
|
|
6609
|
-
writeln(`${PREFIX.error} unknown model ${
|
|
6582
|
+
writeln(`${PREFIX.error} unknown model ${c16.cyan(idArg)} ${c16.dim("\u2014 run /models for the full list")}`);
|
|
6610
6583
|
return;
|
|
6611
6584
|
}
|
|
6612
6585
|
modelId = match;
|
|
@@ -6626,7 +6599,7 @@ function handleModelEffort(ctx, effortArg) {
|
|
|
6626
6599
|
return;
|
|
6627
6600
|
}
|
|
6628
6601
|
ctx.setThinkingEffort(effort);
|
|
6629
|
-
writeln(`${PREFIX.success} thinking effort \u2192 ${
|
|
6602
|
+
writeln(`${PREFIX.success} thinking effort \u2192 ${c16.cyan(effort)}`);
|
|
6630
6603
|
}
|
|
6631
6604
|
async function handleModelSelect(ctx) {
|
|
6632
6605
|
ctx.startSpinner("fetching models");
|
|
@@ -6634,20 +6607,20 @@ async function handleModelSelect(ctx) {
|
|
|
6634
6607
|
ctx.stopSpinner();
|
|
6635
6608
|
if (snapshot.models.length === 0) {
|
|
6636
6609
|
writeln(`${PREFIX.error} No models found. Check your API keys or Ollama connection.`);
|
|
6637
|
-
writeln(
|
|
6610
|
+
writeln(c16.dim(" Set OPENCODE_API_KEY for Zen, or start Ollama for local models."));
|
|
6638
6611
|
return;
|
|
6639
6612
|
}
|
|
6640
6613
|
if (snapshot.stale) {
|
|
6641
6614
|
const lastSync = snapshot.lastSyncAt ? new Date(snapshot.lastSyncAt).toLocaleString() : "never";
|
|
6642
6615
|
const refreshTag = snapshot.refreshing ? " (refreshing in background)" : "";
|
|
6643
|
-
writeln(
|
|
6616
|
+
writeln(c16.dim(` model metadata is stale (last sync: ${lastSync})${refreshTag}`));
|
|
6644
6617
|
}
|
|
6645
6618
|
const items = snapshot.models.map((model) => {
|
|
6646
6619
|
const isCurrent = ctx.currentModel === model.id;
|
|
6647
|
-
const freeTag = model.free ?
|
|
6648
|
-
const contextTag = model.context ?
|
|
6649
|
-
const currentTag = isCurrent ?
|
|
6650
|
-
const providerTag =
|
|
6620
|
+
const freeTag = model.free ? c16.green(" free") : "";
|
|
6621
|
+
const contextTag = model.context ? c16.dim(` ${Math.round(model.context / 1000)}k`) : "";
|
|
6622
|
+
const currentTag = isCurrent ? c16.cyan(" \u25C0") : "";
|
|
6623
|
+
const providerTag = c16.dim(` [${model.provider}]`);
|
|
6651
6624
|
return {
|
|
6652
6625
|
label: `${model.displayName}${freeTag}${contextTag}${currentTag}${providerTag}`,
|
|
6653
6626
|
value: model.id,
|
|
@@ -6680,7 +6653,7 @@ async function handleModelCommand(ctx, args) {
|
|
|
6680
6653
|
}
|
|
6681
6654
|
|
|
6682
6655
|
// src/cli/commands-session.ts
|
|
6683
|
-
import * as
|
|
6656
|
+
import * as c17 from "yoctocolors";
|
|
6684
6657
|
import { select as select2 } from "yoctoselect";
|
|
6685
6658
|
async function handleSessionCommand(ctx, args) {
|
|
6686
6659
|
const id = args.trim();
|
|
@@ -6689,15 +6662,15 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6689
6662
|
const ok2 = ctx.switchSession(id);
|
|
6690
6663
|
ctx.stopSpinner();
|
|
6691
6664
|
if (ok2) {
|
|
6692
|
-
writeln(`${PREFIX.success} switched to session ${
|
|
6665
|
+
writeln(`${PREFIX.success} switched to session ${c17.cyan(id)} (${c17.cyan(ctx.currentModel)})`);
|
|
6693
6666
|
} else {
|
|
6694
|
-
writeln(`${PREFIX.error} session ${
|
|
6667
|
+
writeln(`${PREFIX.error} session ${c17.cyan(id)} not found`);
|
|
6695
6668
|
}
|
|
6696
6669
|
return;
|
|
6697
6670
|
}
|
|
6698
6671
|
const sessions = listSessions(50);
|
|
6699
6672
|
if (sessions.length === 0) {
|
|
6700
|
-
writeln(`${PREFIX.info} ${
|
|
6673
|
+
writeln(`${PREFIX.info} ${c17.dim("no sessions found")}`);
|
|
6701
6674
|
return;
|
|
6702
6675
|
}
|
|
6703
6676
|
const items = sessions.map((s) => {
|
|
@@ -6706,7 +6679,7 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6706
6679
|
const cwd = tildePath(s.cwd);
|
|
6707
6680
|
const date = new Date(s.updated_at).toLocaleDateString();
|
|
6708
6681
|
return {
|
|
6709
|
-
label: `${
|
|
6682
|
+
label: `${c17.dim(s.id)} ${title} ${c17.cyan(model)} ${c17.dim(cwd)} ${c17.dim(date)}`,
|
|
6710
6683
|
value: s.id,
|
|
6711
6684
|
filterText: `${s.id} ${s.title} ${s.model} ${s.cwd}`
|
|
6712
6685
|
};
|
|
@@ -6722,9 +6695,9 @@ async function handleSessionCommand(ctx, args) {
|
|
|
6722
6695
|
return;
|
|
6723
6696
|
const ok = ctx.switchSession(picked);
|
|
6724
6697
|
if (ok) {
|
|
6725
|
-
writeln(`${PREFIX.success} switched to session ${
|
|
6698
|
+
writeln(`${PREFIX.success} switched to session ${c17.cyan(picked)} (${c17.cyan(ctx.currentModel)})`);
|
|
6726
6699
|
} else {
|
|
6727
|
-
writeln(`${PREFIX.error} session ${
|
|
6700
|
+
writeln(`${PREFIX.error} session ${c17.cyan(picked)} not found`);
|
|
6728
6701
|
}
|
|
6729
6702
|
}
|
|
6730
6703
|
|
|
@@ -6734,9 +6707,9 @@ async function handleUndo(ctx) {
|
|
|
6734
6707
|
try {
|
|
6735
6708
|
const ok = await ctx.undoLastTurn();
|
|
6736
6709
|
if (ok) {
|
|
6737
|
-
writeln(`${PREFIX.success} ${
|
|
6710
|
+
writeln(`${PREFIX.success} ${c18.dim("last conversation turn removed")}`);
|
|
6738
6711
|
} else {
|
|
6739
|
-
writeln(`${PREFIX.info} ${
|
|
6712
|
+
writeln(`${PREFIX.info} ${c18.dim("nothing to undo")}`);
|
|
6740
6713
|
}
|
|
6741
6714
|
} finally {
|
|
6742
6715
|
ctx.stopSpinner();
|
|
@@ -6794,7 +6767,7 @@ async function handleCommand(command, args, ctx) {
|
|
|
6794
6767
|
if (loaded2) {
|
|
6795
6768
|
const srcPath = skill.source === "local" ? `.agents/skills/${skill.name}/SKILL.md` : `~/.agents/skills/${skill.name}/SKILL.md`;
|
|
6796
6769
|
if (skill.context === "fork") {
|
|
6797
|
-
writeln(`${PREFIX.info} ${
|
|
6770
|
+
writeln(`${PREFIX.info} ${c18.cyan(skill.name)} ${c18.dim(`[${srcPath}] (forked subagent)`)}`);
|
|
6798
6771
|
writeln();
|
|
6799
6772
|
const subagentPrompt = args ? `${loaded2.content}
|
|
6800
6773
|
|
|
@@ -6802,7 +6775,7 @@ ${args}` : loaded2.content;
|
|
|
6802
6775
|
const result = await runForkedSkill(skill.name, subagentPrompt, ctx.cwd);
|
|
6803
6776
|
return { type: "inject-user-message", text: result };
|
|
6804
6777
|
}
|
|
6805
|
-
writeln(`${PREFIX.info} ${
|
|
6778
|
+
writeln(`${PREFIX.info} ${c18.cyan(skill.name)} ${c18.dim(`[${srcPath}]`)}`);
|
|
6806
6779
|
writeln();
|
|
6807
6780
|
const prompt = args ? `${loaded2.content}
|
|
6808
6781
|
|
|
@@ -6810,16 +6783,16 @@ ${args}` : loaded2.content;
|
|
|
6810
6783
|
return { type: "inject-user-message", text: prompt };
|
|
6811
6784
|
}
|
|
6812
6785
|
}
|
|
6813
|
-
writeln(`${PREFIX.error} unknown: /${command} ${
|
|
6786
|
+
writeln(`${PREFIX.error} unknown: /${command} ${c18.dim("\u2014 /help for commands")}`);
|
|
6814
6787
|
return { type: "unknown", command };
|
|
6815
6788
|
}
|
|
6816
6789
|
}
|
|
6817
6790
|
}
|
|
6818
6791
|
async function runForkedSkill(skillName, prompt, cwd) {
|
|
6819
|
-
const tmpFile =
|
|
6820
|
-
|
|
6792
|
+
const tmpFile = join8(tmpdir(), `mc-fork-${randomBytes(8).toString("hex")}.md`);
|
|
6793
|
+
writeFileSync(tmpFile, prompt, "utf8");
|
|
6821
6794
|
try {
|
|
6822
|
-
writeln(`${PREFIX.info} ${
|
|
6795
|
+
writeln(`${PREFIX.info} ${c18.dim("running subagent\u2026")}`);
|
|
6823
6796
|
const proc = Bun.spawn([process.execPath, Bun.main], {
|
|
6824
6797
|
cwd,
|
|
6825
6798
|
stdin: Bun.file(tmpFile),
|
|
@@ -6844,24 +6817,24 @@ ${stderr.trim()}`;
|
|
|
6844
6817
|
${output}`;
|
|
6845
6818
|
} finally {
|
|
6846
6819
|
try {
|
|
6847
|
-
|
|
6820
|
+
unlinkSync(tmpFile);
|
|
6848
6821
|
} catch {}
|
|
6849
6822
|
}
|
|
6850
6823
|
}
|
|
6851
6824
|
function handleBooleanToggleCommand(opts) {
|
|
6852
6825
|
const mode = opts.args.trim().toLowerCase();
|
|
6853
6826
|
if (!mode) {
|
|
6854
|
-
writeln(`${PREFIX.success} ${opts.label} ${opts.current ?
|
|
6827
|
+
writeln(`${PREFIX.success} ${opts.label} ${opts.current ? c18.green("on") : c18.dim("off")}`);
|
|
6855
6828
|
return;
|
|
6856
6829
|
}
|
|
6857
6830
|
if (mode === "on") {
|
|
6858
6831
|
opts.set(true);
|
|
6859
|
-
writeln(`${PREFIX.success} ${opts.label} ${
|
|
6832
|
+
writeln(`${PREFIX.success} ${opts.label} ${c18.green("on")}`);
|
|
6860
6833
|
return;
|
|
6861
6834
|
}
|
|
6862
6835
|
if (mode === "off") {
|
|
6863
6836
|
opts.set(false);
|
|
6864
|
-
writeln(`${PREFIX.success} ${opts.label} ${
|
|
6837
|
+
writeln(`${PREFIX.success} ${opts.label} ${c18.dim("off")}`);
|
|
6865
6838
|
return;
|
|
6866
6839
|
}
|
|
6867
6840
|
writeln(`${PREFIX.error} usage: ${opts.usage}`);
|
|
@@ -6921,13 +6894,12 @@ async function getGitBranch(cwd) {
|
|
|
6921
6894
|
}
|
|
6922
6895
|
async function runInputLoop(opts) {
|
|
6923
6896
|
const { cwd, reporter, cmdCtx, runner } = opts;
|
|
6924
|
-
let lastStatusSignature = null;
|
|
6925
6897
|
while (true) {
|
|
6926
6898
|
const branch = await getGitBranch(cwd);
|
|
6927
6899
|
const status = runner.getStatusInfo();
|
|
6928
6900
|
const cwdDisplay = tildePath(cwd);
|
|
6929
6901
|
const contextWindow = getContextWindow(status.model);
|
|
6930
|
-
|
|
6902
|
+
reporter.renderStatusBar({
|
|
6931
6903
|
model: status.model,
|
|
6932
6904
|
cwd: cwdDisplay,
|
|
6933
6905
|
gitBranch: branch,
|
|
@@ -6937,12 +6909,7 @@ async function runInputLoop(opts) {
|
|
|
6937
6909
|
contextTokens: status.lastContextTokens,
|
|
6938
6910
|
contextWindow,
|
|
6939
6911
|
thinkingEffort: status.thinkingEffort
|
|
6940
|
-
};
|
|
6941
|
-
const statusSignature = buildStatusBarSignature(statusData);
|
|
6942
|
-
if (statusSignature !== lastStatusSignature) {
|
|
6943
|
-
reporter.renderStatusBar(statusData);
|
|
6944
|
-
lastStatusSignature = statusSignature;
|
|
6945
|
-
}
|
|
6912
|
+
});
|
|
6946
6913
|
let input;
|
|
6947
6914
|
try {
|
|
6948
6915
|
input = await readline({ cwd });
|
|
@@ -6951,14 +6918,14 @@ async function runInputLoop(opts) {
|
|
|
6951
6918
|
}
|
|
6952
6919
|
switch (input.type) {
|
|
6953
6920
|
case "eof":
|
|
6954
|
-
reporter.writeText(
|
|
6921
|
+
reporter.writeText(c19.dim("Goodbye."));
|
|
6955
6922
|
return;
|
|
6956
6923
|
case "interrupt":
|
|
6957
6924
|
continue;
|
|
6958
6925
|
case "command": {
|
|
6959
6926
|
const result = await handleCommand(input.command, input.args, cmdCtx);
|
|
6960
6927
|
if (result.type === "exit") {
|
|
6961
|
-
reporter.writeText(
|
|
6928
|
+
reporter.writeText(c19.dim("Goodbye."));
|
|
6962
6929
|
return;
|
|
6963
6930
|
}
|
|
6964
6931
|
if (result.type === "inject-user-message") {
|
|
@@ -7065,13 +7032,12 @@ async function main() {
|
|
|
7065
7032
|
if (last) {
|
|
7066
7033
|
sessionId = last.id;
|
|
7067
7034
|
} else {
|
|
7068
|
-
writeln(
|
|
7035
|
+
writeln(c20.dim("No previous session found, starting fresh."));
|
|
7069
7036
|
}
|
|
7070
7037
|
} else if (args.sessionId) {
|
|
7071
7038
|
sessionId = args.sessionId;
|
|
7072
7039
|
}
|
|
7073
7040
|
const model = args.model ?? getPreferredModel() ?? autoDiscoverModel();
|
|
7074
|
-
bootstrapGlobalDefaults();
|
|
7075
7041
|
if (!prompt) {
|
|
7076
7042
|
renderBanner(model, args.cwd);
|
|
7077
7043
|
}
|