jinzd-ai-cli 0.4.26 → 0.4.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-AHH5I2U6.js → chunk-7SW4XRDJ.js} +1 -1
- package/dist/{chunk-5GZQLJAY.js → chunk-EWJQOJSB.js} +728 -120
- package/dist/{chunk-ETMUP3CY.js → chunk-L7OCMF5F.js} +1 -1
- package/dist/{chunk-SS7BQZ5R.js → chunk-SAJICC6G.js} +2 -2
- package/dist/{hub-JOYPSPR2.js → hub-QJBT4RDT.js} +1 -1
- package/dist/index.js +30 -17
- package/dist/{run-tests-25BZE3KQ.js → run-tests-IPBIZMAR.js} +1 -1
- package/dist/{run-tests-L3JNRB6X.js → run-tests-QRH2Z2OH.js} +1 -1
- package/dist/{server-SZZXQZWY.js → server-46ANSDN7.js} +4 -4
- package/dist/{task-orchestrator-4N5UUA6L.js → task-orchestrator-TDBWMIH4.js} +2 -2
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
10
10
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
11
11
|
runTestsTool
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-7SW4XRDJ.js";
|
|
13
13
|
|
|
14
14
|
// src/tools/builtin/bash.ts
|
|
15
15
|
import { execSync } from "child_process";
|
|
@@ -402,6 +402,110 @@ import { readFileSync as readFileSync2, existsSync as existsSync3, statSync as s
|
|
|
402
402
|
import { execSync as execSync2 } from "child_process";
|
|
403
403
|
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
404
404
|
import { homedir } from "os";
|
|
405
|
+
|
|
406
|
+
// src/tools/builtin/notebook-utils.ts
|
|
407
|
+
function cellSourceToString(source) {
|
|
408
|
+
if (!source) return "";
|
|
409
|
+
if (Array.isArray(source)) return source.join("");
|
|
410
|
+
return source;
|
|
411
|
+
}
|
|
412
|
+
function stringToCellSource(content) {
|
|
413
|
+
if (content.length === 0) return [];
|
|
414
|
+
const lines = content.split("\n");
|
|
415
|
+
return lines.map((l, i) => i < lines.length - 1 ? l + "\n" : l).filter((l, i, arr) => !(i === arr.length - 1 && l === ""));
|
|
416
|
+
}
|
|
417
|
+
function parseNotebook(content) {
|
|
418
|
+
let data;
|
|
419
|
+
try {
|
|
420
|
+
data = JSON.parse(content);
|
|
421
|
+
} catch (err) {
|
|
422
|
+
throw new Error(`Invalid notebook JSON: ${err.message}`);
|
|
423
|
+
}
|
|
424
|
+
const nb = data;
|
|
425
|
+
if (!nb || !Array.isArray(nb.cells)) {
|
|
426
|
+
throw new Error("Invalid notebook format: missing cells array");
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
cells: nb.cells,
|
|
430
|
+
metadata: nb.metadata,
|
|
431
|
+
nbformat: nb.nbformat ?? 4,
|
|
432
|
+
nbformat_minor: nb.nbformat_minor ?? 5
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function extractOutputText(outputs) {
|
|
436
|
+
if (!outputs || outputs.length === 0) return "";
|
|
437
|
+
const parts = [];
|
|
438
|
+
for (const out of outputs) {
|
|
439
|
+
if (out.output_type === "stream") {
|
|
440
|
+
const text = Array.isArray(out.text) ? out.text.join("") : out.text ?? "";
|
|
441
|
+
if (text) parts.push(text);
|
|
442
|
+
} else if (out.output_type === "error") {
|
|
443
|
+
parts.push(`ERROR: ${out.ename}: ${out.evalue}`);
|
|
444
|
+
if (out.traceback) parts.push(out.traceback.join("\n"));
|
|
445
|
+
} else if (out.output_type === "execute_result" || out.output_type === "display_data") {
|
|
446
|
+
const data = out.data ?? {};
|
|
447
|
+
const plain = data["text/plain"];
|
|
448
|
+
if (plain) {
|
|
449
|
+
parts.push(Array.isArray(plain) ? plain.join("") : String(plain));
|
|
450
|
+
} else if (data["text/html"]) {
|
|
451
|
+
parts.push("[HTML output omitted]");
|
|
452
|
+
} else if (data["image/png"] || data["image/jpeg"]) {
|
|
453
|
+
parts.push("[Image output omitted]");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return parts.join("\n").trim();
|
|
458
|
+
}
|
|
459
|
+
function renderNotebookAsText(nb, filePath) {
|
|
460
|
+
const lines = [`[Notebook: ${filePath} | ${nb.cells.length} cells | nbformat ${nb.nbformat}.${nb.nbformat_minor}]`, ""];
|
|
461
|
+
const language = nb.metadata?.["kernelspec"]?.["language"] ?? "python";
|
|
462
|
+
nb.cells.forEach((cell, idx) => {
|
|
463
|
+
const cellNum = idx + 1;
|
|
464
|
+
const type = cell.cell_type;
|
|
465
|
+
const source = cellSourceToString(cell.source);
|
|
466
|
+
if (type === "markdown") {
|
|
467
|
+
lines.push(`## Cell ${cellNum} [markdown]`);
|
|
468
|
+
lines.push(source);
|
|
469
|
+
lines.push("");
|
|
470
|
+
} else if (type === "code") {
|
|
471
|
+
const execLabel = cell.execution_count != null ? ` (execution #${cell.execution_count})` : "";
|
|
472
|
+
lines.push(`## Cell ${cellNum} [code${execLabel}]`);
|
|
473
|
+
lines.push("```" + language);
|
|
474
|
+
lines.push(source);
|
|
475
|
+
lines.push("```");
|
|
476
|
+
const outputText = extractOutputText(cell.outputs);
|
|
477
|
+
if (outputText) {
|
|
478
|
+
lines.push("Output:");
|
|
479
|
+
lines.push("```");
|
|
480
|
+
lines.push(outputText);
|
|
481
|
+
lines.push("```");
|
|
482
|
+
}
|
|
483
|
+
lines.push("");
|
|
484
|
+
} else if (type === "raw") {
|
|
485
|
+
lines.push(`## Cell ${cellNum} [raw]`);
|
|
486
|
+
lines.push(source);
|
|
487
|
+
lines.push("");
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
return lines.join("\n");
|
|
491
|
+
}
|
|
492
|
+
function serializeNotebook(nb) {
|
|
493
|
+
return JSON.stringify(nb, null, 1);
|
|
494
|
+
}
|
|
495
|
+
function createCell(cellType, content) {
|
|
496
|
+
const cell = {
|
|
497
|
+
cell_type: cellType,
|
|
498
|
+
source: stringToCellSource(content),
|
|
499
|
+
metadata: {}
|
|
500
|
+
};
|
|
501
|
+
if (cellType === "code") {
|
|
502
|
+
cell.outputs = [];
|
|
503
|
+
cell.execution_count = null;
|
|
504
|
+
}
|
|
505
|
+
return cell;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/tools/builtin/read-file.ts
|
|
405
509
|
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
406
510
|
function getSensitiveWarning(normalizedPath) {
|
|
407
511
|
const home = homedir();
|
|
@@ -593,6 +697,16 @@ Please use read_file to read the above files.`;
|
|
|
593
697
|
Cannot extract text from this PDF (requires pdftotext or pdfminer.six).
|
|
594
698
|
Suggestion: read existing text versions (.md / .txt) in the project, or install extraction tools via bash and retry.`;
|
|
595
699
|
}
|
|
700
|
+
if (ext === ".ipynb") {
|
|
701
|
+
try {
|
|
702
|
+
const raw = readFileSync2(normalizedPath, "utf-8");
|
|
703
|
+
const nb = parseNotebook(raw);
|
|
704
|
+
return renderNotebookAsText(nb, filePath);
|
|
705
|
+
} catch (err) {
|
|
706
|
+
return `[Notebook parse error: ${filePath}]
|
|
707
|
+
${err.message}`;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
596
710
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
597
711
|
return `[Binary file: ${filePath} (${ext})]
|
|
598
712
|
This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
|
|
@@ -627,7 +741,7 @@ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
|
627
741
|
|
|
628
742
|
// src/tools/types.ts
|
|
629
743
|
function isFileWriteTool(name) {
|
|
630
|
-
return name === "write_file" || name === "edit_file";
|
|
744
|
+
return name === "write_file" || name === "edit_file" || name === "notebook_edit";
|
|
631
745
|
}
|
|
632
746
|
function getDangerLevel(toolName, args) {
|
|
633
747
|
if (toolName.startsWith("mcp__")) return "safe";
|
|
@@ -653,6 +767,9 @@ function getDangerLevel(toolName, args) {
|
|
|
653
767
|
}
|
|
654
768
|
if (toolName === "task_create" || toolName === "task_stop") return "write";
|
|
655
769
|
if (toolName === "task_list") return "safe";
|
|
770
|
+
if (toolName === "git_commit") return "write";
|
|
771
|
+
if (toolName === "git_status" || toolName === "git_diff" || toolName === "git_log") return "safe";
|
|
772
|
+
if (toolName === "notebook_edit") return "write";
|
|
656
773
|
if (toolName === "read_file" || toolName === "list_dir" || toolName === "grep_files" || toolName === "glob_files" || toolName === "web_fetch" || toolName === "save_memory" || toolName === "ask_user" || toolName === "write_todos" || toolName === "google_search" || toolName === "spawn_agent" || toolName === "run_tests") return "safe";
|
|
657
774
|
return "write";
|
|
658
775
|
}
|
|
@@ -877,18 +994,69 @@ function setContextWindow(contextWindow) {
|
|
|
877
994
|
function getActiveMaxChars() {
|
|
878
995
|
return activeMaxChars;
|
|
879
996
|
}
|
|
997
|
+
function collapseRepeatedLines(content) {
|
|
998
|
+
const lines = content.split("\n");
|
|
999
|
+
if (lines.length < 5) return { collapsed: content, savedChars: 0 };
|
|
1000
|
+
const out = [];
|
|
1001
|
+
let i = 0;
|
|
1002
|
+
let savedChars = 0;
|
|
1003
|
+
while (i < lines.length) {
|
|
1004
|
+
const cur = lines[i];
|
|
1005
|
+
let runEnd = i + 1;
|
|
1006
|
+
while (runEnd < lines.length && lines[runEnd] === cur) runEnd++;
|
|
1007
|
+
const runLen = runEnd - i;
|
|
1008
|
+
if (runLen >= 4 && cur.length > 0) {
|
|
1009
|
+
out.push(cur);
|
|
1010
|
+
const omitted = runLen - 1;
|
|
1011
|
+
out.push(`[... previous line repeated ${omitted} more times ...]`);
|
|
1012
|
+
savedChars += omitted * (cur.length + 1) - 50;
|
|
1013
|
+
} else {
|
|
1014
|
+
for (let k = i; k < runEnd; k++) out.push(lines[k]);
|
|
1015
|
+
}
|
|
1016
|
+
i = runEnd;
|
|
1017
|
+
}
|
|
1018
|
+
return { collapsed: out.join("\n"), savedChars: Math.max(0, savedChars) };
|
|
1019
|
+
}
|
|
1020
|
+
function snapToLineBoundary(content, target, direction) {
|
|
1021
|
+
const maxSearch = 200;
|
|
1022
|
+
if (direction === "back") {
|
|
1023
|
+
for (let i = 0; i < maxSearch && target - i > 0; i++) {
|
|
1024
|
+
if (content[target - i] === "\n") return target - i;
|
|
1025
|
+
}
|
|
1026
|
+
return target;
|
|
1027
|
+
} else {
|
|
1028
|
+
for (let i = 0; i < maxSearch && target + i < content.length; i++) {
|
|
1029
|
+
if (content[target + i] === "\n") return target + i + 1;
|
|
1030
|
+
}
|
|
1031
|
+
return target;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
880
1034
|
function truncateOutput(content, toolName, maxChars) {
|
|
881
1035
|
const limit = maxChars ?? activeMaxChars;
|
|
882
1036
|
if (content.length <= limit) return content;
|
|
1037
|
+
const { collapsed, savedChars } = collapseRepeatedLines(content);
|
|
1038
|
+
if (collapsed.length <= limit) {
|
|
1039
|
+
return savedChars > 0 ? collapsed + `
|
|
1040
|
+
|
|
1041
|
+
[Note: collapsed ${savedChars} chars of repeated lines]` : collapsed;
|
|
1042
|
+
}
|
|
1043
|
+
const working = collapsed;
|
|
1044
|
+
const totalLines = working.split("\n").length;
|
|
883
1045
|
const keepHead = Math.floor(limit * 0.7);
|
|
884
1046
|
const keepTail = Math.floor(limit * 0.2);
|
|
885
|
-
const
|
|
886
|
-
const
|
|
887
|
-
const head =
|
|
888
|
-
const tail =
|
|
1047
|
+
const headEnd = snapToLineBoundary(working, keepHead, "back");
|
|
1048
|
+
const tailStart = snapToLineBoundary(working, working.length - keepTail, "forward");
|
|
1049
|
+
const head = working.slice(0, headEnd);
|
|
1050
|
+
const tail = working.slice(tailStart);
|
|
1051
|
+
const headLines = head.split("\n").length;
|
|
1052
|
+
const tailLines = tail.split("\n").length;
|
|
1053
|
+
const omittedLines = Math.max(0, totalLines - headLines - tailLines);
|
|
1054
|
+
const omittedChars = working.length - head.length - tail.length;
|
|
1055
|
+
const repeatNote = savedChars > 0 ? ` (plus ${savedChars} chars of repeated lines collapsed)` : "";
|
|
1056
|
+
const hint = toolName === "bash" ? "narrow the command scope (e.g., head/tail/grep) or redirect to a file" : toolName === "read_file" ? "read specific line ranges with offset/limit" : toolName === "grep_files" ? "narrow the pattern or use max_results" : "request a narrower query";
|
|
889
1057
|
return head + `
|
|
890
1058
|
|
|
891
|
-
... [Output truncated: ${
|
|
1059
|
+
... [Output truncated: ${working.length} chars / ${totalLines} lines total. Showing first ${headLines} lines and last ${tailLines} lines; ${omittedLines} lines / ${omittedChars} chars omitted in the middle${repeatNote}. To see the missing content, ${hint}.] ...
|
|
892
1060
|
|
|
893
1061
|
` + tail;
|
|
894
1062
|
}
|
|
@@ -1214,7 +1382,7 @@ var ToolExecutor = class {
|
|
|
1214
1382
|
rl.resume();
|
|
1215
1383
|
process.stdout.write(prompt);
|
|
1216
1384
|
this.confirming = true;
|
|
1217
|
-
return new Promise((
|
|
1385
|
+
return new Promise((resolve5) => {
|
|
1218
1386
|
let completed = false;
|
|
1219
1387
|
const cleanup = (result) => {
|
|
1220
1388
|
if (completed) return;
|
|
@@ -1224,7 +1392,7 @@ var ToolExecutor = class {
|
|
|
1224
1392
|
rl.pause();
|
|
1225
1393
|
rlAny.output = savedOutput;
|
|
1226
1394
|
this.confirming = false;
|
|
1227
|
-
|
|
1395
|
+
resolve5(result);
|
|
1228
1396
|
};
|
|
1229
1397
|
const onLine = (line) => {
|
|
1230
1398
|
const trimmed = line.trim();
|
|
@@ -1394,7 +1562,7 @@ var ToolExecutor = class {
|
|
|
1394
1562
|
rl.resume();
|
|
1395
1563
|
process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
|
|
1396
1564
|
this.confirming = true;
|
|
1397
|
-
return new Promise((
|
|
1565
|
+
return new Promise((resolve5) => {
|
|
1398
1566
|
let completed = false;
|
|
1399
1567
|
const cleanup = (answer) => {
|
|
1400
1568
|
if (completed) return;
|
|
@@ -1404,7 +1572,7 @@ var ToolExecutor = class {
|
|
|
1404
1572
|
rl.pause();
|
|
1405
1573
|
rlAny.output = savedOutput;
|
|
1406
1574
|
this.confirming = false;
|
|
1407
|
-
|
|
1575
|
+
resolve5(answer === "y");
|
|
1408
1576
|
};
|
|
1409
1577
|
const onLine = (line) => {
|
|
1410
1578
|
const trimmed = line.trim();
|
|
@@ -2265,7 +2433,7 @@ var runInteractiveTool = {
|
|
|
2265
2433
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
2266
2434
|
};
|
|
2267
2435
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
2268
|
-
return new Promise((
|
|
2436
|
+
return new Promise((resolve5) => {
|
|
2269
2437
|
const child = spawn(executable, cmdArgs.map(String), {
|
|
2270
2438
|
cwd: process.cwd(),
|
|
2271
2439
|
env,
|
|
@@ -2298,22 +2466,22 @@ var runInteractiveTool = {
|
|
|
2298
2466
|
setTimeout(writeNextLine, 400);
|
|
2299
2467
|
const timer = setTimeout(() => {
|
|
2300
2468
|
child.kill();
|
|
2301
|
-
|
|
2469
|
+
resolve5(`${prefixWarnings}[Timeout after ${timeout}ms]
|
|
2302
2470
|
${buildOutput(stdout, stderr)}`);
|
|
2303
2471
|
}, timeout);
|
|
2304
2472
|
child.on("close", (code) => {
|
|
2305
2473
|
clearTimeout(timer);
|
|
2306
2474
|
const output = buildOutput(stdout, stderr);
|
|
2307
2475
|
if (code !== 0 && code !== null) {
|
|
2308
|
-
|
|
2476
|
+
resolve5(`${prefixWarnings}Exit code ${code}:
|
|
2309
2477
|
${output}`);
|
|
2310
2478
|
} else {
|
|
2311
|
-
|
|
2479
|
+
resolve5(`${prefixWarnings}${output || "(no output)"}`);
|
|
2312
2480
|
}
|
|
2313
2481
|
});
|
|
2314
2482
|
child.on("error", (err) => {
|
|
2315
2483
|
clearTimeout(timer);
|
|
2316
|
-
|
|
2484
|
+
resolve5(
|
|
2317
2485
|
`${prefixWarnings}Failed to start process "${executable}": ${err.message}
|
|
2318
2486
|
Hint: On Windows, use the full path to the executable, e.g.:
|
|
2319
2487
|
C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
|
|
@@ -2658,7 +2826,7 @@ function promptUser(rl, question) {
|
|
|
2658
2826
|
console.log();
|
|
2659
2827
|
console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
|
|
2660
2828
|
process.stdout.write(chalk4.cyan("> "));
|
|
2661
|
-
return new Promise((
|
|
2829
|
+
return new Promise((resolve5) => {
|
|
2662
2830
|
let completed = false;
|
|
2663
2831
|
const cleanup = (answer) => {
|
|
2664
2832
|
if (completed) return;
|
|
@@ -2668,7 +2836,7 @@ function promptUser(rl, question) {
|
|
|
2668
2836
|
rl.pause();
|
|
2669
2837
|
rlAny.output = savedOutput;
|
|
2670
2838
|
askUserContext.prompting = false;
|
|
2671
|
-
|
|
2839
|
+
resolve5(answer);
|
|
2672
2840
|
};
|
|
2673
2841
|
const onLine = (line) => {
|
|
2674
2842
|
cleanup(line);
|
|
@@ -2947,12 +3115,18 @@ var spawnAgentContext = {
|
|
|
2947
3115
|
configManager: null
|
|
2948
3116
|
};
|
|
2949
3117
|
var PREFIX = theme.dim(" \u2503 ");
|
|
3118
|
+
function agentPrefix(agentIndex) {
|
|
3119
|
+
if (agentIndex === null) return PREFIX;
|
|
3120
|
+
return theme.dim(` \u2503${agentIndex} `);
|
|
3121
|
+
}
|
|
2950
3122
|
var SubAgentExecutor = class {
|
|
2951
|
-
constructor(registry) {
|
|
3123
|
+
constructor(registry, agentIndex = null) {
|
|
2952
3124
|
this.registry = registry;
|
|
3125
|
+
this.prefix = agentPrefix(agentIndex);
|
|
2953
3126
|
}
|
|
2954
3127
|
round = 0;
|
|
2955
3128
|
totalRounds = 0;
|
|
3129
|
+
prefix = PREFIX;
|
|
2956
3130
|
setRoundInfo(current, total) {
|
|
2957
3131
|
this.round = current;
|
|
2958
3132
|
this.totalRounds = total;
|
|
@@ -3000,14 +3174,14 @@ var SubAgentExecutor = class {
|
|
|
3000
3174
|
// ── 带前缀的终端输出 ──
|
|
3001
3175
|
printPrefixed(text) {
|
|
3002
3176
|
for (const line of text.split("\n")) {
|
|
3003
|
-
console.log(
|
|
3177
|
+
console.log(this.prefix + line);
|
|
3004
3178
|
}
|
|
3005
3179
|
}
|
|
3006
3180
|
printToolCall(call, dangerLevel) {
|
|
3007
|
-
console.log(
|
|
3181
|
+
console.log(this.prefix);
|
|
3008
3182
|
const icon = dangerLevel === "write" ? theme.warning("\u270E Tool: ") : theme.toolCall("\u2699 Tool: ");
|
|
3009
3183
|
const roundBadge = this.totalRounds > 0 ? theme.dim(` [${this.round}/${this.totalRounds}]`) : "";
|
|
3010
|
-
console.log(
|
|
3184
|
+
console.log(this.prefix + icon + call.name + roundBadge);
|
|
3011
3185
|
for (const [key, val] of Object.entries(call.arguments)) {
|
|
3012
3186
|
let valStr;
|
|
3013
3187
|
if (Array.isArray(val)) {
|
|
@@ -3018,22 +3192,22 @@ var SubAgentExecutor = class {
|
|
|
3018
3192
|
} else {
|
|
3019
3193
|
valStr = String(val);
|
|
3020
3194
|
}
|
|
3021
|
-
console.log(
|
|
3195
|
+
console.log(this.prefix + theme.dim(` ${key}: `) + valStr);
|
|
3022
3196
|
}
|
|
3023
3197
|
}
|
|
3024
3198
|
printToolResult(name, content, isError, wasTruncated) {
|
|
3025
3199
|
if (isError) {
|
|
3026
3200
|
console.log(
|
|
3027
|
-
|
|
3201
|
+
this.prefix + theme.error(`\u26A0 ${name} error: `) + theme.dim(content.slice(0, 300))
|
|
3028
3202
|
);
|
|
3029
3203
|
} else {
|
|
3030
3204
|
const lines = content.split("\n");
|
|
3031
3205
|
const maxLines = name === "run_interactive" ? 40 : 8;
|
|
3032
3206
|
const preview = lines.slice(0, maxLines);
|
|
3033
|
-
const prefixedPreview = preview.map((l) =>
|
|
3034
|
-
const moreLines = lines.length > maxLines ? "\n" +
|
|
3035
|
-
const truncNote = wasTruncated ? "\n" +
|
|
3036
|
-
console.log(
|
|
3207
|
+
const prefixedPreview = preview.map((l) => this.prefix + " " + theme.dim(l)).join("\n");
|
|
3208
|
+
const moreLines = lines.length > maxLines ? "\n" + this.prefix + theme.dim(` ... (${lines.length - maxLines} more lines)`) : "";
|
|
3209
|
+
const truncNote = wasTruncated ? "\n" + this.prefix + theme.warning(` \u26A1 Output truncated`) : "";
|
|
3210
|
+
console.log(this.prefix + theme.success("\u2713 Result:"));
|
|
3037
3211
|
console.log(prefixedPreview + moreLines + truncNote);
|
|
3038
3212
|
}
|
|
3039
3213
|
}
|
|
@@ -3061,22 +3235,26 @@ ${task}
|
|
|
3061
3235
|
);
|
|
3062
3236
|
return parts.join("\n\n---\n\n");
|
|
3063
3237
|
}
|
|
3064
|
-
function printSubAgentHeader(task, maxRounds) {
|
|
3238
|
+
function printSubAgentHeader(task, maxRounds, agentIndex = null) {
|
|
3239
|
+
const prefix = agentPrefix(agentIndex);
|
|
3065
3240
|
console.log();
|
|
3066
3241
|
console.log(theme.dim(" \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
3067
|
-
|
|
3242
|
+
const label = agentIndex !== null ? `\u{1F916} Sub-Agent #${agentIndex} Spawned` : "\u{1F916} Sub-Agent Spawned";
|
|
3243
|
+
console.log(prefix + theme.toolCall(label));
|
|
3068
3244
|
console.log(
|
|
3069
|
-
|
|
3245
|
+
prefix + theme.dim("Task: ") + (task.slice(0, 120) + (task.length > 120 ? "..." : ""))
|
|
3070
3246
|
);
|
|
3071
|
-
console.log(
|
|
3247
|
+
console.log(prefix + theme.dim(`Max rounds: ${maxRounds}`));
|
|
3072
3248
|
console.log(theme.dim(" \u2503"));
|
|
3073
3249
|
}
|
|
3074
|
-
function printSubAgentFooter(usage) {
|
|
3075
|
-
|
|
3076
|
-
console.log(
|
|
3250
|
+
function printSubAgentFooter(usage, agentIndex = null) {
|
|
3251
|
+
const prefix = agentPrefix(agentIndex);
|
|
3252
|
+
console.log(prefix);
|
|
3253
|
+
const label = agentIndex !== null ? `Sub-Agent #${agentIndex} Complete` : "Sub-Agent Complete";
|
|
3254
|
+
console.log(prefix + theme.toolCall(label));
|
|
3077
3255
|
if (usage.inputTokens > 0 || usage.outputTokens > 0) {
|
|
3078
3256
|
console.log(
|
|
3079
|
-
|
|
3257
|
+
prefix + theme.dim(
|
|
3080
3258
|
`Tokens: ${usage.inputTokens} in / ${usage.outputTokens} out`
|
|
3081
3259
|
)
|
|
3082
3260
|
);
|
|
@@ -3084,27 +3262,114 @@ function printSubAgentFooter(usage) {
|
|
|
3084
3262
|
console.log(theme.dim(" \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
3085
3263
|
console.log();
|
|
3086
3264
|
}
|
|
3265
|
+
async function runSubAgent(task, maxRounds, agentIndex, ctx) {
|
|
3266
|
+
if (!ctx.provider) {
|
|
3267
|
+
throw new ToolError("spawn_agent", "provider not initialized (context not injected)");
|
|
3268
|
+
}
|
|
3269
|
+
const subRegistry = new ToolRegistry();
|
|
3270
|
+
for (const tool of subRegistry.listAll()) {
|
|
3271
|
+
if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name)) {
|
|
3272
|
+
subRegistry.unregister(tool.definition.name);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
const subExecutor = new SubAgentExecutor(subRegistry, agentIndex);
|
|
3276
|
+
const subMessages = [
|
|
3277
|
+
{ role: "user", content: task, timestamp: /* @__PURE__ */ new Date() }
|
|
3278
|
+
];
|
|
3279
|
+
const subSystemPrompt = buildSubAgentSystemPrompt(task, ctx.systemPrompt);
|
|
3280
|
+
const toolDefs = subRegistry.getDefinitions();
|
|
3281
|
+
const extraMessages = [];
|
|
3282
|
+
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
3283
|
+
let finalContent = "";
|
|
3284
|
+
printSubAgentHeader(task, maxRounds, agentIndex);
|
|
3285
|
+
try {
|
|
3286
|
+
for (let round = 0; round < maxRounds; round++) {
|
|
3287
|
+
subExecutor.setRoundInfo(round + 1, maxRounds);
|
|
3288
|
+
const result = await ctx.provider.chatWithTools(
|
|
3289
|
+
{
|
|
3290
|
+
messages: subMessages,
|
|
3291
|
+
model: ctx.model,
|
|
3292
|
+
systemPrompt: subSystemPrompt,
|
|
3293
|
+
stream: false,
|
|
3294
|
+
temperature: ctx.modelParams.temperature,
|
|
3295
|
+
maxTokens: ctx.modelParams.maxTokens,
|
|
3296
|
+
timeout: ctx.modelParams.timeout,
|
|
3297
|
+
thinking: ctx.modelParams.thinking,
|
|
3298
|
+
...extraMessages.length > 0 ? { _extraMessages: extraMessages } : {}
|
|
3299
|
+
},
|
|
3300
|
+
toolDefs
|
|
3301
|
+
);
|
|
3302
|
+
if (result.usage) {
|
|
3303
|
+
totalUsage.inputTokens += result.usage.inputTokens;
|
|
3304
|
+
totalUsage.outputTokens += result.usage.outputTokens;
|
|
3305
|
+
}
|
|
3306
|
+
if ("content" in result) {
|
|
3307
|
+
finalContent = result.content;
|
|
3308
|
+
break;
|
|
3309
|
+
}
|
|
3310
|
+
if (ctx.configManager) {
|
|
3311
|
+
googleSearchContext.configManager = ctx.configManager;
|
|
3312
|
+
}
|
|
3313
|
+
const toolResults = await subExecutor.executeAll(result.toolCalls);
|
|
3314
|
+
const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
|
|
3315
|
+
const newMsgs = ctx.provider.buildToolResultMessages(
|
|
3316
|
+
result.toolCalls,
|
|
3317
|
+
toolResults,
|
|
3318
|
+
reasoningContent
|
|
3319
|
+
);
|
|
3320
|
+
extraMessages.push(...newMsgs);
|
|
3321
|
+
}
|
|
3322
|
+
if (!finalContent) {
|
|
3323
|
+
finalContent = `(Sub-agent reached maximum rounds (${maxRounds}) without producing a final response)`;
|
|
3324
|
+
}
|
|
3325
|
+
} catch (err) {
|
|
3326
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3327
|
+
finalContent = `(Sub-agent error: ${errMsg})`;
|
|
3328
|
+
process.stderr.write(
|
|
3329
|
+
`
|
|
3330
|
+
[spawn_agent] Error in sub-agent loop: ${errMsg}
|
|
3331
|
+
`
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
printSubAgentFooter(totalUsage, agentIndex);
|
|
3335
|
+
return { content: finalContent, usage: totalUsage };
|
|
3336
|
+
}
|
|
3087
3337
|
var spawnAgentTool = {
|
|
3088
3338
|
definition: {
|
|
3089
3339
|
name: "spawn_agent",
|
|
3090
|
-
description: "Delegate
|
|
3340
|
+
description: "Delegate subtask(s) to independent sub-agent(s). Each sub-agent has its own conversation context and agentic tool-call loop, with access to bash, file read/write, edit, search, etc., but cannot interact with the user or spawn more sub-agents. Suitable for independently completable subtasks like: refactoring a module, writing tests, researching a technical approach, or implementing a specific feature. Pass `task` for a single sub-agent, or `tasks` (array) to run multiple sub-agents IN PARALLEL. Parallel mode is ideal for independent research/analysis tasks \u2014 network-bound API calls overlap while the main thread stays responsive. Returns a summary of each sub-agent result.",
|
|
3091
3341
|
parameters: {
|
|
3092
3342
|
task: {
|
|
3093
3343
|
type: "string",
|
|
3094
|
-
description: "
|
|
3095
|
-
required:
|
|
3344
|
+
description: "Single task description. Must include all necessary context: file paths, requirements, constraints, expected output. The sub-agent cannot access the parent conversation history; all info must be in this parameter. Use this OR `tasks`, not both.",
|
|
3345
|
+
required: false
|
|
3346
|
+
},
|
|
3347
|
+
tasks: {
|
|
3348
|
+
type: "array",
|
|
3349
|
+
description: "Array of task descriptions for parallel execution. Each entry spawns an independent sub-agent that runs concurrently. Use this only for TRULY independent tasks (no shared state, no order dependencies). Each task must be fully self-contained. Use this OR `task`, not both.",
|
|
3350
|
+
items: {
|
|
3351
|
+
type: "string",
|
|
3352
|
+
description: "A single self-contained task description."
|
|
3353
|
+
},
|
|
3354
|
+
required: false
|
|
3096
3355
|
},
|
|
3097
3356
|
max_rounds: {
|
|
3098
3357
|
type: "number",
|
|
3099
|
-
description: "Max tool-call rounds
|
|
3358
|
+
description: "Max tool-call rounds per sub-agent (1-15, default 10). Applied to each sub-agent in parallel mode.",
|
|
3100
3359
|
required: false
|
|
3101
3360
|
}
|
|
3102
3361
|
},
|
|
3103
3362
|
dangerous: false
|
|
3104
3363
|
},
|
|
3105
3364
|
async execute(args) {
|
|
3106
|
-
const
|
|
3107
|
-
|
|
3365
|
+
const singleTask = typeof args["task"] === "string" ? args["task"].trim() : "";
|
|
3366
|
+
const tasksArr = Array.isArray(args["tasks"]) ? args["tasks"].map((t) => String(t ?? "").trim()).filter(Boolean) : [];
|
|
3367
|
+
if (!singleTask && tasksArr.length === 0) {
|
|
3368
|
+
throw new ToolError("spawn_agent", "either `task` or `tasks` parameter is required");
|
|
3369
|
+
}
|
|
3370
|
+
if (singleTask && tasksArr.length > 0) {
|
|
3371
|
+
throw new ToolError("spawn_agent", "use `task` OR `tasks`, not both");
|
|
3372
|
+
}
|
|
3108
3373
|
const rawMaxRounds = Number(args["max_rounds"] ?? SUBAGENT_DEFAULT_MAX_ROUNDS);
|
|
3109
3374
|
const maxRounds = Math.min(
|
|
3110
3375
|
Math.max(Math.round(rawMaxRounds), 1),
|
|
@@ -3114,80 +3379,36 @@ var spawnAgentTool = {
|
|
|
3114
3379
|
if (!ctx.provider) {
|
|
3115
3380
|
throw new ToolError("spawn_agent", "provider not initialized (context not injected)");
|
|
3116
3381
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
const subSystemPrompt = buildSubAgentSystemPrompt(task, ctx.systemPrompt);
|
|
3128
|
-
const toolDefs = subRegistry.getDefinitions();
|
|
3129
|
-
const extraMessages = [];
|
|
3130
|
-
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
3131
|
-
let finalContent = "";
|
|
3132
|
-
printSubAgentHeader(task, maxRounds);
|
|
3133
|
-
try {
|
|
3134
|
-
for (let round = 0; round < maxRounds; round++) {
|
|
3135
|
-
subExecutor.setRoundInfo(round + 1, maxRounds);
|
|
3136
|
-
const result = await ctx.provider.chatWithTools(
|
|
3137
|
-
{
|
|
3138
|
-
messages: subMessages,
|
|
3139
|
-
model: ctx.model,
|
|
3140
|
-
systemPrompt: subSystemPrompt,
|
|
3141
|
-
stream: false,
|
|
3142
|
-
temperature: ctx.modelParams.temperature,
|
|
3143
|
-
maxTokens: ctx.modelParams.maxTokens,
|
|
3144
|
-
timeout: ctx.modelParams.timeout,
|
|
3145
|
-
thinking: ctx.modelParams.thinking,
|
|
3146
|
-
...extraMessages.length > 0 ? { _extraMessages: extraMessages } : {}
|
|
3147
|
-
},
|
|
3148
|
-
toolDefs
|
|
3149
|
-
);
|
|
3150
|
-
if (result.usage) {
|
|
3151
|
-
totalUsage.inputTokens += result.usage.inputTokens;
|
|
3152
|
-
totalUsage.outputTokens += result.usage.outputTokens;
|
|
3153
|
-
}
|
|
3154
|
-
if ("content" in result) {
|
|
3155
|
-
finalContent = result.content;
|
|
3156
|
-
break;
|
|
3157
|
-
}
|
|
3158
|
-
if (ctx.configManager) {
|
|
3159
|
-
googleSearchContext.configManager = ctx.configManager;
|
|
3160
|
-
}
|
|
3161
|
-
const toolResults = await subExecutor.executeAll(result.toolCalls);
|
|
3162
|
-
const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
|
|
3163
|
-
const newMsgs = ctx.provider.buildToolResultMessages(
|
|
3164
|
-
result.toolCalls,
|
|
3165
|
-
toolResults,
|
|
3166
|
-
reasoningContent
|
|
3167
|
-
);
|
|
3168
|
-
extraMessages.push(...newMsgs);
|
|
3169
|
-
}
|
|
3170
|
-
if (!finalContent) {
|
|
3171
|
-
finalContent = `(Sub-agent reached maximum rounds (${maxRounds}) without producing a final response)`;
|
|
3172
|
-
}
|
|
3173
|
-
} catch (err) {
|
|
3174
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3175
|
-
finalContent = `(Sub-agent error: ${errMsg})`;
|
|
3176
|
-
process.stderr.write(
|
|
3177
|
-
`
|
|
3178
|
-
[spawn_agent] Error in sub-agent loop: ${errMsg}
|
|
3179
|
-
`
|
|
3180
|
-
);
|
|
3382
|
+
if (singleTask) {
|
|
3383
|
+
const { content, usage } = await runSubAgent(singleTask, maxRounds, null, ctx);
|
|
3384
|
+
return [
|
|
3385
|
+
"## Sub-Agent Result",
|
|
3386
|
+
"",
|
|
3387
|
+
content,
|
|
3388
|
+
"",
|
|
3389
|
+
"---",
|
|
3390
|
+
`Token usage: ${usage.inputTokens} input, ${usage.outputTokens} output`
|
|
3391
|
+
].join("\n");
|
|
3181
3392
|
}
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3393
|
+
console.log();
|
|
3394
|
+
console.log(theme.toolCall(`\u{1F916} Spawning ${tasksArr.length} sub-agents in parallel...`));
|
|
3395
|
+
const results = await Promise.all(
|
|
3396
|
+
tasksArr.map((t, i) => runSubAgent(t, maxRounds, i + 1, ctx))
|
|
3397
|
+
);
|
|
3398
|
+
const totalIn = results.reduce((s, r) => s + r.usage.inputTokens, 0);
|
|
3399
|
+
const totalOut = results.reduce((s, r) => s + r.usage.outputTokens, 0);
|
|
3400
|
+
const lines = [`## Parallel Sub-Agent Results (${results.length} agents)`, ""];
|
|
3401
|
+
results.forEach((r, i) => {
|
|
3402
|
+
lines.push(`### Sub-Agent #${i + 1}`);
|
|
3403
|
+
lines.push(`**Task**: ${tasksArr[i].slice(0, 200)}${tasksArr[i].length > 200 ? "..." : ""}`);
|
|
3404
|
+
lines.push("");
|
|
3405
|
+
lines.push(r.content);
|
|
3406
|
+
lines.push("");
|
|
3407
|
+
lines.push(`*Tokens: ${r.usage.inputTokens} in / ${r.usage.outputTokens} out*`);
|
|
3408
|
+
lines.push("");
|
|
3409
|
+
});
|
|
3410
|
+
lines.push("---");
|
|
3411
|
+
lines.push(`Total token usage: ${totalIn} input, ${totalOut} output`);
|
|
3191
3412
|
return lines.join("\n");
|
|
3192
3413
|
}
|
|
3193
3414
|
};
|
|
@@ -3377,10 +3598,392 @@ var taskStopTool = {
|
|
|
3377
3598
|
}
|
|
3378
3599
|
};
|
|
3379
3600
|
|
|
3601
|
+
// src/tools/builtin/git-tools.ts
|
|
3602
|
+
import { execSync as execSync4 } from "child_process";
|
|
3603
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3604
|
+
import { join as join5 } from "path";
|
|
3605
|
+
function assertGitRepo(cwd) {
|
|
3606
|
+
let dir = cwd;
|
|
3607
|
+
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
3608
|
+
while (dir && dir !== root) {
|
|
3609
|
+
if (existsSync10(join5(dir, ".git"))) return;
|
|
3610
|
+
const parent = join5(dir, "..");
|
|
3611
|
+
if (parent === dir) break;
|
|
3612
|
+
dir = parent;
|
|
3613
|
+
}
|
|
3614
|
+
throw new ToolError("git", "Not inside a git repository. Run this command from a directory under a git-tracked project.");
|
|
3615
|
+
}
|
|
3616
|
+
function runGit(args, cwd, maxBuffer = 10 * 1024 * 1024) {
|
|
3617
|
+
try {
|
|
3618
|
+
const output = execSync4(`git ${args.map((a) => /\s/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a).join(" ")}`, {
|
|
3619
|
+
cwd,
|
|
3620
|
+
encoding: "utf-8",
|
|
3621
|
+
maxBuffer,
|
|
3622
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3623
|
+
});
|
|
3624
|
+
return output;
|
|
3625
|
+
} catch (err) {
|
|
3626
|
+
const e = err;
|
|
3627
|
+
const stderr = e.stderr ? String(e.stderr) : "";
|
|
3628
|
+
const stdout = e.stdout ? String(e.stdout) : "";
|
|
3629
|
+
const msg = stderr || stdout || e.message || "git command failed";
|
|
3630
|
+
throw new ToolError("git", `git ${args[0]} failed: ${msg.trim()}`);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
var gitStatusTool = {
|
|
3634
|
+
definition: {
|
|
3635
|
+
name: "git_status",
|
|
3636
|
+
description: `Show the current git working tree status: staged, modified, untracked files, and current branch. Use this before committing to verify what will be included. Prefer this over "bash git status".`,
|
|
3637
|
+
parameters: {
|
|
3638
|
+
path: {
|
|
3639
|
+
type: "string",
|
|
3640
|
+
description: "Repository path (defaults to current working directory)",
|
|
3641
|
+
required: false
|
|
3642
|
+
}
|
|
3643
|
+
},
|
|
3644
|
+
dangerous: false
|
|
3645
|
+
},
|
|
3646
|
+
async execute(args) {
|
|
3647
|
+
const cwd = String(args["path"] ?? process.cwd());
|
|
3648
|
+
assertGitRepo(cwd);
|
|
3649
|
+
let branch = "(unknown)";
|
|
3650
|
+
try {
|
|
3651
|
+
branch = runGit(["symbolic-ref", "--short", "HEAD"], cwd).trim();
|
|
3652
|
+
} catch {
|
|
3653
|
+
branch = "(detached)";
|
|
3654
|
+
}
|
|
3655
|
+
const porcelain = runGit(["status", "--porcelain=v1", "-b"], cwd);
|
|
3656
|
+
const lines = porcelain.split("\n").filter((l) => l.length > 0);
|
|
3657
|
+
const branchLine = lines[0]?.startsWith("##") ? lines[0].slice(3) : `${branch}`;
|
|
3658
|
+
const fileLines = lines.slice(lines[0]?.startsWith("##") ? 1 : 0);
|
|
3659
|
+
if (fileLines.length === 0) {
|
|
3660
|
+
return `Branch: ${branchLine}
|
|
3661
|
+
Working tree clean.`;
|
|
3662
|
+
}
|
|
3663
|
+
const staged = [];
|
|
3664
|
+
const modified = [];
|
|
3665
|
+
const untracked = [];
|
|
3666
|
+
const conflicts = [];
|
|
3667
|
+
for (const line of fileLines) {
|
|
3668
|
+
const x = line[0];
|
|
3669
|
+
const y = line[1];
|
|
3670
|
+
const file = line.slice(3);
|
|
3671
|
+
if (x === "U" || y === "U" || x === "A" && y === "A" || x === "D" && y === "D") {
|
|
3672
|
+
conflicts.push(file);
|
|
3673
|
+
} else if (x === "?" && y === "?") {
|
|
3674
|
+
untracked.push(file);
|
|
3675
|
+
} else {
|
|
3676
|
+
if (x !== " " && x !== "?") staged.push(`${x} ${file}`);
|
|
3677
|
+
if (y !== " " && y !== "?") modified.push(`${y} ${file}`);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
const parts = [`Branch: ${branchLine}`];
|
|
3681
|
+
if (staged.length) parts.push(`
|
|
3682
|
+
Staged (${staged.length}):
|
|
3683
|
+
` + staged.join("\n "));
|
|
3684
|
+
if (modified.length) parts.push(`
|
|
3685
|
+
Modified (${modified.length}):
|
|
3686
|
+
` + modified.join("\n "));
|
|
3687
|
+
if (untracked.length) parts.push(`
|
|
3688
|
+
Untracked (${untracked.length}):
|
|
3689
|
+
` + untracked.join("\n "));
|
|
3690
|
+
if (conflicts.length) parts.push(`
|
|
3691
|
+
Conflicts (${conflicts.length}):
|
|
3692
|
+
` + conflicts.join("\n "));
|
|
3693
|
+
return parts.join("\n");
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
var gitDiffTool = {
|
|
3697
|
+
definition: {
|
|
3698
|
+
name: "git_diff",
|
|
3699
|
+
description: `Show a unified diff of file changes. Use this to review modifications before committing or after an edit. Supports staged/unstaged diffs, specific files, or commit ranges. Prefer this over "bash git diff".`,
|
|
3700
|
+
parameters: {
|
|
3701
|
+
staged: {
|
|
3702
|
+
type: "boolean",
|
|
3703
|
+
description: "Show staged changes (git diff --cached) instead of unstaged. Default: false",
|
|
3704
|
+
required: false
|
|
3705
|
+
},
|
|
3706
|
+
file: {
|
|
3707
|
+
type: "string",
|
|
3708
|
+
description: "Limit diff to a specific file or directory",
|
|
3709
|
+
required: false
|
|
3710
|
+
},
|
|
3711
|
+
commit_range: {
|
|
3712
|
+
type: "string",
|
|
3713
|
+
description: 'Show diff between commits (e.g. "HEAD~3..HEAD", "main..feature"). Overrides staged/unstaged.',
|
|
3714
|
+
required: false
|
|
3715
|
+
},
|
|
3716
|
+
stat: {
|
|
3717
|
+
type: "boolean",
|
|
3718
|
+
description: "Show summary statistics only (file names + insertions/deletions count), not full diff. Default: false",
|
|
3719
|
+
required: false
|
|
3720
|
+
},
|
|
3721
|
+
path: {
|
|
3722
|
+
type: "string",
|
|
3723
|
+
description: "Repository path (defaults to current working directory)",
|
|
3724
|
+
required: false
|
|
3725
|
+
}
|
|
3726
|
+
},
|
|
3727
|
+
dangerous: false
|
|
3728
|
+
},
|
|
3729
|
+
async execute(args) {
|
|
3730
|
+
const cwd = String(args["path"] ?? process.cwd());
|
|
3731
|
+
assertGitRepo(cwd);
|
|
3732
|
+
const gitArgs = ["diff"];
|
|
3733
|
+
if (args["stat"]) gitArgs.push("--stat");
|
|
3734
|
+
if (args["commit_range"]) {
|
|
3735
|
+
gitArgs.push(String(args["commit_range"]));
|
|
3736
|
+
} else if (args["staged"]) {
|
|
3737
|
+
gitArgs.push("--cached");
|
|
3738
|
+
}
|
|
3739
|
+
if (args["file"]) {
|
|
3740
|
+
gitArgs.push("--", String(args["file"]));
|
|
3741
|
+
}
|
|
3742
|
+
const output = runGit(gitArgs, cwd);
|
|
3743
|
+
if (output.trim().length === 0) {
|
|
3744
|
+
const label = args["staged"] ? "staged" : args["commit_range"] ? String(args["commit_range"]) : "unstaged";
|
|
3745
|
+
return `No ${label} changes.`;
|
|
3746
|
+
}
|
|
3747
|
+
return output;
|
|
3748
|
+
}
|
|
3749
|
+
};
|
|
3750
|
+
var gitLogTool = {
|
|
3751
|
+
definition: {
|
|
3752
|
+
name: "git_log",
|
|
3753
|
+
description: `Show recent git commit history with hash, author, date, and message. Use this to understand project history, find a specific commit, or check who last modified a file. Prefer this over "bash git log".`,
|
|
3754
|
+
parameters: {
|
|
3755
|
+
limit: {
|
|
3756
|
+
type: "number",
|
|
3757
|
+
description: "Number of commits to show (default 10, max 100)",
|
|
3758
|
+
required: false
|
|
3759
|
+
},
|
|
3760
|
+
file: {
|
|
3761
|
+
type: "string",
|
|
3762
|
+
description: "Show only commits that modified this file/directory",
|
|
3763
|
+
required: false
|
|
3764
|
+
},
|
|
3765
|
+
author: {
|
|
3766
|
+
type: "string",
|
|
3767
|
+
description: "Filter commits by author name or email substring",
|
|
3768
|
+
required: false
|
|
3769
|
+
},
|
|
3770
|
+
since: {
|
|
3771
|
+
type: "string",
|
|
3772
|
+
description: 'Show commits more recent than this date (e.g. "2 weeks ago", "2025-01-01")',
|
|
3773
|
+
required: false
|
|
3774
|
+
},
|
|
3775
|
+
oneline: {
|
|
3776
|
+
type: "boolean",
|
|
3777
|
+
description: "Compact one-line format. Default: true (set false for full messages)",
|
|
3778
|
+
required: false
|
|
3779
|
+
},
|
|
3780
|
+
path: {
|
|
3781
|
+
type: "string",
|
|
3782
|
+
description: "Repository path (defaults to current working directory)",
|
|
3783
|
+
required: false
|
|
3784
|
+
}
|
|
3785
|
+
},
|
|
3786
|
+
dangerous: false
|
|
3787
|
+
},
|
|
3788
|
+
async execute(args) {
|
|
3789
|
+
const cwd = String(args["path"] ?? process.cwd());
|
|
3790
|
+
assertGitRepo(cwd);
|
|
3791
|
+
const limit = Math.min(Math.max(1, Number(args["limit"] ?? 10)), 100);
|
|
3792
|
+
const oneline = args["oneline"] !== false;
|
|
3793
|
+
const gitArgs = ["log", `-n${limit}`];
|
|
3794
|
+
if (oneline) {
|
|
3795
|
+
gitArgs.push("--pretty=format:%h | %ad | %an | %s", "--date=short");
|
|
3796
|
+
} else {
|
|
3797
|
+
gitArgs.push("--pretty=format:%h%n Author: %an <%ae>%n Date: %ad%n %s%n%n%b%n---", "--date=iso");
|
|
3798
|
+
}
|
|
3799
|
+
if (args["author"]) gitArgs.push(`--author=${String(args["author"])}`);
|
|
3800
|
+
if (args["since"]) gitArgs.push(`--since=${String(args["since"])}`);
|
|
3801
|
+
if (args["file"]) {
|
|
3802
|
+
gitArgs.push("--", String(args["file"]));
|
|
3803
|
+
}
|
|
3804
|
+
const output = runGit(gitArgs, cwd);
|
|
3805
|
+
if (output.trim().length === 0) {
|
|
3806
|
+
return "No commits found matching the filter.";
|
|
3807
|
+
}
|
|
3808
|
+
return output;
|
|
3809
|
+
}
|
|
3810
|
+
};
|
|
3811
|
+
var gitCommitTool = {
|
|
3812
|
+
definition: {
|
|
3813
|
+
name: "git_commit",
|
|
3814
|
+
description: `Create a git commit with the given message. By default commits already-staged changes; set stage_all=true to also stage all tracked modifications first (equivalent to git commit -am). Untracked files are NEVER auto-staged \u2014 the user must add them explicitly. Prefer this over "bash git commit".`,
|
|
3815
|
+
parameters: {
|
|
3816
|
+
message: {
|
|
3817
|
+
type: "string",
|
|
3818
|
+
description: "Commit message (first line is the subject; blank line + body for details)",
|
|
3819
|
+
required: true
|
|
3820
|
+
},
|
|
3821
|
+
stage_all: {
|
|
3822
|
+
type: "boolean",
|
|
3823
|
+
description: "Stage all tracked modified files before committing (git commit -a). Does NOT add untracked files. Default: false",
|
|
3824
|
+
required: false
|
|
3825
|
+
},
|
|
3826
|
+
files: {
|
|
3827
|
+
type: "array",
|
|
3828
|
+
description: "Specific files to stage before committing (git add <files> then commit). Mutually exclusive with stage_all.",
|
|
3829
|
+
items: { type: "string", description: "File path" },
|
|
3830
|
+
required: false
|
|
3831
|
+
},
|
|
3832
|
+
path: {
|
|
3833
|
+
type: "string",
|
|
3834
|
+
description: "Repository path (defaults to current working directory)",
|
|
3835
|
+
required: false
|
|
3836
|
+
}
|
|
3837
|
+
},
|
|
3838
|
+
dangerous: false
|
|
3839
|
+
},
|
|
3840
|
+
async execute(args) {
|
|
3841
|
+
const cwd = String(args["path"] ?? process.cwd());
|
|
3842
|
+
assertGitRepo(cwd);
|
|
3843
|
+
const message = String(args["message"] ?? "").trim();
|
|
3844
|
+
if (!message) throw new ToolError("git_commit", "Commit message is required.");
|
|
3845
|
+
const stageAll = Boolean(args["stage_all"]);
|
|
3846
|
+
const files = Array.isArray(args["files"]) ? args["files"].map(String) : [];
|
|
3847
|
+
if (stageAll && files.length > 0) {
|
|
3848
|
+
throw new ToolError("git_commit", "Cannot use both stage_all and files at the same time.");
|
|
3849
|
+
}
|
|
3850
|
+
if (files.length > 0) {
|
|
3851
|
+
runGit(["add", "--", ...files], cwd);
|
|
3852
|
+
}
|
|
3853
|
+
const statusBefore = runGit(["status", "--porcelain"], cwd);
|
|
3854
|
+
const hasStaged = statusBefore.split("\n").some((l) => l.length > 0 && l[0] !== " " && l[0] !== "?");
|
|
3855
|
+
const hasUnstaged = statusBefore.split("\n").some((l) => l.length > 1 && l[1] !== " " && l[1] !== "?");
|
|
3856
|
+
if (!hasStaged && !stageAll) {
|
|
3857
|
+
return "Nothing to commit: no staged changes. Use stage_all=true or specify files to stage first.";
|
|
3858
|
+
}
|
|
3859
|
+
if (stageAll && !hasStaged && !hasUnstaged) {
|
|
3860
|
+
return "Nothing to commit: working tree clean.";
|
|
3861
|
+
}
|
|
3862
|
+
const commitArgs = ["commit", "-m", message];
|
|
3863
|
+
if (stageAll) commitArgs.splice(1, 0, "-a");
|
|
3864
|
+
const commitOutput = runGit(commitArgs, cwd);
|
|
3865
|
+
const hash = runGit(["rev-parse", "--short", "HEAD"], cwd).trim();
|
|
3866
|
+
const summary = runGit(["log", "-1", "--pretty=format:%h %s"], cwd).trim();
|
|
3867
|
+
return `\u2713 Committed ${hash}
|
|
3868
|
+
${summary}
|
|
3869
|
+
|
|
3870
|
+
${commitOutput.trim()}`;
|
|
3871
|
+
}
|
|
3872
|
+
};
|
|
3873
|
+
|
|
3874
|
+
// src/tools/builtin/notebook-edit.ts
|
|
3875
|
+
import { readFileSync as readFileSync6, existsSync as existsSync11 } from "fs";
|
|
3876
|
+
import { writeFile } from "fs/promises";
|
|
3877
|
+
import { resolve as resolve4, extname as extname3 } from "path";
|
|
3878
|
+
var notebookEditTool = {
|
|
3879
|
+
definition: {
|
|
3880
|
+
name: "notebook_edit",
|
|
3881
|
+
description: `Edit a Jupyter notebook (.ipynb) by operating on individual cells. Supports replacing, inserting, or deleting cells. Use read_file first to see cell indices \u2014 they are 1-based and match the "Cell N" labels in the rendered output. Preserves notebook metadata (kernel, nbformat, etc.).`,
|
|
3882
|
+
parameters: {
|
|
3883
|
+
path: {
|
|
3884
|
+
type: "string",
|
|
3885
|
+
description: "Notebook file path (must end in .ipynb)",
|
|
3886
|
+
required: true
|
|
3887
|
+
},
|
|
3888
|
+
action: {
|
|
3889
|
+
type: "string",
|
|
3890
|
+
description: 'Operation: "replace" (edit existing cell), "insert" (add new cell), or "delete" (remove cell)',
|
|
3891
|
+
enum: ["replace", "insert", "delete"],
|
|
3892
|
+
required: true
|
|
3893
|
+
},
|
|
3894
|
+
cell_index: {
|
|
3895
|
+
type: "number",
|
|
3896
|
+
description: 'Cell index, 1-based. For "insert", the new cell is added BEFORE this index (use cells.length+1 to append at the end).',
|
|
3897
|
+
required: true
|
|
3898
|
+
},
|
|
3899
|
+
cell_type: {
|
|
3900
|
+
type: "string",
|
|
3901
|
+
description: 'Cell type for "insert" (code/markdown/raw). Defaults to "code" if omitted.',
|
|
3902
|
+
enum: ["code", "markdown", "raw"],
|
|
3903
|
+
required: false
|
|
3904
|
+
},
|
|
3905
|
+
content: {
|
|
3906
|
+
type: "string",
|
|
3907
|
+
description: 'New cell content for "replace" and "insert". Not used for "delete".',
|
|
3908
|
+
required: false
|
|
3909
|
+
}
|
|
3910
|
+
},
|
|
3911
|
+
dangerous: false
|
|
3912
|
+
},
|
|
3913
|
+
async execute(args) {
|
|
3914
|
+
const filePath = String(args["path"] ?? "");
|
|
3915
|
+
const action = String(args["action"] ?? "");
|
|
3916
|
+
const cellIndexRaw = Number(args["cell_index"]);
|
|
3917
|
+
const content = args["content"] != null ? String(args["content"]) : "";
|
|
3918
|
+
const cellType = args["cell_type"] ?? "code";
|
|
3919
|
+
if (!filePath) throw new ToolError("notebook_edit", "path is required");
|
|
3920
|
+
if (!["replace", "insert", "delete"].includes(action)) {
|
|
3921
|
+
throw new ToolError("notebook_edit", `Unknown action: ${action}. Use replace/insert/delete.`);
|
|
3922
|
+
}
|
|
3923
|
+
if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
|
|
3924
|
+
throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
|
|
3925
|
+
}
|
|
3926
|
+
const absPath = resolve4(filePath);
|
|
3927
|
+
if (extname3(absPath).toLowerCase() !== ".ipynb") {
|
|
3928
|
+
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
3929
|
+
}
|
|
3930
|
+
if (!existsSync11(absPath)) {
|
|
3931
|
+
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
3932
|
+
}
|
|
3933
|
+
const raw = readFileSync6(absPath, "utf-8");
|
|
3934
|
+
const nb = parseNotebook(raw);
|
|
3935
|
+
const cellIdx0 = cellIndexRaw - 1;
|
|
3936
|
+
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
3937
|
+
fileCheckpoints.snapshot(absPath, ToolExecutor.currentMessageIndex);
|
|
3938
|
+
let summary = "";
|
|
3939
|
+
if (action === "replace") {
|
|
3940
|
+
if (cellIdx0 < 0 || cellIdx0 >= nb.cells.length) {
|
|
3941
|
+
throw new ToolError(
|
|
3942
|
+
"notebook_edit",
|
|
3943
|
+
`cell_index ${cellIndexRaw} out of range (notebook has ${nb.cells.length} cells)`
|
|
3944
|
+
);
|
|
3945
|
+
}
|
|
3946
|
+
if (content === "") {
|
|
3947
|
+
throw new ToolError("notebook_edit", "content is required for replace action");
|
|
3948
|
+
}
|
|
3949
|
+
const existing = nb.cells[cellIdx0];
|
|
3950
|
+
const oldContent = cellSourceToString(existing.source);
|
|
3951
|
+
existing.source = stringToCellSource(content);
|
|
3952
|
+
if (existing.cell_type === "code") {
|
|
3953
|
+
existing.outputs = [];
|
|
3954
|
+
existing.execution_count = null;
|
|
3955
|
+
}
|
|
3956
|
+
summary = `Replaced cell ${cellIndexRaw} [${existing.cell_type}]: ${oldContent.length} \u2192 ${content.length} chars`;
|
|
3957
|
+
} else if (action === "insert") {
|
|
3958
|
+
if (cellIdx0 < 0 || cellIdx0 > nb.cells.length) {
|
|
3959
|
+
throw new ToolError(
|
|
3960
|
+
"notebook_edit",
|
|
3961
|
+
`cell_index ${cellIndexRaw} out of range for insert (valid: 1-${nb.cells.length + 1})`
|
|
3962
|
+
);
|
|
3963
|
+
}
|
|
3964
|
+
const newCell = createCell(cellType, content);
|
|
3965
|
+
nb.cells.splice(cellIdx0, 0, newCell);
|
|
3966
|
+
summary = `Inserted new [${cellType}] cell at position ${cellIndexRaw} (now ${nb.cells.length} cells total)`;
|
|
3967
|
+
} else {
|
|
3968
|
+
if (cellIdx0 < 0 || cellIdx0 >= nb.cells.length) {
|
|
3969
|
+
throw new ToolError(
|
|
3970
|
+
"notebook_edit",
|
|
3971
|
+
`cell_index ${cellIndexRaw} out of range (notebook has ${nb.cells.length} cells)`
|
|
3972
|
+
);
|
|
3973
|
+
}
|
|
3974
|
+
const removed = nb.cells.splice(cellIdx0, 1)[0];
|
|
3975
|
+
summary = `Deleted cell ${cellIndexRaw} [${removed.cell_type}] (${nb.cells.length} cells remaining)`;
|
|
3976
|
+
}
|
|
3977
|
+
const serialized = serializeNotebook(nb);
|
|
3978
|
+
await writeFile(absPath, serialized, "utf-8");
|
|
3979
|
+
return `\u2713 ${summary}`;
|
|
3980
|
+
}
|
|
3981
|
+
};
|
|
3982
|
+
|
|
3380
3983
|
// src/tools/registry.ts
|
|
3381
3984
|
import { pathToFileURL } from "url";
|
|
3382
|
-
import { existsSync as
|
|
3383
|
-
import { join as
|
|
3985
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readdirSync as readdirSync6 } from "fs";
|
|
3986
|
+
import { join as join6 } from "path";
|
|
3384
3987
|
var ToolRegistry = class {
|
|
3385
3988
|
tools = /* @__PURE__ */ new Map();
|
|
3386
3989
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -3405,6 +4008,11 @@ var ToolRegistry = class {
|
|
|
3405
4008
|
this.register(taskCreateTool);
|
|
3406
4009
|
this.register(taskListTool);
|
|
3407
4010
|
this.register(taskStopTool);
|
|
4011
|
+
this.register(gitStatusTool);
|
|
4012
|
+
this.register(gitDiffTool);
|
|
4013
|
+
this.register(gitLogTool);
|
|
4014
|
+
this.register(gitCommitTool);
|
|
4015
|
+
this.register(notebookEditTool);
|
|
3408
4016
|
}
|
|
3409
4017
|
register(tool) {
|
|
3410
4018
|
this.tools.set(tool.definition.name, tool);
|
|
@@ -3456,7 +4064,7 @@ var ToolRegistry = class {
|
|
|
3456
4064
|
* Returns the number of successfully loaded plugins.
|
|
3457
4065
|
*/
|
|
3458
4066
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
3459
|
-
if (!
|
|
4067
|
+
if (!existsSync12(pluginsDir)) {
|
|
3460
4068
|
try {
|
|
3461
4069
|
mkdirSync4(pluginsDir, { recursive: true });
|
|
3462
4070
|
} catch {
|
|
@@ -3482,12 +4090,12 @@ var ToolRegistry = class {
|
|
|
3482
4090
|
process.stderr.write(
|
|
3483
4091
|
`
|
|
3484
4092
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
3485
|
-
` + files.map((f) => ` + ${
|
|
4093
|
+
` + files.map((f) => ` + ${join6(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
3486
4094
|
);
|
|
3487
4095
|
let loaded = 0;
|
|
3488
4096
|
for (const file of files) {
|
|
3489
4097
|
try {
|
|
3490
|
-
const fileUrl = pathToFileURL(
|
|
4098
|
+
const fileUrl = pathToFileURL(join6(pluginsDir, file)).href;
|
|
3491
4099
|
const mod = await import(fileUrl);
|
|
3492
4100
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
3493
4101
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ProviderNotFoundError,
|
|
8
8
|
RateLimitError,
|
|
9
9
|
schemaToJsonSchema
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EWJQOJSB.js";
|
|
11
11
|
import {
|
|
12
12
|
APP_NAME,
|
|
13
13
|
CONFIG_DIR_NAME,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
MCP_TOOL_PREFIX,
|
|
21
21
|
PLUGINS_DIR_NAME,
|
|
22
22
|
VERSION
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-7SW4XRDJ.js";
|
|
24
24
|
|
|
25
25
|
// src/config/config-manager.ts
|
|
26
26
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -387,7 +387,7 @@ ${content}`);
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
390
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
390
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-TDBWMIH4.js");
|
|
391
391
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
392
392
|
let interrupted = false;
|
|
393
393
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
saveDevState,
|
|
21
21
|
sessionHasMeaningfulContent,
|
|
22
22
|
setupProxy
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-SAJICC6G.js";
|
|
24
24
|
import {
|
|
25
25
|
ToolExecutor,
|
|
26
26
|
ToolRegistry,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
spawnAgentContext,
|
|
34
34
|
theme,
|
|
35
35
|
undoStack
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-EWJQOJSB.js";
|
|
37
37
|
import {
|
|
38
38
|
fileCheckpoints
|
|
39
39
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
SKILLS_DIR_NAME,
|
|
58
58
|
VERSION,
|
|
59
59
|
buildUserIdentityPrompt
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-7SW4XRDJ.js";
|
|
61
61
|
|
|
62
62
|
// src/index.ts
|
|
63
63
|
import { program } from "commander";
|
|
@@ -2083,7 +2083,7 @@ ${hint}` : "")
|
|
|
2083
2083
|
usage: "/test [command|filter]",
|
|
2084
2084
|
async execute(args, ctx) {
|
|
2085
2085
|
try {
|
|
2086
|
-
const { executeTests } = await import("./run-tests-
|
|
2086
|
+
const { executeTests } = await import("./run-tests-QRH2Z2OH.js");
|
|
2087
2087
|
const argStr = args.join(" ").trim();
|
|
2088
2088
|
let testArgs = {};
|
|
2089
2089
|
if (argStr) {
|
|
@@ -3089,17 +3089,27 @@ var CustomCommandManager = class {
|
|
|
3089
3089
|
// src/repl/notify.ts
|
|
3090
3090
|
import { spawn } from "child_process";
|
|
3091
3091
|
import { platform as platform2 } from "os";
|
|
3092
|
+
function safeSpawn(cmd, args) {
|
|
3093
|
+
let child;
|
|
3094
|
+
try {
|
|
3095
|
+
child = spawn(cmd, args, { detached: true, stdio: "ignore" });
|
|
3096
|
+
} catch {
|
|
3097
|
+
return;
|
|
3098
|
+
}
|
|
3099
|
+
child.on("error", () => {
|
|
3100
|
+
});
|
|
3101
|
+
child.unref();
|
|
3102
|
+
}
|
|
3092
3103
|
function sendNotification(title, body) {
|
|
3093
3104
|
const plat = platform2();
|
|
3094
3105
|
try {
|
|
3095
3106
|
if (plat === "darwin") {
|
|
3096
3107
|
const safeTitle = title.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
3097
3108
|
const safeBody = body.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
3098
|
-
|
|
3099
|
-
"
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
).unref();
|
|
3109
|
+
safeSpawn("osascript", [
|
|
3110
|
+
"-e",
|
|
3111
|
+
`display notification "${safeBody}" with title "${safeTitle}"`
|
|
3112
|
+
]);
|
|
3103
3113
|
} else if (plat === "win32") {
|
|
3104
3114
|
const safeTitle = title.replace(/`/g, "``").replace(/"/g, '`"');
|
|
3105
3115
|
const safeBody = body.replace(/`/g, "``").replace(/"/g, '`"');
|
|
@@ -3114,13 +3124,16 @@ function sendNotification(title, body) {
|
|
|
3114
3124
|
"Start-Sleep -Milliseconds 3500",
|
|
3115
3125
|
"$n.Dispose()"
|
|
3116
3126
|
].join("; ");
|
|
3117
|
-
|
|
3118
|
-
"
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3127
|
+
safeSpawn("powershell", [
|
|
3128
|
+
"-NoProfile",
|
|
3129
|
+
"-NonInteractive",
|
|
3130
|
+
"-WindowStyle",
|
|
3131
|
+
"Hidden",
|
|
3132
|
+
"-Command",
|
|
3133
|
+
script
|
|
3134
|
+
]);
|
|
3122
3135
|
} else {
|
|
3123
|
-
|
|
3136
|
+
safeSpawn("notify-send", [title, body]);
|
|
3124
3137
|
}
|
|
3125
3138
|
} catch {
|
|
3126
3139
|
}
|
|
@@ -5365,7 +5378,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5365
5378
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5366
5379
|
process.exit(1);
|
|
5367
5380
|
}
|
|
5368
|
-
const { startWebServer } = await import("./server-
|
|
5381
|
+
const { startWebServer } = await import("./server-46ANSDN7.js");
|
|
5369
5382
|
await startWebServer({ port, host: options.host });
|
|
5370
5383
|
});
|
|
5371
5384
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -5598,7 +5611,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5598
5611
|
}),
|
|
5599
5612
|
config.get("customProviders")
|
|
5600
5613
|
);
|
|
5601
|
-
const { startHub } = await import("./hub-
|
|
5614
|
+
const { startHub } = await import("./hub-QJBT4RDT.js");
|
|
5602
5615
|
await startHub(
|
|
5603
5616
|
{
|
|
5604
5617
|
topic: topic ?? "",
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
hadPreviousWriteToolCalls,
|
|
16
16
|
loadDevState,
|
|
17
17
|
setupProxy
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-SAJICC6G.js";
|
|
19
19
|
import {
|
|
20
20
|
AuthManager
|
|
21
21
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
spawnAgentContext,
|
|
34
34
|
truncateOutput,
|
|
35
35
|
undoStack
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-EWJQOJSB.js";
|
|
37
37
|
import "./chunk-4BKXL7SM.js";
|
|
38
38
|
import {
|
|
39
39
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
SKILLS_DIR_NAME,
|
|
53
53
|
VERSION,
|
|
54
54
|
buildUserIdentityPrompt
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-7SW4XRDJ.js";
|
|
56
56
|
|
|
57
57
|
// src/web/server.ts
|
|
58
58
|
import express from "express";
|
|
@@ -1606,7 +1606,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1606
1606
|
case "test": {
|
|
1607
1607
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1608
1608
|
try {
|
|
1609
|
-
const { executeTests } = await import("./run-tests-
|
|
1609
|
+
const { executeTests } = await import("./run-tests-QRH2Z2OH.js");
|
|
1610
1610
|
const argStr = args.join(" ").trim();
|
|
1611
1611
|
let testArgs = {};
|
|
1612
1612
|
if (argStr) {
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EWJQOJSB.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
9
|
import {
|
|
10
10
|
SUBAGENT_ALLOWED_TOOLS
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7SW4XRDJ.js";
|
|
12
12
|
|
|
13
13
|
// src/hub/task-orchestrator.ts
|
|
14
14
|
import { createInterface } from "readline";
|