@alanwchat/coder 0.1.107 → 0.1.109
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/index.cjs +872 -388
- package/dist/index.cjs.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23,18 +23,12 @@ __export(config_exports, {
|
|
|
23
23
|
loadConfig: () => loadConfig,
|
|
24
24
|
resolveApiKey: () => resolveApiKey,
|
|
25
25
|
resolveProviderConfig: () => resolveProviderConfig,
|
|
26
|
+
resolveTavilyApiKey: () => resolveTavilyApiKey,
|
|
26
27
|
saveConfig: () => saveConfig
|
|
27
28
|
});
|
|
28
29
|
function getConfigDir() {
|
|
29
30
|
const home = (0, import_node_os.homedir)();
|
|
30
|
-
|
|
31
|
-
if (p === "win32") {
|
|
32
|
-
return process.env.APPDATA ? (0, import_node_path10.join)(process.env.APPDATA, "Coder", "cli") : (0, import_node_path10.join)(home, ".config", "coder", "cli");
|
|
33
|
-
}
|
|
34
|
-
if (process.env.XDG_CONFIG_HOME) {
|
|
35
|
-
return (0, import_node_path10.join)(process.env.XDG_CONFIG_HOME, "coder", "cli");
|
|
36
|
-
}
|
|
37
|
-
return (0, import_node_path10.join)(home, ".config", "coder", "cli");
|
|
31
|
+
return (0, import_node_path10.join)(home, ".coder", "cli");
|
|
38
32
|
}
|
|
39
33
|
function createDefaultProviderSettings(provider) {
|
|
40
34
|
const isCustom = provider === "custom";
|
|
@@ -42,8 +36,7 @@ function createDefaultProviderSettings(provider) {
|
|
|
42
36
|
apiKeySource: "env",
|
|
43
37
|
apiKey: "",
|
|
44
38
|
apiKeyEnvVar: isCustom ? "CUSTOM_API_KEY" : PRESET_PROVIDERS[provider]?.defaultApiKeyEnvVar ?? "API_KEY",
|
|
45
|
-
customBaseUrl: isCustom ? "https://api.example.com/v1" : ""
|
|
46
|
-
showUsage: false
|
|
39
|
+
customBaseUrl: isCustom ? "https://api.example.com/v1" : ""
|
|
47
40
|
};
|
|
48
41
|
}
|
|
49
42
|
function createDefaultConfig() {
|
|
@@ -55,7 +48,7 @@ function createDefaultConfig() {
|
|
|
55
48
|
activeProvider: "deepseek",
|
|
56
49
|
providers,
|
|
57
50
|
lastModel: "deepseek-v4-flash",
|
|
58
|
-
|
|
51
|
+
tavilyApiKey: ""
|
|
59
52
|
};
|
|
60
53
|
}
|
|
61
54
|
function getConfigFilePath() {
|
|
@@ -90,9 +83,12 @@ function mergeWithDefaults(raw) {
|
|
|
90
83
|
activeProvider: PROVIDER_IDS.includes(String(raw.activeProvider ?? "")) ? raw.activeProvider : defaults.activeProvider,
|
|
91
84
|
providers: mergeProviders(raw.providers, defaults.providers),
|
|
92
85
|
lastModel: typeof raw.lastModel === "string" ? raw.lastModel : defaults.lastModel,
|
|
93
|
-
|
|
86
|
+
tavilyApiKey: typeof raw.tavilyApiKey === "string" ? raw.tavilyApiKey : defaults.tavilyApiKey
|
|
94
87
|
};
|
|
95
88
|
}
|
|
89
|
+
function isPlainObject(value) {
|
|
90
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
91
|
+
}
|
|
96
92
|
function mergeProviders(raw, defaults) {
|
|
97
93
|
const result = { ...defaults };
|
|
98
94
|
if (!raw || typeof raw !== "object") {
|
|
@@ -106,7 +102,9 @@ function mergeProviders(raw, defaults) {
|
|
|
106
102
|
apiKey: typeof p.apiKey === "string" ? p.apiKey : result[id].apiKey,
|
|
107
103
|
apiKeyEnvVar: typeof p.apiKeyEnvVar === "string" ? p.apiKeyEnvVar : result[id].apiKeyEnvVar,
|
|
108
104
|
customBaseUrl: typeof p.customBaseUrl === "string" ? p.customBaseUrl : result[id].customBaseUrl,
|
|
109
|
-
|
|
105
|
+
...typeof p.supportsThinking === "boolean" ? { supportsThinking: p.supportsThinking } : {},
|
|
106
|
+
...isPlainObject(p.thinkingEnabledParams) ? { thinkingEnabledParams: p.thinkingEnabledParams } : {},
|
|
107
|
+
...isPlainObject(p.thinkingDisabledParams) ? { thinkingDisabledParams: p.thinkingDisabledParams } : {}
|
|
110
108
|
};
|
|
111
109
|
}
|
|
112
110
|
}
|
|
@@ -166,6 +164,12 @@ function resolveApiKey(resolved) {
|
|
|
166
164
|
}
|
|
167
165
|
return envValue;
|
|
168
166
|
}
|
|
167
|
+
function resolveTavilyApiKey(config) {
|
|
168
|
+
if (config.tavilyApiKey.trim()) {
|
|
169
|
+
return config.tavilyApiKey.trim();
|
|
170
|
+
}
|
|
171
|
+
return process.env.TAVILY_API_KEY?.trim() ?? "";
|
|
172
|
+
}
|
|
169
173
|
function getConfigDirPath() {
|
|
170
174
|
return getConfigDir();
|
|
171
175
|
}
|
|
@@ -204,7 +208,7 @@ var init_config = __esm({
|
|
|
204
208
|
]
|
|
205
209
|
};
|
|
206
210
|
PROVIDER_IDS = ["deepseek", "glm", "agnes", "nvidia", "minimax", "custom"];
|
|
207
|
-
CONFIG_FILE = "
|
|
211
|
+
CONFIG_FILE = "settings.json";
|
|
208
212
|
}
|
|
209
213
|
});
|
|
210
214
|
|
|
@@ -491,7 +495,8 @@ var replaceLinesHandler = async (rawArgs, context) => {
|
|
|
491
495
|
action: "modified",
|
|
492
496
|
bytesWritten: Buffer.byteLength(newContent, "utf-8"),
|
|
493
497
|
linesAdded: replacementLines.length,
|
|
494
|
-
linesRemoved: endLine - args.start_line + 1
|
|
498
|
+
linesRemoved: endLine - args.start_line + 1,
|
|
499
|
+
warning: "Rows may have shifted \u2014 re-read the file with read_file before your next edit."
|
|
495
500
|
});
|
|
496
501
|
} catch (error2) {
|
|
497
502
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -990,6 +995,27 @@ function readShellLogs(shellId, stream, offset = 0, limit = 4096) {
|
|
|
990
995
|
truncated: offset + limit < totalBytes
|
|
991
996
|
};
|
|
992
997
|
}
|
|
998
|
+
function killAllShells() {
|
|
999
|
+
for (const shell of shells.values()) {
|
|
1000
|
+
if (shell.status === "running") {
|
|
1001
|
+
try {
|
|
1002
|
+
shell.process.kill("SIGTERM");
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
shells.clear();
|
|
1008
|
+
}
|
|
1009
|
+
function killShellsByTask(taskId) {
|
|
1010
|
+
for (const [id, shell] of shells) {
|
|
1011
|
+
if (shell.taskId === taskId && shell.status === "running") {
|
|
1012
|
+
try {
|
|
1013
|
+
shell.process.kill("SIGTERM");
|
|
1014
|
+
} catch {
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
993
1019
|
|
|
994
1020
|
// src/handlers/shell.ts
|
|
995
1021
|
var shellHandler = async (rawArgs, context) => {
|
|
@@ -1073,13 +1099,16 @@ var readShellLogsHandler = async (rawArgs, _context) => {
|
|
|
1073
1099
|
};
|
|
1074
1100
|
|
|
1075
1101
|
// src/handlers/web-search.ts
|
|
1102
|
+
init_config();
|
|
1076
1103
|
var webSearchHandler = async (rawArgs, _context) => {
|
|
1077
1104
|
const args = rawArgs;
|
|
1078
1105
|
if (!args.search_term?.trim()) {
|
|
1079
1106
|
return toolFailure("web_search", "invalid_arguments", "search_term is required");
|
|
1080
1107
|
}
|
|
1108
|
+
const config = loadConfig();
|
|
1109
|
+
const tavilyKey = resolveTavilyApiKey(config);
|
|
1081
1110
|
try {
|
|
1082
|
-
const results = await performWebSearch(args.search_term, args.max_results ?? 5);
|
|
1111
|
+
const results = await performWebSearch(args.search_term, args.max_results ?? 5, tavilyKey);
|
|
1083
1112
|
return toolSuccess("web_search", {
|
|
1084
1113
|
query: args.search_term,
|
|
1085
1114
|
results
|
|
@@ -1089,7 +1118,33 @@ var webSearchHandler = async (rawArgs, _context) => {
|
|
|
1089
1118
|
return toolFailure("web_search", "search_error", message);
|
|
1090
1119
|
}
|
|
1091
1120
|
};
|
|
1092
|
-
async function performWebSearch(query, maxResults) {
|
|
1121
|
+
async function performWebSearch(query, maxResults, tavilyKey) {
|
|
1122
|
+
if (tavilyKey) {
|
|
1123
|
+
try {
|
|
1124
|
+
const response = await fetch("https://api.tavily.com/search", {
|
|
1125
|
+
method: "POST",
|
|
1126
|
+
headers: { "Content-Type": "application/json" },
|
|
1127
|
+
body: JSON.stringify({
|
|
1128
|
+
api_key: tavilyKey,
|
|
1129
|
+
query,
|
|
1130
|
+
max_results: maxResults,
|
|
1131
|
+
search_depth: "basic"
|
|
1132
|
+
}),
|
|
1133
|
+
signal: AbortSignal.timeout(15e3)
|
|
1134
|
+
});
|
|
1135
|
+
if (response.ok) {
|
|
1136
|
+
const data = await response.json();
|
|
1137
|
+
if (data.results && data.results.length > 0) {
|
|
1138
|
+
return data.results.map((r) => ({
|
|
1139
|
+
title: r.title,
|
|
1140
|
+
url: r.url,
|
|
1141
|
+
snippet: r.content
|
|
1142
|
+
}));
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
} catch {
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1093
1148
|
try {
|
|
1094
1149
|
const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`;
|
|
1095
1150
|
const response = await fetch(url, {
|
|
@@ -1116,10 +1171,13 @@ async function performWebSearch(query, maxResults) {
|
|
|
1116
1171
|
}
|
|
1117
1172
|
}
|
|
1118
1173
|
}
|
|
1174
|
+
if (results.length === 0) {
|
|
1175
|
+
throw new Error("No results");
|
|
1176
|
+
}
|
|
1119
1177
|
return results.slice(0, maxResults);
|
|
1120
1178
|
} catch {
|
|
1121
1179
|
throw new Error(
|
|
1122
|
-
"Web search is currently unavailable. Configure a Tavily API key
|
|
1180
|
+
"Web search is currently unavailable. Configure a Tavily API key via `coder config tavilyApiKey <key>` or set the TAVILY_API_KEY environment variable."
|
|
1123
1181
|
);
|
|
1124
1182
|
}
|
|
1125
1183
|
}
|
|
@@ -1248,125 +1306,9 @@ var todoWriteHandler = async (rawArgs, context) => {
|
|
|
1248
1306
|
});
|
|
1249
1307
|
};
|
|
1250
1308
|
|
|
1251
|
-
// src/handlers/
|
|
1309
|
+
// src/handlers/workspace-tree.ts
|
|
1252
1310
|
var import_node_fs11 = require("node:fs");
|
|
1253
1311
|
var import_node_path12 = require("node:path");
|
|
1254
|
-
init_config();
|
|
1255
|
-
function getPlansDir() {
|
|
1256
|
-
const dir = (0, import_node_path12.join)(getConfigDirPath(), "plans");
|
|
1257
|
-
if (!(0, import_node_fs11.existsSync)(dir)) {
|
|
1258
|
-
(0, import_node_fs11.mkdirSync)(dir, { recursive: true });
|
|
1259
|
-
}
|
|
1260
|
-
return dir;
|
|
1261
|
-
}
|
|
1262
|
-
function getPlanFilePath(name) {
|
|
1263
|
-
const safeName = name.replace(/[^a-z0-9-_.]/gi, "").toLowerCase();
|
|
1264
|
-
return (0, import_node_path12.join)(getPlansDir(), `${safeName}.json`);
|
|
1265
|
-
}
|
|
1266
|
-
function readPlanFile(name) {
|
|
1267
|
-
const filePath = getPlanFilePath(name);
|
|
1268
|
-
if (!(0, import_node_fs11.existsSync)(filePath)) return null;
|
|
1269
|
-
try {
|
|
1270
|
-
return JSON.parse((0, import_node_fs11.readFileSync)(filePath, "utf-8"));
|
|
1271
|
-
} catch {
|
|
1272
|
-
return null;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
function writePlanFile(name, plan) {
|
|
1276
|
-
(0, import_node_fs11.writeFileSync)(getPlanFilePath(name), JSON.stringify(plan, null, 2), "utf-8");
|
|
1277
|
-
}
|
|
1278
|
-
function deletePlanFile(name) {
|
|
1279
|
-
const filePath = getPlanFilePath(name);
|
|
1280
|
-
if (!(0, import_node_fs11.existsSync)(filePath)) return false;
|
|
1281
|
-
try {
|
|
1282
|
-
(0, import_node_fs11.writeFileSync)(filePath, JSON.stringify({ deleted: true }), "utf-8");
|
|
1283
|
-
return true;
|
|
1284
|
-
} catch {
|
|
1285
|
-
return false;
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
function listPlanFiles() {
|
|
1289
|
-
return (0, import_node_fs11.readdirSync)(getPlansDir()).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
1290
|
-
}
|
|
1291
|
-
var planCreateHandler = async (rawArgs, _context) => {
|
|
1292
|
-
const args = rawArgs;
|
|
1293
|
-
if (!args.name?.trim() || !args.content?.trim()) {
|
|
1294
|
-
return toolFailure("plan_create", "invalid_arguments", "name and content are required");
|
|
1295
|
-
}
|
|
1296
|
-
const name = args.name.trim().toLowerCase().replace(/[^a-z0-9-_.]/gi, "");
|
|
1297
|
-
if (readPlanFile(name)) {
|
|
1298
|
-
return toolFailure("plan_create", "exists", `Plan already exists: ${name}`);
|
|
1299
|
-
}
|
|
1300
|
-
writePlanFile(name, {
|
|
1301
|
-
name,
|
|
1302
|
-
title: args.title?.trim() ?? name,
|
|
1303
|
-
content: args.content.trim(),
|
|
1304
|
-
createdAt: Date.now(),
|
|
1305
|
-
updatedAt: Date.now()
|
|
1306
|
-
});
|
|
1307
|
-
return toolSuccess("plan_create", { name, title: args.title?.trim() ?? name });
|
|
1308
|
-
};
|
|
1309
|
-
var planReadHandler = async (rawArgs, _context) => {
|
|
1310
|
-
const args = rawArgs;
|
|
1311
|
-
if (!args.name?.trim()) {
|
|
1312
|
-
return toolFailure("plan_read", "invalid_arguments", "name is required");
|
|
1313
|
-
}
|
|
1314
|
-
const plan = readPlanFile(args.name.trim());
|
|
1315
|
-
if (!plan) {
|
|
1316
|
-
return toolFailure("plan_read", "not_found", `Plan not found: ${args.name}`);
|
|
1317
|
-
}
|
|
1318
|
-
return toolSuccess("plan_read", plan);
|
|
1319
|
-
};
|
|
1320
|
-
var planUpdateHandler = async (rawArgs, _context) => {
|
|
1321
|
-
const args = rawArgs;
|
|
1322
|
-
if (!args.name?.trim()) {
|
|
1323
|
-
return toolFailure("plan_update", "invalid_arguments", "name is required");
|
|
1324
|
-
}
|
|
1325
|
-
const existing = readPlanFile(args.name.trim());
|
|
1326
|
-
if (!existing) {
|
|
1327
|
-
return toolFailure("plan_update", "not_found", `Plan not found: ${args.name}`);
|
|
1328
|
-
}
|
|
1329
|
-
writePlanFile(args.name.trim(), {
|
|
1330
|
-
...existing,
|
|
1331
|
-
title: args.title?.trim() ?? existing.title,
|
|
1332
|
-
content: args.content?.trim() ?? existing.content,
|
|
1333
|
-
updatedAt: Date.now()
|
|
1334
|
-
});
|
|
1335
|
-
return toolSuccess("plan_update", { name: args.name.trim() });
|
|
1336
|
-
};
|
|
1337
|
-
var planEditHandler = async (rawArgs, _context) => {
|
|
1338
|
-
const args = rawArgs;
|
|
1339
|
-
if (!args.name?.trim()) {
|
|
1340
|
-
return toolFailure("plan_edit", "invalid_arguments", "name is required");
|
|
1341
|
-
}
|
|
1342
|
-
const existing = readPlanFile(args.name.trim());
|
|
1343
|
-
if (!existing) {
|
|
1344
|
-
return toolFailure("plan_edit", "not_found", `Plan not found: ${args.name}`);
|
|
1345
|
-
}
|
|
1346
|
-
writePlanFile(args.name.trim(), {
|
|
1347
|
-
...existing,
|
|
1348
|
-
content: existing.content + "\n\n## Edit Instructions\n\n" + (args.instructions?.trim() ?? ""),
|
|
1349
|
-
updatedAt: Date.now()
|
|
1350
|
-
});
|
|
1351
|
-
return toolSuccess("plan_edit", { name: args.name.trim() });
|
|
1352
|
-
};
|
|
1353
|
-
var planDeleteHandler = async (rawArgs, _context) => {
|
|
1354
|
-
const args = rawArgs;
|
|
1355
|
-
if (!args.name?.trim()) {
|
|
1356
|
-
return toolFailure("plan_delete", "invalid_arguments", "name is required");
|
|
1357
|
-
}
|
|
1358
|
-
deletePlanFile(args.name.trim());
|
|
1359
|
-
return toolSuccess("plan_delete", { name: args.name.trim(), deleted: true });
|
|
1360
|
-
};
|
|
1361
|
-
var planListHandler = async (_rawArgs, _context) => {
|
|
1362
|
-
const names = listPlanFiles();
|
|
1363
|
-
const plans = names.map((name) => readPlanFile(name)).filter((p) => p !== null).map((p) => ({ name: p.name, title: p.title }));
|
|
1364
|
-
return toolSuccess("plan_list", { plans, total: plans.length });
|
|
1365
|
-
};
|
|
1366
|
-
|
|
1367
|
-
// src/handlers/workspace-tree.ts
|
|
1368
|
-
var import_node_fs12 = require("node:fs");
|
|
1369
|
-
var import_node_path13 = require("node:path");
|
|
1370
1312
|
var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
1371
1313
|
"node_modules",
|
|
1372
1314
|
".git",
|
|
@@ -1412,13 +1354,13 @@ var getWorkspaceTreeHandler = async (rawArgs, context) => {
|
|
|
1412
1354
|
};
|
|
1413
1355
|
function buildTree(rootDir, currentDir, depth) {
|
|
1414
1356
|
const lines = [];
|
|
1415
|
-
const relPath = (0,
|
|
1357
|
+
const relPath = (0, import_node_path12.relative)(rootDir, currentDir);
|
|
1416
1358
|
if (depth === 0) {
|
|
1417
|
-
lines.push((0,
|
|
1359
|
+
lines.push((0, import_node_path12.relative)(rootDir, currentDir) || ".");
|
|
1418
1360
|
}
|
|
1419
1361
|
let entries;
|
|
1420
1362
|
try {
|
|
1421
|
-
entries = (0,
|
|
1363
|
+
entries = (0, import_node_fs11.readdirSync)(currentDir);
|
|
1422
1364
|
} catch {
|
|
1423
1365
|
return lines;
|
|
1424
1366
|
}
|
|
@@ -1428,9 +1370,9 @@ function buildTree(rootDir, currentDir, depth) {
|
|
|
1428
1370
|
if (name.startsWith(".") && depth === 0 && name === ".git") continue;
|
|
1429
1371
|
if (EXCLUDED_DIRS.has(name)) continue;
|
|
1430
1372
|
if (name.startsWith(".") && depth > 0) continue;
|
|
1431
|
-
const fullPath = (0,
|
|
1373
|
+
const fullPath = (0, import_node_path12.resolve)(currentDir, name);
|
|
1432
1374
|
try {
|
|
1433
|
-
const stats = (0,
|
|
1375
|
+
const stats = (0, import_node_fs11.statSync)(fullPath);
|
|
1434
1376
|
if (stats.isDirectory()) {
|
|
1435
1377
|
dirs.push(name);
|
|
1436
1378
|
} else {
|
|
@@ -1447,10 +1389,10 @@ function buildTree(rootDir, currentDir, depth) {
|
|
|
1447
1389
|
const isLast = i === allEntries.length - 1;
|
|
1448
1390
|
const prefix = depth === 0 ? isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 " : "";
|
|
1449
1391
|
const childPrefix = depth === 0 ? isLast ? " " : "\u2502 " : "";
|
|
1450
|
-
const fullPath = (0,
|
|
1392
|
+
const fullPath = (0, import_node_path12.resolve)(currentDir, name);
|
|
1451
1393
|
lines.push(`${prefix}${name}`);
|
|
1452
1394
|
try {
|
|
1453
|
-
const stats = (0,
|
|
1395
|
+
const stats = (0, import_node_fs11.statSync)(fullPath);
|
|
1454
1396
|
if (stats.isDirectory()) {
|
|
1455
1397
|
const childLines = buildTreeFromDepth(rootDir, fullPath, depth + 1, prefix || "", childPrefix);
|
|
1456
1398
|
lines.push(...childLines);
|
|
@@ -1464,7 +1406,7 @@ function buildTreeFromDepth(rootDir, currentDir, depth, parentPrefix, prefix) {
|
|
|
1464
1406
|
const lines = [];
|
|
1465
1407
|
let entries;
|
|
1466
1408
|
try {
|
|
1467
|
-
entries = (0,
|
|
1409
|
+
entries = (0, import_node_fs11.readdirSync)(currentDir);
|
|
1468
1410
|
} catch {
|
|
1469
1411
|
return lines;
|
|
1470
1412
|
}
|
|
@@ -1473,9 +1415,9 @@ function buildTreeFromDepth(rootDir, currentDir, depth, parentPrefix, prefix) {
|
|
|
1473
1415
|
for (const name of entries) {
|
|
1474
1416
|
if (EXCLUDED_DIRS.has(name)) continue;
|
|
1475
1417
|
if (name.startsWith(".")) continue;
|
|
1476
|
-
const fullPath = (0,
|
|
1418
|
+
const fullPath = (0, import_node_path12.resolve)(currentDir, name);
|
|
1477
1419
|
try {
|
|
1478
|
-
const stats = (0,
|
|
1420
|
+
const stats = (0, import_node_fs11.statSync)(fullPath);
|
|
1479
1421
|
if (stats.isDirectory()) {
|
|
1480
1422
|
dirs.push(name);
|
|
1481
1423
|
} else {
|
|
@@ -1493,9 +1435,9 @@ function buildTreeFromDepth(rootDir, currentDir, depth, parentPrefix, prefix) {
|
|
|
1493
1435
|
const linePrefix = `${prefix}${isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}`;
|
|
1494
1436
|
const childPrefix = `${prefix}${isLast ? " " : "\u2502 "}`;
|
|
1495
1437
|
lines.push(`${linePrefix}${name}`);
|
|
1496
|
-
const fullPath = (0,
|
|
1438
|
+
const fullPath = (0, import_node_path12.resolve)(currentDir, name);
|
|
1497
1439
|
try {
|
|
1498
|
-
const stats = (0,
|
|
1440
|
+
const stats = (0, import_node_fs11.statSync)(fullPath);
|
|
1499
1441
|
if (stats.isDirectory()) {
|
|
1500
1442
|
const childLines = buildTreeFromDepth(rootDir, fullPath, depth + 1, "", childPrefix);
|
|
1501
1443
|
lines.push(...childLines);
|
|
@@ -1600,7 +1542,7 @@ var AGENT_TOOL_DEFINITIONS = [
|
|
|
1600
1542
|
type: "function",
|
|
1601
1543
|
function: {
|
|
1602
1544
|
name: "write_file",
|
|
1603
|
-
description: "Create a new text file. Fails if the file already exists. Use
|
|
1545
|
+
description: "Create a new text file. Fails if the file already exists. Use edit_file or replace_lines to modify existing files.",
|
|
1604
1546
|
parameters: {
|
|
1605
1547
|
type: "object",
|
|
1606
1548
|
properties: {
|
|
@@ -1617,7 +1559,7 @@ var AGENT_TOOL_DEFINITIONS = [
|
|
|
1617
1559
|
type: "function",
|
|
1618
1560
|
function: {
|
|
1619
1561
|
name: "replace_file",
|
|
1620
|
-
description: "Replace
|
|
1562
|
+
description: "Replace an existing text file with new content. Use as a last resort \u2014 prefer edit_file or replace_lines first.",
|
|
1621
1563
|
parameters: {
|
|
1622
1564
|
type: "object",
|
|
1623
1565
|
properties: {
|
|
@@ -1636,7 +1578,7 @@ var AGENT_TOOL_DEFINITIONS = [
|
|
|
1636
1578
|
type: "function",
|
|
1637
1579
|
function: {
|
|
1638
1580
|
name: "edit_file",
|
|
1639
|
-
description: "Apply a
|
|
1581
|
+
description: "Apply a search-and-replace edit to an existing text file. The primary file editing tool \u2014 use this first.",
|
|
1640
1582
|
parameters: {
|
|
1641
1583
|
type: "object",
|
|
1642
1584
|
properties: {
|
|
@@ -1943,13 +1885,7 @@ var HANDLER_MAP = {
|
|
|
1943
1885
|
todo_write: todoWriteHandler,
|
|
1944
1886
|
get_workspace_tree: getWorkspaceTreeHandler,
|
|
1945
1887
|
spawn_subagent: spawnSubAgentHandler,
|
|
1946
|
-
ask_question: askQuestionHandler
|
|
1947
|
-
plan_create: planCreateHandler,
|
|
1948
|
-
plan_read: planReadHandler,
|
|
1949
|
-
plan_update: planUpdateHandler,
|
|
1950
|
-
plan_edit: planEditHandler,
|
|
1951
|
-
plan_delete: planDeleteHandler,
|
|
1952
|
-
plan_list: planListHandler
|
|
1888
|
+
ask_question: askQuestionHandler
|
|
1953
1889
|
};
|
|
1954
1890
|
var ASK_QUESTION_TOOL_NAME = "ask_question";
|
|
1955
1891
|
var AGENT_MODE_EXCLUDED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -1973,9 +1909,6 @@ function getToolDefinitions(agentMode) {
|
|
|
1973
1909
|
(t) => ASK_MODE_TOOL_NAMES.has(t.function.name)
|
|
1974
1910
|
);
|
|
1975
1911
|
}
|
|
1976
|
-
if (agentMode === "plan") {
|
|
1977
|
-
return AGENT_TOOL_DEFINITIONS;
|
|
1978
|
-
}
|
|
1979
1912
|
return AGENT_TOOL_DEFINITIONS.filter(
|
|
1980
1913
|
(t) => !AGENT_MODE_EXCLUDED_TOOL_NAMES.has(t.function.name)
|
|
1981
1914
|
);
|
|
@@ -2011,6 +1944,45 @@ async function executeToolCall(name, rawArguments, context) {
|
|
|
2011
1944
|
return handler(parsedArgs, context);
|
|
2012
1945
|
}
|
|
2013
1946
|
|
|
1947
|
+
// src/agent/thinking-config.ts
|
|
1948
|
+
function resolveThinkingParams(provider, thinkingEnabled, override) {
|
|
1949
|
+
if (override) {
|
|
1950
|
+
return thinkingEnabled ? override.enabled : override.disabled;
|
|
1951
|
+
}
|
|
1952
|
+
const preset = THINKING_PRESETS[provider];
|
|
1953
|
+
if (!preset) return void 0;
|
|
1954
|
+
return thinkingEnabled ? preset.enabled : preset.disabled;
|
|
1955
|
+
}
|
|
1956
|
+
var THINKING_PRESETS = {
|
|
1957
|
+
deepseek: {
|
|
1958
|
+
// @see https://api-docs.deepseek.com/guides/thinking_mode
|
|
1959
|
+
enabled: {
|
|
1960
|
+
thinking: { type: "enabled" },
|
|
1961
|
+
reasoning_effort: "high"
|
|
1962
|
+
},
|
|
1963
|
+
disabled: { thinking: { type: "disabled" } }
|
|
1964
|
+
},
|
|
1965
|
+
glm: {
|
|
1966
|
+
enabled: { thinking: { type: "enabled" } },
|
|
1967
|
+
disabled: { thinking: { type: "disabled" } }
|
|
1968
|
+
},
|
|
1969
|
+
agnes: {
|
|
1970
|
+
enabled: { chat_template_kwargs: { enable_thinking: true } },
|
|
1971
|
+
disabled: {}
|
|
1972
|
+
},
|
|
1973
|
+
nvidia: {
|
|
1974
|
+
enabled: { chat_template_kwargs: { enable_thinking: true } },
|
|
1975
|
+
disabled: { chat_template_kwargs: { enable_thinking: false } }
|
|
1976
|
+
},
|
|
1977
|
+
minimax: {
|
|
1978
|
+
enabled: {
|
|
1979
|
+
thinking: { type: "adaptive" },
|
|
1980
|
+
reasoning_split: true
|
|
1981
|
+
},
|
|
1982
|
+
disabled: { thinking: { type: "disabled" } }
|
|
1983
|
+
}
|
|
1984
|
+
};
|
|
1985
|
+
|
|
2014
1986
|
// src/agent/llm-stream.ts
|
|
2015
1987
|
function chatCompletionsUrl(baseUrl) {
|
|
2016
1988
|
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
@@ -2031,6 +2003,16 @@ async function startLLMStream(options, callbacks) {
|
|
|
2031
2003
|
body.tools = options.tools;
|
|
2032
2004
|
body.tool_choice = "auto";
|
|
2033
2005
|
}
|
|
2006
|
+
if (options.thinkingEnabled !== void 0 && options.thinkingProvider) {
|
|
2007
|
+
const thinkingParams = resolveThinkingParams(
|
|
2008
|
+
options.thinkingProvider,
|
|
2009
|
+
options.thinkingEnabled,
|
|
2010
|
+
options.thinkingOverride
|
|
2011
|
+
);
|
|
2012
|
+
if (thinkingParams) {
|
|
2013
|
+
Object.assign(body, thinkingParams);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2034
2016
|
try {
|
|
2035
2017
|
const response = await fetch(url, {
|
|
2036
2018
|
method: "POST",
|
|
@@ -2039,7 +2021,8 @@ async function startLLMStream(options, callbacks) {
|
|
|
2039
2021
|
"Content-Type": "application/json",
|
|
2040
2022
|
Accept: "text/event-stream"
|
|
2041
2023
|
},
|
|
2042
|
-
body: JSON.stringify(body)
|
|
2024
|
+
body: JSON.stringify(body),
|
|
2025
|
+
signal: options.signal
|
|
2043
2026
|
});
|
|
2044
2027
|
if (!response.ok) {
|
|
2045
2028
|
const errorBody = await response.text().catch(() => "");
|
|
@@ -2056,8 +2039,22 @@ async function startLLMStream(options, callbacks) {
|
|
|
2056
2039
|
}
|
|
2057
2040
|
});
|
|
2058
2041
|
let hasToolCalls = false;
|
|
2042
|
+
const onAbort = () => {
|
|
2043
|
+
reader.cancel().catch(() => {
|
|
2044
|
+
});
|
|
2045
|
+
};
|
|
2046
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2059
2047
|
while (true) {
|
|
2060
|
-
|
|
2048
|
+
if (options.signal?.aborted) {
|
|
2049
|
+
break;
|
|
2050
|
+
}
|
|
2051
|
+
let chunk;
|
|
2052
|
+
try {
|
|
2053
|
+
chunk = await reader.read();
|
|
2054
|
+
} catch {
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
const { done, value } = chunk;
|
|
2061
2058
|
if (done) break;
|
|
2062
2059
|
buffer += decoder.decode(value, { stream: true });
|
|
2063
2060
|
while (true) {
|
|
@@ -2181,7 +2178,24 @@ async function runAgentWithTools(input, toolContext, onEvent) {
|
|
|
2181
2178
|
let messages = [...input.messages];
|
|
2182
2179
|
let cumulativeUsage;
|
|
2183
2180
|
while (true) {
|
|
2184
|
-
|
|
2181
|
+
if (input.signal?.aborted) {
|
|
2182
|
+
killShellsByTask(input.taskId);
|
|
2183
|
+
onEvent({ type: "status", taskId: input.taskId, status: "cancelled" });
|
|
2184
|
+
onEvent({ type: "done", taskId: input.taskId, usage: cumulativeUsage });
|
|
2185
|
+
return messages;
|
|
2186
|
+
}
|
|
2187
|
+
let turn;
|
|
2188
|
+
try {
|
|
2189
|
+
turn = await runSingleAgentTurn(input, messages, onEvent);
|
|
2190
|
+
} catch (err) {
|
|
2191
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2192
|
+
killShellsByTask(input.taskId);
|
|
2193
|
+
onEvent({ type: "status", taskId: input.taskId, status: "cancelled" });
|
|
2194
|
+
onEvent({ type: "done", taskId: input.taskId, usage: cumulativeUsage });
|
|
2195
|
+
return messages;
|
|
2196
|
+
}
|
|
2197
|
+
throw err;
|
|
2198
|
+
}
|
|
2185
2199
|
if (turn.usage) {
|
|
2186
2200
|
cumulativeUsage = cumulativeUsage ? {
|
|
2187
2201
|
promptTokens: cumulativeUsage.promptTokens + turn.usage.promptTokens,
|
|
@@ -2191,14 +2205,14 @@ async function runAgentWithTools(input, toolContext, onEvent) {
|
|
|
2191
2205
|
}
|
|
2192
2206
|
if (turn.toolCalls.length === 0) {
|
|
2193
2207
|
if (turn.content || turn.reasoningContent) {
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
];
|
|
2208
|
+
const assistantMessage = { role: "assistant" };
|
|
2209
|
+
if (turn.content.trim()) {
|
|
2210
|
+
assistantMessage.content = turn.content;
|
|
2211
|
+
}
|
|
2212
|
+
if (turn.reasoningContent.trim()) {
|
|
2213
|
+
assistantMessage.reasoning_content = turn.reasoningContent;
|
|
2214
|
+
}
|
|
2215
|
+
messages = [...messages, assistantMessage];
|
|
2202
2216
|
}
|
|
2203
2217
|
onEvent({ type: "done", taskId: input.taskId, usage: cumulativeUsage ?? turn.usage });
|
|
2204
2218
|
onEvent({ type: "status", taskId: input.taskId, status: "completed" });
|
|
@@ -2219,7 +2233,11 @@ async function runSingleAgentTurn(input, messages, onEvent) {
|
|
|
2219
2233
|
apiKey: input.apiKey,
|
|
2220
2234
|
model: input.model,
|
|
2221
2235
|
messages,
|
|
2222
|
-
tools: getToolDefinitions(input.agentMode)
|
|
2236
|
+
tools: getToolDefinitions(input.agentMode),
|
|
2237
|
+
thinkingProvider: input.provider,
|
|
2238
|
+
thinkingEnabled: input.thinkingEnabled,
|
|
2239
|
+
thinkingOverride: input.thinkingParams,
|
|
2240
|
+
signal: input.signal
|
|
2223
2241
|
},
|
|
2224
2242
|
{
|
|
2225
2243
|
onContent: (delta) => {
|
|
@@ -2329,31 +2347,434 @@ async function appendToolResults(messages, turn, context, onEvent) {
|
|
|
2329
2347
|
// src/agent/environment/index.ts
|
|
2330
2348
|
var import_node_os2 = require("node:os");
|
|
2331
2349
|
var import_node_child_process2 = require("node:child_process");
|
|
2332
|
-
var
|
|
2333
|
-
var
|
|
2350
|
+
var import_node_fs12 = require("node:fs");
|
|
2351
|
+
var import_node_path13 = require("node:path");
|
|
2352
|
+
|
|
2353
|
+
// ../src/features/skills/system/registry.ts
|
|
2354
|
+
var OPERATING_PRINCIPLES_CONTENT = `# Agent Operating Principles
|
|
2355
|
+
|
|
2356
|
+
Your job is to understand the user's intent, make correct changes, verify the result, and communicate accurately.
|
|
2357
|
+
|
|
2358
|
+
### Core rules
|
|
2359
|
+
|
|
2360
|
+
- Follow the user's request. Do not expand scope without a clear reason.
|
|
2361
|
+
- When the request has multiple valid interpretations, state your assumption
|
|
2362
|
+
explicitly and proceed. Only ask when the decision is costly to reverse.
|
|
2363
|
+
- Prefer evidence over confidence. Tool output is more reliable than assumptions.
|
|
2364
|
+
- Never present guesses as facts. Mark uncertainty plainly when evidence is incomplete.
|
|
2365
|
+
- Optimize for user-visible outcomes, not internal activity.
|
|
2366
|
+
- Keep changes correct, readable, maintainable, testable, and secure.
|
|
2367
|
+
- Do not push commits, rewrite history, or perform destructive actions unless explicitly instructed.
|
|
2368
|
+
|
|
2369
|
+
### Decision order
|
|
2370
|
+
|
|
2371
|
+
0. Determine whether this is a question, an exploration, or a change request.
|
|
2372
|
+
Answer questions directly; act only on clear instructions.
|
|
2373
|
+
1. Understand the request.
|
|
2374
|
+
2. Gather only the context needed to act safely.
|
|
2375
|
+
3. Plan when the work has meaningful phases.
|
|
2376
|
+
4. Modify the smallest surface that solves the problem.
|
|
2377
|
+
5. Verify before claiming success.
|
|
2378
|
+
6. Report the outcome, verification, and any remaining risk.
|
|
2379
|
+
`;
|
|
2380
|
+
var CONTEXT_AND_EVIDENCE_CONTENT = `# Context and Evidence
|
|
2381
|
+
|
|
2382
|
+
Treat provided context as useful signal, not guaranteed truth.
|
|
2383
|
+
|
|
2384
|
+
Do not assume:
|
|
2385
|
+
|
|
2386
|
+
- file contents
|
|
2387
|
+
- repository structure
|
|
2388
|
+
- command output
|
|
2389
|
+
- test results
|
|
2390
|
+
- git state
|
|
2391
|
+
- API behavior
|
|
2392
|
+
- web content
|
|
2393
|
+
|
|
2394
|
+
Use tools to confirm facts whenever the answer or change depends on them.
|
|
2395
|
+
|
|
2396
|
+
### Evidence handling
|
|
2397
|
+
|
|
2398
|
+
- Read the relevant file before editing it.
|
|
2399
|
+
- Use search results to decide what to inspect, not as a substitute for inspection.
|
|
2400
|
+
- Treat shell output, linter output, test output, and git output as source-of-truth for the current workspace state.
|
|
2401
|
+
- If evidence contradicts your expectation, update your understanding immediately.
|
|
2402
|
+
- If required evidence is unavailable, say what is missing and avoid pretending the task is fully verified.
|
|
2403
|
+
`;
|
|
2404
|
+
var TOOL_USAGE_CONTENT = `# Tool Usage
|
|
2405
|
+
|
|
2406
|
+
Use tools when they provide evidence that would otherwise be guessed.
|
|
2407
|
+
|
|
2408
|
+
### Local tools
|
|
2409
|
+
|
|
2410
|
+
- Use glob to find files by name pattern.
|
|
2411
|
+
- Use grep to search file contents by unique strings, symbols, paths, routes, config keys, or errors.
|
|
2412
|
+
- Use shell for builds, tests, git operations, package commands, and repository inspection.
|
|
2413
|
+
- Commands run non-interactively. Avoid commands that require interactive input.
|
|
2414
|
+
- For long-running commands such as dev servers or watch mode, run shell with \`block_until_ms=0\`, then await the returned \`shell_id\` when needed.
|
|
2415
|
+
- Use \`get_workspace_tree\` for a bird's-eye view of the project structure. It respects \`.gitignore\`, excludes large directories (\`node_modules\`, \`.git\`, \`dist\`, etc.) automatically, and paginates like \`read_file\` via \`start_line\` and \`max_lines\`. Prefer this over manually calling \`list_dir\` on every subdirectory.
|
|
2416
|
+
- **File editing workflow:** \`edit_file\` (search-and-replace, text-anchored) is the most common file editing tool \u2014 highest priority. If escaping issues arise, fall back to \`replace_lines\` (by line number, read the file again first to avoid line number drift). Last resort: \`replace_file\` (supports both partial and full replacement, but full replacement consumes many tokens \u2014 use sparingly). Use \`write_file\` to create new files. Use \`read_file\`'s \`sha256\` as \`expected_sha256\` for concurrent safety.
|
|
2417
|
+
|
|
2418
|
+
### Web and skills
|
|
2419
|
+
|
|
2420
|
+
- Use web_search for current or external information, such as version-specific behavior or recent documentation.
|
|
2421
|
+
- Use browse_page to read a specific URL after web_search finds a promising primary source.
|
|
2422
|
+
- browse_page may not render JavaScript-heavy pages. Quote retrieved content instead of inventing details.
|
|
2423
|
+
- Use list_skills to inspect available skills.
|
|
2424
|
+
- Use read_skill before following a skill's instructions.
|
|
2425
|
+
- Use create_skill only when the user asks to save reusable instructions.
|
|
2426
|
+
|
|
2427
|
+
### Failure handling
|
|
2428
|
+
|
|
2429
|
+
When a tool fails:
|
|
2430
|
+
|
|
2431
|
+
1. Read the error code and message.
|
|
2432
|
+
2. Form a new hypothesis.
|
|
2433
|
+
3. Adjust the approach.
|
|
2434
|
+
|
|
2435
|
+
Do not repeat the same failing action without learning from the failure.
|
|
2436
|
+
|
|
2437
|
+
### Shell tools
|
|
2438
|
+
|
|
2439
|
+
You have 5 shell tools. Use them together:
|
|
2440
|
+
|
|
2441
|
+
- **shell** \u2014 run a command. Set \`block_until_ms=0\` for background mode; returns a \`shell_id\`.
|
|
2442
|
+
- **await** \u2014 poll a background shell to completion. Pass the \`shell_id\` from shell.
|
|
2443
|
+
- **list_shells** \u2014 list active shells. Default shows running only; use \`status_filter="all"\` to see all states.
|
|
2444
|
+
- **read_shell_logs** \u2014 read stdout/stderr from any shell. Paginate with \`offset\` and \`limit\`.
|
|
2445
|
+
- **kill_shell** \u2014 kill a running shell by \`shell_id\`. Cannot kill human terminals.
|
|
2446
|
+
|
|
2447
|
+
Workflows:
|
|
2448
|
+
1. **background + await**: \`shell(cmd, {block_until_ms: 0})\` \u2192 do other work \u2192 \`await({shell_id})\`
|
|
2449
|
+
2. **monitor progress**: \`shell(background)\` \u2192 \`read_shell_logs\` to peek \u2192 \`await\` when done
|
|
2450
|
+
3. **clean up**: \`list_shells({status_filter: "all"})\` \u2192 \`read_shell_logs\` \u2192 \`kill_shell\` if stuck
|
|
2451
|
+
|
|
2452
|
+
**Remote target limitation:** When using \`shell(target: "<alias>")\`, background mode does NOT return a usable \`shell_id\`, so companion tools (\`await\`, \`list_shells\`, \`kill_shell\`, \`read_shell_logs\`) are not supported. Only blocking mode works for remote commands.
|
|
2453
|
+
|
|
2454
|
+
### spawn_subagent
|
|
2455
|
+
|
|
2456
|
+
Use spawn_subagent for independent tasks that require multi-step exploration, verification, or research.
|
|
2457
|
+
|
|
2458
|
+
Do not use it for simple lookups, single-file reads, or tasks that can be completed with a few direct tool calls.
|
|
2459
|
+
|
|
2460
|
+
Before spawning, ask:
|
|
2461
|
+
1. Is the task independent?
|
|
2462
|
+
2. Does it require significant exploration?
|
|
2463
|
+
3. Would delegation improve focus?
|
|
2464
|
+
|
|
2465
|
+
If not, do the work yourself.
|
|
2466
|
+
|
|
2467
|
+
Provide a clear task description and expected output. Sub-agents should return findings, evidence, and uncertainties.
|
|
2468
|
+
`;
|
|
2469
|
+
var CODE_NAVIGATION_CONTENT = `# Code Navigation
|
|
2470
|
+
|
|
2471
|
+
Prefer direct signals over dependency wandering.
|
|
2472
|
+
|
|
2473
|
+
### Default flow
|
|
2474
|
+
|
|
2475
|
+
\`\`\`text
|
|
2476
|
+
search -> inspect -> modify
|
|
2477
|
+
\`\`\`
|
|
2478
|
+
|
|
2479
|
+
Avoid manually tracing long chains from entry point to imports unless direct search is insufficient.
|
|
2480
|
+
|
|
2481
|
+
### Search signals
|
|
2482
|
+
|
|
2483
|
+
Search for the most unique signal the target code would contain:
|
|
2484
|
+
|
|
2485
|
+
- function or method names: \`calculateTotal\`, \`handleSubmit\`
|
|
2486
|
+
- constants or variables: \`MAX_RETRY_COUNT\`, \`workspaceName\`
|
|
2487
|
+
- route strings: \`"/api/users"\`, \`"/login"\`
|
|
2488
|
+
- framework annotations: \`@RestController\`, \`@Service\`
|
|
2489
|
+
- trait, interface, or class names: \`UserRepository\`, \`impl Iterator\`
|
|
2490
|
+
- test descriptions: \`"should return 401"\`, \`testShould\`
|
|
2491
|
+
- config keys: \`database.url\`, \`logging.level\`
|
|
2492
|
+
- model, table, or schema names: \`users\`, \`class User\`
|
|
2493
|
+
- exact error messages from logs, tests, or users
|
|
2494
|
+
|
|
2495
|
+
### Reading discipline
|
|
2496
|
+
|
|
2497
|
+
- Read only files needed to understand or modify the target behavior.
|
|
2498
|
+
- Prefer narrow reads around relevant code when files are large.
|
|
2499
|
+
- Expand outward only when the local context is insufficient.
|
|
2500
|
+
`;
|
|
2501
|
+
var TASK_PLANNING_CONTENT = `# Task Planning
|
|
2502
|
+
|
|
2503
|
+
Use the task-progress list to make meaningful multi-step work legible.
|
|
2504
|
+
|
|
2505
|
+
### Create a task list when
|
|
2506
|
+
|
|
2507
|
+
- implementing a feature across exploration, edits, and verification
|
|
2508
|
+
- debugging with multiple hypotheses
|
|
2509
|
+
- refactoring or migrating several files or layers
|
|
2510
|
+
- finishing work with clear phases such as design, implement, test, polish
|
|
2511
|
+
- running long work where the user may return later
|
|
2512
|
+
|
|
2513
|
+
### Skip a task list for
|
|
2514
|
+
|
|
2515
|
+
- short answers or explanations
|
|
2516
|
+
- one-command requests
|
|
2517
|
+
- a single obvious edit
|
|
2518
|
+
- pure exploration questions
|
|
2519
|
+
- trivial follow-ups
|
|
2520
|
+
|
|
2521
|
+
When unsure, prefer no list over a noisy one.
|
|
2522
|
+
|
|
2523
|
+
### Task design
|
|
2524
|
+
|
|
2525
|
+
- Write outcome-oriented tasks: "Add OAuth callback validation", not "Read auth file".
|
|
2526
|
+
- Keep tasks coarse enough for the user to follow, usually 3-5 items.
|
|
2527
|
+
- Keep at most one task in progress.
|
|
2528
|
+
- Mark tasks complete as soon as they are actually complete.
|
|
2529
|
+
- Rename, add, cancel, or remove tasks when scope changes.
|
|
2530
|
+
`;
|
|
2531
|
+
var CODE_MODIFICATION_CONTENT = `# Code Modification
|
|
2532
|
+
|
|
2533
|
+
Make changes as a maintainer, not as a patch generator.
|
|
2534
|
+
|
|
2535
|
+
### Before editing
|
|
2536
|
+
|
|
2537
|
+
1. Locate the relevant implementation.
|
|
2538
|
+
2. Understand surrounding context.
|
|
2539
|
+
3. Identify the smallest change that solves the requested problem.
|
|
2540
|
+
|
|
2541
|
+
### Editing rules
|
|
2542
|
+
|
|
2543
|
+
- Prefer minimal, targeted changes.
|
|
2544
|
+
- Follow existing naming, architecture, formatting, testing, and error-handling patterns.
|
|
2545
|
+
- Do not rewrite working code unnecessarily.
|
|
2546
|
+
- Do not change unrelated behavior.
|
|
2547
|
+
- Do not introduce style-only edits unless requested.
|
|
2548
|
+
- Do not remove functionality without a clear reason.
|
|
2549
|
+
- Do not overwrite user changes. If the working tree is dirty, work with existing changes instead of reverting them.
|
|
2550
|
+
- Use structured APIs or parsers for structured data when available.
|
|
2551
|
+
|
|
2552
|
+
### High-risk areas
|
|
2553
|
+
|
|
2554
|
+
Be extra careful with authentication, authorization, persistence, migrations, production configuration, secrets, and destructive operations.
|
|
2555
|
+
`;
|
|
2556
|
+
var VERIFICATION_CONTENT = `# Verification
|
|
2557
|
+
|
|
2558
|
+
Do not claim success solely because code was changed.
|
|
2559
|
+
|
|
2560
|
+
### After changing code
|
|
2561
|
+
|
|
2562
|
+
1. Re-read the changed area when practical.
|
|
2563
|
+
2. Review the diff to confirm only intended changes are included.
|
|
2564
|
+
3. Before running any verification command, call list_shells first to check whether the user already has a running dev server or relevant process. If one exists, prefer telling the user to reload over starting a new instance. Only run a new verification command when no relevant process is already running.
|
|
2565
|
+
4. Run the most relevant verification available.
|
|
2566
|
+
5. Report what was verified and what was not.
|
|
2567
|
+
|
|
2568
|
+
### Prefer relevant checks
|
|
2569
|
+
|
|
2570
|
+
- TypeScript: \`tsc --noEmit\`, framework type checks, or project scripts.
|
|
2571
|
+
- Tests: focused tests first, broader suites when risk is high.
|
|
2572
|
+
- Builds: run when the change can affect packaging, routing, generated output, or runtime wiring.
|
|
2573
|
+
- Linters: run or inspect diagnostics for changed files.
|
|
2574
|
+
|
|
2575
|
+
If verification fails, read the error, fix what is in scope, and rerun the relevant check. If verification cannot be run, state that clearly.
|
|
2576
|
+
`;
|
|
2577
|
+
var GIT_WORKFLOW_CONTENT = `# Git Workflow
|
|
2578
|
+
|
|
2579
|
+
Git operations must reflect actual repository state.
|
|
2580
|
+
|
|
2581
|
+
### Before committing
|
|
2582
|
+
|
|
2583
|
+
- Review \`git status\`.
|
|
2584
|
+
- Review \`git diff\` and \`git diff --staged\` as appropriate.
|
|
2585
|
+
- Do not assume staged content matches the current task.
|
|
2586
|
+
- Do not commit secrets or credentials.
|
|
2587
|
+
|
|
2588
|
+
### Staging
|
|
2589
|
+
|
|
2590
|
+
- Stage only files related to the intended change.
|
|
2591
|
+
- Avoid \`git add .\` and \`git add -A\` unless the user explicitly wants all changes included.
|
|
2592
|
+
- Leave unrelated modifications unstaged.
|
|
2593
|
+
|
|
2594
|
+
### Commits
|
|
2595
|
+
|
|
2596
|
+
- Generate commit messages from staged changes only.
|
|
2597
|
+
- Use Conventional Commits: \`type(scope): summary\`.
|
|
2598
|
+
- Keep the subject under 72 characters.
|
|
2599
|
+
- Keep commits logically coherent.
|
|
2600
|
+
|
|
2601
|
+
### Push behavior
|
|
2602
|
+
|
|
2603
|
+
- "commit" means commit only.
|
|
2604
|
+
- "push" means push only.
|
|
2605
|
+
- "commit and push" means both.
|
|
2606
|
+
- Never push unless explicitly instructed.
|
|
2607
|
+
`;
|
|
2608
|
+
var COMMUNICATION_CONTENT = `# Communication
|
|
2609
|
+
|
|
2610
|
+
Communicate conclusions, decisions, blockers, and verification results.
|
|
2611
|
+
|
|
2612
|
+
### Style
|
|
2613
|
+
|
|
2614
|
+
- Be concise and direct.
|
|
2615
|
+
- Do not narrate every tool call.
|
|
2616
|
+
- Do not reveal hidden chain-of-thought.
|
|
2617
|
+
- Do not frame internal activity as an accomplishment.
|
|
2618
|
+
- Explain uncertainty and blockers honestly.
|
|
2619
|
+
- When reporting completion, include the user-visible result and verification performed.
|
|
2620
|
+
|
|
2621
|
+
### Reviews
|
|
2622
|
+
|
|
2623
|
+
When the user asks for a review, prioritize findings first:
|
|
2624
|
+
|
|
2625
|
+
- correctness bugs
|
|
2626
|
+
- security risks
|
|
2627
|
+
- behavioral regressions
|
|
2628
|
+
- missing tests for meaningful risk
|
|
2629
|
+
|
|
2630
|
+
Order findings by severity. If no issues are found, say so and mention residual risk or unrun checks.
|
|
2631
|
+
`;
|
|
2632
|
+
var CODE_REVIEW_CONTENT = `# Code Review Workflow
|
|
2633
|
+
|
|
2634
|
+
Use this workflow when reviewing code rather than implementing changes.
|
|
2635
|
+
|
|
2636
|
+
### Review focus
|
|
2637
|
+
|
|
2638
|
+
1. Correctness and edge cases.
|
|
2639
|
+
2. Security issues such as injection, XSS, authorization gaps, and leaked secrets.
|
|
2640
|
+
3. Behavioral regressions.
|
|
2641
|
+
4. Error handling and failure modes.
|
|
2642
|
+
5. Test coverage for meaningful behavior.
|
|
2643
|
+
6. Readability and maintainability when it affects future correctness.
|
|
2644
|
+
|
|
2645
|
+
### Feedback format
|
|
2646
|
+
|
|
2647
|
+
- Lead with findings, ordered by severity.
|
|
2648
|
+
- Cite the specific file or symbol involved.
|
|
2649
|
+
- Explain the impact, not just the preference.
|
|
2650
|
+
- Keep summaries brief and secondary.
|
|
2651
|
+
- If there are no findings, say that clearly and list any verification gaps.
|
|
2652
|
+
`;
|
|
2653
|
+
var SYSTEM_SKILLS = [
|
|
2654
|
+
{
|
|
2655
|
+
id: "agent-operating-principles",
|
|
2656
|
+
slug: "agent-operating-principles",
|
|
2657
|
+
name: "Agent Operating Principles",
|
|
2658
|
+
description: "Core identity, priorities, and decision order for software engineering agent work.",
|
|
2659
|
+
content: OPERATING_PRINCIPLES_CONTENT,
|
|
2660
|
+
defaultEnabled: true,
|
|
2661
|
+
category: "core"
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
id: "context-and-evidence",
|
|
2665
|
+
slug: "context-and-evidence",
|
|
2666
|
+
name: "Context and Evidence",
|
|
2667
|
+
description: "How to treat workspace context, tool output, uncertainty, and evidence before acting.",
|
|
2668
|
+
content: CONTEXT_AND_EVIDENCE_CONTENT,
|
|
2669
|
+
defaultEnabled: true,
|
|
2670
|
+
category: "core"
|
|
2671
|
+
},
|
|
2672
|
+
{
|
|
2673
|
+
id: "tool-usage",
|
|
2674
|
+
slug: "tool-usage",
|
|
2675
|
+
name: "Tool Usage",
|
|
2676
|
+
description: "When and how to use filesystem, shell, web, and skill tools for reliable evidence.",
|
|
2677
|
+
content: TOOL_USAGE_CONTENT,
|
|
2678
|
+
defaultEnabled: true,
|
|
2679
|
+
category: "core"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
id: "code-navigation",
|
|
2683
|
+
slug: "code-navigation",
|
|
2684
|
+
name: "Code Navigation",
|
|
2685
|
+
description: "Search-first navigation rules for locating relevant code without wasting context.",
|
|
2686
|
+
content: CODE_NAVIGATION_CONTENT,
|
|
2687
|
+
defaultEnabled: true,
|
|
2688
|
+
category: "development"
|
|
2689
|
+
},
|
|
2690
|
+
{
|
|
2691
|
+
id: "task-planning",
|
|
2692
|
+
slug: "task-planning",
|
|
2693
|
+
name: "Task Planning",
|
|
2694
|
+
description: "When and how to use the session task-progress list for multi-step work.",
|
|
2695
|
+
content: TASK_PLANNING_CONTENT,
|
|
2696
|
+
defaultEnabled: true,
|
|
2697
|
+
category: "workflow"
|
|
2698
|
+
},
|
|
2699
|
+
{
|
|
2700
|
+
id: "code-modification",
|
|
2701
|
+
slug: "code-modification",
|
|
2702
|
+
name: "Code Modification",
|
|
2703
|
+
description: "Rules for making minimal, maintainable, and project-consistent code changes.",
|
|
2704
|
+
content: CODE_MODIFICATION_CONTENT,
|
|
2705
|
+
defaultEnabled: true,
|
|
2706
|
+
category: "development"
|
|
2707
|
+
},
|
|
2708
|
+
{
|
|
2709
|
+
id: "verification",
|
|
2710
|
+
slug: "verification",
|
|
2711
|
+
name: "Verification",
|
|
2712
|
+
description: "How to validate changes before claiming success and how to report unverified work.",
|
|
2713
|
+
content: VERIFICATION_CONTENT,
|
|
2714
|
+
defaultEnabled: true,
|
|
2715
|
+
category: "workflow"
|
|
2716
|
+
},
|
|
2717
|
+
{
|
|
2718
|
+
id: "git-workflow",
|
|
2719
|
+
slug: "git-workflow",
|
|
2720
|
+
name: "Git Workflow",
|
|
2721
|
+
description: "Safe staging, commit, and push boundaries based on actual git state.",
|
|
2722
|
+
content: GIT_WORKFLOW_CONTENT,
|
|
2723
|
+
defaultEnabled: true,
|
|
2724
|
+
category: "workflow"
|
|
2725
|
+
},
|
|
2726
|
+
{
|
|
2727
|
+
id: "communication",
|
|
2728
|
+
slug: "communication",
|
|
2729
|
+
name: "Communication",
|
|
2730
|
+
description: "How to communicate outcomes, uncertainty, blockers, verification, and review findings.",
|
|
2731
|
+
content: COMMUNICATION_CONTENT,
|
|
2732
|
+
defaultEnabled: true,
|
|
2733
|
+
category: "core"
|
|
2734
|
+
},
|
|
2735
|
+
{
|
|
2736
|
+
id: "code-review",
|
|
2737
|
+
slug: "code-review",
|
|
2738
|
+
name: "Code Review Workflow",
|
|
2739
|
+
description: "Specialized workflow for reviewing code for correctness, security, regressions, and tests.",
|
|
2740
|
+
content: CODE_REVIEW_CONTENT,
|
|
2741
|
+
defaultEnabled: false,
|
|
2742
|
+
category: "review"
|
|
2743
|
+
}
|
|
2744
|
+
];
|
|
2745
|
+
var slugSet = /* @__PURE__ */ new Set();
|
|
2746
|
+
for (const skill of SYSTEM_SKILLS) {
|
|
2747
|
+
if (slugSet.has(skill.slug)) {
|
|
2748
|
+
throw new Error(`Duplicate system skill slug: ${skill.slug}`);
|
|
2749
|
+
}
|
|
2750
|
+
slugSet.add(skill.slug);
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// src/agent/environment/index.ts
|
|
2334
2754
|
function resolveAgentEnvironment(workspaceDir) {
|
|
2335
2755
|
const os = `${(0, import_node_os2.platform)()} (${(0, import_node_os2.release)()})`;
|
|
2336
2756
|
const shell = resolveShell();
|
|
2337
2757
|
const isGitRepository = workspaceDir ? checkIsGitRepository(workspaceDir) : false;
|
|
2338
|
-
const today = (/* @__PURE__ */ new Date())
|
|
2339
|
-
weekday: "long",
|
|
2340
|
-
year: "numeric",
|
|
2341
|
-
month: "long",
|
|
2342
|
-
day: "numeric",
|
|
2343
|
-
timeZone: "UTC"
|
|
2344
|
-
});
|
|
2758
|
+
const today = formatToday(/* @__PURE__ */ new Date());
|
|
2345
2759
|
let agentsMd = null;
|
|
2346
2760
|
if (workspaceDir) {
|
|
2347
|
-
const agentsMdPath = (0,
|
|
2348
|
-
if ((0,
|
|
2349
|
-
const content = (0,
|
|
2761
|
+
const agentsMdPath = (0, import_node_path13.resolve)(workspaceDir, "AGENTS.md");
|
|
2762
|
+
if ((0, import_node_fs12.existsSync)(agentsMdPath)) {
|
|
2763
|
+
const content = (0, import_node_fs12.readFileSync)(agentsMdPath, "utf-8");
|
|
2350
2764
|
agentsMd = {
|
|
2351
2765
|
path: agentsMdPath,
|
|
2352
|
-
content: content.length >
|
|
2353
|
-
truncated: content.length >
|
|
2766
|
+
content: content.length > 32e3 ? content.slice(0, 32e3) + "\n... [truncated]" : content,
|
|
2767
|
+
truncated: content.length > 32e3
|
|
2354
2768
|
};
|
|
2355
2769
|
}
|
|
2356
2770
|
}
|
|
2771
|
+
const enabledSystemSkills = SYSTEM_SKILLS.filter(
|
|
2772
|
+
(skill) => skill.defaultEnabled
|
|
2773
|
+
).map((skill) => ({
|
|
2774
|
+
slug: skill.slug,
|
|
2775
|
+
name: skill.name,
|
|
2776
|
+
content: skill.content
|
|
2777
|
+
}));
|
|
2357
2778
|
return {
|
|
2358
2779
|
workspaceDir,
|
|
2359
2780
|
os,
|
|
@@ -2361,9 +2782,104 @@ function resolveAgentEnvironment(workspaceDir) {
|
|
|
2361
2782
|
isGitRepository,
|
|
2362
2783
|
today,
|
|
2363
2784
|
agentsMd,
|
|
2364
|
-
enabledSystemSkills
|
|
2785
|
+
enabledSystemSkills
|
|
2365
2786
|
};
|
|
2366
2787
|
}
|
|
2788
|
+
function buildSystemPrompt(env, agentMode) {
|
|
2789
|
+
const workspaceLine = env.workspaceDir ?? "not selected";
|
|
2790
|
+
const gitLine = env.isGitRepository ? "yes" : env.workspaceDir ? "no" : "unknown";
|
|
2791
|
+
const modeLine = buildModeLine(agentMode);
|
|
2792
|
+
const modeGuidance = buildModeGuidance(agentMode, env.workspaceDir);
|
|
2793
|
+
const allBlocks = [];
|
|
2794
|
+
allBlocks.push(
|
|
2795
|
+
[
|
|
2796
|
+
"You are Coder, a helpful terminal AI assistant.",
|
|
2797
|
+
"",
|
|
2798
|
+
"## Environment",
|
|
2799
|
+
"",
|
|
2800
|
+
`- workspaceDir: ${workspaceLine}`,
|
|
2801
|
+
`- os: ${env.os}`,
|
|
2802
|
+
`- shell: ${env.shell}`,
|
|
2803
|
+
`- gitRepository: ${gitLine}`,
|
|
2804
|
+
`- date: ${env.today}`,
|
|
2805
|
+
`- mode: ${modeLine}`
|
|
2806
|
+
].join("\n")
|
|
2807
|
+
);
|
|
2808
|
+
allBlocks.push(
|
|
2809
|
+
[
|
|
2810
|
+
"## Communication Rules",
|
|
2811
|
+
"",
|
|
2812
|
+
"1. Reply in the same language the user uses. Be concise, accurate, and friendly.",
|
|
2813
|
+
"2. Do not act unless the user has clearly asked you to. Answering questions, explaining, and analyzing do not require action \u2014 stop before reaching for tools."
|
|
2814
|
+
].join("\n")
|
|
2815
|
+
);
|
|
2816
|
+
for (const skill of env.enabledSystemSkills) {
|
|
2817
|
+
allBlocks.push(
|
|
2818
|
+
`## ${skill.name}
|
|
2819
|
+
|
|
2820
|
+
${stripLeadingMarkdownH1(skill.content)}`
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
if (env.agentsMd?.content.trim()) {
|
|
2824
|
+
allBlocks.push(buildProjectInstructionsSection(env.agentsMd));
|
|
2825
|
+
}
|
|
2826
|
+
if (modeGuidance.length > 0) {
|
|
2827
|
+
allBlocks.push(modeGuidance.join("\n"));
|
|
2828
|
+
}
|
|
2829
|
+
return allBlocks.join("\n\n---\n\n");
|
|
2830
|
+
}
|
|
2831
|
+
function buildModeLine(agentMode) {
|
|
2832
|
+
switch (agentMode) {
|
|
2833
|
+
case "ask":
|
|
2834
|
+
return "ask (read-only: can read files, search code, browse the web, and list skills \u2014 cannot modify files or run shell commands)";
|
|
2835
|
+
default:
|
|
2836
|
+
return "agent (full tool access)";
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
function buildModeGuidance(agentMode, _workspaceDir) {
|
|
2840
|
+
if (agentMode === "ask") {
|
|
2841
|
+
return buildAskModeGuidance();
|
|
2842
|
+
}
|
|
2843
|
+
return [];
|
|
2844
|
+
}
|
|
2845
|
+
function buildAskModeGuidance() {
|
|
2846
|
+
return [
|
|
2847
|
+
"## Mode Guidance",
|
|
2848
|
+
"",
|
|
2849
|
+
"You are in Ask mode \u2014 you can only read files, search, and browse.",
|
|
2850
|
+
"When the user asks you to modify files, run commands, or perform any write operation:",
|
|
2851
|
+
" - Explain that the task requires write access.",
|
|
2852
|
+
" - Tell the user they can switch to Agent mode by using `coder run` instead of `coder ask` to give you full tool access.",
|
|
2853
|
+
`Do NOT silently refuse or just say "I can't do that." Always provide a clear path forward.`
|
|
2854
|
+
];
|
|
2855
|
+
}
|
|
2856
|
+
function buildProjectInstructionsSection(agentsMd) {
|
|
2857
|
+
const lines = [
|
|
2858
|
+
"## Project instructions (AGENTS.md)",
|
|
2859
|
+
"",
|
|
2860
|
+
"Follow these project-specific rules when they do not conflict with the user's current message.",
|
|
2861
|
+
agentsMd.content.trimEnd()
|
|
2862
|
+
];
|
|
2863
|
+
if (agentsMd.truncated) {
|
|
2864
|
+
lines.push(
|
|
2865
|
+
"",
|
|
2866
|
+
`Note: ${agentsMd.path} was truncated to 32 KB. Use read_file on ${agentsMd.path} to read the full file if needed.`
|
|
2867
|
+
);
|
|
2868
|
+
}
|
|
2869
|
+
return lines.join("\n");
|
|
2870
|
+
}
|
|
2871
|
+
function stripLeadingMarkdownH1(content) {
|
|
2872
|
+
return content.replace(/^#\s+[^\n]+\n+/, "").trim();
|
|
2873
|
+
}
|
|
2874
|
+
function formatToday(date) {
|
|
2875
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
2876
|
+
year: "numeric",
|
|
2877
|
+
month: "2-digit",
|
|
2878
|
+
day: "2-digit",
|
|
2879
|
+
weekday: "long",
|
|
2880
|
+
timeZoneName: "longOffset"
|
|
2881
|
+
}).format(date);
|
|
2882
|
+
}
|
|
2367
2883
|
function resolveShell() {
|
|
2368
2884
|
const p = (0, import_node_os2.platform)();
|
|
2369
2885
|
if (p === "win32") {
|
|
@@ -2383,39 +2899,6 @@ function checkIsGitRepository(dir) {
|
|
|
2383
2899
|
return false;
|
|
2384
2900
|
}
|
|
2385
2901
|
}
|
|
2386
|
-
function buildSystemPrompt(env) {
|
|
2387
|
-
const workspaceLine = env.workspaceDir ?? "not selected";
|
|
2388
|
-
const gitLine = env.isGitRepository ? "yes" : env.workspaceDir ? "no" : "unknown";
|
|
2389
|
-
const lines = [
|
|
2390
|
-
"You are Coder, a helpful terminal AI assistant.",
|
|
2391
|
-
"",
|
|
2392
|
-
"## Environment",
|
|
2393
|
-
`- Workspace: ${workspaceLine}`,
|
|
2394
|
-
`- OS: ${env.os}`,
|
|
2395
|
-
`- Shell: ${env.shell}`,
|
|
2396
|
-
`- Git repository: ${gitLine}`,
|
|
2397
|
-
`- Date: ${env.today}`,
|
|
2398
|
-
"",
|
|
2399
|
-
"## Available Tools",
|
|
2400
|
-
"You have access to all standard tools: file operations, shell commands, web search, skills, and more.",
|
|
2401
|
-
"",
|
|
2402
|
-
"## Guidelines",
|
|
2403
|
-
"- Always read files before editing them.",
|
|
2404
|
-
"- Prefer targeted edits over full file replacements.",
|
|
2405
|
-
"- Use shell commands only for builds, tests, git, and non-interactive CLI tasks.",
|
|
2406
|
-
"- When a task requires multiple steps, create a plan first using todo_write.",
|
|
2407
|
-
"- After completing a task, summarize what was done.",
|
|
2408
|
-
"- Use ask_question when you need clarification from the user.",
|
|
2409
|
-
""
|
|
2410
|
-
];
|
|
2411
|
-
if (env.agentsMd) {
|
|
2412
|
-
lines.push("## Project Instructions (AGENTS.md)");
|
|
2413
|
-
lines.push("");
|
|
2414
|
-
lines.push(env.agentsMd.content);
|
|
2415
|
-
lines.push("");
|
|
2416
|
-
}
|
|
2417
|
-
return lines.join("\n");
|
|
2418
|
-
}
|
|
2419
2902
|
|
|
2420
2903
|
// src/agent/session.ts
|
|
2421
2904
|
init_config();
|
|
@@ -2478,12 +2961,28 @@ async function runAgentSession(prompt, options) {
|
|
|
2478
2961
|
const resolvedConfig = resolveProviderConfig(config, providerId);
|
|
2479
2962
|
const apiKey = resolveApiKey(resolvedConfig);
|
|
2480
2963
|
const modelId = options.model ?? config.lastModel;
|
|
2964
|
+
const modelDef = resolvedConfig.models.find((m) => m.id === modelId);
|
|
2965
|
+
const supportsThinking = modelDef?.supportsThinking === true || config.providers[resolvedConfig.provider]?.supportsThinking === true;
|
|
2966
|
+
const thinkingEnabled = options.thinking !== void 0 && supportsThinking ? options.thinking : void 0;
|
|
2967
|
+
let thinkingParamsOverride;
|
|
2968
|
+
if (thinkingEnabled !== void 0) {
|
|
2969
|
+
const settings = config.providers[resolvedConfig.provider];
|
|
2970
|
+
if (settings?.thinkingEnabledParams && settings?.thinkingDisabledParams) {
|
|
2971
|
+
thinkingParamsOverride = {
|
|
2972
|
+
enabled: settings.thinkingEnabledParams,
|
|
2973
|
+
disabled: settings.thinkingDisabledParams
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2481
2977
|
let messages;
|
|
2482
2978
|
if (options.existingMessages && options.existingMessages.length > 0) {
|
|
2483
|
-
messages = [
|
|
2979
|
+
messages = [
|
|
2980
|
+
...sanitizeMessages(options.existingMessages),
|
|
2981
|
+
{ role: "user", content: prompt }
|
|
2982
|
+
];
|
|
2484
2983
|
} else {
|
|
2485
2984
|
const env = resolveAgentEnvironment(workspaceDir);
|
|
2486
|
-
const systemPrompt = buildSystemPrompt(env);
|
|
2985
|
+
const systemPrompt = buildSystemPrompt(env, options.agentMode);
|
|
2487
2986
|
messages = [
|
|
2488
2987
|
{ role: "system", content: systemPrompt },
|
|
2489
2988
|
{ role: "user", content: prompt }
|
|
@@ -2494,6 +2993,7 @@ async function runAgentSession(prompt, options) {
|
|
|
2494
2993
|
let currentContent = "";
|
|
2495
2994
|
let currentThinking = "";
|
|
2496
2995
|
let hasContent = false;
|
|
2996
|
+
let streamStarted = false;
|
|
2497
2997
|
const toolContext = {
|
|
2498
2998
|
workspaceDir,
|
|
2499
2999
|
sessionId: taskId,
|
|
@@ -2514,7 +3014,11 @@ async function runAgentSession(prompt, options) {
|
|
|
2514
3014
|
apiKeyEnvVar: resolvedConfig.apiKeyEnvVar,
|
|
2515
3015
|
model: modelId,
|
|
2516
3016
|
messages,
|
|
2517
|
-
agentMode: options.agentMode
|
|
3017
|
+
agentMode: options.agentMode,
|
|
3018
|
+
provider: providerId,
|
|
3019
|
+
thinkingEnabled,
|
|
3020
|
+
thinkingParams: thinkingParamsOverride,
|
|
3021
|
+
signal: options.signal
|
|
2518
3022
|
},
|
|
2519
3023
|
toolContext,
|
|
2520
3024
|
(event) => {
|
|
@@ -2522,25 +3026,34 @@ async function runAgentSession(prompt, options) {
|
|
|
2522
3026
|
case "thinking_delta": {
|
|
2523
3027
|
currentThinking += event.delta;
|
|
2524
3028
|
if (isStreaming && !hasContent) {
|
|
3029
|
+
if (!streamStarted) {
|
|
3030
|
+
streamStarted = true;
|
|
3031
|
+
writeLine("");
|
|
3032
|
+
}
|
|
2525
3033
|
writeStream(dim(event.delta));
|
|
2526
3034
|
}
|
|
2527
3035
|
break;
|
|
2528
3036
|
}
|
|
2529
3037
|
case "content_delta": {
|
|
2530
|
-
if (!hasContent && currentThinking && isStreaming) {
|
|
2531
|
-
writeLine("");
|
|
2532
|
-
hasContent = true;
|
|
2533
|
-
}
|
|
2534
|
-
hasContent = true;
|
|
2535
|
-
currentContent += event.delta;
|
|
2536
3038
|
if (isStreaming) {
|
|
3039
|
+
if (!streamStarted) {
|
|
3040
|
+
streamStarted = true;
|
|
3041
|
+
}
|
|
3042
|
+
if (!hasContent && currentThinking) {
|
|
3043
|
+
writeLine("");
|
|
3044
|
+
}
|
|
3045
|
+
hasContent = true;
|
|
3046
|
+
currentContent += event.delta;
|
|
2537
3047
|
writeStream(event.delta);
|
|
2538
3048
|
}
|
|
2539
3049
|
break;
|
|
2540
3050
|
}
|
|
2541
3051
|
case "tool_call_started": {
|
|
2542
3052
|
if (isStreaming) {
|
|
2543
|
-
if (
|
|
3053
|
+
if (!streamStarted) {
|
|
3054
|
+
streamStarted = true;
|
|
3055
|
+
writeLine("");
|
|
3056
|
+
} else if (hasContent || currentThinking) {
|
|
2544
3057
|
writeLine("");
|
|
2545
3058
|
}
|
|
2546
3059
|
writeLine(`${dim("\u{1F527}")} ${bold(event.name)}${dim("...")}`);
|
|
@@ -2559,34 +3072,25 @@ async function runAgentSession(prompt, options) {
|
|
|
2559
3072
|
if (event.status === "completed") {
|
|
2560
3073
|
if (isStreaming) {
|
|
2561
3074
|
writeLine("");
|
|
2562
|
-
writeLine(success(
|
|
2563
|
-
\u2713 Task completed`));
|
|
3075
|
+
writeLine(success("\u2713 Task completed"));
|
|
2564
3076
|
} else {
|
|
2565
|
-
writeLine(success(
|
|
3077
|
+
writeLine(success("\u2713 Task completed"));
|
|
2566
3078
|
if (currentContent) {
|
|
2567
3079
|
writeLine("\n" + currentContent);
|
|
2568
3080
|
}
|
|
2569
3081
|
}
|
|
2570
3082
|
} else if (event.status === "failed") {
|
|
2571
|
-
writeLine(error(`
|
|
2572
|
-
\u2717 Task failed`));
|
|
3083
|
+
writeLine(error(`\u2717 Task failed`));
|
|
2573
3084
|
} else if (event.status === "cancelled") {
|
|
2574
|
-
writeLine(warning(`
|
|
2575
|
-
\u26A0 Task cancelled`));
|
|
3085
|
+
writeLine(warning(`\u26A0 Task cancelled`));
|
|
2576
3086
|
}
|
|
2577
3087
|
break;
|
|
2578
3088
|
}
|
|
2579
3089
|
case "done": {
|
|
2580
|
-
if (event.usage && config.showUsage) {
|
|
2581
|
-
writeLine(dim(
|
|
2582
|
-
` Tokens: ${event.usage.promptTokens}\u2191 ${event.usage.completionTokens}\u2193 ${event.usage.totalTokens}\u2211`
|
|
2583
|
-
));
|
|
2584
|
-
}
|
|
2585
3090
|
break;
|
|
2586
3091
|
}
|
|
2587
3092
|
case "error": {
|
|
2588
|
-
writeLine(error(`
|
|
2589
|
-
\u2717 Error: ${event.message}`));
|
|
3093
|
+
writeLine(error(`\u2717 Error: ${event.message}`));
|
|
2590
3094
|
break;
|
|
2591
3095
|
}
|
|
2592
3096
|
}
|
|
@@ -2594,11 +3098,19 @@ async function runAgentSession(prompt, options) {
|
|
|
2594
3098
|
);
|
|
2595
3099
|
return finalMessages;
|
|
2596
3100
|
} catch (err) {
|
|
3101
|
+
killAllShells();
|
|
2597
3102
|
const message = err instanceof Error ? err.message : String(err);
|
|
2598
3103
|
writeError(error(`Fatal error: ${message}`));
|
|
2599
3104
|
process.exit(1);
|
|
2600
3105
|
}
|
|
2601
3106
|
}
|
|
3107
|
+
function sanitizeMessages(messages) {
|
|
3108
|
+
return messages.filter((msg) => {
|
|
3109
|
+
if (msg.role !== "assistant") return true;
|
|
3110
|
+
const text = typeof msg.content === "string" ? msg.content : void 0;
|
|
3111
|
+
return Boolean(text?.trim()) || Boolean(msg.reasoning_content?.trim()) || Boolean(msg.tool_calls?.length);
|
|
3112
|
+
});
|
|
3113
|
+
}
|
|
2602
3114
|
|
|
2603
3115
|
// src/commands/run.ts
|
|
2604
3116
|
async function runCommand(prompt, options) {
|
|
@@ -2620,16 +3132,6 @@ async function askCommand(prompt, options) {
|
|
|
2620
3132
|
});
|
|
2621
3133
|
}
|
|
2622
3134
|
|
|
2623
|
-
// src/commands/plan.ts
|
|
2624
|
-
async function planCommand(prompt, options) {
|
|
2625
|
-
await runAgentSession(prompt, {
|
|
2626
|
-
...options,
|
|
2627
|
-
agentMode: "plan",
|
|
2628
|
-
workspaceDir: options.workspace ?? process.cwd(),
|
|
2629
|
-
interactive: false
|
|
2630
|
-
});
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
3135
|
// src/commands/repl.ts
|
|
2634
3136
|
var import_node_readline2 = require("node:readline");
|
|
2635
3137
|
init_config();
|
|
@@ -2642,10 +3144,13 @@ async function replCommand(options) {
|
|
|
2642
3144
|
writeLine(dim(` Model: ${config.lastModel || "not set"}`));
|
|
2643
3145
|
writeLine(dim(` Provider: ${config.activeProvider}`));
|
|
2644
3146
|
writeLine(dim(` Workspace: ${workspaceDir}`));
|
|
2645
|
-
writeLine(dim(` Type 'exit' or Ctrl+C to quit`));
|
|
3147
|
+
writeLine(dim(` Type '/help' for commands, '/exit' or Ctrl+C to quit`));
|
|
2646
3148
|
writeLine(dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
2647
3149
|
writeLine("");
|
|
2648
3150
|
let conversationMessages;
|
|
3151
|
+
let thinkingEnabled = true;
|
|
3152
|
+
let abortController = new AbortController();
|
|
3153
|
+
let agentRunning = false;
|
|
2649
3154
|
const rl = (0, import_node_readline2.createInterface)({
|
|
2650
3155
|
input: process.stdin,
|
|
2651
3156
|
output: process.stdout,
|
|
@@ -2659,28 +3164,41 @@ async function replCommand(options) {
|
|
|
2659
3164
|
rl.prompt();
|
|
2660
3165
|
return;
|
|
2661
3166
|
}
|
|
2662
|
-
if (
|
|
3167
|
+
if (agentRunning) {
|
|
3168
|
+
rl.prompt();
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
if (trimmed === "/exit" || trimmed === "/quit") {
|
|
2663
3172
|
rl.close();
|
|
2664
3173
|
return;
|
|
2665
3174
|
}
|
|
2666
|
-
if (trimmed === "clear") {
|
|
3175
|
+
if (trimmed === "/clear") {
|
|
2667
3176
|
console.clear();
|
|
2668
3177
|
rl.prompt();
|
|
2669
3178
|
return;
|
|
2670
3179
|
}
|
|
2671
|
-
if (trimmed === "
|
|
3180
|
+
if (trimmed === "/new") {
|
|
3181
|
+
conversationMessages = void 0;
|
|
3182
|
+
abortController = new AbortController();
|
|
3183
|
+
writeLine(info("Started a new session. Context has been cleared."));
|
|
3184
|
+
rl.prompt();
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
if (trimmed === "/help") {
|
|
2672
3188
|
writeLine(bold("\nREPL Commands:"));
|
|
2673
|
-
writeLine(" <prompt>
|
|
2674
|
-
writeLine(" exit /
|
|
2675
|
-
writeLine(" clear
|
|
2676
|
-
writeLine("
|
|
2677
|
-
writeLine("
|
|
3189
|
+
writeLine(" <prompt> Ask the agent anything");
|
|
3190
|
+
writeLine(" /exit, /quit Exit REPL");
|
|
3191
|
+
writeLine(" /clear Clear screen");
|
|
3192
|
+
writeLine(" /new Clear context, start a new session");
|
|
3193
|
+
writeLine(" /help Show this help");
|
|
3194
|
+
writeLine(" /model <id> Switch model");
|
|
3195
|
+
writeLine(` /thinking <on|off> Toggle deep thinking (currently: ${thinkingEnabled ? "on" : "off"})`);
|
|
2678
3196
|
writeLine("");
|
|
2679
3197
|
rl.prompt();
|
|
2680
3198
|
return;
|
|
2681
3199
|
}
|
|
2682
|
-
if (trimmed.startsWith("model ")) {
|
|
2683
|
-
const modelId = trimmed.slice(
|
|
3200
|
+
if (trimmed.startsWith("/model ")) {
|
|
3201
|
+
const modelId = trimmed.slice(7).trim();
|
|
2684
3202
|
if (modelId) {
|
|
2685
3203
|
config.lastModel = modelId;
|
|
2686
3204
|
const { saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -2690,109 +3208,52 @@ async function replCommand(options) {
|
|
|
2690
3208
|
rl.prompt();
|
|
2691
3209
|
return;
|
|
2692
3210
|
}
|
|
2693
|
-
|
|
3211
|
+
if (trimmed.startsWith("/thinking ")) {
|
|
3212
|
+
const value = trimmed.slice(10).trim().toLowerCase();
|
|
3213
|
+
if (value === "on" || value === "true") {
|
|
3214
|
+
thinkingEnabled = true;
|
|
3215
|
+
writeLine(info("Deep thinking: on"));
|
|
3216
|
+
} else if (value === "off" || value === "false") {
|
|
3217
|
+
thinkingEnabled = false;
|
|
3218
|
+
writeLine(info("Deep thinking: off"));
|
|
3219
|
+
} else {
|
|
3220
|
+
writeLine(error(`Usage: /thinking <on|off> (currently: ${thinkingEnabled ? "on" : "off"})`));
|
|
3221
|
+
}
|
|
3222
|
+
rl.prompt();
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
agentRunning = true;
|
|
2694
3226
|
try {
|
|
2695
3227
|
conversationMessages = await runAgentSession(trimmed, {
|
|
2696
3228
|
agentMode: "agent",
|
|
2697
3229
|
workspaceDir,
|
|
2698
3230
|
interactive: true,
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
3231
|
+
thinking: thinkingEnabled,
|
|
3232
|
+
existingMessages: conversationMessages,
|
|
3233
|
+
signal: abortController.signal
|
|
2702
3234
|
});
|
|
2703
3235
|
} catch (err) {
|
|
2704
3236
|
const message = err instanceof Error ? err.message : String(err);
|
|
2705
3237
|
writeError(error(`Error: ${message}`));
|
|
2706
3238
|
}
|
|
3239
|
+
agentRunning = false;
|
|
3240
|
+
abortController = new AbortController();
|
|
2707
3241
|
writeLine("");
|
|
2708
3242
|
rl.prompt();
|
|
2709
|
-
rl.resume();
|
|
2710
3243
|
});
|
|
2711
3244
|
rl.on("close", () => {
|
|
3245
|
+
killAllShells();
|
|
2712
3246
|
writeLine(dim("\nGoodbye! \u{1F44B}"));
|
|
2713
3247
|
process.exit(0);
|
|
2714
3248
|
});
|
|
2715
3249
|
rl.on("SIGINT", () => {
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
// src/commands/init.ts
|
|
2721
|
-
init_config();
|
|
2722
|
-
var import_node_readline3 = require("node:readline");
|
|
2723
|
-
async function initCommand() {
|
|
2724
|
-
writeLine(bold("\nCoder CLI \u2014 Initialization"));
|
|
2725
|
-
writeLine(dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
2726
|
-
writeLine(`Config directory: ${getConfigDirPath()}`);
|
|
2727
|
-
writeLine("");
|
|
2728
|
-
const config = loadConfig();
|
|
2729
|
-
writeLine(info("Select your AI provider:"));
|
|
2730
|
-
const providers = ["deepseek", "glm", "agnes", "nvidia", "minimax", "custom"];
|
|
2731
|
-
for (let i = 0; i < providers.length; i++) {
|
|
2732
|
-
writeLine(` ${i + 1}. ${providers[i]}`);
|
|
2733
|
-
}
|
|
2734
|
-
writeLine("");
|
|
2735
|
-
const providerIndex = await askQuestion("Provider number [1]: ");
|
|
2736
|
-
const providerChoice = parseInt(providerIndex || "1", 10);
|
|
2737
|
-
const selectedProvider = providers[Math.max(0, Math.min(providerChoice - 1, providers.length - 1))];
|
|
2738
|
-
config.activeProvider = selectedProvider;
|
|
2739
|
-
writeLine(` Selected: ${bold(selectedProvider)}`);
|
|
2740
|
-
writeLine("");
|
|
2741
|
-
const providerSettings = config.providers[selectedProvider];
|
|
2742
|
-
const envVar = providerSettings.apiKeyEnvVar;
|
|
2743
|
-
writeLine(info(`API Key Configuration for "${selectedProvider}":`));
|
|
2744
|
-
writeLine(` You can set the ${bold(envVar)} environment variable,`);
|
|
2745
|
-
writeLine(` or enter the key directly (stored in config file).`);
|
|
2746
|
-
writeLine("");
|
|
2747
|
-
const source = await askQuestion("Use environment variable? [Y/n]: ");
|
|
2748
|
-
const useEnv = source.toLowerCase() !== "n";
|
|
2749
|
-
if (useEnv) {
|
|
2750
|
-
providerSettings.apiKeySource = "env";
|
|
2751
|
-
writeLine(` Using ${bold(envVar)} environment variable.`);
|
|
2752
|
-
writeLine(` Make sure to set it: export ${envVar}=<your-api-key>`);
|
|
2753
|
-
} else {
|
|
2754
|
-
providerSettings.apiKeySource = "manual";
|
|
2755
|
-
const key = await askQuestion("Enter API key: ");
|
|
2756
|
-
if (key.trim()) {
|
|
2757
|
-
providerSettings.apiKey = key.trim();
|
|
2758
|
-
writeLine(" API key saved to config.");
|
|
3250
|
+
if (agentRunning) {
|
|
3251
|
+
abortController.abort();
|
|
3252
|
+
writeLine("");
|
|
3253
|
+
writeLine(warning("\u26A0 Cancelled"));
|
|
2759
3254
|
} else {
|
|
2760
|
-
writeLine(` ${error("No key entered. Set it later with: coder config")}`);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
writeLine("");
|
|
2764
|
-
if (selectedProvider === "custom") {
|
|
2765
|
-
const baseUrl = await askQuestion("Custom API base URL: ");
|
|
2766
|
-
if (baseUrl.trim()) {
|
|
2767
|
-
providerSettings.customBaseUrl = baseUrl.trim();
|
|
2768
|
-
}
|
|
2769
|
-
} else {
|
|
2770
|
-
const customUrl = await askQuestion(`Custom base URL (leave empty for default): `);
|
|
2771
|
-
if (customUrl.trim()) {
|
|
2772
|
-
providerSettings.customBaseUrl = customUrl.trim();
|
|
2773
|
-
}
|
|
2774
|
-
writeLine("");
|
|
2775
|
-
}
|
|
2776
|
-
saveConfig(config);
|
|
2777
|
-
writeLine(success("\n\u2713 Configuration saved!"));
|
|
2778
|
-
writeLine(` Config file: ${getConfigFilePathExplicit()}`);
|
|
2779
|
-
writeLine("");
|
|
2780
|
-
writeLine(info("Quick start:"));
|
|
2781
|
-
writeLine(' coder "What is in this directory?"');
|
|
2782
|
-
writeLine(' coder ask "Explain this code"');
|
|
2783
|
-
writeLine(" coder repl");
|
|
2784
|
-
writeLine("");
|
|
2785
|
-
}
|
|
2786
|
-
function askQuestion(query) {
|
|
2787
|
-
const rl = (0, import_node_readline3.createInterface)({
|
|
2788
|
-
input: process.stdin,
|
|
2789
|
-
output: process.stderr
|
|
2790
|
-
});
|
|
2791
|
-
return new Promise((resolve12) => {
|
|
2792
|
-
rl.question(query, (answer) => {
|
|
2793
3255
|
rl.close();
|
|
2794
|
-
|
|
2795
|
-
});
|
|
3256
|
+
}
|
|
2796
3257
|
});
|
|
2797
3258
|
}
|
|
2798
3259
|
|
|
@@ -2820,7 +3281,8 @@ function showConfig(config) {
|
|
|
2820
3281
|
writeLine(bold("Active Settings:"));
|
|
2821
3282
|
writeLine(` Active provider: ${config.activeProvider}`);
|
|
2822
3283
|
writeLine(` Last model: ${config.lastModel}`);
|
|
2823
|
-
|
|
3284
|
+
const tavilyKey = resolveTavilyApiKey(config);
|
|
3285
|
+
writeLine(` Web search: ${tavilyKey ? "configured" : "not configured (set via coder config tavilyApiKey <key> or TAVILY_API_KEY env var)"}`);
|
|
2824
3286
|
writeLine("");
|
|
2825
3287
|
for (const providerId of PROVIDER_IDS2) {
|
|
2826
3288
|
const p = config.providers[providerId];
|
|
@@ -2829,13 +3291,38 @@ function showConfig(config) {
|
|
|
2829
3291
|
writeLine(` API Key Env Var: ${p.apiKeyEnvVar}`);
|
|
2830
3292
|
writeLine(` API Key (stored): ${p.apiKey ? "***" : "(not set)"}`);
|
|
2831
3293
|
writeLine(` Custom Base URL: ${p.customBaseUrl || "(default)"}`);
|
|
3294
|
+
if (providerId === "custom") {
|
|
3295
|
+
writeLine(` Deep Thinking: ${p.supportsThinking ? "supported" : "not declared"}`);
|
|
3296
|
+
if (p.thinkingEnabledParams) {
|
|
3297
|
+
writeLine(` Thinking (on): ${JSON.stringify(p.thinkingEnabledParams)}`);
|
|
3298
|
+
}
|
|
3299
|
+
if (p.thinkingDisabledParams) {
|
|
3300
|
+
writeLine(` Thinking (off): ${JSON.stringify(p.thinkingDisabledParams)}`);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
2832
3303
|
writeLine("");
|
|
2833
3304
|
}
|
|
2834
3305
|
writeLine(dim("To change a value: coder config <key> <value>"));
|
|
2835
|
-
writeLine(dim("
|
|
2836
|
-
writeLine(dim(
|
|
2837
|
-
writeLine(dim(
|
|
2838
|
-
writeLine(dim(' coder config providers.
|
|
3306
|
+
writeLine(dim(""));
|
|
3307
|
+
writeLine(dim("Quick start \u2014 custom provider:"));
|
|
3308
|
+
writeLine(dim(" coder config activeProvider custom"));
|
|
3309
|
+
writeLine(dim(' coder config providers.custom.customBaseUrl "https://api.openai.com/v1"'));
|
|
3310
|
+
writeLine(dim(" coder config providers.custom.supportsThinking true"));
|
|
3311
|
+
writeLine(dim(' coder config lastModel "gpt-4o"'));
|
|
3312
|
+
writeLine(dim(""));
|
|
3313
|
+
writeLine(dim(" # Way 1: env var (recommended)"));
|
|
3314
|
+
writeLine(dim(" coder config providers.custom.apiKeySource env"));
|
|
3315
|
+
writeLine(dim(' coder config providers.custom.apiKeyEnvVar "OPENAI_API_KEY"'));
|
|
3316
|
+
writeLine(dim(" export OPENAI_API_KEY=sk-xxx"));
|
|
3317
|
+
writeLine(dim(""));
|
|
3318
|
+
writeLine(dim(" # Way 2: stored in config"));
|
|
3319
|
+
writeLine(dim(" coder config providers.custom.apiKeySource manual"));
|
|
3320
|
+
writeLine(dim(' coder config providers.custom.apiKey "sk-xxx"'));
|
|
3321
|
+
writeLine(dim(""));
|
|
3322
|
+
writeLine(dim("View a value:"));
|
|
3323
|
+
writeLine(dim(" coder config activeProvider"));
|
|
3324
|
+
writeLine(dim(" coder config providers.deepseek.customBaseUrl"));
|
|
3325
|
+
writeLine(dim(" coder config providers.custom.thinkingEnabledParams"));
|
|
2839
3326
|
writeLine("");
|
|
2840
3327
|
}
|
|
2841
3328
|
function showConfigValue(config, key) {
|
|
@@ -2847,6 +3334,16 @@ function showConfigValue(config, key) {
|
|
|
2847
3334
|
writeLine(String(value));
|
|
2848
3335
|
}
|
|
2849
3336
|
async function setConfigValue(config, key, value) {
|
|
3337
|
+
if (key === "activeProvider" && !PROVIDER_IDS2.includes(value)) {
|
|
3338
|
+
writeLine(error(`Invalid provider "${value}". Valid providers: ${PROVIDER_IDS2.join(", ")}`));
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
if (key.endsWith(".apiKeySource") || key === "apiKeySource") {
|
|
3342
|
+
if (value !== "env" && value !== "manual") {
|
|
3343
|
+
writeLine(error(`Invalid apiKeySource "${value}". Must be "env" or "manual".`));
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
2850
3347
|
setNestedValue(config, key, parseValue(value));
|
|
2851
3348
|
saveConfig(config);
|
|
2852
3349
|
writeLine(info(`Config updated: ${key} = ${value}`));
|
|
@@ -2888,36 +3385,30 @@ function setGlobalOptions(opts) {
|
|
|
2888
3385
|
}
|
|
2889
3386
|
|
|
2890
3387
|
// src/index.ts
|
|
2891
|
-
var VERSION = "0.1.
|
|
3388
|
+
var VERSION = "0.1.109";
|
|
2892
3389
|
async function main() {
|
|
2893
3390
|
const rawArgs = process.argv.slice(2);
|
|
2894
3391
|
if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
|
|
2895
3392
|
printHelp();
|
|
2896
3393
|
return;
|
|
2897
3394
|
}
|
|
2898
|
-
if (rawArgs.includes("--version") || rawArgs.includes("-
|
|
3395
|
+
if (rawArgs.includes("--version") || rawArgs.includes("-v")) {
|
|
2899
3396
|
console.log(VERSION);
|
|
2900
3397
|
return;
|
|
2901
3398
|
}
|
|
3399
|
+
const globalOpts = extractGlobalOptions(rawArgs);
|
|
3400
|
+
setGlobalOptions(globalOpts);
|
|
2902
3401
|
if (rawArgs.length === 0) {
|
|
2903
3402
|
await replCommand({});
|
|
2904
3403
|
return;
|
|
2905
3404
|
}
|
|
2906
|
-
const globalOpts = extractGlobalOptions(rawArgs);
|
|
2907
|
-
setGlobalOptions(globalOpts);
|
|
2908
3405
|
const firstNonFlag = rawArgs.find((a) => !a.startsWith("-"));
|
|
2909
3406
|
const knownSubcommands = {
|
|
2910
3407
|
ask: async (args, opts) => {
|
|
2911
3408
|
await askCommand(args.join(" "), opts);
|
|
2912
3409
|
},
|
|
2913
|
-
|
|
2914
|
-
await
|
|
2915
|
-
},
|
|
2916
|
-
repl: async () => {
|
|
2917
|
-
await replCommand(globalOpts);
|
|
2918
|
-
},
|
|
2919
|
-
init: async () => {
|
|
2920
|
-
await initCommand();
|
|
3410
|
+
run: async (args, opts) => {
|
|
3411
|
+
await runCommand(args.join(" "), opts);
|
|
2921
3412
|
},
|
|
2922
3413
|
config: async (args) => {
|
|
2923
3414
|
await configCommand(args[0], args[1]);
|
|
@@ -2928,54 +3419,47 @@ async function main() {
|
|
|
2928
3419
|
await knownSubcommands[firstNonFlag](cmdArgs, globalOpts);
|
|
2929
3420
|
return;
|
|
2930
3421
|
}
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
}
|
|
2937
|
-
await runCommand(prompt, globalOpts);
|
|
3422
|
+
writeLine(error(`Unknown subcommand "${firstNonFlag}".`));
|
|
3423
|
+
writeLine("");
|
|
3424
|
+
writeLine(info("Usage: coder <command> [options] [prompt...]"));
|
|
3425
|
+
writeLine(info("Run 'coder --help' for available commands."));
|
|
3426
|
+
process.exit(1);
|
|
2938
3427
|
}
|
|
2939
3428
|
function extractGlobalOptions(args) {
|
|
2940
3429
|
const opts = {};
|
|
2941
3430
|
for (let i = 0; i < args.length; i++) {
|
|
2942
3431
|
const arg = args[i];
|
|
2943
|
-
if (arg === "-
|
|
2944
|
-
opts.model = args[++i] ?? "";
|
|
2945
|
-
} else if (arg === "-p" || arg === "--provider") {
|
|
2946
|
-
opts.provider = args[++i] ?? "";
|
|
2947
|
-
} else if (arg === "-w" || arg === "--workspace") {
|
|
3432
|
+
if (arg === "-w" || arg === "--workspace") {
|
|
2948
3433
|
opts.workspace = args[++i] ?? "";
|
|
2949
|
-
} else if (arg === "-y" || arg === "--yes") {
|
|
2950
|
-
opts.yes = true;
|
|
2951
3434
|
} else if (arg === "--no-stream") {
|
|
2952
3435
|
opts.stream = false;
|
|
2953
3436
|
} else if (arg === "--stream") {
|
|
2954
3437
|
opts.stream = true;
|
|
3438
|
+
} else if (arg === "--thinking") {
|
|
3439
|
+
opts.thinking = true;
|
|
3440
|
+
} else if (arg === "--no-thinking") {
|
|
3441
|
+
opts.thinking = false;
|
|
2955
3442
|
}
|
|
2956
3443
|
}
|
|
2957
3444
|
return opts;
|
|
2958
3445
|
}
|
|
2959
3446
|
function printHelp() {
|
|
2960
3447
|
console.log(`
|
|
2961
|
-
Usage: coder [options] [prompt...]
|
|
3448
|
+
Usage: coder <command> [options] [prompt...]
|
|
2962
3449
|
|
|
2963
3450
|
Coder CLI \u2014 AI-powered coding assistant in the terminal
|
|
2964
3451
|
|
|
2965
3452
|
Options:
|
|
2966
|
-
-
|
|
2967
|
-
-m, --model <model> Model ID to use
|
|
2968
|
-
-p, --provider <provider> Provider ID (deepseek, glm, agnes, nvidia, minimax, custom)
|
|
3453
|
+
-v, --version output the version number
|
|
2969
3454
|
-w, --workspace <path> Workspace directory
|
|
2970
|
-
|
|
2971
|
-
|
|
3455
|
+
--no-stream Disable streaming output
|
|
3456
|
+
--thinking Enable deep thinking (for supported models)
|
|
3457
|
+
--no-thinking Disable deep thinking
|
|
2972
3458
|
-h, --help display help for command
|
|
2973
3459
|
|
|
2974
3460
|
Commands:
|
|
2975
3461
|
ask [prompt...] Ask a question (read-only mode)
|
|
2976
|
-
|
|
2977
|
-
repl Start interactive REPL session
|
|
2978
|
-
init Initialize Coder CLI configuration
|
|
3462
|
+
run [prompt...] Run in full agent mode (can modify files, run commands)
|
|
2979
3463
|
config [key] [value] View or edit configuration
|
|
2980
3464
|
`);
|
|
2981
3465
|
}
|