@llmist/cli 16.0.4 → 16.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1777 -1136
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +96 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -110,7 +110,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
|
|
|
110
110
|
// package.json
|
|
111
111
|
var package_default = {
|
|
112
112
|
name: "@llmist/cli",
|
|
113
|
-
version: "16.0
|
|
113
|
+
version: "16.2.0",
|
|
114
114
|
description: "CLI for llmist - run LLM agents from the command line",
|
|
115
115
|
type: "module",
|
|
116
116
|
main: "dist/cli.js",
|
|
@@ -167,7 +167,7 @@ var package_default = {
|
|
|
167
167
|
node: ">=22.0.0"
|
|
168
168
|
},
|
|
169
169
|
dependencies: {
|
|
170
|
-
llmist: "^16.0
|
|
170
|
+
llmist: "^16.2.0",
|
|
171
171
|
"@unblessed/node": "^1.0.0-alpha.23",
|
|
172
172
|
"diff-match-patch": "^1.0.5",
|
|
173
173
|
chalk: "^5.6.2",
|
|
@@ -182,7 +182,7 @@ var package_default = {
|
|
|
182
182
|
zod: "^4.1.12"
|
|
183
183
|
},
|
|
184
184
|
devDependencies: {
|
|
185
|
-
"@llmist/testing": "^16.0
|
|
185
|
+
"@llmist/testing": "^16.2.0",
|
|
186
186
|
"@types/diff": "^8.0.0",
|
|
187
187
|
"@types/diff-match-patch": "^1.0.36",
|
|
188
188
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -211,15 +211,15 @@ var CONVERSION_TIMEOUT_MS = 3e4;
|
|
|
211
211
|
var ffmpegCheckPromise = null;
|
|
212
212
|
async function isFFmpegAvailable() {
|
|
213
213
|
if (ffmpegCheckPromise !== null) return ffmpegCheckPromise;
|
|
214
|
-
ffmpegCheckPromise = new Promise((
|
|
214
|
+
ffmpegCheckPromise = new Promise((resolve4) => {
|
|
215
215
|
const proc = spawn("ffmpeg", ["-version"], { stdio: "ignore" });
|
|
216
|
-
proc.on("error", () =>
|
|
217
|
-
proc.on("close", (code) =>
|
|
216
|
+
proc.on("error", () => resolve4(false));
|
|
217
|
+
proc.on("close", (code) => resolve4(code === 0));
|
|
218
218
|
});
|
|
219
219
|
return ffmpegCheckPromise;
|
|
220
220
|
}
|
|
221
221
|
async function convertToMp3(input, inputFormat, timeout = CONVERSION_TIMEOUT_MS) {
|
|
222
|
-
return new Promise((
|
|
222
|
+
return new Promise((resolve4) => {
|
|
223
223
|
let timeoutId;
|
|
224
224
|
const inputArgs = inputFormat === "pcm16" ? ["-f", "s16le", "-ar", "24000", "-ac", "1"] : ["-f", inputFormat];
|
|
225
225
|
const proc = spawn(
|
|
@@ -244,15 +244,15 @@ async function convertToMp3(input, inputFormat, timeout = CONVERSION_TIMEOUT_MS)
|
|
|
244
244
|
proc.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
245
245
|
proc.on("error", () => {
|
|
246
246
|
clearTimeout(timeoutId);
|
|
247
|
-
|
|
247
|
+
resolve4(null);
|
|
248
248
|
});
|
|
249
249
|
proc.on("close", (code) => {
|
|
250
250
|
clearTimeout(timeoutId);
|
|
251
|
-
|
|
251
|
+
resolve4(code === 0 ? Buffer.concat(chunks) : null);
|
|
252
252
|
});
|
|
253
253
|
timeoutId = setTimeout(() => {
|
|
254
254
|
proc.kill();
|
|
255
|
-
|
|
255
|
+
resolve4(null);
|
|
256
256
|
}, timeout);
|
|
257
257
|
proc.stdin.on("error", () => {
|
|
258
258
|
});
|
|
@@ -453,7 +453,6 @@ var finish = createGadget2({
|
|
|
453
453
|
throw new TaskCompletionSignal("Task completed");
|
|
454
454
|
}
|
|
455
455
|
});
|
|
456
|
-
var builtinGadgets = [askUser, tellUser, finish, createTextToSpeech()];
|
|
457
456
|
function getBuiltinGadgets(speechConfig) {
|
|
458
457
|
return [askUser, tellUser, finish, createTextToSpeech(speechConfig)];
|
|
459
458
|
}
|
|
@@ -507,8 +506,6 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
507
506
|
// Add to inherited gadgets
|
|
508
507
|
"gadget-remove",
|
|
509
508
|
// Remove from inherited gadgets
|
|
510
|
-
"gadget",
|
|
511
|
-
// DEPRECATED: alias for gadgets
|
|
512
509
|
"builtins",
|
|
513
510
|
"builtin-interaction",
|
|
514
511
|
"gadget-start-prefix",
|
|
@@ -559,18 +556,18 @@ var VALID_REASONING_EFFORTS = /* @__PURE__ */ new Set(["none", "low", "medium",
|
|
|
559
556
|
|
|
560
557
|
// src/paths.ts
|
|
561
558
|
import { homedir } from "os";
|
|
562
|
-
function expandTildePath(
|
|
563
|
-
if (!
|
|
564
|
-
return
|
|
559
|
+
function expandTildePath(path7) {
|
|
560
|
+
if (!path7.startsWith("~")) {
|
|
561
|
+
return path7;
|
|
565
562
|
}
|
|
566
|
-
return
|
|
563
|
+
return path7.replace(/^~/, homedir());
|
|
567
564
|
}
|
|
568
565
|
|
|
569
566
|
// src/config-validators.ts
|
|
570
567
|
var ConfigError = class extends Error {
|
|
571
|
-
constructor(message,
|
|
572
|
-
super(
|
|
573
|
-
this.path =
|
|
568
|
+
constructor(message, path7) {
|
|
569
|
+
super(path7 ? `${path7}: ${message}` : message);
|
|
570
|
+
this.path = path7;
|
|
574
571
|
this.name = "ConfigError";
|
|
575
572
|
}
|
|
576
573
|
};
|
|
@@ -935,9 +932,6 @@ function validateAgentFields(rawObj, section, result) {
|
|
|
935
932
|
section
|
|
936
933
|
);
|
|
937
934
|
}
|
|
938
|
-
if ("gadget" in rawObj) {
|
|
939
|
-
result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
|
|
940
|
-
}
|
|
941
935
|
if ("builtins" in rawObj) {
|
|
942
936
|
result.builtins = validateBoolean(rawObj.builtins, "builtins", section);
|
|
943
937
|
}
|
|
@@ -1189,13 +1183,13 @@ function createTemplateEngine(prompts, configPath) {
|
|
|
1189
1183
|
functionHeader: "const includeFile = (path) => it.__includeFile(path);"
|
|
1190
1184
|
});
|
|
1191
1185
|
const includeStack = [];
|
|
1192
|
-
eta.__includeFileImpl = (
|
|
1193
|
-
if (includeStack.includes(
|
|
1194
|
-
throw new Error(`Circular include detected: ${[...includeStack,
|
|
1186
|
+
eta.__includeFileImpl = (path7) => {
|
|
1187
|
+
if (includeStack.includes(path7)) {
|
|
1188
|
+
throw new Error(`Circular include detected: ${[...includeStack, path7].join(" -> ")}`);
|
|
1195
1189
|
}
|
|
1196
|
-
includeStack.push(
|
|
1190
|
+
includeStack.push(path7);
|
|
1197
1191
|
try {
|
|
1198
|
-
const content = loadFileContents(
|
|
1192
|
+
const content = loadFileContents(path7, configDir);
|
|
1199
1193
|
if (hasTemplateSyntax(content)) {
|
|
1200
1194
|
const context = {
|
|
1201
1195
|
env: process.env,
|
|
@@ -1278,15 +1272,9 @@ function hasTemplateSyntax(str) {
|
|
|
1278
1272
|
// src/config-resolution.ts
|
|
1279
1273
|
function resolveGadgets(section, inheritedGadgets, sectionName, configPath) {
|
|
1280
1274
|
const hasGadgets = "gadgets" in section;
|
|
1281
|
-
const hasGadgetLegacy = "gadget" in section;
|
|
1282
1275
|
const hasGadgetAdd = "gadget-add" in section;
|
|
1283
1276
|
const hasGadgetRemove = "gadget-remove" in section;
|
|
1284
|
-
if (
|
|
1285
|
-
console.warn(
|
|
1286
|
-
`[config] Warning: [${sectionName}].gadget is deprecated, use 'gadgets' (plural) instead`
|
|
1287
|
-
);
|
|
1288
|
-
}
|
|
1289
|
-
if ((hasGadgets || hasGadgetLegacy) && (hasGadgetAdd || hasGadgetRemove)) {
|
|
1277
|
+
if (hasGadgets && (hasGadgetAdd || hasGadgetRemove)) {
|
|
1290
1278
|
throw new ConfigError(
|
|
1291
1279
|
`[${sectionName}] Cannot use 'gadgets' with 'gadget-add'/'gadget-remove'. Use either full replacement (gadgets) OR modification (gadget-add/gadget-remove).`,
|
|
1292
1280
|
configPath
|
|
@@ -1295,9 +1283,6 @@ function resolveGadgets(section, inheritedGadgets, sectionName, configPath) {
|
|
|
1295
1283
|
if (hasGadgets) {
|
|
1296
1284
|
return section.gadgets;
|
|
1297
1285
|
}
|
|
1298
|
-
if (hasGadgetLegacy) {
|
|
1299
|
-
return section.gadget;
|
|
1300
|
-
}
|
|
1301
1286
|
let result = [...inheritedGadgets];
|
|
1302
1287
|
if (hasGadgetRemove) {
|
|
1303
1288
|
const toRemove = new Set(section["gadget-remove"]);
|
|
@@ -1336,7 +1321,6 @@ function resolveInheritance(config, configPath) {
|
|
|
1336
1321
|
const {
|
|
1337
1322
|
inherits: _inherits,
|
|
1338
1323
|
gadgets: _gadgets,
|
|
1339
|
-
gadget: _gadget,
|
|
1340
1324
|
"gadget-add": _gadgetAdd,
|
|
1341
1325
|
"gadget-remove": _gadgetRemove,
|
|
1342
1326
|
...ownValues
|
|
@@ -1346,7 +1330,6 @@ function resolveInheritance(config, configPath) {
|
|
|
1346
1330
|
if (resolvedGadgets.length > 0) {
|
|
1347
1331
|
merged.gadgets = resolvedGadgets;
|
|
1348
1332
|
}
|
|
1349
|
-
delete merged.gadget;
|
|
1350
1333
|
delete merged["gadget-add"];
|
|
1351
1334
|
delete merged["gadget-remove"];
|
|
1352
1335
|
resolving.delete(name);
|
|
@@ -1430,6 +1413,48 @@ function resolveTemplatesInConfig(config, configPath) {
|
|
|
1430
1413
|
return result;
|
|
1431
1414
|
}
|
|
1432
1415
|
|
|
1416
|
+
// src/skills/config-types.ts
|
|
1417
|
+
var SKILLS_CONFIG_KEYS = /* @__PURE__ */ new Set(["sources", "overrides"]);
|
|
1418
|
+
var SKILL_OVERRIDE_KEYS = /* @__PURE__ */ new Set(["model", "enabled"]);
|
|
1419
|
+
function validateSkillsConfig(value, sectionName) {
|
|
1420
|
+
if (typeof value !== "object" || value === null) {
|
|
1421
|
+
throw new Error(`[${sectionName}] must be a table`);
|
|
1422
|
+
}
|
|
1423
|
+
const raw = value;
|
|
1424
|
+
const result = {};
|
|
1425
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
1426
|
+
if (key === "sources") {
|
|
1427
|
+
if (!Array.isArray(val)) {
|
|
1428
|
+
throw new Error(`[${sectionName}].sources must be an array`);
|
|
1429
|
+
}
|
|
1430
|
+
result.sources = val.map(String);
|
|
1431
|
+
} else if (key === "overrides") {
|
|
1432
|
+
if (typeof val !== "object" || val === null) {
|
|
1433
|
+
throw new Error(`[${sectionName}].overrides must be a table`);
|
|
1434
|
+
}
|
|
1435
|
+
result.overrides = {};
|
|
1436
|
+
for (const [skillName, override] of Object.entries(val)) {
|
|
1437
|
+
if (typeof override !== "object" || override === null) {
|
|
1438
|
+
throw new Error(`[${sectionName}].overrides.${skillName} must be a table`);
|
|
1439
|
+
}
|
|
1440
|
+
const overrideObj = override;
|
|
1441
|
+
const skillOverride = {};
|
|
1442
|
+
for (const [oKey, oVal] of Object.entries(overrideObj)) {
|
|
1443
|
+
if (!SKILL_OVERRIDE_KEYS.has(oKey)) {
|
|
1444
|
+
throw new Error(`[${sectionName}].overrides.${skillName}: unknown key "${oKey}"`);
|
|
1445
|
+
}
|
|
1446
|
+
if (oKey === "model") skillOverride.model = String(oVal);
|
|
1447
|
+
if (oKey === "enabled") skillOverride.enabled = Boolean(oVal);
|
|
1448
|
+
}
|
|
1449
|
+
result.overrides[skillName] = skillOverride;
|
|
1450
|
+
}
|
|
1451
|
+
} else if (!SKILLS_CONFIG_KEYS.has(key)) {
|
|
1452
|
+
throw new Error(`[${sectionName}]: unknown key "${key}"`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return result;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1433
1458
|
// src/config.ts
|
|
1434
1459
|
function getConfigPath() {
|
|
1435
1460
|
return join(homedir2(), ".llmist", "cli.toml");
|
|
@@ -1460,6 +1485,8 @@ function validateConfig(raw, configPath) {
|
|
|
1460
1485
|
result["rate-limits"] = validateRateLimitsConfig(value, key);
|
|
1461
1486
|
} else if (key === "retry") {
|
|
1462
1487
|
result.retry = validateRetryConfig(value, key);
|
|
1488
|
+
} else if (key === "skills") {
|
|
1489
|
+
result.skills = validateSkillsConfig(value, key);
|
|
1463
1490
|
} else {
|
|
1464
1491
|
result[key] = validateCustomConfig(value, key);
|
|
1465
1492
|
}
|
|
@@ -1600,18 +1627,99 @@ async function readFileBuffer(filePath, options = {}) {
|
|
|
1600
1627
|
}
|
|
1601
1628
|
|
|
1602
1629
|
// src/gadgets.ts
|
|
1603
|
-
import
|
|
1630
|
+
import fs7 from "fs";
|
|
1604
1631
|
import { createRequire } from "module";
|
|
1605
|
-
import
|
|
1632
|
+
import path6 from "path";
|
|
1606
1633
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1607
1634
|
import { createJiti } from "jiti";
|
|
1608
1635
|
import { AbstractGadget } from "llmist";
|
|
1609
1636
|
|
|
1610
|
-
// src/builtins/filesystem/
|
|
1611
|
-
import
|
|
1637
|
+
// src/builtins/filesystem/delete-file.ts
|
|
1638
|
+
import fs2 from "fs";
|
|
1612
1639
|
import { createGadget as createGadget3 } from "llmist";
|
|
1613
1640
|
import { z as z3 } from "zod";
|
|
1614
1641
|
|
|
1642
|
+
// src/builtins/filesystem/utils.ts
|
|
1643
|
+
import fs from "fs";
|
|
1644
|
+
import path from "path";
|
|
1645
|
+
var PathSandboxException = class extends Error {
|
|
1646
|
+
constructor(inputPath, reason) {
|
|
1647
|
+
super(`Path access denied: ${inputPath}. ${reason}`);
|
|
1648
|
+
this.name = "PathSandboxException";
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
function validatePathIsWithinCwd(inputPath) {
|
|
1652
|
+
const cwd = process.cwd();
|
|
1653
|
+
const resolvedPath = path.resolve(cwd, inputPath);
|
|
1654
|
+
let realCwd;
|
|
1655
|
+
try {
|
|
1656
|
+
realCwd = fs.realpathSync(cwd);
|
|
1657
|
+
} catch {
|
|
1658
|
+
realCwd = cwd;
|
|
1659
|
+
}
|
|
1660
|
+
let finalPath;
|
|
1661
|
+
try {
|
|
1662
|
+
finalPath = fs.realpathSync(resolvedPath);
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
const nodeError = error;
|
|
1665
|
+
if (nodeError.code === "ENOENT") {
|
|
1666
|
+
finalPath = resolvedPath;
|
|
1667
|
+
} else {
|
|
1668
|
+
throw error;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
const cwdWithSep = realCwd + path.sep;
|
|
1672
|
+
if (!finalPath.startsWith(cwdWithSep) && finalPath !== realCwd) {
|
|
1673
|
+
throw new PathSandboxException(inputPath, "Path is outside the current working directory");
|
|
1674
|
+
}
|
|
1675
|
+
return finalPath;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
// src/builtins/filesystem/delete-file.ts
|
|
1679
|
+
var deleteFile = createGadget3({
|
|
1680
|
+
name: "DeleteFile",
|
|
1681
|
+
description: "Delete a file or directory from the local filesystem. The path must be within the current working directory or its subdirectories.",
|
|
1682
|
+
maxConcurrent: 1,
|
|
1683
|
+
// Sequential execution to prevent race conditions
|
|
1684
|
+
schema: z3.object({
|
|
1685
|
+
filePath: z3.string().describe("Path to the file or directory to delete (relative or absolute)"),
|
|
1686
|
+
recursive: z3.boolean().optional().default(false).describe("If true, perform a recursive deletion (required for directories)")
|
|
1687
|
+
}),
|
|
1688
|
+
examples: [
|
|
1689
|
+
{
|
|
1690
|
+
params: { filePath: "temp.txt", recursive: false },
|
|
1691
|
+
output: "path=temp.txt\n\nDeleted file successfully",
|
|
1692
|
+
comment: "Delete a single file"
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
params: { filePath: "tmp-dir", recursive: true },
|
|
1696
|
+
output: "path=tmp-dir\n\nDeleted directory successfully",
|
|
1697
|
+
comment: "Delete a directory and its contents"
|
|
1698
|
+
}
|
|
1699
|
+
],
|
|
1700
|
+
execute: ({ filePath, recursive }) => {
|
|
1701
|
+
const validatedPath = validatePathIsWithinCwd(filePath);
|
|
1702
|
+
if (!fs2.existsSync(validatedPath)) {
|
|
1703
|
+
return `Error: Path does not exist: ${filePath}`;
|
|
1704
|
+
}
|
|
1705
|
+
const stats = fs2.statSync(validatedPath);
|
|
1706
|
+
const isDirectory = stats.isDirectory();
|
|
1707
|
+
if (isDirectory && !recursive) {
|
|
1708
|
+
return `Error: ${filePath} is a directory. Set recursive=true to delete it.`;
|
|
1709
|
+
}
|
|
1710
|
+
fs2.rmSync(validatedPath, { recursive, force: true });
|
|
1711
|
+
const type = isDirectory ? "directory" : "file";
|
|
1712
|
+
return `path=${filePath}
|
|
1713
|
+
|
|
1714
|
+
Deleted ${type} successfully`;
|
|
1715
|
+
}
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
// src/builtins/filesystem/edit-file.ts
|
|
1719
|
+
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1720
|
+
import { createGadget as createGadget4 } from "llmist";
|
|
1721
|
+
import { z as z4 } from "zod";
|
|
1722
|
+
|
|
1615
1723
|
// src/builtins/filesystem/editfile/matcher.ts
|
|
1616
1724
|
import DiffMatchPatch from "diff-match-patch";
|
|
1617
1725
|
var dmp = new DiffMatchPatch();
|
|
@@ -2029,36 +2137,6 @@ function formatMultipleMatches(content, matches, maxMatches = 5) {
|
|
|
2029
2137
|
return output.join("\n");
|
|
2030
2138
|
}
|
|
2031
2139
|
|
|
2032
|
-
// src/builtins/filesystem/utils.ts
|
|
2033
|
-
import fs from "fs";
|
|
2034
|
-
import path from "path";
|
|
2035
|
-
var PathSandboxException = class extends Error {
|
|
2036
|
-
constructor(inputPath, reason) {
|
|
2037
|
-
super(`Path access denied: ${inputPath}. ${reason}`);
|
|
2038
|
-
this.name = "PathSandboxException";
|
|
2039
|
-
}
|
|
2040
|
-
};
|
|
2041
|
-
function validatePathIsWithinCwd(inputPath) {
|
|
2042
|
-
const cwd = process.cwd();
|
|
2043
|
-
const resolvedPath = path.resolve(cwd, inputPath);
|
|
2044
|
-
let finalPath;
|
|
2045
|
-
try {
|
|
2046
|
-
finalPath = fs.realpathSync(resolvedPath);
|
|
2047
|
-
} catch (error) {
|
|
2048
|
-
const nodeError = error;
|
|
2049
|
-
if (nodeError.code === "ENOENT") {
|
|
2050
|
-
finalPath = resolvedPath;
|
|
2051
|
-
} else {
|
|
2052
|
-
throw error;
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
const cwdWithSep = cwd + path.sep;
|
|
2056
|
-
if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
|
|
2057
|
-
throw new PathSandboxException(inputPath, "Path is outside the current working directory");
|
|
2058
|
-
}
|
|
2059
|
-
return finalPath;
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
2140
|
// src/builtins/filesystem/edit-file.ts
|
|
2063
2141
|
function formatFailure(filePath, search, failure, fileContent) {
|
|
2064
2142
|
const lines = [
|
|
@@ -2090,7 +2168,7 @@ function formatFailure(filePath, search, failure, fileContent) {
|
|
|
2090
2168
|
lines.push("", "CURRENT FILE CONTENT:", "```", fileContent, "```");
|
|
2091
2169
|
return lines.join("\n");
|
|
2092
2170
|
}
|
|
2093
|
-
var editFile =
|
|
2171
|
+
var editFile = createGadget4({
|
|
2094
2172
|
name: "EditFile",
|
|
2095
2173
|
description: `Edit a file by searching for content and replacing it.
|
|
2096
2174
|
|
|
@@ -2109,12 +2187,12 @@ Options:
|
|
|
2109
2187
|
- expectedCount: Validate exact number of matches before applying`,
|
|
2110
2188
|
maxConcurrent: 1,
|
|
2111
2189
|
// Sequential execution to prevent race conditions
|
|
2112
|
-
schema:
|
|
2113
|
-
filePath:
|
|
2114
|
-
search:
|
|
2115
|
-
replace:
|
|
2116
|
-
replaceAll:
|
|
2117
|
-
expectedCount:
|
|
2190
|
+
schema: z4.object({
|
|
2191
|
+
filePath: z4.string().describe("Path to the file to edit (relative or absolute)"),
|
|
2192
|
+
search: z4.string().describe("The content to search for in the file"),
|
|
2193
|
+
replace: z4.string().describe("The content to replace it with (empty string to delete)"),
|
|
2194
|
+
replaceAll: z4.boolean().optional().default(false).describe("Replace all occurrences instead of just the first match"),
|
|
2195
|
+
expectedCount: z4.number().int().positive().optional().describe("Expected number of matches. Edit fails if actual count differs")
|
|
2118
2196
|
}),
|
|
2119
2197
|
examples: [
|
|
2120
2198
|
{
|
|
@@ -2266,19 +2344,19 @@ function executeReplaceAll(content, matches, replace) {
|
|
|
2266
2344
|
}
|
|
2267
2345
|
|
|
2268
2346
|
// src/builtins/filesystem/list-directory.ts
|
|
2269
|
-
import
|
|
2347
|
+
import fs3 from "fs";
|
|
2270
2348
|
import path2 from "path";
|
|
2271
|
-
import { createGadget as
|
|
2272
|
-
import { z as
|
|
2349
|
+
import { createGadget as createGadget5 } from "llmist";
|
|
2350
|
+
import { z as z5 } from "zod";
|
|
2273
2351
|
function listFiles(dirPath, basePath = dirPath, maxDepth = 1, currentDepth = 1) {
|
|
2274
2352
|
const entries = [];
|
|
2275
2353
|
try {
|
|
2276
|
-
const items =
|
|
2354
|
+
const items = fs3.readdirSync(dirPath);
|
|
2277
2355
|
for (const item of items) {
|
|
2278
2356
|
const fullPath = path2.join(dirPath, item);
|
|
2279
2357
|
const relativePath = path2.relative(basePath, fullPath);
|
|
2280
2358
|
try {
|
|
2281
|
-
const stats =
|
|
2359
|
+
const stats = fs3.lstatSync(fullPath);
|
|
2282
2360
|
let type;
|
|
2283
2361
|
let size;
|
|
2284
2362
|
if (stats.isSymbolicLink()) {
|
|
@@ -2353,12 +2431,12 @@ function formatEntriesAsString(entries) {
|
|
|
2353
2431
|
);
|
|
2354
2432
|
return [header, ...rows].join("\n");
|
|
2355
2433
|
}
|
|
2356
|
-
var listDirectory =
|
|
2434
|
+
var listDirectory = createGadget5({
|
|
2357
2435
|
name: "ListDirectory",
|
|
2358
2436
|
description: "List files and directories in a directory with full details (names, types, sizes, modification dates). Use maxDepth to explore subdirectories recursively. The directory path must be within the current working directory or its subdirectories.",
|
|
2359
|
-
schema:
|
|
2360
|
-
directoryPath:
|
|
2361
|
-
maxDepth:
|
|
2437
|
+
schema: z5.object({
|
|
2438
|
+
directoryPath: z5.string().default(".").describe("Path to the directory to list"),
|
|
2439
|
+
maxDepth: z5.number().int().min(1).max(10).default(3).describe(
|
|
2362
2440
|
"Maximum depth to recurse (1 = immediate children only, 2 = include grandchildren, etc.)"
|
|
2363
2441
|
)
|
|
2364
2442
|
}),
|
|
@@ -2376,7 +2454,7 @@ var listDirectory = createGadget4({
|
|
|
2376
2454
|
],
|
|
2377
2455
|
execute: ({ directoryPath, maxDepth }) => {
|
|
2378
2456
|
const validatedPath = validatePathIsWithinCwd(directoryPath);
|
|
2379
|
-
const stats =
|
|
2457
|
+
const stats = fs3.statSync(validatedPath);
|
|
2380
2458
|
if (!stats.isDirectory()) {
|
|
2381
2459
|
throw new Error(`Path is not a directory: ${directoryPath}`);
|
|
2382
2460
|
}
|
|
@@ -2389,14 +2467,14 @@ ${formattedList}`;
|
|
|
2389
2467
|
});
|
|
2390
2468
|
|
|
2391
2469
|
// src/builtins/filesystem/read-file.ts
|
|
2392
|
-
import
|
|
2393
|
-
import { createGadget as
|
|
2394
|
-
import { z as
|
|
2395
|
-
var readFile2 =
|
|
2470
|
+
import fs4 from "fs";
|
|
2471
|
+
import { createGadget as createGadget6 } from "llmist";
|
|
2472
|
+
import { z as z6 } from "zod";
|
|
2473
|
+
var readFile2 = createGadget6({
|
|
2396
2474
|
name: "ReadFile",
|
|
2397
2475
|
description: "Read the entire content of a file and return it as text. The file path must be within the current working directory or its subdirectories.",
|
|
2398
|
-
schema:
|
|
2399
|
-
filePath:
|
|
2476
|
+
schema: z6.object({
|
|
2477
|
+
filePath: z6.string().describe("Path to the file to read (relative or absolute)")
|
|
2400
2478
|
}),
|
|
2401
2479
|
examples: [
|
|
2402
2480
|
{
|
|
@@ -2412,7 +2490,7 @@ var readFile2 = createGadget5({
|
|
|
2412
2490
|
],
|
|
2413
2491
|
execute: ({ filePath }) => {
|
|
2414
2492
|
const validatedPath = validatePathIsWithinCwd(filePath);
|
|
2415
|
-
const content =
|
|
2493
|
+
const content = fs4.readFileSync(validatedPath, "utf-8");
|
|
2416
2494
|
return `path=${filePath}
|
|
2417
2495
|
|
|
2418
2496
|
${content}`;
|
|
@@ -2420,18 +2498,18 @@ ${content}`;
|
|
|
2420
2498
|
});
|
|
2421
2499
|
|
|
2422
2500
|
// src/builtins/filesystem/write-file.ts
|
|
2423
|
-
import
|
|
2501
|
+
import fs5 from "fs";
|
|
2424
2502
|
import path3 from "path";
|
|
2425
|
-
import { createGadget as
|
|
2426
|
-
import { z as
|
|
2427
|
-
var writeFile =
|
|
2503
|
+
import { createGadget as createGadget7 } from "llmist";
|
|
2504
|
+
import { z as z7 } from "zod";
|
|
2505
|
+
var writeFile = createGadget7({
|
|
2428
2506
|
name: "WriteFile",
|
|
2429
2507
|
description: "Write content to a file. Creates parent directories if needed. Overwrites existing files. The file path must be within the current working directory or its subdirectories.",
|
|
2430
2508
|
maxConcurrent: 1,
|
|
2431
2509
|
// Sequential execution to prevent race conditions
|
|
2432
|
-
schema:
|
|
2433
|
-
filePath:
|
|
2434
|
-
content:
|
|
2510
|
+
schema: z7.object({
|
|
2511
|
+
filePath: z7.string().describe("Path to the file to write (relative or absolute)"),
|
|
2512
|
+
content: z7.string().describe("Content to write to the file")
|
|
2435
2513
|
}),
|
|
2436
2514
|
examples: [
|
|
2437
2515
|
{
|
|
@@ -2461,12 +2539,12 @@ console.log(\`Server running on http://localhost:\${port}\`);`
|
|
|
2461
2539
|
const validatedPath = validatePathIsWithinCwd(filePath);
|
|
2462
2540
|
const parentDir = path3.dirname(validatedPath);
|
|
2463
2541
|
let createdDir = false;
|
|
2464
|
-
if (!
|
|
2542
|
+
if (!fs5.existsSync(parentDir)) {
|
|
2465
2543
|
validatePathIsWithinCwd(parentDir);
|
|
2466
|
-
|
|
2544
|
+
fs5.mkdirSync(parentDir, { recursive: true });
|
|
2467
2545
|
createdDir = true;
|
|
2468
2546
|
}
|
|
2469
|
-
|
|
2547
|
+
fs5.writeFileSync(validatedPath, content, "utf-8");
|
|
2470
2548
|
const bytesWritten = Buffer.byteLength(content, "utf-8");
|
|
2471
2549
|
const dirNote = createdDir ? ` (created directory: ${path3.dirname(filePath)})` : "";
|
|
2472
2550
|
return `path=${filePath}
|
|
@@ -2475,9 +2553,141 @@ Wrote ${bytesWritten} bytes${dirNote}`;
|
|
|
2475
2553
|
}
|
|
2476
2554
|
});
|
|
2477
2555
|
|
|
2556
|
+
// src/builtins/read-image.ts
|
|
2557
|
+
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
2558
|
+
import path4 from "path";
|
|
2559
|
+
import { createGadget as createGadget8, getErrorMessage as getErrorMessage2, resultWithImage } from "llmist";
|
|
2560
|
+
import { z as z8 } from "zod";
|
|
2561
|
+
var MAX_IMAGE_SIZE = 50 * 1024 * 1024;
|
|
2562
|
+
var URL_FETCH_TIMEOUT_MS = 3e4;
|
|
2563
|
+
var SUPPORTED_FORMATS = "JPEG, PNG, GIF, WebP";
|
|
2564
|
+
var USER_AGENT = "llmist-cli";
|
|
2565
|
+
var MIME_TO_EXT = {
|
|
2566
|
+
"image/jpeg": ".jpg",
|
|
2567
|
+
"image/png": ".png",
|
|
2568
|
+
"image/gif": ".gif",
|
|
2569
|
+
"image/webp": ".webp"
|
|
2570
|
+
};
|
|
2571
|
+
function isUrl(source) {
|
|
2572
|
+
return source.startsWith("http://") || source.startsWith("https://");
|
|
2573
|
+
}
|
|
2574
|
+
function fileNameFromUrl(source) {
|
|
2575
|
+
try {
|
|
2576
|
+
const urlPath = new URL(source).pathname;
|
|
2577
|
+
const base = path4.basename(urlPath);
|
|
2578
|
+
if (base && base.includes(".")) {
|
|
2579
|
+
return base;
|
|
2580
|
+
}
|
|
2581
|
+
} catch {
|
|
2582
|
+
}
|
|
2583
|
+
return void 0;
|
|
2584
|
+
}
|
|
2585
|
+
async function readImageFromFile(source) {
|
|
2586
|
+
const resolvedPath = path4.resolve(source);
|
|
2587
|
+
let stats;
|
|
2588
|
+
try {
|
|
2589
|
+
stats = await stat2(resolvedPath);
|
|
2590
|
+
} catch (error) {
|
|
2591
|
+
return `error: Cannot read image file "${source}": ${getErrorMessage2(error)}`;
|
|
2592
|
+
}
|
|
2593
|
+
if (stats.size > MAX_IMAGE_SIZE) {
|
|
2594
|
+
return `error: Image file "${source}" is too large (${formatFileSize(stats.size)}). Maximum: ${formatFileSize(MAX_IMAGE_SIZE)}.`;
|
|
2595
|
+
}
|
|
2596
|
+
let buffer;
|
|
2597
|
+
try {
|
|
2598
|
+
buffer = await readFile3(resolvedPath);
|
|
2599
|
+
} catch (error) {
|
|
2600
|
+
return `error: Cannot read image file "${source}": ${getErrorMessage2(error)}`;
|
|
2601
|
+
}
|
|
2602
|
+
const fileName = path4.basename(resolvedPath);
|
|
2603
|
+
try {
|
|
2604
|
+
return resultWithImage(`source=${source}
|
|
2605
|
+
size=${formatFileSize(buffer.length)}`, buffer, {
|
|
2606
|
+
description: `Image: ${fileName}`,
|
|
2607
|
+
fileName
|
|
2608
|
+
});
|
|
2609
|
+
} catch (error) {
|
|
2610
|
+
if (getErrorMessage2(error).includes("MIME type")) {
|
|
2611
|
+
return `error: File "${source}" is not a supported image format. Supported formats: ${SUPPORTED_FORMATS}.`;
|
|
2612
|
+
}
|
|
2613
|
+
throw error;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
async function fetchImageFromUrl(source) {
|
|
2617
|
+
const response = await fetch(source, {
|
|
2618
|
+
signal: AbortSignal.timeout(URL_FETCH_TIMEOUT_MS),
|
|
2619
|
+
headers: { "User-Agent": USER_AGENT }
|
|
2620
|
+
});
|
|
2621
|
+
if (!response.ok) {
|
|
2622
|
+
return `error: Failed to fetch image: HTTP ${response.status} ${response.statusText}`;
|
|
2623
|
+
}
|
|
2624
|
+
const contentLength = response.headers.get("content-length");
|
|
2625
|
+
if (contentLength && Number.parseInt(contentLength, 10) > MAX_IMAGE_SIZE) {
|
|
2626
|
+
return `error: Image at URL is too large (${formatFileSize(Number.parseInt(contentLength, 10))}). Maximum: ${formatFileSize(MAX_IMAGE_SIZE)}.`;
|
|
2627
|
+
}
|
|
2628
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
2629
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
2630
|
+
if (buffer.length > MAX_IMAGE_SIZE) {
|
|
2631
|
+
return `error: Downloaded image is too large (${formatFileSize(buffer.length)}). Maximum: ${formatFileSize(MAX_IMAGE_SIZE)}.`;
|
|
2632
|
+
}
|
|
2633
|
+
try {
|
|
2634
|
+
const result = resultWithImage(
|
|
2635
|
+
`source=${source}
|
|
2636
|
+
size=${formatFileSize(buffer.length)}`,
|
|
2637
|
+
buffer,
|
|
2638
|
+
{ description: `Image from ${source}` }
|
|
2639
|
+
);
|
|
2640
|
+
const detectedMime = result.media?.[0]?.mimeType;
|
|
2641
|
+
const fileName = fileNameFromUrl(source) ?? `image${detectedMime && MIME_TO_EXT[detectedMime] || ".bin"}`;
|
|
2642
|
+
if (result.media?.[0]) {
|
|
2643
|
+
result.media[0].fileName = fileName;
|
|
2644
|
+
}
|
|
2645
|
+
return result;
|
|
2646
|
+
} catch (error) {
|
|
2647
|
+
if (getErrorMessage2(error).includes("MIME type")) {
|
|
2648
|
+
return `error: Content at "${source}" is not a supported image format. Supported formats: ${SUPPORTED_FORMATS}.`;
|
|
2649
|
+
}
|
|
2650
|
+
throw error;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
var readImage = createGadget8({
|
|
2654
|
+
name: "ReadImage",
|
|
2655
|
+
description: "Read an image from a local file path or HTTP/HTTPS URL and return it for visual analysis. Supports JPEG, PNG, GIF, and WebP formats.",
|
|
2656
|
+
schema: z8.object({
|
|
2657
|
+
source: z8.string().min(1).describe("Path to a local image file or an HTTP/HTTPS URL")
|
|
2658
|
+
}),
|
|
2659
|
+
examples: [
|
|
2660
|
+
{
|
|
2661
|
+
params: { source: "./screenshot.png" },
|
|
2662
|
+
output: "source=./screenshot.png\nsize=1.2 MB",
|
|
2663
|
+
comment: "Read a local PNG image"
|
|
2664
|
+
},
|
|
2665
|
+
{
|
|
2666
|
+
params: { source: "https://example.com/photo.jpg" },
|
|
2667
|
+
output: "source=https://example.com/photo.jpg\nsize=245.3 KB",
|
|
2668
|
+
comment: "Fetch an image from a URL"
|
|
2669
|
+
},
|
|
2670
|
+
{
|
|
2671
|
+
params: { source: "/home/user/photos/cat.webp" },
|
|
2672
|
+
output: "source=/home/user/photos/cat.webp\nsize=89.4 KB",
|
|
2673
|
+
comment: "Read an image with absolute path (no CWD restriction)"
|
|
2674
|
+
}
|
|
2675
|
+
],
|
|
2676
|
+
execute: async ({ source }) => {
|
|
2677
|
+
try {
|
|
2678
|
+
if (isUrl(source)) {
|
|
2679
|
+
return await fetchImageFromUrl(source);
|
|
2680
|
+
}
|
|
2681
|
+
return await readImageFromFile(source);
|
|
2682
|
+
} catch (error) {
|
|
2683
|
+
return `error: ${getErrorMessage2(error)}`;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
});
|
|
2687
|
+
|
|
2478
2688
|
// src/builtins/run-command.ts
|
|
2479
|
-
import { createGadget as
|
|
2480
|
-
import { z as
|
|
2689
|
+
import { createGadget as createGadget9 } from "llmist";
|
|
2690
|
+
import { z as z9 } from "zod";
|
|
2481
2691
|
|
|
2482
2692
|
// src/spawn.ts
|
|
2483
2693
|
import { spawn as nodeSpawn } from "child_process";
|
|
@@ -2510,9 +2720,9 @@ function spawn2(argv, options = {}) {
|
|
|
2510
2720
|
options.stderr === "pipe" ? "pipe" : options.stderr ?? "ignore"
|
|
2511
2721
|
]
|
|
2512
2722
|
});
|
|
2513
|
-
const exited = new Promise((
|
|
2723
|
+
const exited = new Promise((resolve4, reject) => {
|
|
2514
2724
|
proc.on("exit", (code) => {
|
|
2515
|
-
|
|
2725
|
+
resolve4(code ?? 1);
|
|
2516
2726
|
});
|
|
2517
2727
|
proc.on("error", (err) => {
|
|
2518
2728
|
reject(err);
|
|
@@ -2538,13 +2748,13 @@ function spawn2(argv, options = {}) {
|
|
|
2538
2748
|
}
|
|
2539
2749
|
|
|
2540
2750
|
// src/builtins/run-command.ts
|
|
2541
|
-
var runCommand =
|
|
2751
|
+
var runCommand = createGadget9({
|
|
2542
2752
|
name: "RunCommand",
|
|
2543
2753
|
description: "Execute a command with arguments and return its output. Uses argv array to bypass shell - arguments are passed directly without interpretation. Returns stdout/stderr combined with exit status.",
|
|
2544
|
-
schema:
|
|
2545
|
-
argv:
|
|
2546
|
-
cwd:
|
|
2547
|
-
timeout:
|
|
2754
|
+
schema: z9.object({
|
|
2755
|
+
argv: z9.array(z9.string()).describe("Command and arguments as array (e.g., ['git', 'commit', '-m', 'message'])"),
|
|
2756
|
+
cwd: z9.string().optional().describe("Working directory for the command (default: current directory)"),
|
|
2757
|
+
timeout: z9.number().default(3e4).describe("Timeout in milliseconds (default: 30000)")
|
|
2548
2758
|
}),
|
|
2549
2759
|
examples: [
|
|
2550
2760
|
{
|
|
@@ -2649,8 +2859,10 @@ error: ${message}`;
|
|
|
2649
2859
|
var builtinGadgetRegistry = {
|
|
2650
2860
|
ListDirectory: listDirectory,
|
|
2651
2861
|
ReadFile: readFile2,
|
|
2862
|
+
ReadImage: readImage,
|
|
2652
2863
|
WriteFile: writeFile,
|
|
2653
2864
|
EditFile: editFile,
|
|
2865
|
+
DeleteFile: deleteFile,
|
|
2654
2866
|
RunCommand: runCommand,
|
|
2655
2867
|
TextToSpeech: textToSpeech
|
|
2656
2868
|
};
|
|
@@ -2660,12 +2872,15 @@ function getBuiltinGadget(name) {
|
|
|
2660
2872
|
function isBuiltinGadgetName(name) {
|
|
2661
2873
|
return name in builtinGadgetRegistry;
|
|
2662
2874
|
}
|
|
2875
|
+
function getBuiltinGadgetNames() {
|
|
2876
|
+
return Object.keys(builtinGadgetRegistry);
|
|
2877
|
+
}
|
|
2663
2878
|
|
|
2664
2879
|
// src/external-gadgets.ts
|
|
2665
2880
|
import { execSync } from "child_process";
|
|
2666
|
-
import
|
|
2881
|
+
import fs6 from "fs";
|
|
2667
2882
|
import os from "os";
|
|
2668
|
-
import
|
|
2883
|
+
import path5 from "path";
|
|
2669
2884
|
import { pathToFileURL } from "url";
|
|
2670
2885
|
function isCommandAvailable(cmd) {
|
|
2671
2886
|
try {
|
|
@@ -2676,7 +2891,7 @@ function isCommandAvailable(cmd) {
|
|
|
2676
2891
|
return false;
|
|
2677
2892
|
}
|
|
2678
2893
|
}
|
|
2679
|
-
var CACHE_DIR =
|
|
2894
|
+
var CACHE_DIR = path5.join(os.homedir(), ".llmist", "gadget-cache");
|
|
2680
2895
|
function isExternalPackageSpecifier(specifier) {
|
|
2681
2896
|
if (/^(?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*(?:@[\w.-]+)?(?::[a-z]+)?(?:\/[A-Z]\w*)?$/i.test(
|
|
2682
2897
|
specifier
|
|
@@ -2766,21 +2981,21 @@ function parseGadgetSpecifier(specifier) {
|
|
|
2766
2981
|
function getCacheDir(spec) {
|
|
2767
2982
|
const versionSuffix = spec.version ? `@${spec.version}` : "@latest";
|
|
2768
2983
|
if (spec.type === "npm") {
|
|
2769
|
-
return
|
|
2984
|
+
return path5.join(CACHE_DIR, "npm", `${spec.package}${versionSuffix}`);
|
|
2770
2985
|
}
|
|
2771
2986
|
const sanitizedUrl = spec.package.replace(/[/:]/g, "-").replace(/^-+|-+$/g, "");
|
|
2772
|
-
return
|
|
2987
|
+
return path5.join(CACHE_DIR, "git", `${sanitizedUrl}${versionSuffix}`);
|
|
2773
2988
|
}
|
|
2774
2989
|
function isCached(cacheDir) {
|
|
2775
|
-
const packageJsonPath =
|
|
2776
|
-
if (!
|
|
2990
|
+
const packageJsonPath = path5.join(cacheDir, "package.json");
|
|
2991
|
+
if (!fs6.existsSync(packageJsonPath)) {
|
|
2777
2992
|
return false;
|
|
2778
2993
|
}
|
|
2779
2994
|
try {
|
|
2780
|
-
const packageJson = JSON.parse(
|
|
2995
|
+
const packageJson = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
2781
2996
|
const entryPoint = packageJson.llmist?.gadgets || "./dist/index.js";
|
|
2782
|
-
const entryPointPath =
|
|
2783
|
-
if (!
|
|
2997
|
+
const entryPointPath = path5.join(cacheDir, entryPoint);
|
|
2998
|
+
if (!fs6.existsSync(entryPointPath)) {
|
|
2784
2999
|
return false;
|
|
2785
3000
|
}
|
|
2786
3001
|
} catch {
|
|
@@ -2789,13 +3004,13 @@ function isCached(cacheDir) {
|
|
|
2789
3004
|
return true;
|
|
2790
3005
|
}
|
|
2791
3006
|
async function installNpmPackage(spec, cacheDir) {
|
|
2792
|
-
|
|
3007
|
+
fs6.mkdirSync(cacheDir, { recursive: true });
|
|
2793
3008
|
const packageJson = {
|
|
2794
3009
|
name: "llmist-gadget-cache",
|
|
2795
3010
|
private: true,
|
|
2796
3011
|
type: "module"
|
|
2797
3012
|
};
|
|
2798
|
-
|
|
3013
|
+
fs6.writeFileSync(path5.join(cacheDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
2799
3014
|
const packageSpec = spec.version ? `${spec.package}@${spec.version}` : spec.package;
|
|
2800
3015
|
const quietEnv = { ...process.env, DISABLE_OPENCOLLECTIVE: "1", ADBLOCK: "1" };
|
|
2801
3016
|
if (!isCommandAvailable("npm")) {
|
|
@@ -2813,18 +3028,18 @@ Please install Node.js: https://nodejs.org/`
|
|
|
2813
3028
|
}
|
|
2814
3029
|
}
|
|
2815
3030
|
async function installGitPackage(spec, cacheDir) {
|
|
2816
|
-
|
|
2817
|
-
if (
|
|
3031
|
+
fs6.mkdirSync(path5.dirname(cacheDir), { recursive: true });
|
|
3032
|
+
if (fs6.existsSync(cacheDir)) {
|
|
2818
3033
|
try {
|
|
2819
3034
|
execSync("git fetch", { cwd: cacheDir, stdio: "pipe" });
|
|
2820
3035
|
if (spec.version) {
|
|
2821
3036
|
execSync(`git checkout ${spec.version}`, { cwd: cacheDir, stdio: "pipe" });
|
|
2822
3037
|
}
|
|
2823
3038
|
} catch (error) {
|
|
2824
|
-
|
|
3039
|
+
fs6.rmSync(cacheDir, { recursive: true, force: true });
|
|
2825
3040
|
}
|
|
2826
3041
|
}
|
|
2827
|
-
if (!
|
|
3042
|
+
if (!fs6.existsSync(cacheDir)) {
|
|
2828
3043
|
try {
|
|
2829
3044
|
const cloneCmd = spec.version ? `git clone --branch ${spec.version} "${spec.package}" "${cacheDir}"` : `git clone "${spec.package}" "${cacheDir}"`;
|
|
2830
3045
|
execSync(cloneCmd, { stdio: "pipe" });
|
|
@@ -2832,7 +3047,7 @@ async function installGitPackage(spec, cacheDir) {
|
|
|
2832
3047
|
const message = error instanceof Error ? error.message : String(error);
|
|
2833
3048
|
throw new Error(`Failed to clone git repository '${spec.package}': ${message}`);
|
|
2834
3049
|
}
|
|
2835
|
-
if (
|
|
3050
|
+
if (fs6.existsSync(path5.join(cacheDir, "package.json"))) {
|
|
2836
3051
|
const hasNpm = isCommandAvailable("npm");
|
|
2837
3052
|
if (!hasNpm) {
|
|
2838
3053
|
throw new Error(
|
|
@@ -2851,14 +3066,14 @@ Please install Node.js: https://nodejs.org/`
|
|
|
2851
3066
|
`Failed to install dependencies for '${spec.package}' using npm: ${message}`
|
|
2852
3067
|
);
|
|
2853
3068
|
}
|
|
2854
|
-
const packageJson = JSON.parse(
|
|
3069
|
+
const packageJson = JSON.parse(fs6.readFileSync(path5.join(cacheDir, "package.json"), "utf-8"));
|
|
2855
3070
|
if (packageJson.scripts?.build) {
|
|
2856
3071
|
try {
|
|
2857
3072
|
execSync(`${runCmd} build`, { cwd: cacheDir, stdio: "inherit", env: quietEnv });
|
|
2858
3073
|
} catch (error) {
|
|
2859
3074
|
const entryPoint = packageJson.llmist?.gadgets || "./dist/index.js";
|
|
2860
|
-
const entryPointPath =
|
|
2861
|
-
if (!
|
|
3075
|
+
const entryPointPath = path5.join(cacheDir, entryPoint);
|
|
3076
|
+
if (!fs6.existsSync(entryPointPath)) {
|
|
2862
3077
|
const message = error instanceof Error ? error.message : String(error);
|
|
2863
3078
|
throw new Error(`Failed to build package '${spec.package}': ${message}`);
|
|
2864
3079
|
}
|
|
@@ -2868,20 +3083,20 @@ Please install Node.js: https://nodejs.org/`
|
|
|
2868
3083
|
}
|
|
2869
3084
|
}
|
|
2870
3085
|
function readManifest(packageDir) {
|
|
2871
|
-
const packageJsonPath =
|
|
2872
|
-
if (!
|
|
3086
|
+
const packageJsonPath = path5.join(packageDir, "package.json");
|
|
3087
|
+
if (!fs6.existsSync(packageJsonPath)) {
|
|
2873
3088
|
return null;
|
|
2874
3089
|
}
|
|
2875
3090
|
try {
|
|
2876
|
-
const packageJson = JSON.parse(
|
|
3091
|
+
const packageJson = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
2877
3092
|
return packageJson.llmist || null;
|
|
2878
3093
|
} catch {
|
|
2879
3094
|
return null;
|
|
2880
3095
|
}
|
|
2881
3096
|
}
|
|
2882
3097
|
function getPackagePath(cacheDir, packageName) {
|
|
2883
|
-
const nodeModulesPath =
|
|
2884
|
-
if (
|
|
3098
|
+
const nodeModulesPath = path5.join(cacheDir, "node_modules", packageName);
|
|
3099
|
+
if (fs6.existsSync(nodeModulesPath)) {
|
|
2885
3100
|
return nodeModulesPath;
|
|
2886
3101
|
}
|
|
2887
3102
|
return cacheDir;
|
|
@@ -2927,8 +3142,8 @@ async function loadExternalGadgets(specifier, forceInstall = false) {
|
|
|
2927
3142
|
} else {
|
|
2928
3143
|
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
2929
3144
|
}
|
|
2930
|
-
const resolvedEntryPoint =
|
|
2931
|
-
if (!
|
|
3145
|
+
const resolvedEntryPoint = path5.resolve(packagePath, entryPoint);
|
|
3146
|
+
if (!fs6.existsSync(resolvedEntryPoint)) {
|
|
2932
3147
|
throw new Error(
|
|
2933
3148
|
`Entry point not found: ${resolvedEntryPoint}. Make sure the package is built (run 'npm run build' in the package directory).`
|
|
2934
3149
|
);
|
|
@@ -3071,7 +3286,7 @@ function parseLocalSpecifier(specifier) {
|
|
|
3071
3286
|
return { path: specifier };
|
|
3072
3287
|
}
|
|
3073
3288
|
function isFileLikeSpecifier(specifier) {
|
|
3074
|
-
return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(
|
|
3289
|
+
return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(path6.sep);
|
|
3075
3290
|
}
|
|
3076
3291
|
function tryResolveBuiltin(specifier) {
|
|
3077
3292
|
if (specifier.startsWith(BUILTIN_PREFIX)) {
|
|
@@ -3079,7 +3294,7 @@ function tryResolveBuiltin(specifier) {
|
|
|
3079
3294
|
const gadget = getBuiltinGadget(name);
|
|
3080
3295
|
if (!gadget) {
|
|
3081
3296
|
throw new Error(
|
|
3082
|
-
`Unknown builtin gadget: ${name}. Available builtins:
|
|
3297
|
+
`Unknown builtin gadget: ${name}. Available builtins: ${getBuiltinGadgetNames().join(", ")}`
|
|
3083
3298
|
);
|
|
3084
3299
|
}
|
|
3085
3300
|
return gadget;
|
|
@@ -3094,8 +3309,8 @@ function resolveGadgetSpecifier(specifier, cwd) {
|
|
|
3094
3309
|
return specifier;
|
|
3095
3310
|
}
|
|
3096
3311
|
const expanded = expandTildePath(specifier);
|
|
3097
|
-
const resolvedPath =
|
|
3098
|
-
if (!
|
|
3312
|
+
const resolvedPath = path6.resolve(cwd, expanded);
|
|
3313
|
+
if (!fs7.existsSync(resolvedPath)) {
|
|
3099
3314
|
throw new Error(`Gadget module not found at ${resolvedPath}`);
|
|
3100
3315
|
}
|
|
3101
3316
|
return pathToFileURL2(resolvedPath).href;
|
|
@@ -3262,7 +3477,7 @@ async function resolvePrompt(promptArg, env) {
|
|
|
3262
3477
|
}
|
|
3263
3478
|
|
|
3264
3479
|
// src/ui/formatters.ts
|
|
3265
|
-
import
|
|
3480
|
+
import chalk4 from "chalk";
|
|
3266
3481
|
import { format } from "llmist";
|
|
3267
3482
|
|
|
3268
3483
|
// src/ui/call-number.ts
|
|
@@ -3376,6 +3591,62 @@ function stripProviderPrefix(model) {
|
|
|
3376
3591
|
return model.includes(":") ? model.split(":")[1] : model;
|
|
3377
3592
|
}
|
|
3378
3593
|
|
|
3594
|
+
// src/ui/metric-parts.ts
|
|
3595
|
+
import chalk3 from "chalk";
|
|
3596
|
+
function tokenPart(direction, tokens, opts) {
|
|
3597
|
+
const prefix = opts?.estimated ? "~" : "";
|
|
3598
|
+
const formatted = `${prefix}${formatTokens(tokens)}`;
|
|
3599
|
+
switch (direction) {
|
|
3600
|
+
case "input":
|
|
3601
|
+
return chalk3.dim("\u2191") + chalk3.yellow(` ${formatted}`);
|
|
3602
|
+
case "cached":
|
|
3603
|
+
return chalk3.dim("\u27F3") + chalk3.blue(` ${formatted}`);
|
|
3604
|
+
case "output":
|
|
3605
|
+
return chalk3.dim("\u2193") + chalk3.green(` ${formatted}`);
|
|
3606
|
+
case "reasoning":
|
|
3607
|
+
return chalk3.dim("\u{1F4AD}") + chalk3.magenta(` ${formatted}`);
|
|
3608
|
+
case "cacheCreation":
|
|
3609
|
+
return chalk3.dim("\u270E") + chalk3.magenta(` ${formatted}`);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
function costPart(cost) {
|
|
3613
|
+
return chalk3.cyan(`$${formatCost(cost)}`);
|
|
3614
|
+
}
|
|
3615
|
+
function timePart(seconds) {
|
|
3616
|
+
return chalk3.dim(`${seconds.toFixed(1)}s`);
|
|
3617
|
+
}
|
|
3618
|
+
function finishReasonPart(reason) {
|
|
3619
|
+
const upper = reason.toUpperCase();
|
|
3620
|
+
if (upper === "STOP" || upper === "END_TURN") {
|
|
3621
|
+
return chalk3.green(upper);
|
|
3622
|
+
}
|
|
3623
|
+
return chalk3.yellow(upper);
|
|
3624
|
+
}
|
|
3625
|
+
function buildTokenMetrics(opts) {
|
|
3626
|
+
const parts = [];
|
|
3627
|
+
if (opts.input && opts.input > 0) {
|
|
3628
|
+
parts.push(tokenPart("input", opts.input, { estimated: opts.estimated?.input }));
|
|
3629
|
+
}
|
|
3630
|
+
if (opts.cached && opts.cached > 0) {
|
|
3631
|
+
parts.push(tokenPart("cached", opts.cached));
|
|
3632
|
+
}
|
|
3633
|
+
if (opts.output && opts.output > 0) {
|
|
3634
|
+
parts.push(tokenPart("output", opts.output, { estimated: opts.estimated?.output }));
|
|
3635
|
+
}
|
|
3636
|
+
if (opts.reasoning && opts.reasoning > 0) {
|
|
3637
|
+
parts.push(tokenPart("reasoning", opts.reasoning));
|
|
3638
|
+
}
|
|
3639
|
+
if (opts.cacheCreation && opts.cacheCreation > 0) {
|
|
3640
|
+
parts.push(tokenPart("cacheCreation", opts.cacheCreation));
|
|
3641
|
+
}
|
|
3642
|
+
return parts;
|
|
3643
|
+
}
|
|
3644
|
+
function joinParts(parts) {
|
|
3645
|
+
const filtered = parts.filter((p) => p.length > 0);
|
|
3646
|
+
if (filtered.length === 0) return "";
|
|
3647
|
+
return filtered.join(chalk3.dim(" | "));
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3379
3650
|
// src/ui/formatters.ts
|
|
3380
3651
|
function formatLLMCallLine(info) {
|
|
3381
3652
|
const parts = [];
|
|
@@ -3384,85 +3655,81 @@ function formatLLMCallLine(info) {
|
|
|
3384
3655
|
info.parentCallNumber,
|
|
3385
3656
|
info.gadgetInvocationId
|
|
3386
3657
|
);
|
|
3387
|
-
parts.push(`${
|
|
3658
|
+
parts.push(`${chalk4.cyan(callNumber)} ${chalk4.magenta(info.model)}`);
|
|
3388
3659
|
if (info.contextPercent !== void 0 && info.contextPercent !== null) {
|
|
3389
3660
|
const formatted = `${Math.round(info.contextPercent)}%`;
|
|
3390
3661
|
if (info.contextPercent >= 80) {
|
|
3391
|
-
parts.push(
|
|
3662
|
+
parts.push(chalk4.red(formatted));
|
|
3392
3663
|
} else if (info.contextPercent >= 50) {
|
|
3393
|
-
parts.push(
|
|
3664
|
+
parts.push(chalk4.yellow(formatted));
|
|
3394
3665
|
} else {
|
|
3395
|
-
parts.push(
|
|
3666
|
+
parts.push(chalk4.green(formatted));
|
|
3396
3667
|
}
|
|
3397
3668
|
}
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3669
|
+
parts.push(
|
|
3670
|
+
...buildTokenMetrics({
|
|
3671
|
+
input: info.inputTokens,
|
|
3672
|
+
cached: info.cachedInputTokens,
|
|
3673
|
+
estimated: info.estimated
|
|
3674
|
+
})
|
|
3675
|
+
);
|
|
3405
3676
|
if (info.outputTokens !== void 0 && info.outputTokens > 0 || info.isStreaming) {
|
|
3406
3677
|
const prefix = info.estimated?.output ? "~" : "";
|
|
3407
|
-
parts.push(
|
|
3678
|
+
parts.push(chalk4.dim("\u2193") + chalk4.green(` ${prefix}${formatTokens(info.outputTokens ?? 0)}`));
|
|
3408
3679
|
}
|
|
3409
|
-
parts.push(
|
|
3680
|
+
parts.push(timePart(info.elapsedSeconds));
|
|
3410
3681
|
if (info.cost !== void 0 && info.cost > 0) {
|
|
3411
|
-
parts.push(
|
|
3682
|
+
parts.push(costPart(info.cost));
|
|
3412
3683
|
}
|
|
3413
3684
|
if (!info.isStreaming && info.finishReason !== void 0) {
|
|
3414
3685
|
const reason = info.finishReason || "stop";
|
|
3415
|
-
|
|
3416
|
-
parts.push(chalk3.green(reason.toUpperCase()));
|
|
3417
|
-
} else {
|
|
3418
|
-
parts.push(chalk3.yellow(reason.toUpperCase()));
|
|
3419
|
-
}
|
|
3686
|
+
parts.push(finishReasonPart(reason));
|
|
3420
3687
|
}
|
|
3421
|
-
const line = parts
|
|
3688
|
+
const line = joinParts(parts);
|
|
3422
3689
|
if (info.isStreaming && info.spinner) {
|
|
3423
|
-
return `${
|
|
3690
|
+
return `${chalk4.cyan(info.spinner)} ${line}`;
|
|
3424
3691
|
}
|
|
3425
3692
|
if (!info.isStreaming) {
|
|
3426
|
-
return `${
|
|
3693
|
+
return `${chalk4.green("\u2713")} ${line}`;
|
|
3427
3694
|
}
|
|
3428
3695
|
return line;
|
|
3429
3696
|
}
|
|
3430
3697
|
function renderSummary(metadata) {
|
|
3431
3698
|
const parts = [];
|
|
3432
3699
|
if (metadata.iterations !== void 0) {
|
|
3433
|
-
const iterPart =
|
|
3700
|
+
const iterPart = chalk4.cyan(`#${metadata.iterations}`);
|
|
3434
3701
|
if (metadata.model) {
|
|
3435
|
-
parts.push(`${iterPart} ${
|
|
3702
|
+
parts.push(`${iterPart} ${chalk4.magenta(metadata.model)}`);
|
|
3436
3703
|
} else {
|
|
3437
3704
|
parts.push(iterPart);
|
|
3438
3705
|
}
|
|
3439
3706
|
} else if (metadata.model) {
|
|
3440
|
-
parts.push(
|
|
3707
|
+
parts.push(chalk4.magenta(metadata.model));
|
|
3441
3708
|
}
|
|
3442
3709
|
if (metadata.usage) {
|
|
3443
3710
|
const { inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens } = metadata.usage;
|
|
3444
|
-
parts.push(
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3711
|
+
parts.push(
|
|
3712
|
+
...buildTokenMetrics({
|
|
3713
|
+
input: inputTokens,
|
|
3714
|
+
cached: cachedInputTokens,
|
|
3715
|
+
cacheCreation: cacheCreationInputTokens,
|
|
3716
|
+
output: outputTokens
|
|
3717
|
+
})
|
|
3718
|
+
);
|
|
3452
3719
|
}
|
|
3453
3720
|
if (metadata.elapsedSeconds !== void 0 && metadata.elapsedSeconds > 0) {
|
|
3454
|
-
parts.push(
|
|
3721
|
+
parts.push(timePart(metadata.elapsedSeconds));
|
|
3455
3722
|
}
|
|
3456
3723
|
if (metadata.cost !== void 0 && metadata.cost > 0) {
|
|
3457
|
-
parts.push(
|
|
3724
|
+
parts.push(costPart(metadata.cost));
|
|
3458
3725
|
}
|
|
3459
3726
|
if (metadata.finishReason) {
|
|
3460
|
-
parts.push(
|
|
3727
|
+
parts.push(chalk4.dim(metadata.finishReason));
|
|
3461
3728
|
}
|
|
3462
3729
|
if (parts.length === 0) {
|
|
3463
3730
|
return null;
|
|
3464
3731
|
}
|
|
3465
|
-
return parts
|
|
3732
|
+
return joinParts(parts);
|
|
3466
3733
|
}
|
|
3467
3734
|
function getRawValue(value) {
|
|
3468
3735
|
if (typeof value === "string") {
|
|
@@ -3516,48 +3783,48 @@ function formatParametersInline(params, maxWidth) {
|
|
|
3516
3783
|
}
|
|
3517
3784
|
return entries.map(([key, _], i) => {
|
|
3518
3785
|
const formatted = truncateValue(rawValues[i], limits[i]);
|
|
3519
|
-
return `${
|
|
3520
|
-
}).join(
|
|
3786
|
+
return `${chalk4.dim(key)}${chalk4.dim("=")}${chalk4.cyan(formatted)}`;
|
|
3787
|
+
}).join(chalk4.dim(", "));
|
|
3521
3788
|
}
|
|
3522
3789
|
function formatGadgetLine(info, maxWidth) {
|
|
3523
3790
|
const terminalWidth = maxWidth ?? process.stdout.columns ?? 80;
|
|
3524
|
-
const gadgetLabel =
|
|
3791
|
+
const gadgetLabel = chalk4.magenta.bold(info.name);
|
|
3525
3792
|
const timeStr = `${info.elapsedSeconds.toFixed(1)}s`;
|
|
3526
|
-
const timeLabel =
|
|
3793
|
+
const timeLabel = chalk4.dim(timeStr);
|
|
3527
3794
|
const fixedLength = 3 + info.name.length + 2 + 1 + timeStr.length;
|
|
3528
3795
|
const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
|
|
3529
3796
|
const paramsStr = formatParametersInline(info.parameters, availableForParams);
|
|
3530
|
-
const paramsLabel = paramsStr ? `${
|
|
3797
|
+
const paramsLabel = paramsStr ? `${chalk4.dim("(")}${paramsStr}${chalk4.dim(")")}` : "";
|
|
3531
3798
|
if (info.error) {
|
|
3532
3799
|
const errorMsg = info.error.length > 50 ? `${info.error.slice(0, 50)}\u2026` : info.error;
|
|
3533
|
-
return `${
|
|
3800
|
+
return `${chalk4.red("\u2717")} ${gadgetLabel}${paramsLabel} ${chalk4.red("error:")} ${errorMsg} ${timeLabel}`;
|
|
3534
3801
|
}
|
|
3535
3802
|
if (!info.isComplete) {
|
|
3536
3803
|
const parts = [];
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3804
|
+
parts.push(
|
|
3805
|
+
...buildTokenMetrics({
|
|
3806
|
+
input: info.subagentInputTokens,
|
|
3807
|
+
output: info.subagentOutputTokens
|
|
3808
|
+
})
|
|
3809
|
+
);
|
|
3543
3810
|
if (info.subagentCost && info.subagentCost > 0) {
|
|
3544
|
-
parts.push(
|
|
3811
|
+
parts.push(costPart(info.subagentCost));
|
|
3545
3812
|
}
|
|
3546
|
-
parts.push(
|
|
3547
|
-
const metricsStr = parts.length > 0 ? ` ${parts
|
|
3548
|
-
return `${
|
|
3813
|
+
parts.push(timePart(info.elapsedSeconds));
|
|
3814
|
+
const metricsStr = parts.length > 0 ? ` ${joinParts(parts)}` : "";
|
|
3815
|
+
return `${chalk4.blue("\u23F5")} ${gadgetLabel}${metricsStr}`;
|
|
3549
3816
|
}
|
|
3550
3817
|
let outputLabel;
|
|
3551
3818
|
if (info.tokenCount !== void 0 && info.tokenCount > 0) {
|
|
3552
|
-
outputLabel =
|
|
3819
|
+
outputLabel = chalk4.dim("\u2193") + chalk4.green(` ${formatTokens(info.tokenCount)} `);
|
|
3553
3820
|
} else if (info.outputBytes !== void 0 && info.outputBytes > 0) {
|
|
3554
|
-
outputLabel = `${
|
|
3821
|
+
outputLabel = `${chalk4.green(format.bytes(info.outputBytes))} `;
|
|
3555
3822
|
} else {
|
|
3556
3823
|
outputLabel = "";
|
|
3557
3824
|
}
|
|
3558
|
-
const resultIcon = info.breaksLoop ?
|
|
3559
|
-
const nameRef =
|
|
3560
|
-
const line1 = `${
|
|
3825
|
+
const resultIcon = info.breaksLoop ? chalk4.yellow("\u23F9") : chalk4.green("\u2713");
|
|
3826
|
+
const nameRef = chalk4.magenta(info.name);
|
|
3827
|
+
const line1 = `${chalk4.dim("\u2192")} ${gadgetLabel}${paramsLabel}`;
|
|
3561
3828
|
const line2Prefix = ` ${resultIcon} ${nameRef} ${outputLabel}`;
|
|
3562
3829
|
const line2 = `${line2Prefix}${timeLabel}`;
|
|
3563
3830
|
return `${line1}
|
|
@@ -3979,7 +4246,7 @@ var NestedOperationTracker = class {
|
|
|
3979
4246
|
};
|
|
3980
4247
|
|
|
3981
4248
|
// src/progress/progress-renderer.ts
|
|
3982
|
-
import
|
|
4249
|
+
import chalk5 from "chalk";
|
|
3983
4250
|
import { FALLBACK_CHARS_PER_TOKEN } from "llmist";
|
|
3984
4251
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3985
4252
|
var SPINNER_DELAY_MS = 500;
|
|
@@ -4163,7 +4430,7 @@ var ProgressRenderer = class {
|
|
|
4163
4430
|
const elapsedSeconds2 = (Date.now() - op.startTime) / 1e3;
|
|
4164
4431
|
const nestedMetrics = op.id ? this.nestedOperationTracker.getAggregatedSubagentMetrics(op.id) : { inputTokens: 0, outputTokens: 0, cachedInputTokens: 0, cost: 0, callCount: 0 };
|
|
4165
4432
|
const termWidth2 = process.stdout.columns ?? 80;
|
|
4166
|
-
const parentPrefix = `${
|
|
4433
|
+
const parentPrefix = `${chalk5.dim(`${gadget.name}:`)} `;
|
|
4167
4434
|
const line2 = formatGadgetLine(
|
|
4168
4435
|
{
|
|
4169
4436
|
name: op.name ?? "",
|
|
@@ -4186,7 +4453,7 @@ var ProgressRenderer = class {
|
|
|
4186
4453
|
}
|
|
4187
4454
|
for (const stream of activeNestedStreams) {
|
|
4188
4455
|
const indent = " ".repeat(stream.depth + 2);
|
|
4189
|
-
const parentPrefix = `${
|
|
4456
|
+
const parentPrefix = `${chalk5.dim(`${stream.parentGadgetName}:`)} `;
|
|
4190
4457
|
const elapsedSeconds = (Date.now() - stream.startTime) / 1e3;
|
|
4191
4458
|
const line = formatLLMCallLine({
|
|
4192
4459
|
iteration: stream.iteration,
|
|
@@ -4244,21 +4511,19 @@ var ProgressRenderer = class {
|
|
|
4244
4511
|
const elapsed = ((Date.now() - this.callStatsTracker.totalStartTime) / 1e3).toFixed(1);
|
|
4245
4512
|
const parts = [];
|
|
4246
4513
|
if (this.callStatsTracker.model) {
|
|
4247
|
-
parts.push(
|
|
4514
|
+
parts.push(chalk5.cyan(this.callStatsTracker.model));
|
|
4248
4515
|
}
|
|
4249
4516
|
if (this.callStatsTracker.totalTokens > 0) {
|
|
4250
|
-
parts.push(
|
|
4517
|
+
parts.push(chalk5.dim("total:") + chalk5.magenta(` ${this.callStatsTracker.totalTokens}`));
|
|
4251
4518
|
}
|
|
4252
4519
|
if (this.callStatsTracker.iterations > 0) {
|
|
4253
|
-
parts.push(
|
|
4520
|
+
parts.push(chalk5.dim("iter:") + chalk5.blue(` ${this.callStatsTracker.iterations}`));
|
|
4254
4521
|
}
|
|
4255
4522
|
if (this.callStatsTracker.totalCost > 0) {
|
|
4256
|
-
parts.push(
|
|
4257
|
-
chalk4.dim("cost:") + chalk4.cyan(` $${formatCost(this.callStatsTracker.totalCost)}`)
|
|
4258
|
-
);
|
|
4523
|
+
parts.push(`${chalk5.dim("cost:")} ${costPart(this.callStatsTracker.totalCost)}`);
|
|
4259
4524
|
}
|
|
4260
|
-
parts.push(
|
|
4261
|
-
return `${parts.join(
|
|
4525
|
+
parts.push(chalk5.dim(`${elapsed}s`));
|
|
4526
|
+
return `${parts.join(chalk5.dim(" | "))} ${chalk5.cyan(spinner)}`;
|
|
4262
4527
|
}
|
|
4263
4528
|
/**
|
|
4264
4529
|
* Returns a formatted stats string for cancellation messages.
|
|
@@ -4269,15 +4534,21 @@ var ProgressRenderer = class {
|
|
|
4269
4534
|
const elapsed = ((Date.now() - this.callStatsTracker.callStartTime) / 1e3).toFixed(1);
|
|
4270
4535
|
const outTokens = this.callStatsTracker.callOutputTokensEstimated ? Math.round(this.callStatsTracker.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callStatsTracker.callOutputTokens;
|
|
4271
4536
|
if (this.callStatsTracker.callInputTokens > 0) {
|
|
4272
|
-
|
|
4273
|
-
|
|
4537
|
+
parts.push(
|
|
4538
|
+
tokenPart("input", this.callStatsTracker.callInputTokens, {
|
|
4539
|
+
estimated: this.callStatsTracker.callInputTokensEstimated
|
|
4540
|
+
})
|
|
4541
|
+
);
|
|
4274
4542
|
}
|
|
4275
4543
|
if (outTokens > 0) {
|
|
4276
|
-
|
|
4277
|
-
|
|
4544
|
+
parts.push(
|
|
4545
|
+
tokenPart("output", outTokens, {
|
|
4546
|
+
estimated: this.callStatsTracker.callOutputTokensEstimated
|
|
4547
|
+
})
|
|
4548
|
+
);
|
|
4278
4549
|
}
|
|
4279
4550
|
parts.push(`${elapsed}s`);
|
|
4280
|
-
return parts
|
|
4551
|
+
return joinParts(parts);
|
|
4281
4552
|
}
|
|
4282
4553
|
/**
|
|
4283
4554
|
* Returns a formatted prompt string with stats (like bash PS1).
|
|
@@ -4291,30 +4562,30 @@ var ProgressRenderer = class {
|
|
|
4291
4562
|
const outTokens = this.callStatsTracker.callOutputTokensEstimated ? Math.round(this.callStatsTracker.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callStatsTracker.callOutputTokens;
|
|
4292
4563
|
const outEstimated = this.callStatsTracker.callOutputTokensEstimated;
|
|
4293
4564
|
if (this.callStatsTracker.callInputTokens > 0) {
|
|
4294
|
-
const prefix = this.callStatsTracker.callInputTokensEstimated ? "~" : "";
|
|
4295
4565
|
parts.push(
|
|
4296
|
-
|
|
4566
|
+
tokenPart("input", this.callStatsTracker.callInputTokens, {
|
|
4567
|
+
estimated: this.callStatsTracker.callInputTokensEstimated
|
|
4568
|
+
})
|
|
4297
4569
|
);
|
|
4298
4570
|
}
|
|
4299
4571
|
if (outTokens > 0) {
|
|
4300
|
-
|
|
4301
|
-
parts.push(chalk4.dim("\u2193") + chalk4.green(` ${prefix}${formatTokens(outTokens)}`));
|
|
4572
|
+
parts.push(tokenPart("output", outTokens, { estimated: outEstimated }));
|
|
4302
4573
|
}
|
|
4303
|
-
parts.push(
|
|
4574
|
+
parts.push(chalk5.dim(`${elapsed}s`));
|
|
4304
4575
|
} else {
|
|
4305
4576
|
const elapsed = Math.round((Date.now() - this.callStatsTracker.totalStartTime) / 1e3);
|
|
4306
4577
|
if (this.callStatsTracker.totalTokens > 0) {
|
|
4307
|
-
parts.push(
|
|
4578
|
+
parts.push(chalk5.magenta(formatTokens(this.callStatsTracker.totalTokens)));
|
|
4308
4579
|
}
|
|
4309
4580
|
if (this.callStatsTracker.iterations > 0) {
|
|
4310
|
-
parts.push(
|
|
4581
|
+
parts.push(chalk5.blue(`i${this.callStatsTracker.iterations}`));
|
|
4311
4582
|
}
|
|
4312
4583
|
if (this.callStatsTracker.totalCost > 0) {
|
|
4313
|
-
parts.push(
|
|
4584
|
+
parts.push(costPart(this.callStatsTracker.totalCost));
|
|
4314
4585
|
}
|
|
4315
|
-
parts.push(
|
|
4586
|
+
parts.push(chalk5.dim(`${elapsed}s`));
|
|
4316
4587
|
}
|
|
4317
|
-
return `${parts.join(
|
|
4588
|
+
return `${parts.join(chalk5.dim(" | "))} ${chalk5.green(">")} `;
|
|
4318
4589
|
}
|
|
4319
4590
|
};
|
|
4320
4591
|
|
|
@@ -4556,7 +4827,7 @@ function addAgentOptions(cmd, defaults) {
|
|
|
4556
4827
|
...previous,
|
|
4557
4828
|
value
|
|
4558
4829
|
];
|
|
4559
|
-
const defaultGadgets = defaults?.gadgets ??
|
|
4830
|
+
const defaultGadgets = defaults?.gadgets ?? [];
|
|
4560
4831
|
return cmd.option(OPTION_FLAGS.model, OPTION_DESCRIPTIONS.model, defaults?.model ?? DEFAULT_MODEL).option(OPTION_FLAGS.systemPrompt, OPTION_DESCRIPTIONS.systemPrompt, defaults?.system).option(OPTION_FLAGS.systemPromptFile, OPTION_DESCRIPTIONS.systemPromptFile).option(
|
|
4561
4832
|
OPTION_FLAGS.temperature,
|
|
4562
4833
|
OPTION_DESCRIPTIONS.temperature,
|
|
@@ -4637,14 +4908,14 @@ function configToCompleteOptions(config) {
|
|
|
4637
4908
|
if (rl["tokens-per-minute"] !== void 0) result.rateLimitTpm = rl["tokens-per-minute"];
|
|
4638
4909
|
if (rl["tokens-per-day"] !== void 0) result.rateLimitDaily = rl["tokens-per-day"];
|
|
4639
4910
|
if (rl["safety-margin"] !== void 0) result.rateLimitSafetyMargin = rl["safety-margin"];
|
|
4640
|
-
if (rl.enabled === false) result.
|
|
4911
|
+
if (rl.enabled === false) result.rateLimit = false;
|
|
4641
4912
|
}
|
|
4642
4913
|
if (config.retry) {
|
|
4643
4914
|
const r = config.retry;
|
|
4644
4915
|
if (r.retries !== void 0) result.maxRetries = r.retries;
|
|
4645
4916
|
if (r["min-timeout"] !== void 0) result.retryMinTimeout = r["min-timeout"];
|
|
4646
4917
|
if (r["max-timeout"] !== void 0) result.retryMaxTimeout = r["max-timeout"];
|
|
4647
|
-
if (r.enabled === false) result.
|
|
4918
|
+
if (r.enabled === false) result.retry = false;
|
|
4648
4919
|
}
|
|
4649
4920
|
if (config.reasoning) {
|
|
4650
4921
|
result.profileReasoning = config.reasoning;
|
|
@@ -4658,8 +4929,7 @@ function configToAgentOptions(config) {
|
|
|
4658
4929
|
if (config.temperature !== void 0) result.temperature = config.temperature;
|
|
4659
4930
|
if (config["max-iterations"] !== void 0) result.maxIterations = config["max-iterations"];
|
|
4660
4931
|
if (config.budget !== void 0) result.budget = config.budget;
|
|
4661
|
-
|
|
4662
|
-
if (gadgets !== void 0) result.gadget = gadgets;
|
|
4932
|
+
if (config.gadgets !== void 0) result.gadget = config.gadgets;
|
|
4663
4933
|
if (config.builtins !== void 0) result.builtins = config.builtins;
|
|
4664
4934
|
if (config["builtin-interaction"] !== void 0)
|
|
4665
4935
|
result.builtinInteraction = config["builtin-interaction"];
|
|
@@ -4680,14 +4950,14 @@ function configToAgentOptions(config) {
|
|
|
4680
4950
|
if (rl["tokens-per-minute"] !== void 0) result.rateLimitTpm = rl["tokens-per-minute"];
|
|
4681
4951
|
if (rl["tokens-per-day"] !== void 0) result.rateLimitDaily = rl["tokens-per-day"];
|
|
4682
4952
|
if (rl["safety-margin"] !== void 0) result.rateLimitSafetyMargin = rl["safety-margin"];
|
|
4683
|
-
if (rl.enabled === false) result.
|
|
4953
|
+
if (rl.enabled === false) result.rateLimit = false;
|
|
4684
4954
|
}
|
|
4685
4955
|
if (config.retry) {
|
|
4686
4956
|
const r = config.retry;
|
|
4687
4957
|
if (r.retries !== void 0) result.maxRetries = r.retries;
|
|
4688
4958
|
if (r["min-timeout"] !== void 0) result.retryMinTimeout = r["min-timeout"];
|
|
4689
4959
|
if (r["max-timeout"] !== void 0) result.retryMaxTimeout = r["max-timeout"];
|
|
4690
|
-
if (r.enabled === false) result.
|
|
4960
|
+
if (r.enabled === false) result.retry = false;
|
|
4691
4961
|
}
|
|
4692
4962
|
if (config.reasoning) {
|
|
4693
4963
|
result.profileReasoning = config.reasoning;
|
|
@@ -4731,7 +5001,7 @@ function detectProvider(model) {
|
|
|
4731
5001
|
}
|
|
4732
5002
|
}
|
|
4733
5003
|
function resolveRateLimitConfig(options, globalConfig, profileConfig, model) {
|
|
4734
|
-
if (options.
|
|
5004
|
+
if (options.rateLimit === false) {
|
|
4735
5005
|
return { enabled: false, safetyMargin: 0.8 };
|
|
4736
5006
|
}
|
|
4737
5007
|
let resolved;
|
|
@@ -4856,12 +5126,82 @@ function resolveRetryConfig(options, globalConfig, profileConfig) {
|
|
|
4856
5126
|
if (options.retryMaxTimeout !== void 0) {
|
|
4857
5127
|
resolved.maxTimeout = options.retryMaxTimeout;
|
|
4858
5128
|
}
|
|
4859
|
-
if (options.
|
|
5129
|
+
if (options.retry === false) {
|
|
4860
5130
|
resolved.enabled = false;
|
|
4861
5131
|
}
|
|
4862
5132
|
return resolved;
|
|
4863
5133
|
}
|
|
4864
5134
|
|
|
5135
|
+
// src/skills/skill-manager.ts
|
|
5136
|
+
import { homedir as homedir3 } from "os";
|
|
5137
|
+
import { join as join2, resolve as resolve3 } from "path";
|
|
5138
|
+
import { discoverSkills, loadSkillsFromDirectory } from "llmist";
|
|
5139
|
+
var CLISkillManager = class {
|
|
5140
|
+
/**
|
|
5141
|
+
* Load all skills from standard locations and configured sources.
|
|
5142
|
+
*
|
|
5143
|
+
* @param config - Skills configuration from cli.toml [skills] section
|
|
5144
|
+
* @param projectDir - Project directory (cwd)
|
|
5145
|
+
*/
|
|
5146
|
+
async loadAll(config, projectDir) {
|
|
5147
|
+
const registry = discoverSkills({
|
|
5148
|
+
projectDir: projectDir ?? process.cwd()
|
|
5149
|
+
});
|
|
5150
|
+
if (config?.sources) {
|
|
5151
|
+
for (const source of config.sources) {
|
|
5152
|
+
const resolvedSource = this.resolveSource(source);
|
|
5153
|
+
const skills = loadSkillsFromDirectory(resolvedSource, {
|
|
5154
|
+
type: "directory",
|
|
5155
|
+
path: resolvedSource
|
|
5156
|
+
});
|
|
5157
|
+
registry.registerMany(skills);
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
if (config?.overrides) {
|
|
5161
|
+
for (const [skillName, override] of Object.entries(config.overrides)) {
|
|
5162
|
+
if (override.enabled === false) {
|
|
5163
|
+
registry.remove(skillName);
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
return registry;
|
|
5168
|
+
}
|
|
5169
|
+
/**
|
|
5170
|
+
* Resolve a source string to an absolute directory path.
|
|
5171
|
+
*/
|
|
5172
|
+
resolveSource(source) {
|
|
5173
|
+
if (source.startsWith("~")) {
|
|
5174
|
+
return join2(homedir3(), source.slice(1));
|
|
5175
|
+
}
|
|
5176
|
+
return resolve3(source);
|
|
5177
|
+
}
|
|
5178
|
+
};
|
|
5179
|
+
|
|
5180
|
+
// src/skills/slash-handler.ts
|
|
5181
|
+
function parseSlashCommand(input, registry) {
|
|
5182
|
+
const trimmed = input.trim();
|
|
5183
|
+
if (!trimmed.startsWith("/")) {
|
|
5184
|
+
return { isSkillInvocation: false };
|
|
5185
|
+
}
|
|
5186
|
+
const match = trimmed.match(/^\/(\S+)(?:\s+(.*))?$/);
|
|
5187
|
+
if (!match) {
|
|
5188
|
+
return { isSkillInvocation: false };
|
|
5189
|
+
}
|
|
5190
|
+
const [, commandName, args] = match;
|
|
5191
|
+
if (commandName === "skills") {
|
|
5192
|
+
return { isSkillInvocation: true, isListCommand: true };
|
|
5193
|
+
}
|
|
5194
|
+
const skill = registry.get(commandName);
|
|
5195
|
+
if (!skill || !skill.isUserInvocable) {
|
|
5196
|
+
return { isSkillInvocation: false };
|
|
5197
|
+
}
|
|
5198
|
+
return {
|
|
5199
|
+
isSkillInvocation: true,
|
|
5200
|
+
skillName: commandName,
|
|
5201
|
+
arguments: args?.trim()
|
|
5202
|
+
};
|
|
5203
|
+
}
|
|
5204
|
+
|
|
4865
5205
|
// src/subagent-config.ts
|
|
4866
5206
|
var INHERIT_MODEL = "inherit";
|
|
4867
5207
|
function resolveSubagentConfig(subagentName, parentModel, profileConfig, globalConfig) {
|
|
@@ -4910,11 +5250,34 @@ function extractSubagentConfig(globalConfig, subagentName) {
|
|
|
4910
5250
|
return {};
|
|
4911
5251
|
}
|
|
4912
5252
|
|
|
4913
|
-
// src/tui/
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
5253
|
+
// src/tui/raw-viewer-data.ts
|
|
5254
|
+
function isRawViewerNode(node) {
|
|
5255
|
+
return node.type === "llm_call" || node.type === "gadget";
|
|
5256
|
+
}
|
|
5257
|
+
function createRawViewerData(node, mode) {
|
|
5258
|
+
if (node.type === "llm_call") {
|
|
5259
|
+
return {
|
|
5260
|
+
mode,
|
|
5261
|
+
request: node.rawRequest,
|
|
5262
|
+
response: node.rawResponse,
|
|
5263
|
+
iteration: node.iteration,
|
|
5264
|
+
model: node.model
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
return {
|
|
5268
|
+
mode,
|
|
5269
|
+
gadgetName: node.name,
|
|
5270
|
+
parameters: node.parameters,
|
|
5271
|
+
result: node.result,
|
|
5272
|
+
error: node.error
|
|
5273
|
+
};
|
|
5274
|
+
}
|
|
5275
|
+
|
|
5276
|
+
// src/tui/block-renderer.ts
|
|
5277
|
+
import { Box } from "@unblessed/node";
|
|
5278
|
+
|
|
5279
|
+
// src/ui/block-formatters.ts
|
|
5280
|
+
import chalk6 from "chalk";
|
|
4918
5281
|
var BOX = {
|
|
4919
5282
|
topLeft: "\u250C",
|
|
4920
5283
|
topRight: "\u2510",
|
|
@@ -4930,44 +5293,35 @@ var COMPLETE_INDICATOR = "\u2713";
|
|
|
4930
5293
|
var ERROR_INDICATOR = "\u2717";
|
|
4931
5294
|
function formatLLMCallCollapsed(node, selected) {
|
|
4932
5295
|
const indicator = node.isComplete ? COMPLETE_INDICATOR : PROGRESS_INDICATOR;
|
|
4933
|
-
const indicatorColor = node.isComplete ?
|
|
5296
|
+
const indicatorColor = node.isComplete ? chalk6.green : chalk6.blue;
|
|
4934
5297
|
const parts = [];
|
|
4935
|
-
const callNumber =
|
|
4936
|
-
const model =
|
|
5298
|
+
const callNumber = chalk6.cyan(`#${node.iteration}`);
|
|
5299
|
+
const model = chalk6.magenta(node.model);
|
|
4937
5300
|
parts.push(`${callNumber} ${model}`);
|
|
4938
5301
|
if (node.details) {
|
|
4939
5302
|
const d = node.details;
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
}
|
|
4949
|
-
if (d.reasoningTokens && d.reasoningTokens > 0) {
|
|
4950
|
-
parts.push(chalk5.dim("\u{1F4AD}") + chalk5.magenta(` ${formatTokens(d.reasoningTokens)}`));
|
|
4951
|
-
}
|
|
5303
|
+
parts.push(
|
|
5304
|
+
...buildTokenMetrics({
|
|
5305
|
+
input: d.inputTokens,
|
|
5306
|
+
cached: d.cachedInputTokens,
|
|
5307
|
+
output: d.outputTokens,
|
|
5308
|
+
reasoning: d.reasoningTokens
|
|
5309
|
+
})
|
|
5310
|
+
);
|
|
4952
5311
|
if (d.elapsedSeconds !== void 0) {
|
|
4953
|
-
parts.push(
|
|
5312
|
+
parts.push(timePart(d.elapsedSeconds));
|
|
4954
5313
|
}
|
|
4955
5314
|
if (d.cost !== void 0 && d.cost > 0) {
|
|
4956
|
-
parts.push(
|
|
5315
|
+
parts.push(costPart(d.cost));
|
|
4957
5316
|
}
|
|
4958
5317
|
if (node.isComplete && d.finishReason) {
|
|
4959
|
-
|
|
4960
|
-
if (reason === "STOP" || reason === "END_TURN") {
|
|
4961
|
-
parts.push(chalk5.green(reason));
|
|
4962
|
-
} else {
|
|
4963
|
-
parts.push(chalk5.yellow(reason));
|
|
4964
|
-
}
|
|
5318
|
+
parts.push(finishReasonPart(d.finishReason));
|
|
4965
5319
|
}
|
|
4966
5320
|
}
|
|
4967
|
-
const line = parts
|
|
5321
|
+
const line = joinParts(parts);
|
|
4968
5322
|
const prefix = indicatorColor(indicator);
|
|
4969
5323
|
if (selected) {
|
|
4970
|
-
return
|
|
5324
|
+
return chalk6.bgBlue.white(`${prefix} ${line}`);
|
|
4971
5325
|
}
|
|
4972
5326
|
return `${prefix} ${line}`;
|
|
4973
5327
|
}
|
|
@@ -4976,58 +5330,58 @@ function formatLLMCallExpanded(node) {
|
|
|
4976
5330
|
const indent = " ";
|
|
4977
5331
|
const d = node.details;
|
|
4978
5332
|
if (!d) {
|
|
4979
|
-
lines.push(`${indent}${
|
|
5333
|
+
lines.push(`${indent}${chalk6.dim("No details available")}`);
|
|
4980
5334
|
return lines;
|
|
4981
5335
|
}
|
|
4982
5336
|
const width = Math.min(60, (process.stdout.columns || 80) - 4);
|
|
4983
5337
|
const headerLine = `${BOX.topLeft}${BOX.horizontal} Details ${BOX.horizontal.repeat(width - 11)}`;
|
|
4984
|
-
lines.push(`${indent}${
|
|
4985
|
-
lines.push(`${indent}${
|
|
5338
|
+
lines.push(`${indent}${chalk6.dim(headerLine)}`);
|
|
5339
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} Model: ${chalk6.magenta(node.model)}`);
|
|
4986
5340
|
if (d.inputTokens !== void 0) {
|
|
4987
|
-
let inputLine = `${indent}${
|
|
5341
|
+
let inputLine = `${indent}${chalk6.dim(BOX.vertical)} Input: ${chalk6.yellow(formatTokens(d.inputTokens))} tokens`;
|
|
4988
5342
|
if (d.cachedInputTokens && d.cachedInputTokens > 0) {
|
|
4989
5343
|
const cachePercent = (d.cachedInputTokens / d.inputTokens * 100).toFixed(1);
|
|
4990
|
-
inputLine +=
|
|
5344
|
+
inputLine += chalk6.blue(` (${formatTokens(d.cachedInputTokens)} cached, ${cachePercent}%)`);
|
|
4991
5345
|
}
|
|
4992
5346
|
lines.push(inputLine);
|
|
4993
5347
|
}
|
|
4994
5348
|
if (d.outputTokens !== void 0) {
|
|
4995
5349
|
lines.push(
|
|
4996
|
-
`${indent}${
|
|
5350
|
+
`${indent}${chalk6.dim(BOX.vertical)} Output: ${chalk6.green(formatTokens(d.outputTokens))} tokens`
|
|
4997
5351
|
);
|
|
4998
5352
|
}
|
|
4999
5353
|
if (d.reasoningTokens !== void 0 && d.reasoningTokens > 0) {
|
|
5000
5354
|
lines.push(
|
|
5001
|
-
`${indent}${
|
|
5355
|
+
`${indent}${chalk6.dim(BOX.vertical)} Reason: ${chalk6.magenta(formatTokens(d.reasoningTokens))} tokens`
|
|
5002
5356
|
);
|
|
5003
5357
|
}
|
|
5004
5358
|
if (d.contextPercent !== void 0) {
|
|
5005
|
-
let contextColor =
|
|
5006
|
-
if (d.contextPercent >= 80) contextColor =
|
|
5007
|
-
else if (d.contextPercent >= 50) contextColor =
|
|
5359
|
+
let contextColor = chalk6.green;
|
|
5360
|
+
if (d.contextPercent >= 80) contextColor = chalk6.red;
|
|
5361
|
+
else if (d.contextPercent >= 50) contextColor = chalk6.yellow;
|
|
5008
5362
|
lines.push(
|
|
5009
|
-
`${indent}${
|
|
5363
|
+
`${indent}${chalk6.dim(BOX.vertical)} Context: ${contextColor(`${Math.round(d.contextPercent)}%`)}`
|
|
5010
5364
|
);
|
|
5011
5365
|
}
|
|
5012
5366
|
if (d.elapsedSeconds !== void 0) {
|
|
5013
|
-
let timeLine = `${indent}${
|
|
5367
|
+
let timeLine = `${indent}${chalk6.dim(BOX.vertical)} Time: ${chalk6.dim(`${d.elapsedSeconds.toFixed(1)}s`)}`;
|
|
5014
5368
|
if (d.outputTokens && d.elapsedSeconds > 0) {
|
|
5015
5369
|
const tokensPerSec = Math.round(d.outputTokens / d.elapsedSeconds);
|
|
5016
|
-
timeLine +=
|
|
5370
|
+
timeLine += chalk6.dim(` (${tokensPerSec} tok/s)`);
|
|
5017
5371
|
}
|
|
5018
5372
|
lines.push(timeLine);
|
|
5019
5373
|
}
|
|
5020
5374
|
if (d.cost !== void 0 && d.cost > 0) {
|
|
5021
5375
|
lines.push(
|
|
5022
|
-
`${indent}${
|
|
5376
|
+
`${indent}${chalk6.dim(BOX.vertical)} Cost: ${chalk6.cyan(`$${formatCost(d.cost)}`)}`
|
|
5023
5377
|
);
|
|
5024
5378
|
}
|
|
5025
5379
|
if (d.finishReason) {
|
|
5026
5380
|
const reason = d.finishReason.toUpperCase();
|
|
5027
|
-
const reasonColor = reason === "STOP" || reason === "END_TURN" ?
|
|
5028
|
-
lines.push(`${indent}${
|
|
5381
|
+
const reasonColor = reason === "STOP" || reason === "END_TURN" ? chalk6.green : chalk6.yellow;
|
|
5382
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} Finish: ${reasonColor(reason)}`);
|
|
5029
5383
|
}
|
|
5030
|
-
lines.push(`${indent}${
|
|
5384
|
+
lines.push(`${indent}${chalk6.dim(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5031
5385
|
return lines;
|
|
5032
5386
|
}
|
|
5033
5387
|
function formatGadgetCollapsed(node, selected) {
|
|
@@ -5035,15 +5389,15 @@ function formatGadgetCollapsed(node, selected) {
|
|
|
5035
5389
|
let indicatorColor;
|
|
5036
5390
|
if (node.error) {
|
|
5037
5391
|
indicator = ERROR_INDICATOR;
|
|
5038
|
-
indicatorColor =
|
|
5392
|
+
indicatorColor = chalk6.red;
|
|
5039
5393
|
} else if (node.isComplete) {
|
|
5040
5394
|
indicator = COMPLETE_INDICATOR;
|
|
5041
|
-
indicatorColor =
|
|
5395
|
+
indicatorColor = chalk6.green;
|
|
5042
5396
|
} else {
|
|
5043
5397
|
indicator = PROGRESS_INDICATOR;
|
|
5044
|
-
indicatorColor =
|
|
5398
|
+
indicatorColor = chalk6.blue;
|
|
5045
5399
|
}
|
|
5046
|
-
const gadgetLabel =
|
|
5400
|
+
const gadgetLabel = chalk6.magenta.bold(node.name);
|
|
5047
5401
|
let paramsStr = "";
|
|
5048
5402
|
if (node.parameters && Object.keys(node.parameters).length > 0) {
|
|
5049
5403
|
if (node.name === "TellUser") {
|
|
@@ -5065,15 +5419,15 @@ function formatGadgetCollapsed(node, selected) {
|
|
|
5065
5419
|
const formatted = entries.map(([key, value]) => {
|
|
5066
5420
|
const strValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
5067
5421
|
const truncated = strValue.length > maxParamLen ? `${strValue.slice(0, maxParamLen - 3)}...` : strValue;
|
|
5068
|
-
return `${
|
|
5422
|
+
return `${chalk6.dim(key)}=${chalk6.cyan(truncated)}`;
|
|
5069
5423
|
});
|
|
5070
|
-
paramsStr = `${
|
|
5424
|
+
paramsStr = `${chalk6.dim("(")}${formatted.join(chalk6.dim(", "))}${chalk6.dim(")")}`;
|
|
5071
5425
|
}
|
|
5072
5426
|
}
|
|
5073
5427
|
let errorStr = "";
|
|
5074
5428
|
if (node.error) {
|
|
5075
5429
|
const truncated = node.error.length > 40 ? `${node.error.slice(0, 37)}...` : node.error;
|
|
5076
|
-
errorStr = ` ${
|
|
5430
|
+
errorStr = ` ${chalk6.red("error:")} ${truncated}`;
|
|
5077
5431
|
}
|
|
5078
5432
|
const metrics = [];
|
|
5079
5433
|
if (node.executionTimeMs !== void 0) {
|
|
@@ -5081,24 +5435,23 @@ function formatGadgetCollapsed(node, selected) {
|
|
|
5081
5435
|
}
|
|
5082
5436
|
if (node.subagentStats && node.subagentStats.llmCallCount > 0) {
|
|
5083
5437
|
const { inputTokens, cachedTokens, outputTokens } = node.subagentStats;
|
|
5084
|
-
const
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
}
|
|
5089
|
-
|
|
5090
|
-
metrics.push(tokenParts.join(" "));
|
|
5438
|
+
const tokenMetrics = buildTokenMetrics({
|
|
5439
|
+
input: inputTokens,
|
|
5440
|
+
cached: cachedTokens,
|
|
5441
|
+
output: outputTokens
|
|
5442
|
+
});
|
|
5443
|
+
metrics.push(tokenMetrics.join(" "));
|
|
5091
5444
|
} else if (node.resultTokens && node.resultTokens > 0) {
|
|
5092
|
-
metrics.push(
|
|
5445
|
+
metrics.push(chalk6.dim("\u2193") + chalk6.green(` ${formatTokens(node.resultTokens)}`));
|
|
5093
5446
|
}
|
|
5094
5447
|
if (node.cost && node.cost > 0) {
|
|
5095
|
-
metrics.push(
|
|
5448
|
+
metrics.push(costPart(node.cost));
|
|
5096
5449
|
}
|
|
5097
|
-
const metricsStr = metrics.length > 0 ? ` ${
|
|
5450
|
+
const metricsStr = metrics.length > 0 ? ` ${chalk6.dim(metrics.join(" | "))}` : "";
|
|
5098
5451
|
const line = `${indicatorColor(indicator)} ${gadgetLabel}${paramsStr}${errorStr}${metricsStr}`;
|
|
5099
5452
|
let result;
|
|
5100
5453
|
if (selected) {
|
|
5101
|
-
result =
|
|
5454
|
+
result = chalk6.bgBlue.white(line);
|
|
5102
5455
|
} else {
|
|
5103
5456
|
result = line;
|
|
5104
5457
|
}
|
|
@@ -5120,7 +5473,7 @@ function formatGadgetExpanded(node) {
|
|
|
5120
5473
|
const paramsToShow = node.parameters ? Object.entries(node.parameters).filter(([key]) => !(isTellUser && key === "message")) : [];
|
|
5121
5474
|
if (paramsToShow.length > 0) {
|
|
5122
5475
|
const headerLine = `${BOX.topLeft}${BOX.horizontal} Parameters ${BOX.horizontal.repeat(width - 14)}`;
|
|
5123
|
-
lines.push(`${indent}${
|
|
5476
|
+
lines.push(`${indent}${chalk6.dim(headerLine)}`);
|
|
5124
5477
|
for (const [key, value] of paramsToShow) {
|
|
5125
5478
|
const strValue = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
5126
5479
|
const valueLines = strValue.split("\n");
|
|
@@ -5128,30 +5481,30 @@ function formatGadgetExpanded(node) {
|
|
|
5128
5481
|
if (valueLines.length === 1) {
|
|
5129
5482
|
const truncated = strValue.length > maxValueLen ? `${strValue.slice(0, maxValueLen - 3)}...` : strValue;
|
|
5130
5483
|
lines.push(
|
|
5131
|
-
`${indent}${
|
|
5484
|
+
`${indent}${chalk6.dim(BOX.vertical)} ${chalk6.dim(key)}: ${chalk6.cyan(truncated)}`
|
|
5132
5485
|
);
|
|
5133
5486
|
} else {
|
|
5134
|
-
lines.push(`${indent}${
|
|
5487
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} ${chalk6.dim(key)}:`);
|
|
5135
5488
|
for (const line of valueLines.slice(0, 5)) {
|
|
5136
|
-
lines.push(`${indent}${
|
|
5489
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} ${chalk6.cyan(line)}`);
|
|
5137
5490
|
}
|
|
5138
5491
|
if (valueLines.length > 5) {
|
|
5139
5492
|
lines.push(
|
|
5140
|
-
`${indent}${
|
|
5493
|
+
`${indent}${chalk6.dim(BOX.vertical)} ${chalk6.dim(`... (${valueLines.length - 5} more lines)`)}`
|
|
5141
5494
|
);
|
|
5142
5495
|
}
|
|
5143
5496
|
}
|
|
5144
5497
|
}
|
|
5145
|
-
lines.push(`${indent}${
|
|
5498
|
+
lines.push(`${indent}${chalk6.dim(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5146
5499
|
}
|
|
5147
5500
|
if (node.name === "TellUser" && node.parameters?.message) {
|
|
5148
5501
|
const message = String(node.parameters.message);
|
|
5149
5502
|
const messageType = node.parameters.type || "info";
|
|
5150
5503
|
const typeIndicators = {
|
|
5151
|
-
info: { emoji: "\u2139\uFE0F", color:
|
|
5152
|
-
success: { emoji: "\u2705", color:
|
|
5153
|
-
warning: { emoji: "\u26A0\uFE0F", color:
|
|
5154
|
-
error: { emoji: "\u274C", color:
|
|
5504
|
+
info: { emoji: "\u2139\uFE0F", color: chalk6.blue },
|
|
5505
|
+
success: { emoji: "\u2705", color: chalk6.green },
|
|
5506
|
+
warning: { emoji: "\u26A0\uFE0F", color: chalk6.yellow },
|
|
5507
|
+
error: { emoji: "\u274C", color: chalk6.red }
|
|
5155
5508
|
};
|
|
5156
5509
|
const typeInfo = typeIndicators[messageType] || typeIndicators.info;
|
|
5157
5510
|
const headerLine = `${BOX.topLeft}${BOX.horizontal} ${typeInfo.emoji} Message ${BOX.horizontal.repeat(width - 13)}`;
|
|
@@ -5159,86 +5512,84 @@ function formatGadgetExpanded(node) {
|
|
|
5159
5512
|
const rendered = renderMarkdown(message);
|
|
5160
5513
|
const renderedLines = rendered.split("\n");
|
|
5161
5514
|
for (const line of renderedLines) {
|
|
5162
|
-
lines.push(`${indent}${
|
|
5515
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} ${line}`);
|
|
5163
5516
|
}
|
|
5164
5517
|
lines.push(`${indent}${typeInfo.color(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5165
5518
|
} else if (node.result || node.error) {
|
|
5166
5519
|
const headerText = node.error ? " Error " : " Result ";
|
|
5167
5520
|
const headerLine = `${BOX.topLeft}${BOX.horizontal}${headerText}${BOX.horizontal.repeat(width - headerText.length - 2)}`;
|
|
5168
|
-
lines.push(`${indent}${
|
|
5521
|
+
lines.push(`${indent}${chalk6.dim(headerLine)}`);
|
|
5169
5522
|
const content = node.error || node.result || "";
|
|
5170
5523
|
const contentLines = content.split("\n");
|
|
5171
5524
|
const maxLines = 10;
|
|
5172
5525
|
const displayLines = contentLines.slice(0, maxLines);
|
|
5173
5526
|
for (const line of displayLines) {
|
|
5174
5527
|
const truncated = line.length > width - 4 ? `${line.slice(0, width - 7)}...` : line;
|
|
5175
|
-
const color = node.error ?
|
|
5176
|
-
lines.push(`${indent}${
|
|
5528
|
+
const color = node.error ? chalk6.red : chalk6.white;
|
|
5529
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} ${color(truncated)}`);
|
|
5177
5530
|
}
|
|
5178
5531
|
if (contentLines.length > maxLines) {
|
|
5179
5532
|
lines.push(
|
|
5180
|
-
`${indent}${
|
|
5533
|
+
`${indent}${chalk6.dim(BOX.vertical)} ${chalk6.dim(`... (${contentLines.length - maxLines} more lines)`)}`
|
|
5181
5534
|
);
|
|
5182
5535
|
}
|
|
5183
5536
|
if (node.executionTimeMs !== void 0) {
|
|
5184
5537
|
lines.push(
|
|
5185
|
-
`${indent}${
|
|
5538
|
+
`${indent}${chalk6.dim(BOX.vertical)} Time: ${chalk6.dim(formatExecutionTime(node.executionTimeMs))}`
|
|
5186
5539
|
);
|
|
5187
5540
|
}
|
|
5188
|
-
lines.push(`${indent}${
|
|
5541
|
+
lines.push(`${indent}${chalk6.dim(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5189
5542
|
}
|
|
5190
5543
|
if (node.mediaOutputs && node.mediaOutputs.length > 0) {
|
|
5191
5544
|
const headerLine = `${BOX.topLeft}${BOX.horizontal} Media ${BOX.horizontal.repeat(width - 9)}`;
|
|
5192
|
-
lines.push(`${indent}${
|
|
5545
|
+
lines.push(`${indent}${chalk6.dim(headerLine)}`);
|
|
5193
5546
|
for (const media of node.mediaOutputs) {
|
|
5194
5547
|
const kindEmoji = media.kind === "audio" ? "\u{1F50A}" : media.kind === "image" ? "\u{1F5BC}\uFE0F" : media.kind === "video" ? "\u{1F3AC}" : "\u{1F4C4}";
|
|
5195
5548
|
const maxPathLen = width - 8;
|
|
5196
5549
|
const displayPath = media.path.length > maxPathLen ? `...${media.path.slice(-(maxPathLen - 3))}` : media.path;
|
|
5197
|
-
lines.push(`${indent}${
|
|
5550
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} ${kindEmoji} ${chalk6.cyan(displayPath)}`);
|
|
5198
5551
|
}
|
|
5199
|
-
lines.push(`${indent}${
|
|
5552
|
+
lines.push(`${indent}${chalk6.dim(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5200
5553
|
}
|
|
5201
5554
|
if (node.children.length > 0) {
|
|
5202
5555
|
const headerLine = `${BOX.topLeft}${BOX.horizontal} Subagent Activity ${BOX.horizontal.repeat(width - 21)}`;
|
|
5203
|
-
lines.push(`${indent}${
|
|
5556
|
+
lines.push(`${indent}${chalk6.dim(headerLine)}`);
|
|
5204
5557
|
lines.push(
|
|
5205
|
-
`${indent}${
|
|
5558
|
+
`${indent}${chalk6.dim(BOX.vertical)} ${chalk6.dim(`${node.children.length} nested calls (expand children to see details)`)}`
|
|
5206
5559
|
);
|
|
5207
|
-
lines.push(`${indent}${
|
|
5560
|
+
lines.push(`${indent}${chalk6.dim(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5208
5561
|
}
|
|
5209
5562
|
if (node.executionTimeMs !== void 0 || node.cost || node.resultTokens || node.subagentStats) {
|
|
5210
5563
|
const metricsHeaderLine = `${BOX.topLeft}${BOX.horizontal} Metrics ${BOX.horizontal.repeat(width - 11)}`;
|
|
5211
|
-
lines.push(`${indent}${
|
|
5564
|
+
lines.push(`${indent}${chalk6.dim(metricsHeaderLine)}`);
|
|
5212
5565
|
if (node.executionTimeMs !== void 0) {
|
|
5213
5566
|
lines.push(
|
|
5214
|
-
`${indent}${
|
|
5567
|
+
`${indent}${chalk6.dim(BOX.vertical)} Duration: ${chalk6.dim(formatExecutionTime(node.executionTimeMs))}`
|
|
5215
5568
|
);
|
|
5216
5569
|
}
|
|
5217
5570
|
if (node.resultTokens && node.resultTokens > 0) {
|
|
5218
5571
|
lines.push(
|
|
5219
|
-
`${indent}${
|
|
5572
|
+
`${indent}${chalk6.dim(BOX.vertical)} Output: ${chalk6.green(`~${formatTokens(node.resultTokens)}`)} tokens`
|
|
5220
5573
|
);
|
|
5221
5574
|
}
|
|
5222
5575
|
if (node.cost && node.cost > 0) {
|
|
5223
5576
|
lines.push(
|
|
5224
|
-
`${indent}${
|
|
5577
|
+
`${indent}${chalk6.dim(BOX.vertical)} Cost: ${chalk6.cyan(`$${formatCost(node.cost)}`)}`
|
|
5225
5578
|
);
|
|
5226
5579
|
}
|
|
5227
5580
|
if (node.subagentStats && node.subagentStats.llmCallCount > 0) {
|
|
5228
5581
|
const s = node.subagentStats;
|
|
5229
|
-
const
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
}
|
|
5234
|
-
|
|
5235
|
-
const tokenStr = tokenParts.join(" ");
|
|
5236
|
-
lines.push(`${indent}${chalk5.dim(BOX.vertical)} LLM calls: ${s.llmCallCount} (${tokenStr})`);
|
|
5582
|
+
const tokenStr = buildTokenMetrics({
|
|
5583
|
+
input: s.inputTokens,
|
|
5584
|
+
cached: s.cachedTokens,
|
|
5585
|
+
output: s.outputTokens
|
|
5586
|
+
}).join(" ");
|
|
5587
|
+
lines.push(`${indent}${chalk6.dim(BOX.vertical)} LLM calls: ${s.llmCallCount} (${tokenStr})`);
|
|
5237
5588
|
}
|
|
5238
|
-
lines.push(`${indent}${
|
|
5589
|
+
lines.push(`${indent}${chalk6.dim(BOX.bottomLeft + BOX.horizontal.repeat(width - 1))}`);
|
|
5239
5590
|
}
|
|
5240
5591
|
if (lines.length === 0) {
|
|
5241
|
-
lines.push(`${indent}${
|
|
5592
|
+
lines.push(`${indent}${chalk6.dim("No details available")}`);
|
|
5242
5593
|
}
|
|
5243
5594
|
return lines;
|
|
5244
5595
|
}
|
|
@@ -5253,6 +5604,163 @@ function getContinuationIndent(depth) {
|
|
|
5253
5604
|
return `${" ".repeat(depth)} `;
|
|
5254
5605
|
}
|
|
5255
5606
|
|
|
5607
|
+
// src/tui/block-content-formatter.ts
|
|
5608
|
+
function formatBlockContent(node, selected, expanded) {
|
|
5609
|
+
const indent = getIndent(node.depth);
|
|
5610
|
+
switch (node.type) {
|
|
5611
|
+
case "llm_call": {
|
|
5612
|
+
const collapsed = formatLLMCallCollapsed(node, selected);
|
|
5613
|
+
if (!expanded) {
|
|
5614
|
+
return indent + collapsed;
|
|
5615
|
+
}
|
|
5616
|
+
const expandedLines = formatLLMCallExpanded(node);
|
|
5617
|
+
const contIndent = getContinuationIndent(node.depth);
|
|
5618
|
+
return [indent + collapsed, ...expandedLines.map((line) => contIndent + line)].join("\n");
|
|
5619
|
+
}
|
|
5620
|
+
case "gadget": {
|
|
5621
|
+
const collapsed = formatGadgetCollapsed(node, selected);
|
|
5622
|
+
if (!expanded) {
|
|
5623
|
+
return indent + collapsed;
|
|
5624
|
+
}
|
|
5625
|
+
const expandedLines = formatGadgetExpanded(node);
|
|
5626
|
+
const contIndent = getContinuationIndent(node.depth);
|
|
5627
|
+
return [indent + collapsed, ...expandedLines.map((line) => contIndent + line)].join("\n");
|
|
5628
|
+
}
|
|
5629
|
+
case "text": {
|
|
5630
|
+
if (node.id.startsWith("user_")) {
|
|
5631
|
+
return formatUserMessage(node.content);
|
|
5632
|
+
}
|
|
5633
|
+
const fullContent = renderMarkdown(node.content);
|
|
5634
|
+
if (expanded) {
|
|
5635
|
+
return `
|
|
5636
|
+
${fullContent}
|
|
5637
|
+
`;
|
|
5638
|
+
}
|
|
5639
|
+
return abbreviateToLines(fullContent, 2, selected);
|
|
5640
|
+
}
|
|
5641
|
+
case "thinking": {
|
|
5642
|
+
return formatThinkingContent(node, indent, expanded);
|
|
5643
|
+
}
|
|
5644
|
+
case "system_message": {
|
|
5645
|
+
const icon = getSystemMessageIcon(node.category);
|
|
5646
|
+
const color = getSystemMessageColor(node.category);
|
|
5647
|
+
const RESET3 = "\x1B[0m";
|
|
5648
|
+
return `${indent}${color}${icon} ${node.message}${RESET3}`;
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
function formatThinkingContent(node, indent, expanded) {
|
|
5653
|
+
const DIM2 = "\x1B[2m";
|
|
5654
|
+
const RED_DIM = "\x1B[2;31m";
|
|
5655
|
+
const RESET3 = "\x1B[0m";
|
|
5656
|
+
const contIndent = getContinuationIndent(node.depth);
|
|
5657
|
+
if (node.thinkingType === "redacted") {
|
|
5658
|
+
return `${indent}${RED_DIM}\u{1F512} [Redacted thinking block]${RESET3}`;
|
|
5659
|
+
}
|
|
5660
|
+
if (!expanded) {
|
|
5661
|
+
const firstLine = node.content.split("\n")[0]?.slice(0, 60) ?? "";
|
|
5662
|
+
const suffix = node.isComplete ? "" : "...";
|
|
5663
|
+
return `${indent}${DIM2}\u{1F4AD} Thinking${suffix} ${firstLine}${RESET3}`;
|
|
5664
|
+
}
|
|
5665
|
+
const tokenInfo = node.isComplete ? ` (${Math.ceil(node.content.length / 4)} tokens est.)` : "";
|
|
5666
|
+
const header = `${indent}${DIM2}\u25BC \u{1F4AD} Thinking${tokenInfo}${RESET3}`;
|
|
5667
|
+
const contentLines = node.content.split("\n").map((line) => `${contIndent}${DIM2}${line}${RESET3}`);
|
|
5668
|
+
return [header, ...contentLines].join("\n");
|
|
5669
|
+
}
|
|
5670
|
+
function getSystemMessageIcon(category) {
|
|
5671
|
+
switch (category) {
|
|
5672
|
+
case "throttle":
|
|
5673
|
+
return "\u23F8";
|
|
5674
|
+
case "retry":
|
|
5675
|
+
return "\u{1F504}";
|
|
5676
|
+
case "info":
|
|
5677
|
+
return "\u2139\uFE0F";
|
|
5678
|
+
case "warning":
|
|
5679
|
+
return "\u26A0\uFE0F";
|
|
5680
|
+
case "error":
|
|
5681
|
+
return "\u274C";
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
function getSystemMessageColor(category) {
|
|
5685
|
+
const YELLOW2 = "\x1B[33m";
|
|
5686
|
+
const BLUE = "\x1B[34m";
|
|
5687
|
+
const GRAY2 = "\x1B[90m";
|
|
5688
|
+
const RED2 = "\x1B[31m";
|
|
5689
|
+
switch (category) {
|
|
5690
|
+
case "throttle":
|
|
5691
|
+
return YELLOW2;
|
|
5692
|
+
case "retry":
|
|
5693
|
+
return BLUE;
|
|
5694
|
+
case "info":
|
|
5695
|
+
return GRAY2;
|
|
5696
|
+
case "warning":
|
|
5697
|
+
return YELLOW2;
|
|
5698
|
+
case "error":
|
|
5699
|
+
return RED2;
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
function abbreviateToLines(text3, maxLines, selected) {
|
|
5703
|
+
const lines = text3.split("\n");
|
|
5704
|
+
let startIndex = 0;
|
|
5705
|
+
while (startIndex < lines.length && lines[startIndex].trim() === "") {
|
|
5706
|
+
startIndex++;
|
|
5707
|
+
}
|
|
5708
|
+
const contentLines = lines.slice(startIndex);
|
|
5709
|
+
if (contentLines.length <= maxLines) {
|
|
5710
|
+
return `
|
|
5711
|
+
${contentLines.join("\n")}`;
|
|
5712
|
+
}
|
|
5713
|
+
const truncatedLines = contentLines.slice(0, maxLines);
|
|
5714
|
+
const indicator = selected ? "\u25B6 ..." : " ...";
|
|
5715
|
+
return `
|
|
5716
|
+
${truncatedLines.join("\n")}
|
|
5717
|
+
${indicator}`;
|
|
5718
|
+
}
|
|
5719
|
+
function isNodeVisibleInFilterMode(node, contentFilterMode) {
|
|
5720
|
+
if (contentFilterMode === "full") {
|
|
5721
|
+
return true;
|
|
5722
|
+
}
|
|
5723
|
+
switch (node.type) {
|
|
5724
|
+
case "text":
|
|
5725
|
+
return true;
|
|
5726
|
+
case "gadget":
|
|
5727
|
+
return shouldRenderAsText(node, contentFilterMode);
|
|
5728
|
+
default:
|
|
5729
|
+
return false;
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5732
|
+
function shouldRenderAsText(node, contentFilterMode) {
|
|
5733
|
+
if (contentFilterMode !== "focused") return false;
|
|
5734
|
+
if (node.type !== "gadget") return false;
|
|
5735
|
+
const name = node.name;
|
|
5736
|
+
return name === "TellUser" || name === "AskUser" || name === "Finish";
|
|
5737
|
+
}
|
|
5738
|
+
function formatGadgetAsText(node) {
|
|
5739
|
+
if (node.name === "TellUser") {
|
|
5740
|
+
const message = node.parameters?.message;
|
|
5741
|
+
if (typeof message === "string") {
|
|
5742
|
+
return `
|
|
5743
|
+
${renderMarkdown(message)}
|
|
5744
|
+
`;
|
|
5745
|
+
}
|
|
5746
|
+
} else if (node.name === "AskUser") {
|
|
5747
|
+
const question = node.parameters?.question;
|
|
5748
|
+
if (typeof question === "string") {
|
|
5749
|
+
return `
|
|
5750
|
+
? ${question}
|
|
5751
|
+
`;
|
|
5752
|
+
}
|
|
5753
|
+
} else if (node.name === "Finish") {
|
|
5754
|
+
const message = node.parameters?.message;
|
|
5755
|
+
if (typeof message === "string" && message.trim()) {
|
|
5756
|
+
return `
|
|
5757
|
+
\x1B[32m\u2713\x1B[0m ${renderMarkdown(message)}
|
|
5758
|
+
`;
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
return "";
|
|
5762
|
+
}
|
|
5763
|
+
|
|
5256
5764
|
// src/tui/node-store.ts
|
|
5257
5765
|
var NodeStore = class {
|
|
5258
5766
|
/** All nodes in the tree (flat for easy lookup) */
|
|
@@ -5675,6 +6183,28 @@ var NodeStore = class {
|
|
|
5675
6183
|
}
|
|
5676
6184
|
};
|
|
5677
6185
|
|
|
6186
|
+
// src/tui/tree-layout.ts
|
|
6187
|
+
function traverseNodeTree(nodeId, getNode, visit, top) {
|
|
6188
|
+
const node = getNode(nodeId);
|
|
6189
|
+
if (!node) {
|
|
6190
|
+
return top;
|
|
6191
|
+
}
|
|
6192
|
+
let nextTop = visit(nodeId, node, top);
|
|
6193
|
+
if ("children" in node) {
|
|
6194
|
+
for (const childId of node.children) {
|
|
6195
|
+
nextTop = traverseNodeTree(childId, getNode, visit, nextTop);
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
return nextTop;
|
|
6199
|
+
}
|
|
6200
|
+
function traverseRootTrees(rootIds, getNode, visit) {
|
|
6201
|
+
let top = 0;
|
|
6202
|
+
for (const rootId of rootIds) {
|
|
6203
|
+
top = traverseNodeTree(rootId, getNode, visit, top);
|
|
6204
|
+
}
|
|
6205
|
+
return top;
|
|
6206
|
+
}
|
|
6207
|
+
|
|
5678
6208
|
// src/tui/scroll-manager.ts
|
|
5679
6209
|
var ScrollManager = class _ScrollManager {
|
|
5680
6210
|
container;
|
|
@@ -5803,25 +6333,14 @@ var ScrollManager = class _ScrollManager {
|
|
|
5803
6333
|
* Calculate total height of all rendered blocks.
|
|
5804
6334
|
*/
|
|
5805
6335
|
getTotalContentHeight() {
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
sumNodeTreeHeight(nodeId, currentHeight) {
|
|
5813
|
-
const node = this.accessors.getNode(nodeId);
|
|
5814
|
-
if (!node) return currentHeight;
|
|
5815
|
-
const block = this.accessors.getBlock(nodeId);
|
|
5816
|
-
if (block) {
|
|
5817
|
-
currentHeight += getBlockHeight(block);
|
|
5818
|
-
}
|
|
5819
|
-
if ("children" in node) {
|
|
5820
|
-
for (const childId of node.children) {
|
|
5821
|
-
currentHeight = this.sumNodeTreeHeight(childId, currentHeight);
|
|
6336
|
+
return traverseRootTrees(
|
|
6337
|
+
this.accessors.getRootIds(),
|
|
6338
|
+
(id) => this.accessors.getNode(id),
|
|
6339
|
+
(nodeId, _node, currentHeight) => {
|
|
6340
|
+
const block = this.accessors.getBlock(nodeId);
|
|
6341
|
+
return currentHeight + (block ? getBlockHeight(block) : 0);
|
|
5822
6342
|
}
|
|
5823
|
-
|
|
5824
|
-
return currentHeight;
|
|
6343
|
+
);
|
|
5825
6344
|
}
|
|
5826
6345
|
/**
|
|
5827
6346
|
* Calculate vertical offset to push content to bottom when content < viewport.
|
|
@@ -5838,17 +6357,18 @@ var ScrollManager = class _ScrollManager {
|
|
|
5838
6357
|
* Apply vertical offset to a node tree (for bottom alignment).
|
|
5839
6358
|
*/
|
|
5840
6359
|
applyOffsetToNodeTree(nodeId, offset) {
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
}
|
|
5851
|
-
|
|
6360
|
+
traverseNodeTree(
|
|
6361
|
+
nodeId,
|
|
6362
|
+
(id) => this.accessors.getNode(id),
|
|
6363
|
+
(currentNodeId, _node, top) => {
|
|
6364
|
+
const block = this.accessors.getBlock(currentNodeId);
|
|
6365
|
+
if (block) {
|
|
6366
|
+
block.box.top = block.box.top + offset;
|
|
6367
|
+
}
|
|
6368
|
+
return top;
|
|
6369
|
+
},
|
|
6370
|
+
0
|
|
6371
|
+
);
|
|
5852
6372
|
}
|
|
5853
6373
|
};
|
|
5854
6374
|
function getBlockHeight(block) {
|
|
@@ -5856,6 +6376,167 @@ function getBlockHeight(block) {
|
|
|
5856
6376
|
return content.split("\n").length;
|
|
5857
6377
|
}
|
|
5858
6378
|
|
|
6379
|
+
// src/tui/tree-bridge.ts
|
|
6380
|
+
var TreeBridge = class {
|
|
6381
|
+
/** Unsubscribe function for tree events */
|
|
6382
|
+
treeUnsubscribe = null;
|
|
6383
|
+
/** Map tree node IDs to block node IDs */
|
|
6384
|
+
treeNodeToBlockId = /* @__PURE__ */ new Map();
|
|
6385
|
+
callbacks;
|
|
6386
|
+
constructor(callbacks) {
|
|
6387
|
+
this.callbacks = callbacks;
|
|
6388
|
+
}
|
|
6389
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
6390
|
+
// Public API
|
|
6391
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
6392
|
+
/**
|
|
6393
|
+
* Check if tree subscription is active.
|
|
6394
|
+
*/
|
|
6395
|
+
isSubscribed() {
|
|
6396
|
+
return this.treeUnsubscribe !== null;
|
|
6397
|
+
}
|
|
6398
|
+
/**
|
|
6399
|
+
* Subscribe to an ExecutionTree for automatic block updates.
|
|
6400
|
+
*
|
|
6401
|
+
* When subscribed, the TreeBridge will automatically create and update
|
|
6402
|
+
* blocks based on tree events, calling the provided callbacks.
|
|
6403
|
+
*
|
|
6404
|
+
* @param tree - The ExecutionTree to subscribe to
|
|
6405
|
+
* @returns Unsubscribe function to stop listening
|
|
6406
|
+
*
|
|
6407
|
+
* @example
|
|
6408
|
+
* ```typescript
|
|
6409
|
+
* const agent = builder.ask("Hello");
|
|
6410
|
+
* const unsubscribe = treeBridge.subscribeToTree(agent.getTree());
|
|
6411
|
+
*
|
|
6412
|
+
* for await (const event of agent.run()) {
|
|
6413
|
+
* // Blocks are automatically updated via tree subscription
|
|
6414
|
+
* }
|
|
6415
|
+
*
|
|
6416
|
+
* unsubscribe();
|
|
6417
|
+
* ```
|
|
6418
|
+
*/
|
|
6419
|
+
subscribeToTree(tree) {
|
|
6420
|
+
if (this.treeUnsubscribe) {
|
|
6421
|
+
this.treeUnsubscribe();
|
|
6422
|
+
}
|
|
6423
|
+
this.treeNodeToBlockId.clear();
|
|
6424
|
+
this.callbacks.onClearIdempotencyMaps();
|
|
6425
|
+
this.treeUnsubscribe = tree.onAll((event) => {
|
|
6426
|
+
this.handleTreeEvent(event, tree);
|
|
6427
|
+
});
|
|
6428
|
+
return () => {
|
|
6429
|
+
if (this.treeUnsubscribe) {
|
|
6430
|
+
this.treeUnsubscribe();
|
|
6431
|
+
this.treeUnsubscribe = null;
|
|
6432
|
+
}
|
|
6433
|
+
};
|
|
6434
|
+
}
|
|
6435
|
+
/**
|
|
6436
|
+
* Get block ID for a tree node ID.
|
|
6437
|
+
* Useful for external code that needs to correlate tree nodes with blocks.
|
|
6438
|
+
*/
|
|
6439
|
+
getBlockIdForTreeNode(treeNodeId) {
|
|
6440
|
+
return this.treeNodeToBlockId.get(treeNodeId);
|
|
6441
|
+
}
|
|
6442
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
6443
|
+
// Event Handling
|
|
6444
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
6445
|
+
/**
|
|
6446
|
+
* Handle an ExecutionTree event.
|
|
6447
|
+
*/
|
|
6448
|
+
handleTreeEvent(event, tree) {
|
|
6449
|
+
switch (event.type) {
|
|
6450
|
+
case "llm_call_start": {
|
|
6451
|
+
this.callbacks.onResetThinking();
|
|
6452
|
+
let parentBlockId;
|
|
6453
|
+
if (event.parentId) {
|
|
6454
|
+
parentBlockId = this.treeNodeToBlockId.get(event.parentId);
|
|
6455
|
+
}
|
|
6456
|
+
const blockId = this.callbacks.onAddLLMCall(
|
|
6457
|
+
event.iteration + 1,
|
|
6458
|
+
event.model,
|
|
6459
|
+
parentBlockId,
|
|
6460
|
+
event.depth > 0
|
|
6461
|
+
);
|
|
6462
|
+
this.treeNodeToBlockId.set(event.nodeId, blockId);
|
|
6463
|
+
const startNode = tree.getNode(event.nodeId);
|
|
6464
|
+
if (startNode?.type === "llm_call" && startNode.request) {
|
|
6465
|
+
this.callbacks.onSetLLMCallRequest(blockId, startNode.request);
|
|
6466
|
+
}
|
|
6467
|
+
break;
|
|
6468
|
+
}
|
|
6469
|
+
case "llm_call_complete": {
|
|
6470
|
+
this.callbacks.onCompleteThinking();
|
|
6471
|
+
const blockId = this.treeNodeToBlockId.get(event.nodeId);
|
|
6472
|
+
if (blockId) {
|
|
6473
|
+
this.callbacks.onCompleteLLMCall(blockId, {
|
|
6474
|
+
inputTokens: event.usage?.inputTokens,
|
|
6475
|
+
cachedInputTokens: event.usage?.cachedInputTokens,
|
|
6476
|
+
outputTokens: event.usage?.outputTokens,
|
|
6477
|
+
reasoningTokens: event.usage?.reasoningTokens,
|
|
6478
|
+
cost: event.cost,
|
|
6479
|
+
finishReason: event.finishReason ?? void 0
|
|
6480
|
+
});
|
|
6481
|
+
const completeNode = tree.getNode(event.nodeId);
|
|
6482
|
+
if (completeNode?.type === "llm_call" && completeNode.response) {
|
|
6483
|
+
this.callbacks.onSetLLMCallResponse(blockId, completeNode.response);
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
break;
|
|
6487
|
+
}
|
|
6488
|
+
case "thinking": {
|
|
6489
|
+
this.callbacks.onAddThinking(event.content, event.thinkingType);
|
|
6490
|
+
break;
|
|
6491
|
+
}
|
|
6492
|
+
case "gadget_call": {
|
|
6493
|
+
let parentBlockId;
|
|
6494
|
+
if (event.parentId) {
|
|
6495
|
+
parentBlockId = this.treeNodeToBlockId.get(event.parentId);
|
|
6496
|
+
}
|
|
6497
|
+
const previousLLMCallId = this.callbacks.onGetCurrentLLMCallId();
|
|
6498
|
+
if (parentBlockId) {
|
|
6499
|
+
this.callbacks.onSetCurrentLLMCall(parentBlockId);
|
|
6500
|
+
}
|
|
6501
|
+
const blockId = this.callbacks.onAddGadget(
|
|
6502
|
+
event.invocationId,
|
|
6503
|
+
event.name,
|
|
6504
|
+
event.parameters
|
|
6505
|
+
);
|
|
6506
|
+
this.treeNodeToBlockId.set(event.nodeId, blockId);
|
|
6507
|
+
this.callbacks.onSetCurrentLLMCall(previousLLMCallId);
|
|
6508
|
+
break;
|
|
6509
|
+
}
|
|
6510
|
+
case "gadget_complete": {
|
|
6511
|
+
const mediaOutputs = event.storedMedia?.map((m) => ({
|
|
6512
|
+
kind: m.kind,
|
|
6513
|
+
path: m.path,
|
|
6514
|
+
mimeType: m.mimeType,
|
|
6515
|
+
description: m.description
|
|
6516
|
+
}));
|
|
6517
|
+
this.callbacks.onCompleteGadget(event.invocationId, {
|
|
6518
|
+
result: event.result,
|
|
6519
|
+
executionTimeMs: event.executionTimeMs,
|
|
6520
|
+
cost: event.cost,
|
|
6521
|
+
mediaOutputs
|
|
6522
|
+
});
|
|
6523
|
+
break;
|
|
6524
|
+
}
|
|
6525
|
+
case "gadget_error": {
|
|
6526
|
+
this.callbacks.onCompleteGadget(event.invocationId, {
|
|
6527
|
+
error: event.error,
|
|
6528
|
+
executionTimeMs: event.executionTimeMs
|
|
6529
|
+
});
|
|
6530
|
+
break;
|
|
6531
|
+
}
|
|
6532
|
+
case "gadget_skipped": {
|
|
6533
|
+
this.callbacks.onSkipGadget(event.invocationId, `Skipped: ${event.failedDependencyError}`);
|
|
6534
|
+
break;
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6537
|
+
}
|
|
6538
|
+
};
|
|
6539
|
+
|
|
5859
6540
|
// src/tui/block-renderer.ts
|
|
5860
6541
|
var BlockRenderer = class {
|
|
5861
6542
|
container;
|
|
@@ -5865,6 +6546,8 @@ var BlockRenderer = class {
|
|
|
5865
6546
|
nodeStore;
|
|
5866
6547
|
/** Scroll manager — manages scroll position, follow mode, and bottom alignment */
|
|
5867
6548
|
scrollManager;
|
|
6549
|
+
/** Tree bridge — manages ExecutionTree subscriptions and tree→block mapping */
|
|
6550
|
+
treeBridge;
|
|
5868
6551
|
/** Rendered blocks with UI state */
|
|
5869
6552
|
blocks = /* @__PURE__ */ new Map();
|
|
5870
6553
|
/** IDs of selectable blocks in display order */
|
|
@@ -5894,6 +6577,21 @@ var BlockRenderer = class {
|
|
|
5894
6577
|
getBlock: (id) => this.blocks.get(id),
|
|
5895
6578
|
getSelectedBlock: () => this.getSelectedBlock()
|
|
5896
6579
|
});
|
|
6580
|
+
this.treeBridge = new TreeBridge({
|
|
6581
|
+
onResetThinking: () => this.nodeStore.resetCurrentThinking(),
|
|
6582
|
+
onSetCurrentLLMCall: (llmCallId) => this.setCurrentLLMCall(llmCallId),
|
|
6583
|
+
onClearIdempotencyMaps: () => this.nodeStore.clearIdempotencyMaps(),
|
|
6584
|
+
onAddLLMCall: (iteration, model, parentGadgetId, isNested) => this.addLLMCall(iteration, model, parentGadgetId, isNested),
|
|
6585
|
+
onCompleteLLMCall: (id, details, rawResponse) => this.completeLLMCall(id, details, rawResponse),
|
|
6586
|
+
onSetLLMCallRequest: (id, messages) => this.setLLMCallRequest(id, messages),
|
|
6587
|
+
onSetLLMCallResponse: (id, rawResponse) => this.setLLMCallResponse(id, rawResponse),
|
|
6588
|
+
onCompleteThinking: () => this.completeThinking(),
|
|
6589
|
+
onAddThinking: (content, thinkingType) => this.addThinking(content, thinkingType),
|
|
6590
|
+
onAddGadget: (invocationId, name, parameters) => this.addGadget(invocationId, name, parameters),
|
|
6591
|
+
onCompleteGadget: (invocationId, options) => this.completeGadget(invocationId, options),
|
|
6592
|
+
onSkipGadget: (invocationId, reason) => this.skipGadget(invocationId, reason),
|
|
6593
|
+
onGetCurrentLLMCallId: () => this.getCurrentLLMCallId()
|
|
6594
|
+
});
|
|
5897
6595
|
}
|
|
5898
6596
|
// ───────────────────────────────────────────────────────────────────────────
|
|
5899
6597
|
// Public API - Node Management
|
|
@@ -5939,6 +6637,12 @@ var BlockRenderer = class {
|
|
|
5939
6637
|
completeGadget(invocationId, options = {}) {
|
|
5940
6638
|
this.nodeStore.completeGadget(invocationId, options);
|
|
5941
6639
|
}
|
|
6640
|
+
/**
|
|
6641
|
+
* Mark a gadget as skipped with a rendered reason.
|
|
6642
|
+
*/
|
|
6643
|
+
skipGadget(invocationId, reason) {
|
|
6644
|
+
this.nodeStore.completeGadget(invocationId, { error: reason });
|
|
6645
|
+
}
|
|
5942
6646
|
/**
|
|
5943
6647
|
* Add a text node (flows between LLM calls).
|
|
5944
6648
|
*/
|
|
@@ -6006,9 +6710,8 @@ var BlockRenderer = class {
|
|
|
6006
6710
|
/**
|
|
6007
6711
|
* Get the current LLM call ID.
|
|
6008
6712
|
*
|
|
6009
|
-
* In tree mode,
|
|
6010
|
-
*
|
|
6011
|
-
* determined by event.parentId in handleTreeEvent(), not by this method.
|
|
6713
|
+
* In tree mode, TreeBridge uses this to restore the current parent context
|
|
6714
|
+
* while translating gadget events into renderer updates.
|
|
6012
6715
|
*/
|
|
6013
6716
|
getCurrentLLMCallId() {
|
|
6014
6717
|
return this.nodeStore.currentLLMCallId;
|
|
@@ -6018,7 +6721,7 @@ var BlockRenderer = class {
|
|
|
6018
6721
|
* When active, external code should skip block creation (tree handles it).
|
|
6019
6722
|
*/
|
|
6020
6723
|
isTreeSubscribed() {
|
|
6021
|
-
return this.
|
|
6724
|
+
return this.treeBridge.isSubscribed();
|
|
6022
6725
|
}
|
|
6023
6726
|
/**
|
|
6024
6727
|
* Store raw response for an LLM call (enrichment only).
|
|
@@ -6189,22 +6892,7 @@ var BlockRenderer = class {
|
|
|
6189
6892
|
* Called when nodes are added/removed.
|
|
6190
6893
|
*/
|
|
6191
6894
|
rebuildBlocks() {
|
|
6192
|
-
|
|
6193
|
-
child.detach();
|
|
6194
|
-
}
|
|
6195
|
-
this.container.setContent("");
|
|
6196
|
-
this.blocks.clear();
|
|
6197
|
-
this.selectableIds = [];
|
|
6198
|
-
let top = 0;
|
|
6199
|
-
for (const rootId of this.nodeStore.rootIds) {
|
|
6200
|
-
top = this.renderNodeTree(rootId, top);
|
|
6201
|
-
}
|
|
6202
|
-
if (this.selectedIndex >= this.selectableIds.length) {
|
|
6203
|
-
this.selectedIndex = this.selectableIds.length - 1;
|
|
6204
|
-
}
|
|
6205
|
-
this.applyBottomAlignmentAndScroll();
|
|
6206
|
-
this.renderCallback();
|
|
6207
|
-
this.notifyHasContentChange();
|
|
6895
|
+
this.rebuildBlocksCore(false);
|
|
6208
6896
|
}
|
|
6209
6897
|
/**
|
|
6210
6898
|
* Render a node and its children recursively.
|
|
@@ -6218,64 +6906,29 @@ var BlockRenderer = class {
|
|
|
6218
6906
|
* (no headers, just content) for a clean chat-like experience.
|
|
6219
6907
|
*/
|
|
6220
6908
|
renderNodeTree(nodeId, top) {
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
this.
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
}
|
|
6238
|
-
}
|
|
6239
|
-
return top;
|
|
6240
|
-
}
|
|
6241
|
-
/**
|
|
6242
|
-
* Check if a gadget should render as plain text in focused mode.
|
|
6243
|
-
* TellUser, AskUser, and Finish render as text for a chat-like experience.
|
|
6244
|
-
*/
|
|
6245
|
-
shouldRenderAsText(node) {
|
|
6246
|
-
if (this.contentFilterMode !== "focused") return false;
|
|
6247
|
-
if (node.type !== "gadget") return false;
|
|
6248
|
-
const name = node.name;
|
|
6249
|
-
return name === "TellUser" || name === "AskUser" || name === "Finish";
|
|
6909
|
+
return traverseNodeTree(
|
|
6910
|
+
nodeId,
|
|
6911
|
+
(id) => this.getNode(id),
|
|
6912
|
+
(currentNodeId, node, currentTop) => {
|
|
6913
|
+
if (!this.isNodeVisible(node)) {
|
|
6914
|
+
return currentTop;
|
|
6915
|
+
}
|
|
6916
|
+
const block = shouldRenderAsText(node, this.contentFilterMode) ? this.createTextLikeBlock(node, currentTop) : this.createBlock(node, currentTop);
|
|
6917
|
+
this.blocks.set(currentNodeId, block);
|
|
6918
|
+
if (block.selectable) {
|
|
6919
|
+
this.selectableIds.push(currentNodeId);
|
|
6920
|
+
}
|
|
6921
|
+
return currentTop + getBlockHeight(block);
|
|
6922
|
+
},
|
|
6923
|
+
top
|
|
6924
|
+
);
|
|
6250
6925
|
}
|
|
6251
6926
|
/**
|
|
6252
6927
|
* Create a text-like block for TellUser/AskUser/Finish gadgets in focused mode.
|
|
6253
6928
|
* Renders just the content without the gadget header.
|
|
6254
6929
|
*/
|
|
6255
6930
|
createTextLikeBlock(node, top) {
|
|
6256
|
-
|
|
6257
|
-
if (node.name === "TellUser") {
|
|
6258
|
-
const message = node.parameters?.message;
|
|
6259
|
-
if (typeof message === "string") {
|
|
6260
|
-
content = `
|
|
6261
|
-
${renderMarkdown(message)}
|
|
6262
|
-
`;
|
|
6263
|
-
}
|
|
6264
|
-
} else if (node.name === "AskUser") {
|
|
6265
|
-
const question = node.parameters?.question;
|
|
6266
|
-
if (typeof question === "string") {
|
|
6267
|
-
content = `
|
|
6268
|
-
? ${question}
|
|
6269
|
-
`;
|
|
6270
|
-
}
|
|
6271
|
-
} else if (node.name === "Finish") {
|
|
6272
|
-
const message = node.parameters?.message;
|
|
6273
|
-
if (typeof message === "string" && message.trim()) {
|
|
6274
|
-
content = `
|
|
6275
|
-
\x1B[32m\u2713\x1B[0m ${renderMarkdown(message)}
|
|
6276
|
-
`;
|
|
6277
|
-
}
|
|
6278
|
-
}
|
|
6931
|
+
const content = formatGadgetAsText(node);
|
|
6279
6932
|
const box = new Box({
|
|
6280
6933
|
parent: this.container,
|
|
6281
6934
|
top,
|
|
@@ -6300,7 +6953,7 @@ ${renderMarkdown(message)}
|
|
|
6300
6953
|
const isSelected = this.selectableIds.length === this.selectedIndex;
|
|
6301
6954
|
const selectable = node.type !== "text" || !node.id.startsWith("user_");
|
|
6302
6955
|
const expanded = this.expandedStates.get(node.id) ?? false;
|
|
6303
|
-
const content =
|
|
6956
|
+
const content = formatBlockContent(node, isSelected, expanded);
|
|
6304
6957
|
const box = new Box({
|
|
6305
6958
|
parent: this.container,
|
|
6306
6959
|
top,
|
|
@@ -6318,133 +6971,6 @@ ${renderMarkdown(message)}
|
|
|
6318
6971
|
selectable
|
|
6319
6972
|
};
|
|
6320
6973
|
}
|
|
6321
|
-
/**
|
|
6322
|
-
* Format block content based on type and state.
|
|
6323
|
-
*/
|
|
6324
|
-
formatBlockContent(node, selected, expanded) {
|
|
6325
|
-
const indent = getIndent(node.depth);
|
|
6326
|
-
switch (node.type) {
|
|
6327
|
-
case "llm_call": {
|
|
6328
|
-
const collapsed = formatLLMCallCollapsed(node, selected);
|
|
6329
|
-
if (!expanded) {
|
|
6330
|
-
return indent + collapsed;
|
|
6331
|
-
}
|
|
6332
|
-
const expandedLines = formatLLMCallExpanded(node);
|
|
6333
|
-
const contIndent = getContinuationIndent(node.depth);
|
|
6334
|
-
return [indent + collapsed, ...expandedLines.map((line) => contIndent + line)].join("\n");
|
|
6335
|
-
}
|
|
6336
|
-
case "gadget": {
|
|
6337
|
-
const collapsed = formatGadgetCollapsed(node, selected);
|
|
6338
|
-
if (!expanded) {
|
|
6339
|
-
return indent + collapsed;
|
|
6340
|
-
}
|
|
6341
|
-
const expandedLines = formatGadgetExpanded(node);
|
|
6342
|
-
const contIndent = getContinuationIndent(node.depth);
|
|
6343
|
-
return [indent + collapsed, ...expandedLines.map((line) => contIndent + line)].join("\n");
|
|
6344
|
-
}
|
|
6345
|
-
case "text": {
|
|
6346
|
-
if (node.id.startsWith("user_")) {
|
|
6347
|
-
return formatUserMessage(node.content);
|
|
6348
|
-
}
|
|
6349
|
-
const fullContent = renderMarkdown(node.content);
|
|
6350
|
-
if (expanded) {
|
|
6351
|
-
return `
|
|
6352
|
-
${fullContent}
|
|
6353
|
-
`;
|
|
6354
|
-
}
|
|
6355
|
-
return this.abbreviateToLines(fullContent, 2, selected);
|
|
6356
|
-
}
|
|
6357
|
-
case "thinking": {
|
|
6358
|
-
const DIM2 = "\x1B[2m";
|
|
6359
|
-
const RED_DIM = "\x1B[2;31m";
|
|
6360
|
-
const RESET3 = "\x1B[0m";
|
|
6361
|
-
const contIndent = getContinuationIndent(node.depth);
|
|
6362
|
-
if (node.thinkingType === "redacted") {
|
|
6363
|
-
const header2 = `${indent}${RED_DIM}\u{1F512} [Redacted thinking block]${RESET3}`;
|
|
6364
|
-
return header2;
|
|
6365
|
-
}
|
|
6366
|
-
if (!expanded) {
|
|
6367
|
-
const firstLine = node.content.split("\n")[0]?.slice(0, 60) ?? "";
|
|
6368
|
-
const suffix = node.isComplete ? "" : "...";
|
|
6369
|
-
return `${indent}${DIM2}\u{1F4AD} Thinking${suffix} ${firstLine}${RESET3}`;
|
|
6370
|
-
}
|
|
6371
|
-
const tokenInfo = node.isComplete ? ` (${Math.ceil(node.content.length / 4)} tokens est.)` : "";
|
|
6372
|
-
const header = `${indent}${DIM2}\u25BC \u{1F4AD} Thinking${tokenInfo}${RESET3}`;
|
|
6373
|
-
const contentLines = node.content.split("\n").map((line) => `${contIndent}${DIM2}${line}${RESET3}`);
|
|
6374
|
-
return [header, ...contentLines].join("\n");
|
|
6375
|
-
}
|
|
6376
|
-
case "system_message": {
|
|
6377
|
-
const icon = this.getSystemMessageIcon(node.category);
|
|
6378
|
-
const color = this.getSystemMessageColor(node.category);
|
|
6379
|
-
const RESET3 = "\x1B[0m";
|
|
6380
|
-
return `${indent}${color}${icon} ${node.message}${RESET3}`;
|
|
6381
|
-
}
|
|
6382
|
-
}
|
|
6383
|
-
}
|
|
6384
|
-
/**
|
|
6385
|
-
* Get icon for system message category.
|
|
6386
|
-
*/
|
|
6387
|
-
getSystemMessageIcon(category) {
|
|
6388
|
-
switch (category) {
|
|
6389
|
-
case "throttle":
|
|
6390
|
-
return "\u23F8";
|
|
6391
|
-
case "retry":
|
|
6392
|
-
return "\u{1F504}";
|
|
6393
|
-
case "info":
|
|
6394
|
-
return "\u2139\uFE0F";
|
|
6395
|
-
case "warning":
|
|
6396
|
-
return "\u26A0\uFE0F";
|
|
6397
|
-
case "error":
|
|
6398
|
-
return "\u274C";
|
|
6399
|
-
}
|
|
6400
|
-
}
|
|
6401
|
-
/**
|
|
6402
|
-
* Get ANSI color code for system message category.
|
|
6403
|
-
*/
|
|
6404
|
-
getSystemMessageColor(category) {
|
|
6405
|
-
const YELLOW2 = "\x1B[33m";
|
|
6406
|
-
const BLUE = "\x1B[34m";
|
|
6407
|
-
const GRAY2 = "\x1B[90m";
|
|
6408
|
-
const RED2 = "\x1B[31m";
|
|
6409
|
-
switch (category) {
|
|
6410
|
-
case "throttle":
|
|
6411
|
-
return YELLOW2;
|
|
6412
|
-
case "retry":
|
|
6413
|
-
return BLUE;
|
|
6414
|
-
case "info":
|
|
6415
|
-
return GRAY2;
|
|
6416
|
-
case "warning":
|
|
6417
|
-
return YELLOW2;
|
|
6418
|
-
case "error":
|
|
6419
|
-
return RED2;
|
|
6420
|
-
}
|
|
6421
|
-
}
|
|
6422
|
-
/**
|
|
6423
|
-
* Abbreviate text content to a maximum number of lines.
|
|
6424
|
-
* Shows truncation indicator if content exceeds limit.
|
|
6425
|
-
*
|
|
6426
|
-
* @param text - The text to abbreviate
|
|
6427
|
-
* @param maxLines - Maximum number of lines to show
|
|
6428
|
-
* @param selected - Whether this block is selected (for indicator styling)
|
|
6429
|
-
* @returns Abbreviated text with truncation indicator if needed
|
|
6430
|
-
*/
|
|
6431
|
-
abbreviateToLines(text3, maxLines, selected) {
|
|
6432
|
-
const lines = text3.split("\n");
|
|
6433
|
-
let startIndex = 0;
|
|
6434
|
-
while (startIndex < lines.length && lines[startIndex].trim() === "") {
|
|
6435
|
-
startIndex++;
|
|
6436
|
-
}
|
|
6437
|
-
const contentLines = lines.slice(startIndex);
|
|
6438
|
-
if (contentLines.length <= maxLines) {
|
|
6439
|
-
return `
|
|
6440
|
-
${contentLines.join("\n")}`;
|
|
6441
|
-
}
|
|
6442
|
-
const truncatedLines = contentLines.slice(0, maxLines);
|
|
6443
|
-
const indicator = selected ? "\u25B6 ..." : " ...";
|
|
6444
|
-
return `
|
|
6445
|
-
${truncatedLines.join("\n")}
|
|
6446
|
-
${indicator}`;
|
|
6447
|
-
}
|
|
6448
6974
|
/**
|
|
6449
6975
|
* Update a single block (after state change).
|
|
6450
6976
|
*/
|
|
@@ -6453,7 +6979,7 @@ ${indicator}`;
|
|
|
6453
6979
|
const node = this.getNode(nodeId);
|
|
6454
6980
|
if (!block || !node) return;
|
|
6455
6981
|
const isSelected = this.selectableIds[this.selectedIndex] === nodeId;
|
|
6456
|
-
const content =
|
|
6982
|
+
const content = formatBlockContent(node, isSelected, block.expanded);
|
|
6457
6983
|
const oldHeight = getBlockHeight(block);
|
|
6458
6984
|
block.box.setContent(content);
|
|
6459
6985
|
const newHeight = content.split("\n").length;
|
|
@@ -6471,7 +6997,7 @@ ${indicator}`;
|
|
|
6471
6997
|
const block = this.blocks.get(id);
|
|
6472
6998
|
if (block) {
|
|
6473
6999
|
const isSelected = this.selectableIds[this.selectedIndex] === id;
|
|
6474
|
-
const content =
|
|
7000
|
+
const content = formatBlockContent(block.node, isSelected, block.expanded);
|
|
6475
7001
|
block.box.setContent(content);
|
|
6476
7002
|
}
|
|
6477
7003
|
}
|
|
@@ -6486,20 +7012,19 @@ ${indicator}`;
|
|
|
6486
7012
|
this.scrollManager.repositionBlocks((rootId, top) => this.repositionNodeTree(rootId, top));
|
|
6487
7013
|
}
|
|
6488
7014
|
repositionNodeTree(nodeId, top) {
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
return top;
|
|
7015
|
+
return traverseNodeTree(
|
|
7016
|
+
nodeId,
|
|
7017
|
+
(id) => this.getNode(id),
|
|
7018
|
+
(currentNodeId, _node, currentTop) => {
|
|
7019
|
+
const block = this.blocks.get(currentNodeId);
|
|
7020
|
+
if (!block) {
|
|
7021
|
+
return currentTop;
|
|
7022
|
+
}
|
|
7023
|
+
block.box.top = currentTop;
|
|
7024
|
+
return currentTop + getBlockHeight(block);
|
|
7025
|
+
},
|
|
7026
|
+
top
|
|
7027
|
+
);
|
|
6503
7028
|
}
|
|
6504
7029
|
/**
|
|
6505
7030
|
* Scroll container to keep selected block visible.
|
|
@@ -6566,13 +7091,25 @@ ${indicator}`;
|
|
|
6566
7091
|
* Used for mode switches where we need to ensure the screen is fully cleared.
|
|
6567
7092
|
*/
|
|
6568
7093
|
rebuildBlocksImmediate() {
|
|
7094
|
+
this.rebuildBlocksCore(true);
|
|
7095
|
+
}
|
|
7096
|
+
/**
|
|
7097
|
+
* Shared implementation for rebuildBlocks and rebuildBlocksImmediate.
|
|
7098
|
+
*
|
|
7099
|
+
* @param immediate - When true, forces a synchronous render pass before
|
|
7100
|
+
* and after building blocks to clear visual artifacts (used on mode switch).
|
|
7101
|
+
* When false, defers a single render to the end via renderCallback.
|
|
7102
|
+
*/
|
|
7103
|
+
rebuildBlocksCore(immediate) {
|
|
6569
7104
|
for (const child of [...this.container.children]) {
|
|
6570
7105
|
child.detach();
|
|
6571
7106
|
}
|
|
6572
7107
|
this.container.setContent("");
|
|
6573
7108
|
this.blocks.clear();
|
|
6574
7109
|
this.selectableIds = [];
|
|
6575
|
-
|
|
7110
|
+
if (immediate) {
|
|
7111
|
+
this.renderNowCallback();
|
|
7112
|
+
}
|
|
6576
7113
|
let top = 0;
|
|
6577
7114
|
for (const rootId of this.nodeStore.rootIds) {
|
|
6578
7115
|
top = this.renderNodeTree(rootId, top);
|
|
@@ -6581,7 +7118,11 @@ ${indicator}`;
|
|
|
6581
7118
|
this.selectedIndex = this.selectableIds.length - 1;
|
|
6582
7119
|
}
|
|
6583
7120
|
this.applyBottomAlignmentAndScroll();
|
|
6584
|
-
|
|
7121
|
+
if (immediate) {
|
|
7122
|
+
this.renderNowCallback();
|
|
7123
|
+
} else {
|
|
7124
|
+
this.renderCallback();
|
|
7125
|
+
}
|
|
6585
7126
|
this.notifyHasContentChange();
|
|
6586
7127
|
}
|
|
6587
7128
|
/**
|
|
@@ -6593,38 +7134,15 @@ ${indicator}`;
|
|
|
6593
7134
|
/**
|
|
6594
7135
|
* Check if a node should be visible in the current content filter mode.
|
|
6595
7136
|
*
|
|
6596
|
-
* In focused mode
|
|
6597
|
-
*
|
|
6598
|
-
* - LLM call blocks are hidden
|
|
6599
|
-
* - Gadget blocks are hidden EXCEPT TellUser and AskUser
|
|
6600
|
-
* (Finish is hidden - status bar indicates completion)
|
|
7137
|
+
* In focused mode, only text and user-facing gadgets remain visible.
|
|
7138
|
+
* TellUser, AskUser, and Finish render as plain text for a chat-like view.
|
|
6601
7139
|
*/
|
|
6602
7140
|
isNodeVisible(node) {
|
|
6603
|
-
|
|
6604
|
-
return true;
|
|
6605
|
-
}
|
|
6606
|
-
switch (node.type) {
|
|
6607
|
-
case "text":
|
|
6608
|
-
return true;
|
|
6609
|
-
case "llm_call":
|
|
6610
|
-
return false;
|
|
6611
|
-
case "thinking":
|
|
6612
|
-
return false;
|
|
6613
|
-
case "gadget": {
|
|
6614
|
-
const name = node.name;
|
|
6615
|
-
return name === "TellUser" || name === "AskUser" || name === "Finish";
|
|
6616
|
-
}
|
|
6617
|
-
default:
|
|
6618
|
-
return false;
|
|
6619
|
-
}
|
|
7141
|
+
return isNodeVisibleInFilterMode(node, this.contentFilterMode);
|
|
6620
7142
|
}
|
|
6621
7143
|
// ───────────────────────────────────────────────────────────────────────────
|
|
6622
7144
|
// ExecutionTree Integration
|
|
6623
7145
|
// ───────────────────────────────────────────────────────────────────────────
|
|
6624
|
-
/** Unsubscribe function for tree events */
|
|
6625
|
-
treeUnsubscribe = null;
|
|
6626
|
-
/** Map tree node IDs to block node IDs */
|
|
6627
|
-
treeNodeToBlockId = /* @__PURE__ */ new Map();
|
|
6628
7146
|
/**
|
|
6629
7147
|
* Subscribe to an ExecutionTree for automatic block updates.
|
|
6630
7148
|
*
|
|
@@ -6648,121 +7166,14 @@ ${indicator}`;
|
|
|
6648
7166
|
* ```
|
|
6649
7167
|
*/
|
|
6650
7168
|
subscribeToTree(tree) {
|
|
6651
|
-
|
|
6652
|
-
this.treeUnsubscribe();
|
|
6653
|
-
}
|
|
6654
|
-
this.treeNodeToBlockId.clear();
|
|
6655
|
-
this.nodeStore.clearIdempotencyMaps();
|
|
6656
|
-
this.treeUnsubscribe = tree.onAll((event) => {
|
|
6657
|
-
this.handleTreeEvent(event, tree);
|
|
6658
|
-
});
|
|
6659
|
-
return () => {
|
|
6660
|
-
if (this.treeUnsubscribe) {
|
|
6661
|
-
this.treeUnsubscribe();
|
|
6662
|
-
this.treeUnsubscribe = null;
|
|
6663
|
-
}
|
|
6664
|
-
};
|
|
6665
|
-
}
|
|
6666
|
-
/**
|
|
6667
|
-
* Handle an ExecutionTree event.
|
|
6668
|
-
*/
|
|
6669
|
-
handleTreeEvent(event, tree) {
|
|
6670
|
-
switch (event.type) {
|
|
6671
|
-
case "llm_call_start": {
|
|
6672
|
-
this.nodeStore.resetCurrentThinking();
|
|
6673
|
-
let parentBlockId;
|
|
6674
|
-
if (event.parentId) {
|
|
6675
|
-
parentBlockId = this.treeNodeToBlockId.get(event.parentId);
|
|
6676
|
-
}
|
|
6677
|
-
const blockId = this.addLLMCall(
|
|
6678
|
-
event.iteration + 1,
|
|
6679
|
-
event.model,
|
|
6680
|
-
parentBlockId,
|
|
6681
|
-
event.depth > 0
|
|
6682
|
-
);
|
|
6683
|
-
this.treeNodeToBlockId.set(event.nodeId, blockId);
|
|
6684
|
-
const startNode = tree.getNode(event.nodeId);
|
|
6685
|
-
if (startNode?.type === "llm_call" && startNode.request) {
|
|
6686
|
-
this.setLLMCallRequest(blockId, startNode.request);
|
|
6687
|
-
}
|
|
6688
|
-
break;
|
|
6689
|
-
}
|
|
6690
|
-
case "llm_call_complete": {
|
|
6691
|
-
this.completeThinking();
|
|
6692
|
-
const blockId = this.treeNodeToBlockId.get(event.nodeId);
|
|
6693
|
-
if (blockId) {
|
|
6694
|
-
this.completeLLMCall(blockId, {
|
|
6695
|
-
inputTokens: event.usage?.inputTokens,
|
|
6696
|
-
cachedInputTokens: event.usage?.cachedInputTokens,
|
|
6697
|
-
outputTokens: event.usage?.outputTokens,
|
|
6698
|
-
reasoningTokens: event.usage?.reasoningTokens,
|
|
6699
|
-
cost: event.cost,
|
|
6700
|
-
finishReason: event.finishReason ?? void 0
|
|
6701
|
-
});
|
|
6702
|
-
const completeNode = tree.getNode(event.nodeId);
|
|
6703
|
-
if (completeNode?.type === "llm_call" && completeNode.response) {
|
|
6704
|
-
this.setLLMCallResponse(blockId, completeNode.response);
|
|
6705
|
-
}
|
|
6706
|
-
}
|
|
6707
|
-
break;
|
|
6708
|
-
}
|
|
6709
|
-
case "thinking": {
|
|
6710
|
-
this.addThinking(event.content, event.thinkingType);
|
|
6711
|
-
break;
|
|
6712
|
-
}
|
|
6713
|
-
case "gadget_call": {
|
|
6714
|
-
let parentBlockId;
|
|
6715
|
-
if (event.parentId) {
|
|
6716
|
-
parentBlockId = this.treeNodeToBlockId.get(event.parentId);
|
|
6717
|
-
}
|
|
6718
|
-
const previousLLMCallId = this.getCurrentLLMCallId();
|
|
6719
|
-
if (parentBlockId) {
|
|
6720
|
-
this.setCurrentLLMCall(parentBlockId);
|
|
6721
|
-
}
|
|
6722
|
-
const blockId = this.addGadget(event.invocationId, event.name, event.parameters);
|
|
6723
|
-
this.treeNodeToBlockId.set(event.nodeId, blockId);
|
|
6724
|
-
this.setCurrentLLMCall(previousLLMCallId);
|
|
6725
|
-
break;
|
|
6726
|
-
}
|
|
6727
|
-
case "gadget_complete": {
|
|
6728
|
-
const mediaOutputs = event.storedMedia?.map((m) => ({
|
|
6729
|
-
kind: m.kind,
|
|
6730
|
-
path: m.path,
|
|
6731
|
-
mimeType: m.mimeType,
|
|
6732
|
-
description: m.description
|
|
6733
|
-
}));
|
|
6734
|
-
this.completeGadget(event.invocationId, {
|
|
6735
|
-
result: event.result,
|
|
6736
|
-
executionTimeMs: event.executionTimeMs,
|
|
6737
|
-
cost: event.cost,
|
|
6738
|
-
mediaOutputs
|
|
6739
|
-
});
|
|
6740
|
-
break;
|
|
6741
|
-
}
|
|
6742
|
-
case "gadget_error": {
|
|
6743
|
-
this.completeGadget(event.invocationId, {
|
|
6744
|
-
error: event.error,
|
|
6745
|
-
executionTimeMs: event.executionTimeMs
|
|
6746
|
-
});
|
|
6747
|
-
break;
|
|
6748
|
-
}
|
|
6749
|
-
case "gadget_skipped": {
|
|
6750
|
-
const node = this.findGadgetByInvocationId(event.invocationId);
|
|
6751
|
-
if (node) {
|
|
6752
|
-
node.isComplete = true;
|
|
6753
|
-
node.error = `Skipped: ${event.failedDependencyError}`;
|
|
6754
|
-
this.updateBlock(node.id);
|
|
6755
|
-
}
|
|
6756
|
-
break;
|
|
6757
|
-
}
|
|
6758
|
-
}
|
|
7169
|
+
return this.treeBridge.subscribeToTree(tree);
|
|
6759
7170
|
}
|
|
6760
7171
|
/**
|
|
6761
7172
|
* Get block ID for a tree node ID.
|
|
6762
7173
|
* Useful for external code that needs to correlate tree nodes with blocks.
|
|
6763
7174
|
*/
|
|
6764
7175
|
getBlockIdForTreeNode(treeNodeId) {
|
|
6765
|
-
return this.
|
|
7176
|
+
return this.treeBridge.getBlockIdForTreeNode(treeNodeId);
|
|
6766
7177
|
}
|
|
6767
7178
|
};
|
|
6768
7179
|
|
|
@@ -7055,6 +7466,26 @@ var TUIController = class {
|
|
|
7055
7466
|
}
|
|
7056
7467
|
};
|
|
7057
7468
|
|
|
7469
|
+
// src/tui/event-router.ts
|
|
7470
|
+
var EventRouter = class {
|
|
7471
|
+
constructor(blockRenderer) {
|
|
7472
|
+
this.blockRenderer = blockRenderer;
|
|
7473
|
+
}
|
|
7474
|
+
/**
|
|
7475
|
+
* Route an agent stream event to the appropriate renderer method.
|
|
7476
|
+
*
|
|
7477
|
+
* Only handles text/thinking events - gadgets and LLM calls are managed
|
|
7478
|
+
* via ExecutionTree subscription in TreeSubscriptionManager.
|
|
7479
|
+
*/
|
|
7480
|
+
handleEvent(event) {
|
|
7481
|
+
if (event.type === "text") {
|
|
7482
|
+
this.blockRenderer.addText(event.content);
|
|
7483
|
+
} else if (event.type === "thinking") {
|
|
7484
|
+
this.blockRenderer.addThinking(event.content, event.thinkingType);
|
|
7485
|
+
}
|
|
7486
|
+
}
|
|
7487
|
+
};
|
|
7488
|
+
|
|
7058
7489
|
// src/tui/hints-bar.ts
|
|
7059
7490
|
var GRAY = "\x1B[90m";
|
|
7060
7491
|
var RESET = "\x1B[0m";
|
|
@@ -7137,10 +7568,10 @@ var HintsBar = class {
|
|
|
7137
7568
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7138
7569
|
import { readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
7139
7570
|
import { tmpdir } from "os";
|
|
7140
|
-
import { join as
|
|
7571
|
+
import { join as join3 } from "path";
|
|
7141
7572
|
function openEditorSync(initialContent = "") {
|
|
7142
7573
|
const editor = process.env.VISUAL || process.env.EDITOR || "vi";
|
|
7143
|
-
const tmpFile =
|
|
7574
|
+
const tmpFile = join3(tmpdir(), `llmist-input-${Date.now()}.txt`);
|
|
7144
7575
|
writeFileSync2(tmpFile, initialContent, "utf-8");
|
|
7145
7576
|
try {
|
|
7146
7577
|
const parts = editor.split(/\s+/);
|
|
@@ -7375,11 +7806,11 @@ var InputHandler = class {
|
|
|
7375
7806
|
* @returns Promise that resolves with user's response
|
|
7376
7807
|
*/
|
|
7377
7808
|
async waitForInput(question, gadgetName) {
|
|
7378
|
-
return new Promise((
|
|
7809
|
+
return new Promise((resolve4, reject) => {
|
|
7379
7810
|
this.pendingInput = {
|
|
7380
7811
|
question,
|
|
7381
7812
|
gadgetName,
|
|
7382
|
-
resolve:
|
|
7813
|
+
resolve: resolve4,
|
|
7383
7814
|
reject
|
|
7384
7815
|
};
|
|
7385
7816
|
this.setActive();
|
|
@@ -7394,11 +7825,11 @@ var InputHandler = class {
|
|
|
7394
7825
|
* @returns Promise that resolves with user's prompt
|
|
7395
7826
|
*/
|
|
7396
7827
|
async waitForPrompt() {
|
|
7397
|
-
return new Promise((
|
|
7828
|
+
return new Promise((resolve4, reject) => {
|
|
7398
7829
|
this.pendingInput = {
|
|
7399
7830
|
question: "",
|
|
7400
7831
|
gadgetName: "prompt",
|
|
7401
|
-
resolve:
|
|
7832
|
+
resolve: resolve4,
|
|
7402
7833
|
reject
|
|
7403
7834
|
};
|
|
7404
7835
|
this.setPendingPrompt();
|
|
@@ -7494,10 +7925,10 @@ var InputHandler = class {
|
|
|
7494
7925
|
return;
|
|
7495
7926
|
}
|
|
7496
7927
|
if (this.pendingInput) {
|
|
7497
|
-
const { resolve:
|
|
7928
|
+
const { resolve: resolve4 } = this.pendingInput;
|
|
7498
7929
|
this.pendingInput = null;
|
|
7499
7930
|
this.setIdle();
|
|
7500
|
-
|
|
7931
|
+
resolve4(value);
|
|
7501
7932
|
} else if (this.midSessionHandler) {
|
|
7502
7933
|
this.midSessionHandler(value);
|
|
7503
7934
|
this.setIdle();
|
|
@@ -7657,6 +8088,104 @@ var InputHandler = class {
|
|
|
7657
8088
|
}
|
|
7658
8089
|
};
|
|
7659
8090
|
|
|
8091
|
+
// src/tui/key-action-handler.ts
|
|
8092
|
+
var KeyActionHandler = class {
|
|
8093
|
+
constructor(controller, blockRenderer, statusBar, screenCtx, modalManager, layout) {
|
|
8094
|
+
this.controller = controller;
|
|
8095
|
+
this.blockRenderer = blockRenderer;
|
|
8096
|
+
this.statusBar = statusBar;
|
|
8097
|
+
this.screenCtx = screenCtx;
|
|
8098
|
+
this.modalManager = modalManager;
|
|
8099
|
+
this.layout = layout;
|
|
8100
|
+
}
|
|
8101
|
+
/**
|
|
8102
|
+
* Handle high-level keyboard actions from KeyboardManager or InputHandler.
|
|
8103
|
+
*/
|
|
8104
|
+
handleKeyAction(action) {
|
|
8105
|
+
switch (action.type) {
|
|
8106
|
+
case "ctrl_c": {
|
|
8107
|
+
const result = this.controller.handleCtrlC();
|
|
8108
|
+
if (result === "show_hint") {
|
|
8109
|
+
this.blockRenderer.addText("\n[Press Ctrl+C again to quit]\n");
|
|
8110
|
+
} else if (result === "quit") {
|
|
8111
|
+
process.exit(130);
|
|
8112
|
+
}
|
|
8113
|
+
break;
|
|
8114
|
+
}
|
|
8115
|
+
case "cancel":
|
|
8116
|
+
this.controller.triggerCancel();
|
|
8117
|
+
this.controller.abort();
|
|
8118
|
+
break;
|
|
8119
|
+
case "toggle_focus_mode":
|
|
8120
|
+
this.controller.toggleFocusMode();
|
|
8121
|
+
break;
|
|
8122
|
+
case "toggle_content_filter":
|
|
8123
|
+
this.controller.toggleContentFilterMode();
|
|
8124
|
+
break;
|
|
8125
|
+
case "cycle_profile":
|
|
8126
|
+
this.statusBar.cycleProfile();
|
|
8127
|
+
break;
|
|
8128
|
+
case "scroll_page": {
|
|
8129
|
+
const body = this.layout.body;
|
|
8130
|
+
if (!body.scroll) return;
|
|
8131
|
+
const containerHeight = body.height;
|
|
8132
|
+
const scrollAmount = Math.max(1, containerHeight - 2);
|
|
8133
|
+
if (action.direction < 0) {
|
|
8134
|
+
body.scroll(-scrollAmount);
|
|
8135
|
+
} else {
|
|
8136
|
+
body.scroll(scrollAmount);
|
|
8137
|
+
}
|
|
8138
|
+
this.blockRenderer.handleUserScroll();
|
|
8139
|
+
this.screenCtx.renderNow();
|
|
8140
|
+
break;
|
|
8141
|
+
}
|
|
8142
|
+
case "scroll_line": {
|
|
8143
|
+
const body = this.layout.body;
|
|
8144
|
+
if (!body.scroll) return;
|
|
8145
|
+
body.scroll(action.direction);
|
|
8146
|
+
this.blockRenderer.handleUserScroll();
|
|
8147
|
+
this.screenCtx.renderNow();
|
|
8148
|
+
break;
|
|
8149
|
+
}
|
|
8150
|
+
case "navigation":
|
|
8151
|
+
switch (action.action) {
|
|
8152
|
+
case "select_next":
|
|
8153
|
+
this.blockRenderer.selectNext();
|
|
8154
|
+
break;
|
|
8155
|
+
case "select_previous":
|
|
8156
|
+
this.blockRenderer.selectPrevious();
|
|
8157
|
+
break;
|
|
8158
|
+
case "select_first":
|
|
8159
|
+
this.blockRenderer.selectFirst();
|
|
8160
|
+
break;
|
|
8161
|
+
case "select_last":
|
|
8162
|
+
this.blockRenderer.selectLast();
|
|
8163
|
+
this.blockRenderer.enableFollowMode();
|
|
8164
|
+
break;
|
|
8165
|
+
case "toggle_expand":
|
|
8166
|
+
this.blockRenderer.toggleExpand();
|
|
8167
|
+
break;
|
|
8168
|
+
case "collapse":
|
|
8169
|
+
this.blockRenderer.collapseOrDeselect();
|
|
8170
|
+
break;
|
|
8171
|
+
}
|
|
8172
|
+
this.screenCtx.renderNow();
|
|
8173
|
+
break;
|
|
8174
|
+
case "raw_viewer":
|
|
8175
|
+
void (async () => {
|
|
8176
|
+
const selected = this.blockRenderer.getSelectedBlock();
|
|
8177
|
+
if (!selected) return;
|
|
8178
|
+
if (!isRawViewerNode(selected.node)) return;
|
|
8179
|
+
await this.modalManager.showRawViewer(
|
|
8180
|
+
this.screenCtx.screen,
|
|
8181
|
+
createRawViewerData(selected.node, action.mode)
|
|
8182
|
+
);
|
|
8183
|
+
})();
|
|
8184
|
+
break;
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8187
|
+
};
|
|
8188
|
+
|
|
7660
8189
|
// src/tui/keymap.ts
|
|
7661
8190
|
var KeyboardManager = class {
|
|
7662
8191
|
config;
|
|
@@ -7857,7 +8386,7 @@ import { Box as Box3 } from "@unblessed/node";
|
|
|
7857
8386
|
var MAX_PREVIEW_LINES = 10;
|
|
7858
8387
|
var MAX_PARAM_VALUE_LENGTH = 60;
|
|
7859
8388
|
function showApprovalDialog(screen, context) {
|
|
7860
|
-
return new Promise((
|
|
8389
|
+
return new Promise((resolve4) => {
|
|
7861
8390
|
const content = buildDialogContent(context);
|
|
7862
8391
|
const dialog = new Box3({
|
|
7863
8392
|
parent: screen,
|
|
@@ -7912,7 +8441,7 @@ function showApprovalDialog(screen, context) {
|
|
|
7912
8441
|
if (response) {
|
|
7913
8442
|
dialog.destroy();
|
|
7914
8443
|
screen.render();
|
|
7915
|
-
|
|
8444
|
+
resolve4(response);
|
|
7916
8445
|
}
|
|
7917
8446
|
};
|
|
7918
8447
|
dialog.on("keypress", handleKey);
|
|
@@ -7982,7 +8511,7 @@ var WHITE = "\x1B[37m";
|
|
|
7982
8511
|
function showRawViewer(options) {
|
|
7983
8512
|
let closeCallback = () => {
|
|
7984
8513
|
};
|
|
7985
|
-
const closed = new Promise((
|
|
8514
|
+
const closed = new Promise((resolve4) => {
|
|
7986
8515
|
const {
|
|
7987
8516
|
screen,
|
|
7988
8517
|
mode,
|
|
@@ -8078,7 +8607,7 @@ ${error}`;
|
|
|
8078
8607
|
helpBar.destroy();
|
|
8079
8608
|
viewer.destroy();
|
|
8080
8609
|
screen.render();
|
|
8081
|
-
|
|
8610
|
+
resolve4();
|
|
8082
8611
|
};
|
|
8083
8612
|
closeCallback = close;
|
|
8084
8613
|
viewer.key(["escape", "q"], close);
|
|
@@ -8354,6 +8883,47 @@ function createScreen(options) {
|
|
|
8354
8883
|
};
|
|
8355
8884
|
}
|
|
8356
8885
|
|
|
8886
|
+
// src/tui/session-manager.ts
|
|
8887
|
+
var SessionManager = class {
|
|
8888
|
+
constructor(blockRenderer, statusBar) {
|
|
8889
|
+
this.blockRenderer = blockRenderer;
|
|
8890
|
+
this.statusBar = statusBar;
|
|
8891
|
+
}
|
|
8892
|
+
/**
|
|
8893
|
+
* Start a new session. Called at the start of each REPL turn.
|
|
8894
|
+
*/
|
|
8895
|
+
startNewSession() {
|
|
8896
|
+
this.blockRenderer.startNewSession();
|
|
8897
|
+
}
|
|
8898
|
+
/**
|
|
8899
|
+
* Clear blocks from the previous session only.
|
|
8900
|
+
* Keeps current content visible during the session.
|
|
8901
|
+
*/
|
|
8902
|
+
clearPreviousSession() {
|
|
8903
|
+
this.blockRenderer.clearPreviousSession();
|
|
8904
|
+
}
|
|
8905
|
+
/**
|
|
8906
|
+
* Clear all blocks and reset BlockRenderer state.
|
|
8907
|
+
* Prevents memory leaks between iterations.
|
|
8908
|
+
*/
|
|
8909
|
+
clearAllBlocks() {
|
|
8910
|
+
this.blockRenderer.clear();
|
|
8911
|
+
}
|
|
8912
|
+
/**
|
|
8913
|
+
* Clear status bar activity state.
|
|
8914
|
+
*/
|
|
8915
|
+
clearStatusBar() {
|
|
8916
|
+
this.statusBar.clearActivity();
|
|
8917
|
+
}
|
|
8918
|
+
/**
|
|
8919
|
+
* Full cleanup between REPL iterations.
|
|
8920
|
+
*/
|
|
8921
|
+
resetAll() {
|
|
8922
|
+
this.clearAllBlocks();
|
|
8923
|
+
this.clearStatusBar();
|
|
8924
|
+
}
|
|
8925
|
+
};
|
|
8926
|
+
|
|
8357
8927
|
// src/tui/status-bar.ts
|
|
8358
8928
|
var CHARS_PER_TOKEN = 4;
|
|
8359
8929
|
var SPINNER_FRAMES2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -8880,6 +9450,195 @@ var StatusBar = class {
|
|
|
8880
9450
|
}
|
|
8881
9451
|
};
|
|
8882
9452
|
|
|
9453
|
+
// src/tui/tree-subscription-manager.ts
|
|
9454
|
+
var TreeSubscriptionManager = class {
|
|
9455
|
+
constructor(blockRenderer, statusBar) {
|
|
9456
|
+
this.blockRenderer = blockRenderer;
|
|
9457
|
+
this.statusBar = statusBar;
|
|
9458
|
+
}
|
|
9459
|
+
treeUnsubscribe = null;
|
|
9460
|
+
/**
|
|
9461
|
+
* Subscribe to an ExecutionTree for automatic updates.
|
|
9462
|
+
* Handles previous unsubscription and returns a combined unsubscribe function.
|
|
9463
|
+
*/
|
|
9464
|
+
subscribe(tree) {
|
|
9465
|
+
this.unsubscribe();
|
|
9466
|
+
const unsubBlock = this.blockRenderer.subscribeToTree(tree);
|
|
9467
|
+
const unsubStatus = this.statusBar.subscribeToTree(tree);
|
|
9468
|
+
this.treeUnsubscribe = () => {
|
|
9469
|
+
unsubBlock();
|
|
9470
|
+
unsubStatus();
|
|
9471
|
+
};
|
|
9472
|
+
return () => this.unsubscribe();
|
|
9473
|
+
}
|
|
9474
|
+
/**
|
|
9475
|
+
* Unsubscribe from the current tree if any.
|
|
9476
|
+
*/
|
|
9477
|
+
unsubscribe() {
|
|
9478
|
+
if (this.treeUnsubscribe) {
|
|
9479
|
+
this.treeUnsubscribe();
|
|
9480
|
+
this.treeUnsubscribe = null;
|
|
9481
|
+
}
|
|
9482
|
+
}
|
|
9483
|
+
};
|
|
9484
|
+
|
|
9485
|
+
// src/tui/tui-app-effects.ts
|
|
9486
|
+
function applyFocusMode(mode, layout, statusBar, inputHandler, screenCtx) {
|
|
9487
|
+
statusBar.setFocusMode(mode);
|
|
9488
|
+
if (mode === "input") {
|
|
9489
|
+
inputHandler.activate();
|
|
9490
|
+
} else {
|
|
9491
|
+
inputHandler.deactivate();
|
|
9492
|
+
layout.body.focus();
|
|
9493
|
+
}
|
|
9494
|
+
screenCtx.renderNow();
|
|
9495
|
+
}
|
|
9496
|
+
function applyContentFilterMode(mode, blockRenderer, statusBar, screenCtx) {
|
|
9497
|
+
blockRenderer.setContentFilterMode(mode);
|
|
9498
|
+
statusBar.setContentFilterMode(mode);
|
|
9499
|
+
screenCtx.renderNow();
|
|
9500
|
+
}
|
|
9501
|
+
|
|
9502
|
+
// src/tui/tui-app-bootstrap.ts
|
|
9503
|
+
function createTUIAppDependencies(options) {
|
|
9504
|
+
const screenCtx = createScreen({
|
|
9505
|
+
stdin: options.stdin,
|
|
9506
|
+
stdout: options.stdout,
|
|
9507
|
+
title: "llmist"
|
|
9508
|
+
});
|
|
9509
|
+
const { screen } = screenCtx;
|
|
9510
|
+
const showHints = options.showHints ?? true;
|
|
9511
|
+
const layout = createBlockLayout(screen, showHints);
|
|
9512
|
+
const hintsBar = createHintsBar(layout, screenCtx, showHints);
|
|
9513
|
+
const statusBar = createStatusBar(layout, options.model, screenCtx);
|
|
9514
|
+
const inputHandler = createInputHandler(layout, screenCtx, screen, showHints);
|
|
9515
|
+
const blockRenderer = createBlockRenderer(layout, screenCtx);
|
|
9516
|
+
wireHintsBar(blockRenderer, hintsBar);
|
|
9517
|
+
const controller = createController(
|
|
9518
|
+
layout,
|
|
9519
|
+
statusBar,
|
|
9520
|
+
inputHandler,
|
|
9521
|
+
screenCtx,
|
|
9522
|
+
blockRenderer,
|
|
9523
|
+
hintsBar
|
|
9524
|
+
);
|
|
9525
|
+
const modalManager = new ModalManager();
|
|
9526
|
+
const keyActionHandler = new KeyActionHandler(
|
|
9527
|
+
controller,
|
|
9528
|
+
blockRenderer,
|
|
9529
|
+
statusBar,
|
|
9530
|
+
screenCtx,
|
|
9531
|
+
modalManager,
|
|
9532
|
+
layout
|
|
9533
|
+
);
|
|
9534
|
+
const keyboardManager = new KeyboardManager({
|
|
9535
|
+
screen,
|
|
9536
|
+
getFocusMode: () => controller.getFocusMode(),
|
|
9537
|
+
getContentFilterMode: () => controller.getContentFilterMode(),
|
|
9538
|
+
isWaitingForREPLPrompt: () => inputHandler.isWaitingForREPLPrompt(),
|
|
9539
|
+
hasPendingInput: () => inputHandler.hasPendingInput(),
|
|
9540
|
+
isBlockExpanded: () => blockRenderer.getSelectedBlock()?.expanded ?? false,
|
|
9541
|
+
onAction: (action) => {
|
|
9542
|
+
keyActionHandler.handleKeyAction(action);
|
|
9543
|
+
}
|
|
9544
|
+
});
|
|
9545
|
+
const subscriptionManager = new TreeSubscriptionManager(blockRenderer, statusBar);
|
|
9546
|
+
const sessionManager = new SessionManager(blockRenderer, statusBar);
|
|
9547
|
+
const eventRouter = new EventRouter(blockRenderer);
|
|
9548
|
+
wireInputHandlers(inputHandler, keyboardManager, keyActionHandler, controller);
|
|
9549
|
+
wireScreenEvents(layout, screen, blockRenderer);
|
|
9550
|
+
keyboardManager.setup();
|
|
9551
|
+
applyFocusMode(controller.getFocusMode(), layout, statusBar, inputHandler, screenCtx);
|
|
9552
|
+
screenCtx.requestRender();
|
|
9553
|
+
return {
|
|
9554
|
+
screenCtx,
|
|
9555
|
+
statusBar,
|
|
9556
|
+
inputHandler,
|
|
9557
|
+
blockRenderer,
|
|
9558
|
+
controller,
|
|
9559
|
+
modalManager,
|
|
9560
|
+
subscriptionManager,
|
|
9561
|
+
sessionManager,
|
|
9562
|
+
eventRouter
|
|
9563
|
+
};
|
|
9564
|
+
}
|
|
9565
|
+
function createHintsBar(layout, screenCtx, showHints) {
|
|
9566
|
+
if (!showHints || !layout.hintsBar) {
|
|
9567
|
+
return null;
|
|
9568
|
+
}
|
|
9569
|
+
return new HintsBar(layout.hintsBar, () => screenCtx.requestRender());
|
|
9570
|
+
}
|
|
9571
|
+
function createStatusBar(layout, model, screenCtx) {
|
|
9572
|
+
return new StatusBar(
|
|
9573
|
+
layout.statusBar,
|
|
9574
|
+
model,
|
|
9575
|
+
() => screenCtx.requestRender(),
|
|
9576
|
+
() => screenCtx.renderNow()
|
|
9577
|
+
);
|
|
9578
|
+
}
|
|
9579
|
+
function createInputHandler(layout, screenCtx, screen, showHints) {
|
|
9580
|
+
return new InputHandler(
|
|
9581
|
+
layout.inputBar,
|
|
9582
|
+
layout.promptLabel,
|
|
9583
|
+
layout.body,
|
|
9584
|
+
screen,
|
|
9585
|
+
() => screenCtx.requestRender(),
|
|
9586
|
+
() => screenCtx.renderNow(),
|
|
9587
|
+
showHints
|
|
9588
|
+
);
|
|
9589
|
+
}
|
|
9590
|
+
function createBlockRenderer(layout, screenCtx) {
|
|
9591
|
+
return new BlockRenderer(
|
|
9592
|
+
layout.body,
|
|
9593
|
+
() => screenCtx.requestRender(),
|
|
9594
|
+
() => screenCtx.renderNow()
|
|
9595
|
+
);
|
|
9596
|
+
}
|
|
9597
|
+
function wireHintsBar(blockRenderer, hintsBar) {
|
|
9598
|
+
if (!hintsBar) {
|
|
9599
|
+
return;
|
|
9600
|
+
}
|
|
9601
|
+
blockRenderer.onHasContentChange((hasContent) => {
|
|
9602
|
+
hintsBar.setHasContent(hasContent);
|
|
9603
|
+
});
|
|
9604
|
+
}
|
|
9605
|
+
function createController(layout, statusBar, inputHandler, screenCtx, blockRenderer, hintsBar) {
|
|
9606
|
+
return new TUIController({
|
|
9607
|
+
onFocusModeChange: (mode) => {
|
|
9608
|
+
applyFocusMode(mode, layout, statusBar, inputHandler, screenCtx);
|
|
9609
|
+
hintsBar?.setFocusMode(mode);
|
|
9610
|
+
},
|
|
9611
|
+
onContentFilterModeChange: (mode) => {
|
|
9612
|
+
applyContentFilterMode(mode, blockRenderer, statusBar, screenCtx);
|
|
9613
|
+
hintsBar?.setContentFilterMode(mode);
|
|
9614
|
+
}
|
|
9615
|
+
});
|
|
9616
|
+
}
|
|
9617
|
+
function wireInputHandlers(inputHandler, keyboardManager, keyActionHandler, controller) {
|
|
9618
|
+
inputHandler.onCtrlC(() => keyboardManager.handleForwardedKey("C-c"));
|
|
9619
|
+
inputHandler.onCtrlB(() => keyboardManager.handleForwardedKey("C-b"));
|
|
9620
|
+
inputHandler.onCtrlK(() => keyboardManager.handleForwardedKey("C-k"));
|
|
9621
|
+
inputHandler.onCtrlI(() => keyboardManager.handleForwardedKey("C-i"));
|
|
9622
|
+
inputHandler.onCtrlJ(() => keyboardManager.handleForwardedKey("C-j"));
|
|
9623
|
+
inputHandler.onCtrlP(() => keyboardManager.handleForwardedKey("C-p"));
|
|
9624
|
+
inputHandler.onArrowUp(() => {
|
|
9625
|
+
keyActionHandler.handleKeyAction({ type: "scroll_line", direction: -1 });
|
|
9626
|
+
});
|
|
9627
|
+
inputHandler.onArrowDown(() => {
|
|
9628
|
+
keyActionHandler.handleKeyAction({ type: "scroll_line", direction: 1 });
|
|
9629
|
+
});
|
|
9630
|
+
inputHandler.setGetFocusMode(() => controller.getFocusMode());
|
|
9631
|
+
inputHandler.setGetContentFilterMode(() => controller.getContentFilterMode());
|
|
9632
|
+
}
|
|
9633
|
+
function wireScreenEvents(layout, screen, blockRenderer) {
|
|
9634
|
+
layout.body.on("scroll", () => {
|
|
9635
|
+
blockRenderer.handleUserScroll();
|
|
9636
|
+
});
|
|
9637
|
+
screen.on("resize", () => {
|
|
9638
|
+
blockRenderer.handleResize();
|
|
9639
|
+
});
|
|
9640
|
+
}
|
|
9641
|
+
|
|
8883
9642
|
// src/tui/index.ts
|
|
8884
9643
|
var TUIApp = class _TUIApp {
|
|
8885
9644
|
screenCtx;
|
|
@@ -8889,135 +9648,36 @@ var TUIApp = class _TUIApp {
|
|
|
8889
9648
|
// New extracted components
|
|
8890
9649
|
controller;
|
|
8891
9650
|
modalManager;
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
9651
|
+
subscriptionManager;
|
|
9652
|
+
sessionManager;
|
|
9653
|
+
eventRouter;
|
|
9654
|
+
constructor(screenCtx, statusBar, inputHandler, blockRenderer, controller, modalManager, subscriptionManager, sessionManager, eventRouter) {
|
|
8895
9655
|
this.screenCtx = screenCtx;
|
|
8896
9656
|
this.statusBar = statusBar;
|
|
8897
9657
|
this.inputHandler = inputHandler;
|
|
8898
9658
|
this.blockRenderer = blockRenderer;
|
|
8899
9659
|
this.controller = controller;
|
|
8900
9660
|
this.modalManager = modalManager;
|
|
9661
|
+
this.subscriptionManager = subscriptionManager;
|
|
9662
|
+
this.sessionManager = sessionManager;
|
|
9663
|
+
this.eventRouter = eventRouter;
|
|
8901
9664
|
}
|
|
8902
9665
|
/**
|
|
8903
9666
|
* Create a new TUI application instance.
|
|
8904
9667
|
*/
|
|
8905
9668
|
static async create(options) {
|
|
8906
|
-
const
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
}
|
|
8918
|
-
const statusBar = new StatusBar(
|
|
8919
|
-
layout.statusBar,
|
|
8920
|
-
options.model,
|
|
8921
|
-
() => screenCtx.requestRender(),
|
|
8922
|
-
() => screenCtx.renderNow()
|
|
8923
|
-
);
|
|
8924
|
-
const inputHandler = new InputHandler(
|
|
8925
|
-
layout.inputBar,
|
|
8926
|
-
layout.promptLabel,
|
|
8927
|
-
layout.body,
|
|
8928
|
-
screen,
|
|
8929
|
-
() => screenCtx.requestRender(),
|
|
8930
|
-
() => screenCtx.renderNow(),
|
|
8931
|
-
showHints
|
|
8932
|
-
);
|
|
8933
|
-
const blockRenderer = new BlockRenderer(
|
|
8934
|
-
layout.body,
|
|
8935
|
-
() => screenCtx.requestRender(),
|
|
8936
|
-
() => screenCtx.renderNow()
|
|
8937
|
-
);
|
|
8938
|
-
if (hintsBar) {
|
|
8939
|
-
blockRenderer.onHasContentChange((hasContent) => {
|
|
8940
|
-
hintsBar.setHasContent(hasContent);
|
|
8941
|
-
});
|
|
8942
|
-
}
|
|
8943
|
-
const controller = new TUIController({
|
|
8944
|
-
onFocusModeChange: (mode) => {
|
|
8945
|
-
applyFocusMode(mode, layout, statusBar, inputHandler, screenCtx);
|
|
8946
|
-
hintsBar?.setFocusMode(mode);
|
|
8947
|
-
},
|
|
8948
|
-
onContentFilterModeChange: (mode) => {
|
|
8949
|
-
applyContentFilterMode(mode, blockRenderer, statusBar, screenCtx);
|
|
8950
|
-
hintsBar?.setContentFilterMode(mode);
|
|
8951
|
-
}
|
|
8952
|
-
});
|
|
8953
|
-
const modalManager = new ModalManager();
|
|
8954
|
-
const keyboardManager = new KeyboardManager({
|
|
8955
|
-
screen,
|
|
8956
|
-
getFocusMode: () => controller.getFocusMode(),
|
|
8957
|
-
getContentFilterMode: () => controller.getContentFilterMode(),
|
|
8958
|
-
isWaitingForREPLPrompt: () => inputHandler.isWaitingForREPLPrompt(),
|
|
8959
|
-
hasPendingInput: () => inputHandler.hasPendingInput(),
|
|
8960
|
-
isBlockExpanded: () => blockRenderer.getSelectedBlock()?.expanded ?? false,
|
|
8961
|
-
onAction: (action) => {
|
|
8962
|
-
handleKeyAction(
|
|
8963
|
-
action,
|
|
8964
|
-
controller,
|
|
8965
|
-
blockRenderer,
|
|
8966
|
-
statusBar,
|
|
8967
|
-
screenCtx,
|
|
8968
|
-
modalManager,
|
|
8969
|
-
layout
|
|
8970
|
-
);
|
|
8971
|
-
}
|
|
8972
|
-
});
|
|
8973
|
-
const app = new _TUIApp(
|
|
8974
|
-
screenCtx,
|
|
8975
|
-
statusBar,
|
|
8976
|
-
inputHandler,
|
|
8977
|
-
blockRenderer,
|
|
8978
|
-
controller,
|
|
8979
|
-
modalManager
|
|
9669
|
+
const dependencies = createTUIAppDependencies(options);
|
|
9670
|
+
return new _TUIApp(
|
|
9671
|
+
dependencies.screenCtx,
|
|
9672
|
+
dependencies.statusBar,
|
|
9673
|
+
dependencies.inputHandler,
|
|
9674
|
+
dependencies.blockRenderer,
|
|
9675
|
+
dependencies.controller,
|
|
9676
|
+
dependencies.modalManager,
|
|
9677
|
+
dependencies.subscriptionManager,
|
|
9678
|
+
dependencies.sessionManager,
|
|
9679
|
+
dependencies.eventRouter
|
|
8980
9680
|
);
|
|
8981
|
-
keyboardManager.setup();
|
|
8982
|
-
inputHandler.onCtrlC(() => keyboardManager.handleForwardedKey("C-c"));
|
|
8983
|
-
inputHandler.onCtrlB(() => keyboardManager.handleForwardedKey("C-b"));
|
|
8984
|
-
inputHandler.onCtrlK(() => keyboardManager.handleForwardedKey("C-k"));
|
|
8985
|
-
inputHandler.onCtrlI(() => keyboardManager.handleForwardedKey("C-i"));
|
|
8986
|
-
inputHandler.onCtrlJ(() => keyboardManager.handleForwardedKey("C-j"));
|
|
8987
|
-
inputHandler.onCtrlP(() => keyboardManager.handleForwardedKey("C-p"));
|
|
8988
|
-
inputHandler.onArrowUp(() => {
|
|
8989
|
-
handleKeyAction(
|
|
8990
|
-
{ type: "scroll_line", direction: -1 },
|
|
8991
|
-
controller,
|
|
8992
|
-
blockRenderer,
|
|
8993
|
-
statusBar,
|
|
8994
|
-
screenCtx,
|
|
8995
|
-
modalManager,
|
|
8996
|
-
layout
|
|
8997
|
-
);
|
|
8998
|
-
});
|
|
8999
|
-
inputHandler.onArrowDown(() => {
|
|
9000
|
-
handleKeyAction(
|
|
9001
|
-
{ type: "scroll_line", direction: 1 },
|
|
9002
|
-
controller,
|
|
9003
|
-
blockRenderer,
|
|
9004
|
-
statusBar,
|
|
9005
|
-
screenCtx,
|
|
9006
|
-
modalManager,
|
|
9007
|
-
layout
|
|
9008
|
-
);
|
|
9009
|
-
});
|
|
9010
|
-
inputHandler.setGetFocusMode(() => controller.getFocusMode());
|
|
9011
|
-
inputHandler.setGetContentFilterMode(() => controller.getContentFilterMode());
|
|
9012
|
-
layout.body.on("scroll", () => {
|
|
9013
|
-
blockRenderer.handleUserScroll();
|
|
9014
|
-
});
|
|
9015
|
-
screen.on("resize", () => {
|
|
9016
|
-
blockRenderer.handleResize();
|
|
9017
|
-
});
|
|
9018
|
-
applyFocusMode(controller.getFocusMode(), layout, statusBar, inputHandler, screenCtx);
|
|
9019
|
-
screenCtx.requestRender();
|
|
9020
|
-
return app;
|
|
9021
9681
|
}
|
|
9022
9682
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
9023
9683
|
// Focus Mode Management (delegated to controller)
|
|
@@ -9053,11 +9713,7 @@ var TUIApp = class _TUIApp {
|
|
|
9053
9713
|
* automatically by ExecutionTree subscription via subscribeToTree().
|
|
9054
9714
|
*/
|
|
9055
9715
|
handleEvent(event) {
|
|
9056
|
-
|
|
9057
|
-
this.blockRenderer.addText(event.content);
|
|
9058
|
-
} else if (event.type === "thinking") {
|
|
9059
|
-
this.blockRenderer.addThinking(event.content, event.thinkingType);
|
|
9060
|
-
}
|
|
9716
|
+
this.eventRouter.handleEvent(event);
|
|
9061
9717
|
}
|
|
9062
9718
|
/**
|
|
9063
9719
|
* Show an LLM call starting.
|
|
@@ -9082,26 +9738,11 @@ var TUIApp = class _TUIApp {
|
|
|
9082
9738
|
async showRawViewer(mode) {
|
|
9083
9739
|
if (this.controller.getFocusMode() !== "browse") return;
|
|
9084
9740
|
const selected = this.blockRenderer.getSelectedBlock();
|
|
9085
|
-
if (!selected) return;
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
request: node.rawRequest,
|
|
9091
|
-
response: node.rawResponse,
|
|
9092
|
-
iteration: node.iteration,
|
|
9093
|
-
model: node.model
|
|
9094
|
-
});
|
|
9095
|
-
} else if (selected.node.type === "gadget") {
|
|
9096
|
-
const node = selected.node;
|
|
9097
|
-
await this.modalManager.showRawViewer(this.screenCtx.screen, {
|
|
9098
|
-
mode,
|
|
9099
|
-
gadgetName: node.name,
|
|
9100
|
-
parameters: node.parameters,
|
|
9101
|
-
result: node.result,
|
|
9102
|
-
error: node.error
|
|
9103
|
-
});
|
|
9104
|
-
}
|
|
9741
|
+
if (!selected || !isRawViewerNode(selected.node)) return;
|
|
9742
|
+
await this.modalManager.showRawViewer(
|
|
9743
|
+
this.screenCtx.screen,
|
|
9744
|
+
createRawViewerData(selected.node, mode)
|
|
9745
|
+
);
|
|
9105
9746
|
}
|
|
9106
9747
|
/**
|
|
9107
9748
|
* Show approval dialog for gadget execution.
|
|
@@ -9156,21 +9797,7 @@ var TUIApp = class _TUIApp {
|
|
|
9156
9797
|
* Subscribe to an ExecutionTree for automatic block updates.
|
|
9157
9798
|
*/
|
|
9158
9799
|
subscribeToTree(tree) {
|
|
9159
|
-
|
|
9160
|
-
this.treeUnsubscribe();
|
|
9161
|
-
}
|
|
9162
|
-
const unsubBlock = this.blockRenderer.subscribeToTree(tree);
|
|
9163
|
-
const unsubStatus = this.statusBar.subscribeToTree(tree);
|
|
9164
|
-
this.treeUnsubscribe = () => {
|
|
9165
|
-
unsubBlock();
|
|
9166
|
-
unsubStatus();
|
|
9167
|
-
};
|
|
9168
|
-
return () => {
|
|
9169
|
-
if (this.treeUnsubscribe) {
|
|
9170
|
-
this.treeUnsubscribe();
|
|
9171
|
-
this.treeUnsubscribe = null;
|
|
9172
|
-
}
|
|
9173
|
-
};
|
|
9800
|
+
return this.subscriptionManager.subscribe(tree);
|
|
9174
9801
|
}
|
|
9175
9802
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
9176
9803
|
// Memory Cleanup (REPL mode)
|
|
@@ -9182,21 +9809,21 @@ var TUIApp = class _TUIApp {
|
|
|
9182
9809
|
* Call this after each agent run completes (after unsubscribing from tree).
|
|
9183
9810
|
*/
|
|
9184
9811
|
clearBlockRenderer() {
|
|
9185
|
-
this.
|
|
9812
|
+
this.sessionManager.clearAllBlocks();
|
|
9186
9813
|
}
|
|
9187
9814
|
/**
|
|
9188
9815
|
* Clear status bar activity state.
|
|
9189
9816
|
* Called between REPL turns to prevent stale state.
|
|
9190
9817
|
*/
|
|
9191
9818
|
clearStatusBar() {
|
|
9192
|
-
this.
|
|
9819
|
+
this.sessionManager.clearStatusBar();
|
|
9193
9820
|
}
|
|
9194
9821
|
/**
|
|
9195
9822
|
* Start a new session. Called at the start of each REPL turn.
|
|
9196
9823
|
* Increments the session counter so new blocks get the new sessionId.
|
|
9197
9824
|
*/
|
|
9198
9825
|
startNewSession() {
|
|
9199
|
-
this.
|
|
9826
|
+
this.sessionManager.startNewSession();
|
|
9200
9827
|
}
|
|
9201
9828
|
/**
|
|
9202
9829
|
* Clear blocks from the previous session only.
|
|
@@ -9204,7 +9831,7 @@ var TUIApp = class _TUIApp {
|
|
|
9204
9831
|
* The previous session's content was kept visible during this session for context.
|
|
9205
9832
|
*/
|
|
9206
9833
|
clearPreviousSession() {
|
|
9207
|
-
this.
|
|
9834
|
+
this.sessionManager.clearPreviousSession();
|
|
9208
9835
|
}
|
|
9209
9836
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
9210
9837
|
// Abort Control (delegated to controller)
|
|
@@ -9339,127 +9966,12 @@ var TUIApp = class _TUIApp {
|
|
|
9339
9966
|
* Clean up and restore terminal.
|
|
9340
9967
|
*/
|
|
9341
9968
|
destroy() {
|
|
9342
|
-
|
|
9343
|
-
this.treeUnsubscribe();
|
|
9344
|
-
this.treeUnsubscribe = null;
|
|
9345
|
-
}
|
|
9969
|
+
this.subscriptionManager.unsubscribe();
|
|
9346
9970
|
this.modalManager.closeAll();
|
|
9347
9971
|
this.inputHandler.cancelPending();
|
|
9348
9972
|
this.screenCtx.destroy();
|
|
9349
9973
|
}
|
|
9350
9974
|
};
|
|
9351
|
-
function applyFocusMode(mode, layout, statusBar, inputHandler, screenCtx) {
|
|
9352
|
-
statusBar.setFocusMode(mode);
|
|
9353
|
-
if (mode === "input") {
|
|
9354
|
-
inputHandler.activate();
|
|
9355
|
-
} else {
|
|
9356
|
-
inputHandler.deactivate();
|
|
9357
|
-
layout.body.focus();
|
|
9358
|
-
}
|
|
9359
|
-
screenCtx.renderNow();
|
|
9360
|
-
}
|
|
9361
|
-
function applyContentFilterMode(mode, blockRenderer, statusBar, screenCtx) {
|
|
9362
|
-
blockRenderer.setContentFilterMode(mode);
|
|
9363
|
-
statusBar.setContentFilterMode(mode);
|
|
9364
|
-
screenCtx.renderNow();
|
|
9365
|
-
}
|
|
9366
|
-
function handleKeyAction(action, controller, blockRenderer, statusBar, screenCtx, modalManager, layout) {
|
|
9367
|
-
switch (action.type) {
|
|
9368
|
-
case "ctrl_c": {
|
|
9369
|
-
const result = controller.handleCtrlC();
|
|
9370
|
-
if (result === "show_hint") {
|
|
9371
|
-
blockRenderer.addText("\n[Press Ctrl+C again to quit]\n");
|
|
9372
|
-
} else if (result === "quit") {
|
|
9373
|
-
process.exit(130);
|
|
9374
|
-
}
|
|
9375
|
-
break;
|
|
9376
|
-
}
|
|
9377
|
-
case "cancel":
|
|
9378
|
-
controller.triggerCancel();
|
|
9379
|
-
controller.abort();
|
|
9380
|
-
break;
|
|
9381
|
-
case "toggle_focus_mode":
|
|
9382
|
-
controller.toggleFocusMode();
|
|
9383
|
-
break;
|
|
9384
|
-
case "toggle_content_filter":
|
|
9385
|
-
controller.toggleContentFilterMode();
|
|
9386
|
-
break;
|
|
9387
|
-
case "cycle_profile":
|
|
9388
|
-
statusBar.cycleProfile();
|
|
9389
|
-
break;
|
|
9390
|
-
case "scroll_page": {
|
|
9391
|
-
const body = layout.body;
|
|
9392
|
-
if (!body.scroll) return;
|
|
9393
|
-
const containerHeight = body.height;
|
|
9394
|
-
const scrollAmount = Math.max(1, containerHeight - 2);
|
|
9395
|
-
if (action.direction < 0) {
|
|
9396
|
-
body.scroll(-scrollAmount);
|
|
9397
|
-
} else {
|
|
9398
|
-
body.scroll(scrollAmount);
|
|
9399
|
-
}
|
|
9400
|
-
blockRenderer.handleUserScroll();
|
|
9401
|
-
screenCtx.renderNow();
|
|
9402
|
-
break;
|
|
9403
|
-
}
|
|
9404
|
-
case "scroll_line": {
|
|
9405
|
-
const body = layout.body;
|
|
9406
|
-
if (!body.scroll) return;
|
|
9407
|
-
body.scroll(action.direction);
|
|
9408
|
-
blockRenderer.handleUserScroll();
|
|
9409
|
-
screenCtx.renderNow();
|
|
9410
|
-
break;
|
|
9411
|
-
}
|
|
9412
|
-
case "navigation":
|
|
9413
|
-
switch (action.action) {
|
|
9414
|
-
case "select_next":
|
|
9415
|
-
blockRenderer.selectNext();
|
|
9416
|
-
break;
|
|
9417
|
-
case "select_previous":
|
|
9418
|
-
blockRenderer.selectPrevious();
|
|
9419
|
-
break;
|
|
9420
|
-
case "select_first":
|
|
9421
|
-
blockRenderer.selectFirst();
|
|
9422
|
-
break;
|
|
9423
|
-
case "select_last":
|
|
9424
|
-
blockRenderer.selectLast();
|
|
9425
|
-
blockRenderer.enableFollowMode();
|
|
9426
|
-
break;
|
|
9427
|
-
case "toggle_expand":
|
|
9428
|
-
blockRenderer.toggleExpand();
|
|
9429
|
-
break;
|
|
9430
|
-
case "collapse":
|
|
9431
|
-
blockRenderer.collapseOrDeselect();
|
|
9432
|
-
break;
|
|
9433
|
-
}
|
|
9434
|
-
screenCtx.renderNow();
|
|
9435
|
-
break;
|
|
9436
|
-
case "raw_viewer":
|
|
9437
|
-
void (async () => {
|
|
9438
|
-
const selected = blockRenderer.getSelectedBlock();
|
|
9439
|
-
if (!selected) return;
|
|
9440
|
-
if (selected.node.type === "llm_call") {
|
|
9441
|
-
const node = selected.node;
|
|
9442
|
-
await modalManager.showRawViewer(screenCtx.screen, {
|
|
9443
|
-
mode: action.mode,
|
|
9444
|
-
request: node.rawRequest,
|
|
9445
|
-
response: node.rawResponse,
|
|
9446
|
-
iteration: node.iteration,
|
|
9447
|
-
model: node.model
|
|
9448
|
-
});
|
|
9449
|
-
} else if (selected.node.type === "gadget") {
|
|
9450
|
-
const node = selected.node;
|
|
9451
|
-
await modalManager.showRawViewer(screenCtx.screen, {
|
|
9452
|
-
mode: action.mode,
|
|
9453
|
-
gadgetName: node.name,
|
|
9454
|
-
parameters: node.parameters,
|
|
9455
|
-
result: node.result,
|
|
9456
|
-
error: node.error
|
|
9457
|
-
});
|
|
9458
|
-
}
|
|
9459
|
-
})();
|
|
9460
|
-
break;
|
|
9461
|
-
}
|
|
9462
|
-
}
|
|
9463
9975
|
|
|
9464
9976
|
// src/agent-command.ts
|
|
9465
9977
|
async function executeAgent(promptArg, options, env, commandName) {
|
|
@@ -9496,6 +10008,14 @@ async function executeAgent(promptArg, options, env, commandName) {
|
|
|
9496
10008
|
registry.registerByClass(gadget);
|
|
9497
10009
|
}
|
|
9498
10010
|
}
|
|
10011
|
+
let skillsConfig;
|
|
10012
|
+
try {
|
|
10013
|
+
const fullConfig = loadConfig();
|
|
10014
|
+
skillsConfig = fullConfig.skills;
|
|
10015
|
+
} catch {
|
|
10016
|
+
}
|
|
10017
|
+
const skillManager = new CLISkillManager();
|
|
10018
|
+
const skillRegistry = await skillManager.loadAll(skillsConfig);
|
|
9499
10019
|
let tui = null;
|
|
9500
10020
|
if (useTUI) {
|
|
9501
10021
|
tui = await TUIApp.create({
|
|
@@ -9534,7 +10054,7 @@ async function executeAgent(promptArg, options, env, commandName) {
|
|
|
9534
10054
|
if (!useTUI) {
|
|
9535
10055
|
process.once("SIGINT", () => process.exit(130));
|
|
9536
10056
|
}
|
|
9537
|
-
const DEFAULT_APPROVAL_REQUIRED = ["RunCommand", "WriteFile", "EditFile"];
|
|
10057
|
+
const DEFAULT_APPROVAL_REQUIRED = ["RunCommand", "WriteFile", "EditFile", "DeleteFile"];
|
|
9538
10058
|
const userApprovals = options.gadgetApproval ?? {};
|
|
9539
10059
|
const gadgetApprovals = {
|
|
9540
10060
|
...userApprovals
|
|
@@ -9671,6 +10191,11 @@ ${ctx.gadgetName} is denied by configuration.`
|
|
|
9671
10191
|
gadgetName: ctx.gadgetName,
|
|
9672
10192
|
parameters: ctx.parameters
|
|
9673
10193
|
});
|
|
10194
|
+
if (response === "always") {
|
|
10195
|
+
gadgetApprovals[ctx.gadgetName] = "allowed";
|
|
10196
|
+
} else if (response === "deny") {
|
|
10197
|
+
gadgetApprovals[ctx.gadgetName] = "denied";
|
|
10198
|
+
}
|
|
9674
10199
|
if (response === "yes" || response === "always") {
|
|
9675
10200
|
return { action: "proceed" };
|
|
9676
10201
|
}
|
|
@@ -9685,7 +10210,7 @@ Denied by user`
|
|
|
9685
10210
|
action: "skip",
|
|
9686
10211
|
syntheticResult: `status=denied
|
|
9687
10212
|
|
|
9688
|
-
${ctx.gadgetName} requires interactive approval.
|
|
10213
|
+
${ctx.gadgetName} requires interactive approval. Enable TUI mode or adjust 'gadget-approval' in your config to allow this gadget.`
|
|
9689
10214
|
};
|
|
9690
10215
|
}
|
|
9691
10216
|
}
|
|
@@ -9724,6 +10249,9 @@ ${ctx.gadgetName} requires interactive approval. Run in a terminal to approve.`
|
|
|
9724
10249
|
if (options.temperature !== void 0) {
|
|
9725
10250
|
builder.withTemperature(options.temperature);
|
|
9726
10251
|
}
|
|
10252
|
+
if (skillRegistry.size > 0) {
|
|
10253
|
+
builder.withSkills(skillRegistry);
|
|
10254
|
+
}
|
|
9727
10255
|
if (options.reasoning === false) {
|
|
9728
10256
|
builder.withoutReasoning();
|
|
9729
10257
|
} else if (options.reasoning !== void 0 || options.reasoningBudget !== void 0) {
|
|
@@ -9800,6 +10328,29 @@ ${ctx.gadgetName} requires interactive approval. Run in a terminal to approve.`
|
|
|
9800
10328
|
);
|
|
9801
10329
|
let currentAgent = null;
|
|
9802
10330
|
const runAgentWithPrompt = async (userPrompt) => {
|
|
10331
|
+
builder.clearPreActivatedSkills();
|
|
10332
|
+
if (skillRegistry.size > 0 && userPrompt.startsWith("/")) {
|
|
10333
|
+
const slashResult = parseSlashCommand(userPrompt, skillRegistry);
|
|
10334
|
+
if (slashResult.isSkillInvocation) {
|
|
10335
|
+
if (slashResult.isListCommand) {
|
|
10336
|
+
const skills = skillRegistry.getUserInvocable();
|
|
10337
|
+
const lines = skills.map((s) => ` /${s.name} \u2014 ${s.description}`);
|
|
10338
|
+
const msg = skills.length > 0 ? `Available skills:
|
|
10339
|
+
${lines.join("\n")}` : "No skills available.";
|
|
10340
|
+
if (tui) {
|
|
10341
|
+
tui.showUserMessage(`/skills`);
|
|
10342
|
+
tui.showUserMessage(msg);
|
|
10343
|
+
} else {
|
|
10344
|
+
env.stdout.write(`${msg}
|
|
10345
|
+
`);
|
|
10346
|
+
}
|
|
10347
|
+
return;
|
|
10348
|
+
}
|
|
10349
|
+
if (slashResult.skillName) {
|
|
10350
|
+
builder.withSkill(slashResult.skillName, slashResult.arguments);
|
|
10351
|
+
}
|
|
10352
|
+
}
|
|
10353
|
+
}
|
|
9803
10354
|
if (tui) {
|
|
9804
10355
|
tui.resetAbort();
|
|
9805
10356
|
tui.startNewSession();
|
|
@@ -9924,11 +10475,11 @@ import {
|
|
|
9924
10475
|
|
|
9925
10476
|
// src/llm-logging.ts
|
|
9926
10477
|
import { mkdir, writeFile as writeFile2 } from "fs/promises";
|
|
9927
|
-
import { join as
|
|
10478
|
+
import { join as join4 } from "path";
|
|
9928
10479
|
import { formatCallNumber as formatCallNumber2, formatLlmRequest } from "llmist";
|
|
9929
10480
|
async function writeLogFile(dir, filename, content) {
|
|
9930
10481
|
await mkdir(dir, { recursive: true });
|
|
9931
|
-
await writeFile2(
|
|
10482
|
+
await writeFile2(join4(dir, filename), content, "utf-8");
|
|
9932
10483
|
}
|
|
9933
10484
|
|
|
9934
10485
|
// src/complete-command.ts
|
|
@@ -10156,7 +10707,7 @@ Profile: ${name}
|
|
|
10156
10707
|
` Temperature: ${section.temperature !== void 0 ? section.temperature : "(default)"}
|
|
10157
10708
|
`
|
|
10158
10709
|
);
|
|
10159
|
-
const gadgets = section.gadgets
|
|
10710
|
+
const gadgets = section.gadgets;
|
|
10160
10711
|
if (gadgets && gadgets.length > 0) {
|
|
10161
10712
|
env.stdout.write("\nGadgets:\n");
|
|
10162
10713
|
for (const g of gadgets) {
|
|
@@ -10206,9 +10757,9 @@ System Prompt (${chars.toLocaleString()} chars, ${lines} lines):
|
|
|
10206
10757
|
}
|
|
10207
10758
|
|
|
10208
10759
|
// src/environment.ts
|
|
10209
|
-
import { join as
|
|
10760
|
+
import { join as join5 } from "path";
|
|
10210
10761
|
import readline from "readline";
|
|
10211
|
-
import
|
|
10762
|
+
import chalk7 from "chalk";
|
|
10212
10763
|
import { createLogger, LLMist } from "llmist";
|
|
10213
10764
|
var LOG_LEVEL_MAP = {
|
|
10214
10765
|
silly: 0,
|
|
@@ -10229,7 +10780,7 @@ function createLoggerFactory(config, sessionLogDir) {
|
|
|
10229
10780
|
}
|
|
10230
10781
|
}
|
|
10231
10782
|
if (sessionLogDir) {
|
|
10232
|
-
const logFile =
|
|
10783
|
+
const logFile = join5(sessionLogDir, "session.log.jsonl");
|
|
10233
10784
|
const originalLogFile = process.env.LLMIST_LOG_FILE;
|
|
10234
10785
|
process.env.LLMIST_LOG_FILE = logFile;
|
|
10235
10786
|
const logger = createLogger(options);
|
|
@@ -10248,22 +10799,22 @@ function createLoggerFactory(config, sessionLogDir) {
|
|
|
10248
10799
|
}
|
|
10249
10800
|
function createPromptFunction(stdin, stdout) {
|
|
10250
10801
|
return (question) => {
|
|
10251
|
-
return new Promise((
|
|
10802
|
+
return new Promise((resolve4) => {
|
|
10252
10803
|
const rl = readline.createInterface({
|
|
10253
10804
|
input: stdin,
|
|
10254
10805
|
output: stdout
|
|
10255
10806
|
});
|
|
10256
10807
|
stdout.write("\n");
|
|
10257
|
-
stdout.write(`${
|
|
10808
|
+
stdout.write(`${chalk7.cyan("\u2500".repeat(60))}
|
|
10258
10809
|
`);
|
|
10259
|
-
stdout.write(
|
|
10810
|
+
stdout.write(chalk7.cyan.bold("\u{1F916} Agent asks:\n"));
|
|
10260
10811
|
stdout.write(`${question}
|
|
10261
10812
|
`);
|
|
10262
|
-
stdout.write(`${
|
|
10813
|
+
stdout.write(`${chalk7.cyan("\u2500".repeat(60))}
|
|
10263
10814
|
`);
|
|
10264
|
-
rl.question(
|
|
10815
|
+
rl.question(chalk7.green.bold("You: "), (answer) => {
|
|
10265
10816
|
rl.close();
|
|
10266
|
-
|
|
10817
|
+
resolve4(answer);
|
|
10267
10818
|
});
|
|
10268
10819
|
});
|
|
10269
10820
|
};
|
|
@@ -10342,12 +10893,12 @@ function registerCustomCommand(program, name, config, env, globalSubagents, glob
|
|
|
10342
10893
|
}
|
|
10343
10894
|
|
|
10344
10895
|
// src/gadget-command.ts
|
|
10345
|
-
import
|
|
10896
|
+
import chalk9 from "chalk";
|
|
10346
10897
|
import { schemaToJSONSchema as schemaToJSONSchema2, validateGadgetSchema } from "llmist";
|
|
10347
10898
|
|
|
10348
10899
|
// src/gadget-prompts.ts
|
|
10349
10900
|
import { createInterface } from "readline/promises";
|
|
10350
|
-
import
|
|
10901
|
+
import chalk8 from "chalk";
|
|
10351
10902
|
import { schemaToJSONSchema } from "llmist";
|
|
10352
10903
|
async function promptForParameters(schema, ctx) {
|
|
10353
10904
|
if (!schema) {
|
|
@@ -10380,16 +10931,16 @@ ${issues}`);
|
|
|
10380
10931
|
async function promptForField(rl, key, prop, required) {
|
|
10381
10932
|
const isRequired = required.includes(key);
|
|
10382
10933
|
const typeHint = formatTypeHint(prop);
|
|
10383
|
-
const defaultHint = prop.default !== void 0 ?
|
|
10384
|
-
const requiredMarker = isRequired ?
|
|
10934
|
+
const defaultHint = prop.default !== void 0 ? chalk8.dim(` [default: ${JSON.stringify(prop.default)}]`) : "";
|
|
10935
|
+
const requiredMarker = isRequired ? chalk8.red("*") : "";
|
|
10385
10936
|
let prompt = `
|
|
10386
|
-
${
|
|
10937
|
+
${chalk8.cyan.bold(key)}${requiredMarker}`;
|
|
10387
10938
|
if (prop.description) {
|
|
10388
|
-
prompt +=
|
|
10939
|
+
prompt += chalk8.dim(` - ${prop.description}`);
|
|
10389
10940
|
}
|
|
10390
10941
|
prompt += `
|
|
10391
10942
|
${typeHint}${defaultHint}
|
|
10392
|
-
${
|
|
10943
|
+
${chalk8.green(">")} `;
|
|
10393
10944
|
const answer = await rl.question(prompt);
|
|
10394
10945
|
const trimmed = answer.trim();
|
|
10395
10946
|
if (!trimmed) {
|
|
@@ -10405,20 +10956,20 @@ ${chalk7.cyan.bold(key)}${requiredMarker}`;
|
|
|
10405
10956
|
}
|
|
10406
10957
|
function formatTypeHint(prop) {
|
|
10407
10958
|
if (prop.enum) {
|
|
10408
|
-
return
|
|
10959
|
+
return chalk8.yellow(`(${prop.enum.join(" | ")})`);
|
|
10409
10960
|
}
|
|
10410
10961
|
if (prop.type === "array") {
|
|
10411
10962
|
const items = prop.items;
|
|
10412
10963
|
if (items?.enum) {
|
|
10413
|
-
return
|
|
10964
|
+
return chalk8.yellow(`(${items.enum.join(" | ")})[] comma-separated`);
|
|
10414
10965
|
}
|
|
10415
10966
|
const itemType = items?.type ?? "any";
|
|
10416
|
-
return
|
|
10967
|
+
return chalk8.yellow(`(${itemType}[]) comma-separated`);
|
|
10417
10968
|
}
|
|
10418
10969
|
if (prop.type === "object" && prop.properties) {
|
|
10419
|
-
return
|
|
10970
|
+
return chalk8.yellow("(object) enter as JSON");
|
|
10420
10971
|
}
|
|
10421
|
-
return
|
|
10972
|
+
return chalk8.yellow(`(${prop.type ?? "any"})`);
|
|
10422
10973
|
}
|
|
10423
10974
|
function parseValue(input, prop, key) {
|
|
10424
10975
|
const type = prop.type;
|
|
@@ -10529,7 +11080,7 @@ Available gadgets:
|
|
|
10529
11080
|
async function executeGadgetRun(file, options, env) {
|
|
10530
11081
|
const cwd = process.cwd();
|
|
10531
11082
|
const { gadget, name } = await selectGadget(file, options.name, cwd);
|
|
10532
|
-
env.stderr.write(
|
|
11083
|
+
env.stderr.write(chalk9.cyan.bold(`
|
|
10533
11084
|
\u{1F527} Running gadget: ${name}
|
|
10534
11085
|
`));
|
|
10535
11086
|
let params;
|
|
@@ -10540,7 +11091,7 @@ async function executeGadgetRun(file, options, env) {
|
|
|
10540
11091
|
// Prompts go to stderr to keep stdout clean
|
|
10541
11092
|
});
|
|
10542
11093
|
} else {
|
|
10543
|
-
env.stderr.write(
|
|
11094
|
+
env.stderr.write(chalk9.dim("Reading parameters from stdin...\n"));
|
|
10544
11095
|
const stdinParams = await readStdinJson(env.stdin);
|
|
10545
11096
|
if (gadget.parameterSchema) {
|
|
10546
11097
|
const result2 = gadget.parameterSchema.safeParse(stdinParams);
|
|
@@ -10554,7 +11105,7 @@ ${issues}`);
|
|
|
10554
11105
|
params = stdinParams;
|
|
10555
11106
|
}
|
|
10556
11107
|
}
|
|
10557
|
-
env.stderr.write(
|
|
11108
|
+
env.stderr.write(chalk9.dim("\nExecuting...\n"));
|
|
10558
11109
|
const startTime = Date.now();
|
|
10559
11110
|
let result;
|
|
10560
11111
|
let cost;
|
|
@@ -10581,7 +11132,7 @@ ${issues}`);
|
|
|
10581
11132
|
}
|
|
10582
11133
|
const elapsed = Date.now() - startTime;
|
|
10583
11134
|
const costInfo = cost !== void 0 && cost > 0 ? ` (Cost: $${cost.toFixed(6)})` : "";
|
|
10584
|
-
env.stderr.write(
|
|
11135
|
+
env.stderr.write(chalk9.green(`
|
|
10585
11136
|
\u2713 Completed in ${elapsed}ms${costInfo}
|
|
10586
11137
|
|
|
10587
11138
|
`));
|
|
@@ -10617,37 +11168,37 @@ async function executeGadgetInfo(file, options, env) {
|
|
|
10617
11168
|
return;
|
|
10618
11169
|
}
|
|
10619
11170
|
env.stdout.write("\n");
|
|
10620
|
-
env.stdout.write(
|
|
11171
|
+
env.stdout.write(chalk9.cyan.bold(`${name}
|
|
10621
11172
|
`));
|
|
10622
|
-
env.stdout.write(
|
|
10623
|
-
env.stdout.write(
|
|
11173
|
+
env.stdout.write(chalk9.cyan("\u2550".repeat(name.length)) + "\n\n");
|
|
11174
|
+
env.stdout.write(chalk9.bold("Description:\n"));
|
|
10624
11175
|
env.stdout.write(` ${gadget.description}
|
|
10625
11176
|
|
|
10626
11177
|
`);
|
|
10627
11178
|
if (gadget.parameterSchema) {
|
|
10628
|
-
env.stdout.write(
|
|
11179
|
+
env.stdout.write(chalk9.bold("Parameters:\n"));
|
|
10629
11180
|
const jsonSchema = schemaToJSONSchema2(gadget.parameterSchema, { target: "draft-7" });
|
|
10630
11181
|
env.stdout.write(formatSchemaAsText(jsonSchema, " ") + "\n\n");
|
|
10631
11182
|
} else {
|
|
10632
|
-
env.stdout.write(
|
|
11183
|
+
env.stdout.write(chalk9.dim("No parameters required.\n\n"));
|
|
10633
11184
|
}
|
|
10634
11185
|
if (gadget.timeoutMs) {
|
|
10635
|
-
env.stdout.write(
|
|
11186
|
+
env.stdout.write(chalk9.bold("Timeout:\n"));
|
|
10636
11187
|
env.stdout.write(` ${gadget.timeoutMs}ms
|
|
10637
11188
|
|
|
10638
11189
|
`);
|
|
10639
11190
|
}
|
|
10640
11191
|
if (gadget.examples && gadget.examples.length > 0) {
|
|
10641
|
-
env.stdout.write(
|
|
11192
|
+
env.stdout.write(chalk9.bold("Examples:\n"));
|
|
10642
11193
|
for (const example of gadget.examples) {
|
|
10643
11194
|
if (example.comment) {
|
|
10644
|
-
env.stdout.write(
|
|
11195
|
+
env.stdout.write(chalk9.dim(` # ${example.comment}
|
|
10645
11196
|
`));
|
|
10646
11197
|
}
|
|
10647
|
-
env.stdout.write(` Input: ${
|
|
11198
|
+
env.stdout.write(` Input: ${chalk9.cyan(JSON.stringify(example.params))}
|
|
10648
11199
|
`);
|
|
10649
11200
|
if (example.output !== void 0) {
|
|
10650
|
-
env.stdout.write(` Output: ${
|
|
11201
|
+
env.stdout.write(` Output: ${chalk9.green(example.output)}
|
|
10651
11202
|
`);
|
|
10652
11203
|
}
|
|
10653
11204
|
env.stdout.write("\n");
|
|
@@ -10680,27 +11231,27 @@ function formatSchemaAsText(schema, indent = "") {
|
|
|
10680
11231
|
const isRequired = required.includes(key);
|
|
10681
11232
|
const enumValues = prop.enum;
|
|
10682
11233
|
const defaultValue = prop.default;
|
|
10683
|
-
let line = `${indent}${
|
|
11234
|
+
let line = `${indent}${chalk9.cyan(key)}`;
|
|
10684
11235
|
if (isRequired) {
|
|
10685
|
-
line +=
|
|
11236
|
+
line += chalk9.red("*");
|
|
10686
11237
|
}
|
|
10687
11238
|
if (type === "array") {
|
|
10688
11239
|
const items = prop.items;
|
|
10689
11240
|
const itemType = items?.type || "any";
|
|
10690
|
-
line +=
|
|
11241
|
+
line += chalk9.dim(` (${itemType}[])`);
|
|
10691
11242
|
} else if (type === "object" && prop.properties) {
|
|
10692
|
-
line +=
|
|
11243
|
+
line += chalk9.dim(" (object)");
|
|
10693
11244
|
} else {
|
|
10694
|
-
line +=
|
|
11245
|
+
line += chalk9.dim(` (${type})`);
|
|
10695
11246
|
}
|
|
10696
11247
|
if (defaultValue !== void 0) {
|
|
10697
|
-
line +=
|
|
11248
|
+
line += chalk9.dim(` [default: ${JSON.stringify(defaultValue)}]`);
|
|
10698
11249
|
}
|
|
10699
11250
|
if (description) {
|
|
10700
11251
|
line += `: ${description}`;
|
|
10701
11252
|
}
|
|
10702
11253
|
if (enumValues) {
|
|
10703
|
-
line +=
|
|
11254
|
+
line += chalk9.yellow(` - one of: ${enumValues.join(", ")}`);
|
|
10704
11255
|
}
|
|
10705
11256
|
lines.push(line);
|
|
10706
11257
|
if (type === "object" && prop.properties) {
|
|
@@ -10740,20 +11291,20 @@ async function executeGadgetValidate(file, env) {
|
|
|
10740
11291
|
throw new Error(`Validation issues:
|
|
10741
11292
|
${issues.map((i) => ` - ${i}`).join("\n")}`);
|
|
10742
11293
|
}
|
|
10743
|
-
env.stdout.write(
|
|
10744
|
-
env.stdout.write(
|
|
11294
|
+
env.stdout.write(chalk9.green.bold("\n\u2713 Valid\n\n"));
|
|
11295
|
+
env.stdout.write(chalk9.bold("Gadgets found:\n"));
|
|
10745
11296
|
for (const gadget of gadgets) {
|
|
10746
11297
|
const name = gadget.name ?? gadget.constructor.name;
|
|
10747
|
-
const schemaInfo = gadget.parameterSchema ?
|
|
10748
|
-
env.stdout.write(` ${
|
|
11298
|
+
const schemaInfo = gadget.parameterSchema ? chalk9.cyan("(with schema)") : chalk9.dim("(no schema)");
|
|
11299
|
+
env.stdout.write(` ${chalk9.bold(name)} ${schemaInfo}
|
|
10749
11300
|
`);
|
|
10750
|
-
env.stdout.write(
|
|
11301
|
+
env.stdout.write(chalk9.dim(` ${gadget.description}
|
|
10751
11302
|
`));
|
|
10752
11303
|
}
|
|
10753
11304
|
env.stdout.write("\n");
|
|
10754
11305
|
} catch (error) {
|
|
10755
11306
|
const message = error instanceof Error ? error.message : String(error);
|
|
10756
|
-
env.stdout.write(
|
|
11307
|
+
env.stdout.write(chalk9.red.bold(`
|
|
10757
11308
|
\u2717 Invalid
|
|
10758
11309
|
|
|
10759
11310
|
`));
|
|
@@ -10937,7 +11488,7 @@ function registerInitCommand(program, env) {
|
|
|
10937
11488
|
}
|
|
10938
11489
|
|
|
10939
11490
|
// src/models-command.ts
|
|
10940
|
-
import
|
|
11491
|
+
import chalk10 from "chalk";
|
|
10941
11492
|
import { MODEL_ALIASES } from "llmist";
|
|
10942
11493
|
async function handleModelsCommand(options, env) {
|
|
10943
11494
|
const client = env.createClient();
|
|
@@ -10956,11 +11507,11 @@ async function handleModelsCommand(options, env) {
|
|
|
10956
11507
|
function renderAllTables(textModels, imageModels, speechModels, verbose, stream) {
|
|
10957
11508
|
const hasAnyModels = textModels.length > 0 || imageModels.length > 0 || speechModels.length > 0;
|
|
10958
11509
|
if (!hasAnyModels) {
|
|
10959
|
-
stream.write(
|
|
11510
|
+
stream.write(chalk10.yellow("\nNo models found matching the specified criteria.\n\n"));
|
|
10960
11511
|
return;
|
|
10961
11512
|
}
|
|
10962
|
-
stream.write(
|
|
10963
|
-
stream.write(
|
|
11513
|
+
stream.write(chalk10.bold.cyan("\nAvailable Models\n"));
|
|
11514
|
+
stream.write(chalk10.cyan("=".repeat(80)) + "\n\n");
|
|
10964
11515
|
if (textModels.length > 0) {
|
|
10965
11516
|
renderTextTable(textModels, verbose, stream);
|
|
10966
11517
|
}
|
|
@@ -10971,12 +11522,12 @@ function renderAllTables(textModels, imageModels, speechModels, verbose, stream)
|
|
|
10971
11522
|
renderSpeechTable(speechModels, verbose, stream);
|
|
10972
11523
|
}
|
|
10973
11524
|
if (textModels.length > 0) {
|
|
10974
|
-
stream.write(
|
|
10975
|
-
stream.write(
|
|
11525
|
+
stream.write(chalk10.bold.magenta("Model Shortcuts\n"));
|
|
11526
|
+
stream.write(chalk10.dim("\u2500".repeat(80)) + "\n");
|
|
10976
11527
|
const shortcuts = Object.entries(MODEL_ALIASES).sort((a, b) => a[0].localeCompare(b[0]));
|
|
10977
11528
|
for (const [shortcut, fullName] of shortcuts) {
|
|
10978
11529
|
stream.write(
|
|
10979
|
-
|
|
11530
|
+
chalk10.cyan(` ${shortcut.padEnd(15)}`) + chalk10.dim(" \u2192 ") + chalk10.white(fullName) + "\n"
|
|
10980
11531
|
);
|
|
10981
11532
|
}
|
|
10982
11533
|
stream.write("\n");
|
|
@@ -10991,13 +11542,13 @@ function renderTextTable(models, verbose, stream) {
|
|
|
10991
11542
|
}
|
|
10992
11543
|
grouped.get(provider).push(model);
|
|
10993
11544
|
}
|
|
10994
|
-
stream.write(
|
|
10995
|
-
stream.write(
|
|
11545
|
+
stream.write(chalk10.bold.blue("\u{1F4DD} Text/LLM Models\n"));
|
|
11546
|
+
stream.write(chalk10.dim("\u2500".repeat(80)) + "\n\n");
|
|
10996
11547
|
const providers = Array.from(grouped.keys()).sort();
|
|
10997
11548
|
for (const provider of providers) {
|
|
10998
11549
|
const providerModels = grouped.get(provider);
|
|
10999
11550
|
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
11000
|
-
stream.write(
|
|
11551
|
+
stream.write(chalk10.bold.yellow(`${providerName}
|
|
11001
11552
|
`));
|
|
11002
11553
|
if (verbose) {
|
|
11003
11554
|
renderVerboseTable(providerModels, stream);
|
|
@@ -11014,58 +11565,58 @@ function renderCompactTable(models, stream) {
|
|
|
11014
11565
|
const inputWidth = 10;
|
|
11015
11566
|
const outputWidth = 10;
|
|
11016
11567
|
stream.write(
|
|
11017
|
-
|
|
11568
|
+
chalk10.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n"
|
|
11018
11569
|
);
|
|
11019
11570
|
stream.write(
|
|
11020
|
-
|
|
11571
|
+
chalk10.bold(
|
|
11021
11572
|
"Model ID".padEnd(idWidth) + " " + "Display Name".padEnd(nameWidth) + " " + "Context".padEnd(contextWidth) + " " + "Input".padEnd(inputWidth) + " " + "Output".padEnd(outputWidth)
|
|
11022
11573
|
) + "\n"
|
|
11023
11574
|
);
|
|
11024
11575
|
stream.write(
|
|
11025
|
-
|
|
11576
|
+
chalk10.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n"
|
|
11026
11577
|
);
|
|
11027
11578
|
for (const model of models) {
|
|
11028
11579
|
const contextFormatted = formatTokensLong(model.contextWindow);
|
|
11029
11580
|
const inputPrice = `$${model.pricing.input.toFixed(2)}`;
|
|
11030
11581
|
const outputPrice = `$${model.pricing.output.toFixed(2)}`;
|
|
11031
11582
|
stream.write(
|
|
11032
|
-
|
|
11583
|
+
chalk10.green(model.modelId.padEnd(idWidth)) + " " + chalk10.white(model.displayName.padEnd(nameWidth)) + " " + chalk10.yellow(contextFormatted.padEnd(contextWidth)) + " " + chalk10.cyan(inputPrice.padEnd(inputWidth)) + " " + chalk10.cyan(outputPrice.padEnd(outputWidth)) + "\n"
|
|
11033
11584
|
);
|
|
11034
11585
|
}
|
|
11035
11586
|
stream.write(
|
|
11036
|
-
|
|
11587
|
+
chalk10.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n"
|
|
11037
11588
|
);
|
|
11038
|
-
stream.write(
|
|
11589
|
+
stream.write(chalk10.dim(` * Prices are per 1M tokens
|
|
11039
11590
|
`));
|
|
11040
11591
|
}
|
|
11041
11592
|
function renderVerboseTable(models, stream) {
|
|
11042
11593
|
for (const model of models) {
|
|
11043
|
-
stream.write(
|
|
11594
|
+
stream.write(chalk10.bold.green(`
|
|
11044
11595
|
${model.modelId}
|
|
11045
11596
|
`));
|
|
11046
|
-
stream.write(
|
|
11047
|
-
stream.write(` ${
|
|
11597
|
+
stream.write(chalk10.dim(" " + "\u2500".repeat(60)) + "\n");
|
|
11598
|
+
stream.write(` ${chalk10.dim("Name:")} ${chalk10.white(model.displayName)}
|
|
11048
11599
|
`);
|
|
11049
11600
|
stream.write(
|
|
11050
|
-
` ${
|
|
11601
|
+
` ${chalk10.dim("Context:")} ${chalk10.yellow(formatTokensLong(model.contextWindow))}
|
|
11051
11602
|
`
|
|
11052
11603
|
);
|
|
11053
11604
|
stream.write(
|
|
11054
|
-
` ${
|
|
11605
|
+
` ${chalk10.dim("Max Output:")} ${chalk10.yellow(formatTokensLong(model.maxOutputTokens))}
|
|
11055
11606
|
`
|
|
11056
11607
|
);
|
|
11057
11608
|
stream.write(
|
|
11058
|
-
` ${
|
|
11609
|
+
` ${chalk10.dim("Pricing:")} ${chalk10.cyan(`$${model.pricing.input.toFixed(2)} input`)} ${chalk10.dim("/")} ${chalk10.cyan(`$${model.pricing.output.toFixed(2)} output`)} ${chalk10.dim("(per 1M tokens)")}
|
|
11059
11610
|
`
|
|
11060
11611
|
);
|
|
11061
11612
|
if (model.pricing.cachedInput !== void 0) {
|
|
11062
11613
|
stream.write(
|
|
11063
|
-
` ${
|
|
11614
|
+
` ${chalk10.dim("Cached Input:")} ${chalk10.cyan(`$${model.pricing.cachedInput.toFixed(2)} per 1M tokens`)}
|
|
11064
11615
|
`
|
|
11065
11616
|
);
|
|
11066
11617
|
}
|
|
11067
11618
|
if (model.knowledgeCutoff) {
|
|
11068
|
-
stream.write(` ${
|
|
11619
|
+
stream.write(` ${chalk10.dim("Knowledge:")} ${model.knowledgeCutoff}
|
|
11069
11620
|
`);
|
|
11070
11621
|
}
|
|
11071
11622
|
const features = [];
|
|
@@ -11076,20 +11627,20 @@ function renderVerboseTable(models, stream) {
|
|
|
11076
11627
|
if (model.features.structuredOutputs) features.push("structured-outputs");
|
|
11077
11628
|
if (model.features.fineTuning) features.push("fine-tuning");
|
|
11078
11629
|
if (features.length > 0) {
|
|
11079
|
-
stream.write(` ${
|
|
11630
|
+
stream.write(` ${chalk10.dim("Features:")} ${chalk10.blue(features.join(", "))}
|
|
11080
11631
|
`);
|
|
11081
11632
|
}
|
|
11082
11633
|
if (model.metadata) {
|
|
11083
11634
|
if (model.metadata.family) {
|
|
11084
|
-
stream.write(` ${
|
|
11635
|
+
stream.write(` ${chalk10.dim("Family:")} ${model.metadata.family}
|
|
11085
11636
|
`);
|
|
11086
11637
|
}
|
|
11087
11638
|
if (model.metadata.releaseDate) {
|
|
11088
|
-
stream.write(` ${
|
|
11639
|
+
stream.write(` ${chalk10.dim("Released:")} ${model.metadata.releaseDate}
|
|
11089
11640
|
`);
|
|
11090
11641
|
}
|
|
11091
11642
|
if (model.metadata.notes) {
|
|
11092
|
-
stream.write(` ${
|
|
11643
|
+
stream.write(` ${chalk10.dim("Notes:")} ${chalk10.italic(model.metadata.notes)}
|
|
11093
11644
|
`);
|
|
11094
11645
|
}
|
|
11095
11646
|
}
|
|
@@ -11097,8 +11648,8 @@ function renderVerboseTable(models, stream) {
|
|
|
11097
11648
|
stream.write("\n");
|
|
11098
11649
|
}
|
|
11099
11650
|
function renderImageTable(models, verbose, stream) {
|
|
11100
|
-
stream.write(
|
|
11101
|
-
stream.write(
|
|
11651
|
+
stream.write(chalk10.bold.green("\u{1F3A8} Image Generation Models\n"));
|
|
11652
|
+
stream.write(chalk10.dim("\u2500".repeat(80)) + "\n\n");
|
|
11102
11653
|
const grouped = /* @__PURE__ */ new Map();
|
|
11103
11654
|
for (const model of models) {
|
|
11104
11655
|
if (!grouped.has(model.provider)) {
|
|
@@ -11108,29 +11659,29 @@ function renderImageTable(models, verbose, stream) {
|
|
|
11108
11659
|
}
|
|
11109
11660
|
for (const [provider, providerModels] of Array.from(grouped.entries()).sort()) {
|
|
11110
11661
|
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
11111
|
-
stream.write(
|
|
11662
|
+
stream.write(chalk10.bold.yellow(`${providerName}
|
|
11112
11663
|
`));
|
|
11113
11664
|
if (verbose) {
|
|
11114
11665
|
for (const model of providerModels) {
|
|
11115
|
-
stream.write(
|
|
11666
|
+
stream.write(chalk10.bold.green(`
|
|
11116
11667
|
${model.modelId}
|
|
11117
11668
|
`));
|
|
11118
|
-
stream.write(
|
|
11119
|
-
stream.write(` ${
|
|
11669
|
+
stream.write(chalk10.dim(" " + "\u2500".repeat(60)) + "\n");
|
|
11670
|
+
stream.write(` ${chalk10.dim("Name:")} ${chalk10.white(model.displayName)}
|
|
11120
11671
|
`);
|
|
11121
11672
|
stream.write(
|
|
11122
|
-
` ${
|
|
11673
|
+
` ${chalk10.dim("Sizes:")} ${chalk10.yellow(model.supportedSizes.join(", "))}
|
|
11123
11674
|
`
|
|
11124
11675
|
);
|
|
11125
11676
|
if (model.supportedQualities) {
|
|
11126
11677
|
stream.write(
|
|
11127
|
-
` ${
|
|
11678
|
+
` ${chalk10.dim("Qualities:")} ${chalk10.yellow(model.supportedQualities.join(", "))}
|
|
11128
11679
|
`
|
|
11129
11680
|
);
|
|
11130
11681
|
}
|
|
11131
|
-
stream.write(` ${
|
|
11682
|
+
stream.write(` ${chalk10.dim("Max Images:")} ${chalk10.yellow(model.maxImages.toString())}
|
|
11132
11683
|
`);
|
|
11133
|
-
stream.write(` ${
|
|
11684
|
+
stream.write(` ${chalk10.dim("Pricing:")} ${chalk10.cyan(formatImagePrice(model))}
|
|
11134
11685
|
`);
|
|
11135
11686
|
if (model.features) {
|
|
11136
11687
|
const features = [];
|
|
@@ -11138,7 +11689,7 @@ function renderImageTable(models, verbose, stream) {
|
|
|
11138
11689
|
if (model.features.transparency) features.push("transparency");
|
|
11139
11690
|
if (model.features.conversational) features.push("conversational");
|
|
11140
11691
|
if (features.length > 0) {
|
|
11141
|
-
stream.write(` ${
|
|
11692
|
+
stream.write(` ${chalk10.dim("Features:")} ${chalk10.blue(features.join(", "))}
|
|
11142
11693
|
`);
|
|
11143
11694
|
}
|
|
11144
11695
|
}
|
|
@@ -11148,27 +11699,27 @@ function renderImageTable(models, verbose, stream) {
|
|
|
11148
11699
|
const nameWidth = 25;
|
|
11149
11700
|
const sizesWidth = 20;
|
|
11150
11701
|
const priceWidth = 15;
|
|
11151
|
-
stream.write(
|
|
11702
|
+
stream.write(chalk10.dim("\u2500".repeat(idWidth + nameWidth + sizesWidth + priceWidth + 6)) + "\n");
|
|
11152
11703
|
stream.write(
|
|
11153
|
-
|
|
11704
|
+
chalk10.bold(
|
|
11154
11705
|
"Model ID".padEnd(idWidth) + " " + "Display Name".padEnd(nameWidth) + " " + "Sizes".padEnd(sizesWidth) + " " + "Price".padEnd(priceWidth)
|
|
11155
11706
|
) + "\n"
|
|
11156
11707
|
);
|
|
11157
|
-
stream.write(
|
|
11708
|
+
stream.write(chalk10.dim("\u2500".repeat(idWidth + nameWidth + sizesWidth + priceWidth + 6)) + "\n");
|
|
11158
11709
|
for (const model of providerModels) {
|
|
11159
11710
|
const sizes = model.supportedSizes.length > 2 ? model.supportedSizes.slice(0, 2).join(", ") + "..." : model.supportedSizes.join(", ");
|
|
11160
11711
|
stream.write(
|
|
11161
|
-
|
|
11712
|
+
chalk10.green(model.modelId.padEnd(idWidth)) + " " + chalk10.white(model.displayName.substring(0, nameWidth - 1).padEnd(nameWidth)) + " " + chalk10.yellow(sizes.padEnd(sizesWidth)) + " " + chalk10.cyan(formatImagePrice(model).padEnd(priceWidth)) + "\n"
|
|
11162
11713
|
);
|
|
11163
11714
|
}
|
|
11164
|
-
stream.write(
|
|
11715
|
+
stream.write(chalk10.dim("\u2500".repeat(idWidth + nameWidth + sizesWidth + priceWidth + 6)) + "\n");
|
|
11165
11716
|
}
|
|
11166
11717
|
stream.write("\n");
|
|
11167
11718
|
}
|
|
11168
11719
|
}
|
|
11169
11720
|
function renderSpeechTable(models, verbose, stream) {
|
|
11170
|
-
stream.write(
|
|
11171
|
-
stream.write(
|
|
11721
|
+
stream.write(chalk10.bold.magenta("\u{1F3A4} Speech (TTS) Models\n"));
|
|
11722
|
+
stream.write(chalk10.dim("\u2500".repeat(80)) + "\n\n");
|
|
11172
11723
|
const grouped = /* @__PURE__ */ new Map();
|
|
11173
11724
|
for (const model of models) {
|
|
11174
11725
|
if (!grouped.has(model.provider)) {
|
|
@@ -11178,34 +11729,34 @@ function renderSpeechTable(models, verbose, stream) {
|
|
|
11178
11729
|
}
|
|
11179
11730
|
for (const [provider, providerModels] of Array.from(grouped.entries()).sort()) {
|
|
11180
11731
|
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
11181
|
-
stream.write(
|
|
11732
|
+
stream.write(chalk10.bold.yellow(`${providerName}
|
|
11182
11733
|
`));
|
|
11183
11734
|
if (verbose) {
|
|
11184
11735
|
for (const model of providerModels) {
|
|
11185
|
-
stream.write(
|
|
11736
|
+
stream.write(chalk10.bold.green(`
|
|
11186
11737
|
${model.modelId}
|
|
11187
11738
|
`));
|
|
11188
|
-
stream.write(
|
|
11189
|
-
stream.write(` ${
|
|
11739
|
+
stream.write(chalk10.dim(" " + "\u2500".repeat(60)) + "\n");
|
|
11740
|
+
stream.write(` ${chalk10.dim("Name:")} ${chalk10.white(model.displayName)}
|
|
11190
11741
|
`);
|
|
11191
11742
|
stream.write(
|
|
11192
|
-
` ${
|
|
11743
|
+
` ${chalk10.dim("Voices:")} ${chalk10.yellow(model.voices.length.toString())} voices
|
|
11193
11744
|
`
|
|
11194
11745
|
);
|
|
11195
11746
|
if (model.voices.length <= 6) {
|
|
11196
|
-
stream.write(` ${
|
|
11747
|
+
stream.write(` ${chalk10.dim(model.voices.join(", "))}
|
|
11197
11748
|
`);
|
|
11198
11749
|
} else {
|
|
11199
|
-
stream.write(` ${
|
|
11750
|
+
stream.write(` ${chalk10.dim(model.voices.slice(0, 6).join(", ") + "...")}
|
|
11200
11751
|
`);
|
|
11201
11752
|
}
|
|
11202
|
-
stream.write(` ${
|
|
11753
|
+
stream.write(` ${chalk10.dim("Formats:")} ${chalk10.yellow(model.formats.join(", "))}
|
|
11203
11754
|
`);
|
|
11204
11755
|
stream.write(
|
|
11205
|
-
` ${
|
|
11756
|
+
` ${chalk10.dim("Max Input:")} ${chalk10.yellow(model.maxInputLength.toString())} chars
|
|
11206
11757
|
`
|
|
11207
11758
|
);
|
|
11208
|
-
stream.write(` ${
|
|
11759
|
+
stream.write(` ${chalk10.dim("Pricing:")} ${chalk10.cyan(formatSpeechPrice(model))}
|
|
11209
11760
|
`);
|
|
11210
11761
|
if (model.features) {
|
|
11211
11762
|
const features = [];
|
|
@@ -11213,7 +11764,7 @@ function renderSpeechTable(models, verbose, stream) {
|
|
|
11213
11764
|
if (model.features.voiceInstructions) features.push("voice-instructions");
|
|
11214
11765
|
if (model.features.languages) features.push(`${model.features.languages} languages`);
|
|
11215
11766
|
if (features.length > 0) {
|
|
11216
|
-
stream.write(` ${
|
|
11767
|
+
stream.write(` ${chalk10.dim("Features:")} ${chalk10.blue(features.join(", "))}
|
|
11217
11768
|
`);
|
|
11218
11769
|
}
|
|
11219
11770
|
}
|
|
@@ -11224,23 +11775,23 @@ function renderSpeechTable(models, verbose, stream) {
|
|
|
11224
11775
|
const voicesWidth = 12;
|
|
11225
11776
|
const priceWidth = 18;
|
|
11226
11777
|
stream.write(
|
|
11227
|
-
|
|
11778
|
+
chalk10.dim("\u2500".repeat(idWidth + nameWidth + voicesWidth + priceWidth + 6)) + "\n"
|
|
11228
11779
|
);
|
|
11229
11780
|
stream.write(
|
|
11230
|
-
|
|
11781
|
+
chalk10.bold(
|
|
11231
11782
|
"Model ID".padEnd(idWidth) + " " + "Display Name".padEnd(nameWidth) + " " + "Voices".padEnd(voicesWidth) + " " + "Price".padEnd(priceWidth)
|
|
11232
11783
|
) + "\n"
|
|
11233
11784
|
);
|
|
11234
11785
|
stream.write(
|
|
11235
|
-
|
|
11786
|
+
chalk10.dim("\u2500".repeat(idWidth + nameWidth + voicesWidth + priceWidth + 6)) + "\n"
|
|
11236
11787
|
);
|
|
11237
11788
|
for (const model of providerModels) {
|
|
11238
11789
|
stream.write(
|
|
11239
|
-
|
|
11790
|
+
chalk10.green(model.modelId.padEnd(idWidth)) + " " + chalk10.white(model.displayName.substring(0, nameWidth - 1).padEnd(nameWidth)) + " " + chalk10.yellow(`${model.voices.length} voices`.padEnd(voicesWidth)) + " " + chalk10.cyan(formatSpeechPrice(model).padEnd(priceWidth)) + "\n"
|
|
11240
11791
|
);
|
|
11241
11792
|
}
|
|
11242
11793
|
stream.write(
|
|
11243
|
-
|
|
11794
|
+
chalk10.dim("\u2500".repeat(idWidth + nameWidth + voicesWidth + priceWidth + 6)) + "\n"
|
|
11244
11795
|
);
|
|
11245
11796
|
}
|
|
11246
11797
|
stream.write("\n");
|
|
@@ -11332,8 +11883,8 @@ function registerModelsCommand(program, env) {
|
|
|
11332
11883
|
// src/session.ts
|
|
11333
11884
|
import { existsSync as existsSync4 } from "fs";
|
|
11334
11885
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
11335
|
-
import { homedir as
|
|
11336
|
-
import { join as
|
|
11886
|
+
import { homedir as homedir4 } from "os";
|
|
11887
|
+
import { join as join6 } from "path";
|
|
11337
11888
|
|
|
11338
11889
|
// src/session-names.ts
|
|
11339
11890
|
var ADJECTIVES = [
|
|
@@ -11468,16 +12019,16 @@ function generateSessionName() {
|
|
|
11468
12019
|
|
|
11469
12020
|
// src/session.ts
|
|
11470
12021
|
var currentSession;
|
|
11471
|
-
var SESSION_LOGS_BASE =
|
|
12022
|
+
var SESSION_LOGS_BASE = join6(homedir4(), ".llmist", "logs");
|
|
11472
12023
|
function findUniqueName(baseName) {
|
|
11473
|
-
const baseDir =
|
|
12024
|
+
const baseDir = join6(SESSION_LOGS_BASE, baseName);
|
|
11474
12025
|
if (!existsSync4(baseDir)) {
|
|
11475
12026
|
return baseName;
|
|
11476
12027
|
}
|
|
11477
12028
|
let suffix = 2;
|
|
11478
12029
|
while (suffix < 1e3) {
|
|
11479
12030
|
const name = `${baseName}-${suffix}`;
|
|
11480
|
-
const dir =
|
|
12031
|
+
const dir = join6(SESSION_LOGS_BASE, name);
|
|
11481
12032
|
if (!existsSync4(dir)) {
|
|
11482
12033
|
return name;
|
|
11483
12034
|
}
|
|
@@ -11491,12 +12042,101 @@ async function initSession() {
|
|
|
11491
12042
|
}
|
|
11492
12043
|
const baseName = generateSessionName();
|
|
11493
12044
|
const name = findUniqueName(baseName);
|
|
11494
|
-
const logDir =
|
|
12045
|
+
const logDir = join6(SESSION_LOGS_BASE, name);
|
|
11495
12046
|
await mkdir2(logDir, { recursive: true });
|
|
11496
12047
|
currentSession = { name, logDir };
|
|
11497
12048
|
return currentSession;
|
|
11498
12049
|
}
|
|
11499
12050
|
|
|
12051
|
+
// src/skills/skill-command.ts
|
|
12052
|
+
function registerSkillCommand(program, env) {
|
|
12053
|
+
const skillCmd = program.command("skill").description("Manage Agent Skills (SKILL.md)");
|
|
12054
|
+
skillCmd.command("list").description("List available skills").action(async () => {
|
|
12055
|
+
const config = safeLoadConfig();
|
|
12056
|
+
const manager = new CLISkillManager();
|
|
12057
|
+
const registry = await manager.loadAll(config?.skills);
|
|
12058
|
+
const skills = registry.getAll();
|
|
12059
|
+
if (skills.length === 0) {
|
|
12060
|
+
env.stdout.write("No skills found.\n");
|
|
12061
|
+
env.stdout.write(
|
|
12062
|
+
"Add skills to ~/.llmist/skills/ or configure [skills].sources in ~/.llmist/cli.toml\n"
|
|
12063
|
+
);
|
|
12064
|
+
return;
|
|
12065
|
+
}
|
|
12066
|
+
env.stdout.write(`Found ${skills.length} skill(s):
|
|
12067
|
+
|
|
12068
|
+
`);
|
|
12069
|
+
for (const skill of skills) {
|
|
12070
|
+
const flags = [];
|
|
12071
|
+
if (!skill.isModelInvocable) flags.push("user-only");
|
|
12072
|
+
if (!skill.isUserInvocable) flags.push("background");
|
|
12073
|
+
if (skill.metadata.context === "fork") flags.push("fork");
|
|
12074
|
+
if (skill.metadata.model) flags.push(`model:${skill.metadata.model}`);
|
|
12075
|
+
const flagStr = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
|
|
12076
|
+
const desc = skill.description.length > 80 ? `${skill.description.slice(0, 77)}...` : skill.description;
|
|
12077
|
+
env.stdout.write(` /${skill.name}${flagStr}
|
|
12078
|
+
`);
|
|
12079
|
+
env.stdout.write(` ${desc}
|
|
12080
|
+
|
|
12081
|
+
`);
|
|
12082
|
+
}
|
|
12083
|
+
});
|
|
12084
|
+
skillCmd.command("info <name>").description("Show detailed skill information").action(async (name) => {
|
|
12085
|
+
const config = safeLoadConfig();
|
|
12086
|
+
const manager = new CLISkillManager();
|
|
12087
|
+
const registry = await manager.loadAll(config?.skills);
|
|
12088
|
+
const skill = registry.get(name);
|
|
12089
|
+
if (!skill) {
|
|
12090
|
+
env.stderr.write(`Skill not found: ${name}
|
|
12091
|
+
`);
|
|
12092
|
+
env.stderr.write(`Available skills: ${registry.getNames().join(", ") || "(none)"}
|
|
12093
|
+
`);
|
|
12094
|
+
process.exitCode = 1;
|
|
12095
|
+
return;
|
|
12096
|
+
}
|
|
12097
|
+
env.stdout.write(`Skill: ${skill.name}
|
|
12098
|
+
`);
|
|
12099
|
+
env.stdout.write(`Description: ${skill.description}
|
|
12100
|
+
`);
|
|
12101
|
+
env.stdout.write(`Source: ${skill.sourcePath}
|
|
12102
|
+
`);
|
|
12103
|
+
if (skill.metadata.model) env.stdout.write(`Model: ${skill.metadata.model}
|
|
12104
|
+
`);
|
|
12105
|
+
if (skill.metadata.context) env.stdout.write(`Context: ${skill.metadata.context}
|
|
12106
|
+
`);
|
|
12107
|
+
if (skill.metadata.agent) env.stdout.write(`Agent: ${skill.metadata.agent}
|
|
12108
|
+
`);
|
|
12109
|
+
if (skill.metadata.allowedTools) {
|
|
12110
|
+
env.stdout.write(`Allowed tools: ${skill.metadata.allowedTools.join(", ")}
|
|
12111
|
+
`);
|
|
12112
|
+
}
|
|
12113
|
+
if (skill.metadata.paths) {
|
|
12114
|
+
env.stdout.write(`Paths: ${skill.metadata.paths.join(", ")}
|
|
12115
|
+
`);
|
|
12116
|
+
}
|
|
12117
|
+
const resources = skill.getResources();
|
|
12118
|
+
if (resources.length > 0) {
|
|
12119
|
+
env.stdout.write(`Resources: ${resources.length}
|
|
12120
|
+
`);
|
|
12121
|
+
for (const r of resources) {
|
|
12122
|
+
env.stdout.write(` ${r.category}/${r.relativePath}
|
|
12123
|
+
`);
|
|
12124
|
+
}
|
|
12125
|
+
}
|
|
12126
|
+
env.stdout.write("\n--- Instructions ---\n\n");
|
|
12127
|
+
const instructions = await skill.getInstructions();
|
|
12128
|
+
env.stdout.write(`${instructions}
|
|
12129
|
+
`);
|
|
12130
|
+
});
|
|
12131
|
+
}
|
|
12132
|
+
function safeLoadConfig() {
|
|
12133
|
+
try {
|
|
12134
|
+
return loadConfig();
|
|
12135
|
+
} catch {
|
|
12136
|
+
return void 0;
|
|
12137
|
+
}
|
|
12138
|
+
}
|
|
12139
|
+
|
|
11500
12140
|
// src/speech-command.ts
|
|
11501
12141
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
11502
12142
|
var DEFAULT_SPEECH_MODEL = "tts-1";
|
|
@@ -11608,6 +12248,7 @@ function createProgram(env, config) {
|
|
|
11608
12248
|
registerVisionCommand(program, env);
|
|
11609
12249
|
registerModelsCommand(program, env);
|
|
11610
12250
|
registerGadgetCommand(program, env);
|
|
12251
|
+
registerSkillCommand(program, env);
|
|
11611
12252
|
registerInitCommand(program, env);
|
|
11612
12253
|
registerConfigCommand(program, env, config);
|
|
11613
12254
|
if (config) {
|