llmist 2.6.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/{chunk-364PEMVT.js → chunk-67MMSOAT.js} +221 -174
- package/dist/chunk-67MMSOAT.js.map +1 -0
- package/dist/{chunk-4IHLIYW5.js → chunk-NBPKLSXJ.js} +6 -6
- package/dist/chunk-NBPKLSXJ.js.map +1 -0
- package/dist/cli.cjs +721 -219
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +501 -45
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +230 -182
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -52
- package/dist/index.d.ts +100 -52
- package/dist/index.js +20 -18
- package/dist/{mock-stream-Jgg5u6Uf.d.cts → mock-stream-COHw8h9b.d.cts} +182 -54
- package/dist/{mock-stream-Jgg5u6Uf.d.ts → mock-stream-COHw8h9b.d.ts} +182 -54
- package/dist/testing/index.cjs +212 -166
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +6 -6
- package/dist/testing/index.d.ts +6 -6
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-364PEMVT.js.map +0 -1
- package/dist/chunk-4IHLIYW5.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-NBPKLSXJ.js";
|
|
3
3
|
import {
|
|
4
|
+
AbstractGadget,
|
|
4
5
|
AgentBuilder,
|
|
5
|
-
BaseGadget,
|
|
6
|
-
BreakLoopException,
|
|
7
6
|
FALLBACK_CHARS_PER_TOKEN,
|
|
8
7
|
GadgetRegistry,
|
|
9
|
-
|
|
8
|
+
HumanInputRequiredException,
|
|
10
9
|
LLMMessageBuilder,
|
|
11
10
|
LLMist,
|
|
12
11
|
MODEL_ALIASES,
|
|
12
|
+
TaskCompletionSignal,
|
|
13
13
|
audioFromBuffer,
|
|
14
14
|
createGadget,
|
|
15
15
|
createLogger,
|
|
16
16
|
detectAudioMimeType,
|
|
17
17
|
detectImageMimeType,
|
|
18
|
-
|
|
18
|
+
extractMessageText,
|
|
19
19
|
imageFromBuffer,
|
|
20
20
|
init_builder,
|
|
21
21
|
init_client,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
schemaToJSONSchema,
|
|
35
35
|
text,
|
|
36
36
|
validateGadgetSchema
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-67MMSOAT.js";
|
|
38
38
|
|
|
39
39
|
// src/cli/constants.ts
|
|
40
40
|
var CLI_NAME = "llmist";
|
|
@@ -46,7 +46,8 @@ var COMMANDS = {
|
|
|
46
46
|
gadget: "gadget",
|
|
47
47
|
image: "image",
|
|
48
48
|
speech: "speech",
|
|
49
|
-
vision: "vision"
|
|
49
|
+
vision: "vision",
|
|
50
|
+
init: "init"
|
|
50
51
|
};
|
|
51
52
|
var LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
|
|
52
53
|
var DEFAULT_MODEL = "openai:gpt-5-nano";
|
|
@@ -122,7 +123,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
|
|
|
122
123
|
// package.json
|
|
123
124
|
var package_default = {
|
|
124
125
|
name: "llmist",
|
|
125
|
-
version: "
|
|
126
|
+
version: "3.0.0",
|
|
126
127
|
description: "TypeScript LLM client with streaming tool execution. Tools fire mid-stream. Built-in function calling works with any model\u2014no structured outputs or native tool support required.",
|
|
127
128
|
type: "module",
|
|
128
129
|
main: "dist/index.cjs",
|
|
@@ -530,7 +531,7 @@ var askUser = createGadget({
|
|
|
530
531
|
}
|
|
531
532
|
],
|
|
532
533
|
execute: ({ question }) => {
|
|
533
|
-
throw new
|
|
534
|
+
throw new HumanInputRequiredException(question);
|
|
534
535
|
}
|
|
535
536
|
});
|
|
536
537
|
var tellUser = createGadget({
|
|
@@ -580,11 +581,59 @@ var finish = createGadget({
|
|
|
580
581
|
}
|
|
581
582
|
],
|
|
582
583
|
execute: () => {
|
|
583
|
-
throw new
|
|
584
|
+
throw new TaskCompletionSignal("Task completed");
|
|
584
585
|
}
|
|
585
586
|
});
|
|
586
587
|
var builtinGadgets = [askUser, tellUser, finish];
|
|
587
588
|
|
|
589
|
+
// src/cli/subagent-config.ts
|
|
590
|
+
var INHERIT_MODEL = "inherit";
|
|
591
|
+
function resolveSubagentConfig(subagentName, parentModel, profileConfig, globalConfig) {
|
|
592
|
+
const resolved = {};
|
|
593
|
+
const globalDefaultModel = globalConfig?.["default-model"];
|
|
594
|
+
const globalSubagent = extractSubagentConfig(globalConfig, subagentName);
|
|
595
|
+
const profileSubagent = profileConfig?.[subagentName] ?? {};
|
|
596
|
+
const merged = { ...globalSubagent, ...profileSubagent };
|
|
597
|
+
const configModel = merged.model ?? globalDefaultModel ?? INHERIT_MODEL;
|
|
598
|
+
resolved.model = configModel === INHERIT_MODEL ? parentModel : configModel;
|
|
599
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
600
|
+
if (key !== "model") {
|
|
601
|
+
resolved[key] = value;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return resolved;
|
|
605
|
+
}
|
|
606
|
+
function buildSubagentConfigMap(parentModel, profileConfig, globalConfig) {
|
|
607
|
+
const subagentNames = /* @__PURE__ */ new Set();
|
|
608
|
+
if (globalConfig) {
|
|
609
|
+
for (const key of Object.keys(globalConfig)) {
|
|
610
|
+
if (key !== "default-model" && typeof globalConfig[key] === "object") {
|
|
611
|
+
subagentNames.add(key);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (profileConfig) {
|
|
616
|
+
for (const key of Object.keys(profileConfig)) {
|
|
617
|
+
subagentNames.add(key);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const result = {};
|
|
621
|
+
for (const name of subagentNames) {
|
|
622
|
+
result[name] = resolveSubagentConfig(name, parentModel, profileConfig, globalConfig);
|
|
623
|
+
}
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
function extractSubagentConfig(globalConfig, subagentName) {
|
|
627
|
+
if (!globalConfig) {
|
|
628
|
+
return {};
|
|
629
|
+
}
|
|
630
|
+
const value = globalConfig[subagentName];
|
|
631
|
+
if (typeof value === "object" && value !== null) {
|
|
632
|
+
return value;
|
|
633
|
+
}
|
|
634
|
+
return {};
|
|
635
|
+
}
|
|
636
|
+
|
|
588
637
|
// src/cli/config.ts
|
|
589
638
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
590
639
|
import { homedir } from "node:os";
|
|
@@ -673,7 +722,7 @@ function hasTemplateSyntax(str) {
|
|
|
673
722
|
}
|
|
674
723
|
|
|
675
724
|
// src/cli/config.ts
|
|
676
|
-
var
|
|
725
|
+
var VALID_PERMISSION_LEVELS = ["allowed", "denied", "approval-required"];
|
|
677
726
|
var GLOBAL_CONFIG_KEYS = /* @__PURE__ */ new Set(["log-level", "log-file", "log-reset"]);
|
|
678
727
|
var VALID_LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
|
|
679
728
|
var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -713,6 +762,8 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
713
762
|
"gadget-end-prefix",
|
|
714
763
|
"gadget-arg-prefix",
|
|
715
764
|
"gadget-approval",
|
|
765
|
+
"subagents",
|
|
766
|
+
// Per-subagent configuration overrides
|
|
716
767
|
"quiet",
|
|
717
768
|
"inherits",
|
|
718
769
|
"log-level",
|
|
@@ -738,9 +789,9 @@ function getConfigPath() {
|
|
|
738
789
|
return join(homedir(), ".llmist", "cli.toml");
|
|
739
790
|
}
|
|
740
791
|
var ConfigError = class extends Error {
|
|
741
|
-
constructor(message,
|
|
742
|
-
super(
|
|
743
|
-
this.path =
|
|
792
|
+
constructor(message, path6) {
|
|
793
|
+
super(path6 ? `${path6}: ${message}` : message);
|
|
794
|
+
this.path = path6;
|
|
744
795
|
this.name = "ConfigError";
|
|
745
796
|
}
|
|
746
797
|
};
|
|
@@ -796,6 +847,63 @@ function validateInherits(value, section) {
|
|
|
796
847
|
}
|
|
797
848
|
throw new ConfigError(`[${section}].inherits must be a string or array of strings`);
|
|
798
849
|
}
|
|
850
|
+
function validateSingleSubagentConfig(value, subagentName, section) {
|
|
851
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
852
|
+
throw new ConfigError(
|
|
853
|
+
`[${section}].${subagentName} must be a table (e.g., { model = "inherit", maxIterations = 20 })`
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
const result = {};
|
|
857
|
+
const rawObj = value;
|
|
858
|
+
for (const [key, val] of Object.entries(rawObj)) {
|
|
859
|
+
if (key === "model") {
|
|
860
|
+
if (typeof val !== "string") {
|
|
861
|
+
throw new ConfigError(`[${section}].${subagentName}.model must be a string`);
|
|
862
|
+
}
|
|
863
|
+
result.model = val;
|
|
864
|
+
} else if (key === "maxIterations") {
|
|
865
|
+
if (typeof val !== "number" || !Number.isInteger(val) || val < 1) {
|
|
866
|
+
throw new ConfigError(
|
|
867
|
+
`[${section}].${subagentName}.maxIterations must be a positive integer`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
result.maxIterations = val;
|
|
871
|
+
} else {
|
|
872
|
+
result[key] = val;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return result;
|
|
876
|
+
}
|
|
877
|
+
function validateSubagentConfigMap(value, section) {
|
|
878
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
879
|
+
throw new ConfigError(
|
|
880
|
+
`[${section}].subagents must be a table (e.g., { BrowseWeb = { model = "inherit" } })`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
const result = {};
|
|
884
|
+
for (const [subagentName, config] of Object.entries(value)) {
|
|
885
|
+
result[subagentName] = validateSingleSubagentConfig(config, subagentName, `${section}.subagents`);
|
|
886
|
+
}
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
function validateGlobalSubagentConfig(value, section) {
|
|
890
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
891
|
+
throw new ConfigError(`[${section}] must be a table`);
|
|
892
|
+
}
|
|
893
|
+
const result = {};
|
|
894
|
+
const rawObj = value;
|
|
895
|
+
for (const [key, val] of Object.entries(rawObj)) {
|
|
896
|
+
if (key === "default-model") {
|
|
897
|
+
if (typeof val !== "string") {
|
|
898
|
+
throw new ConfigError(`[${section}].default-model must be a string`);
|
|
899
|
+
}
|
|
900
|
+
result["default-model"] = val;
|
|
901
|
+
} else {
|
|
902
|
+
result[key] = validateSingleSubagentConfig(val, key, section);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
return result;
|
|
906
|
+
}
|
|
799
907
|
function validateGadgetApproval(value, section) {
|
|
800
908
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
801
909
|
throw new ConfigError(
|
|
@@ -807,9 +915,9 @@ function validateGadgetApproval(value, section) {
|
|
|
807
915
|
if (typeof mode !== "string") {
|
|
808
916
|
throw new ConfigError(`[${section}].gadget-approval.${gadgetName} must be a string`);
|
|
809
917
|
}
|
|
810
|
-
if (!
|
|
918
|
+
if (!VALID_PERMISSION_LEVELS.includes(mode)) {
|
|
811
919
|
throw new ConfigError(
|
|
812
|
-
`[${section}].gadget-approval.${gadgetName} must be one of: ${
|
|
920
|
+
`[${section}].gadget-approval.${gadgetName} must be one of: ${VALID_PERMISSION_LEVELS.join(", ")}`
|
|
813
921
|
);
|
|
814
922
|
}
|
|
815
923
|
result[gadgetName] = mode;
|
|
@@ -978,6 +1086,9 @@ function validateAgentConfig(raw, section) {
|
|
|
978
1086
|
if ("gadget-approval" in rawObj) {
|
|
979
1087
|
result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
|
|
980
1088
|
}
|
|
1089
|
+
if ("subagents" in rawObj) {
|
|
1090
|
+
result.subagents = validateSubagentConfigMap(rawObj.subagents, section);
|
|
1091
|
+
}
|
|
981
1092
|
if ("quiet" in rawObj) {
|
|
982
1093
|
result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
|
|
983
1094
|
}
|
|
@@ -1146,6 +1257,9 @@ function validateCustomConfig(raw, section) {
|
|
|
1146
1257
|
if ("gadget-approval" in rawObj) {
|
|
1147
1258
|
result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
|
|
1148
1259
|
}
|
|
1260
|
+
if ("subagents" in rawObj) {
|
|
1261
|
+
result.subagents = validateSubagentConfigMap(rawObj.subagents, section);
|
|
1262
|
+
}
|
|
1149
1263
|
if ("max-tokens" in rawObj) {
|
|
1150
1264
|
result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
|
|
1151
1265
|
integer: true,
|
|
@@ -1193,6 +1307,8 @@ function validateConfig(raw, configPath) {
|
|
|
1193
1307
|
result.prompts = validatePromptsConfig(value, key);
|
|
1194
1308
|
} else if (key === "docker") {
|
|
1195
1309
|
result.docker = validateDockerConfig(value, key);
|
|
1310
|
+
} else if (key === "subagents") {
|
|
1311
|
+
result.subagents = validateGlobalSubagentConfig(value, key);
|
|
1196
1312
|
} else {
|
|
1197
1313
|
result[key] = validateCustomConfig(value, key);
|
|
1198
1314
|
}
|
|
@@ -1233,7 +1349,16 @@ function loadConfig() {
|
|
|
1233
1349
|
return resolveTemplatesInConfig(inherited, configPath);
|
|
1234
1350
|
}
|
|
1235
1351
|
function getCustomCommandNames(config) {
|
|
1236
|
-
const reserved = /* @__PURE__ */ new Set([
|
|
1352
|
+
const reserved = /* @__PURE__ */ new Set([
|
|
1353
|
+
"global",
|
|
1354
|
+
"complete",
|
|
1355
|
+
"agent",
|
|
1356
|
+
"image",
|
|
1357
|
+
"speech",
|
|
1358
|
+
"prompts",
|
|
1359
|
+
"docker",
|
|
1360
|
+
"subagents"
|
|
1361
|
+
]);
|
|
1237
1362
|
return Object.keys(config).filter((key) => !reserved.has(key));
|
|
1238
1363
|
}
|
|
1239
1364
|
function resolveTemplatesInConfig(config, configPath) {
|
|
@@ -1767,11 +1892,11 @@ function resolveDevMode(config, cliDevMode) {
|
|
|
1767
1892
|
}
|
|
1768
1893
|
return { enabled: true, sourcePath };
|
|
1769
1894
|
}
|
|
1770
|
-
function expandHome(
|
|
1771
|
-
if (
|
|
1772
|
-
return
|
|
1895
|
+
function expandHome(path6) {
|
|
1896
|
+
if (path6.startsWith("~")) {
|
|
1897
|
+
return path6.replace(/^~/, homedir3());
|
|
1773
1898
|
}
|
|
1774
|
-
return
|
|
1899
|
+
return path6;
|
|
1775
1900
|
}
|
|
1776
1901
|
function buildDockerRunArgs(ctx, imageName, devMode) {
|
|
1777
1902
|
const args = ["run", "--rm"];
|
|
@@ -1949,9 +2074,9 @@ async function readFileBuffer(filePath, options = {}) {
|
|
|
1949
2074
|
|
|
1950
2075
|
// src/cli/gadgets.ts
|
|
1951
2076
|
init_gadget();
|
|
1952
|
-
import
|
|
1953
|
-
import
|
|
1954
|
-
import { pathToFileURL } from "node:url";
|
|
2077
|
+
import fs6 from "node:fs";
|
|
2078
|
+
import path5 from "node:path";
|
|
2079
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
1955
2080
|
|
|
1956
2081
|
// src/cli/builtins/filesystem/edit-file.ts
|
|
1957
2082
|
import { z as z2 } from "zod";
|
|
@@ -2404,6 +2529,208 @@ function isBuiltinGadgetName(name) {
|
|
|
2404
2529
|
return name in builtinGadgetRegistry;
|
|
2405
2530
|
}
|
|
2406
2531
|
|
|
2532
|
+
// src/cli/external-gadgets.ts
|
|
2533
|
+
import { execSync } from "node:child_process";
|
|
2534
|
+
import fs5 from "node:fs";
|
|
2535
|
+
import path4 from "node:path";
|
|
2536
|
+
import os from "node:os";
|
|
2537
|
+
import { pathToFileURL } from "node:url";
|
|
2538
|
+
var CACHE_DIR2 = path4.join(os.homedir(), ".llmist", "gadget-cache");
|
|
2539
|
+
function isExternalPackageSpecifier(specifier) {
|
|
2540
|
+
if (/^@?[a-z0-9][\w.-]*(?:@[\w.-]+)?(?::[a-z]+)?(?:\/\w+)?$/i.test(specifier)) {
|
|
2541
|
+
return true;
|
|
2542
|
+
}
|
|
2543
|
+
if (specifier.startsWith("git+")) {
|
|
2544
|
+
return true;
|
|
2545
|
+
}
|
|
2546
|
+
return false;
|
|
2547
|
+
}
|
|
2548
|
+
function parseGadgetSpecifier(specifier) {
|
|
2549
|
+
if (specifier.startsWith("git+")) {
|
|
2550
|
+
const url = specifier.slice(4);
|
|
2551
|
+
const [baseUrl, ref] = url.split("#");
|
|
2552
|
+
return {
|
|
2553
|
+
type: "git",
|
|
2554
|
+
package: baseUrl,
|
|
2555
|
+
version: ref
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
const npmMatch = specifier.match(
|
|
2559
|
+
/^(@?[a-z0-9][\w.-]*)(?:@([\w.-]+))?(?::([a-z]+))?(?:\/(\w+))?$/i
|
|
2560
|
+
);
|
|
2561
|
+
if (npmMatch) {
|
|
2562
|
+
const [, pkg, version, preset, gadgetName] = npmMatch;
|
|
2563
|
+
return {
|
|
2564
|
+
type: "npm",
|
|
2565
|
+
package: pkg,
|
|
2566
|
+
version,
|
|
2567
|
+
preset,
|
|
2568
|
+
gadgetName
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
function getCacheDir(spec) {
|
|
2574
|
+
const versionSuffix = spec.version ? `@${spec.version}` : "@latest";
|
|
2575
|
+
if (spec.type === "npm") {
|
|
2576
|
+
return path4.join(CACHE_DIR2, "npm", `${spec.package}${versionSuffix}`);
|
|
2577
|
+
}
|
|
2578
|
+
const sanitizedUrl = spec.package.replace(/[/:]/g, "-").replace(/^-+|-+$/g, "");
|
|
2579
|
+
return path4.join(CACHE_DIR2, "git", `${sanitizedUrl}${versionSuffix}`);
|
|
2580
|
+
}
|
|
2581
|
+
function isCached(cacheDir) {
|
|
2582
|
+
const packageJsonPath = path4.join(cacheDir, "package.json");
|
|
2583
|
+
return fs5.existsSync(packageJsonPath);
|
|
2584
|
+
}
|
|
2585
|
+
async function installNpmPackage(spec, cacheDir) {
|
|
2586
|
+
fs5.mkdirSync(cacheDir, { recursive: true });
|
|
2587
|
+
const packageJson = {
|
|
2588
|
+
name: "llmist-gadget-cache",
|
|
2589
|
+
private: true,
|
|
2590
|
+
type: "module"
|
|
2591
|
+
};
|
|
2592
|
+
fs5.writeFileSync(path4.join(cacheDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
2593
|
+
const packageSpec = spec.version ? `${spec.package}@${spec.version}` : spec.package;
|
|
2594
|
+
try {
|
|
2595
|
+
execSync(`npm install --prefix "${cacheDir}" "${packageSpec}" --save`, {
|
|
2596
|
+
stdio: "pipe",
|
|
2597
|
+
cwd: cacheDir
|
|
2598
|
+
});
|
|
2599
|
+
} catch (error) {
|
|
2600
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2601
|
+
throw new Error(`Failed to install npm package '${packageSpec}': ${message}`);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
async function installGitPackage(spec, cacheDir) {
|
|
2605
|
+
fs5.mkdirSync(path4.dirname(cacheDir), { recursive: true });
|
|
2606
|
+
if (fs5.existsSync(cacheDir)) {
|
|
2607
|
+
try {
|
|
2608
|
+
execSync("git fetch", { cwd: cacheDir, stdio: "pipe" });
|
|
2609
|
+
if (spec.version) {
|
|
2610
|
+
execSync(`git checkout ${spec.version}`, { cwd: cacheDir, stdio: "pipe" });
|
|
2611
|
+
}
|
|
2612
|
+
} catch (error) {
|
|
2613
|
+
fs5.rmSync(cacheDir, { recursive: true, force: true });
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
if (!fs5.existsSync(cacheDir)) {
|
|
2617
|
+
try {
|
|
2618
|
+
const cloneCmd = spec.version ? `git clone --branch ${spec.version} "${spec.package}" "${cacheDir}"` : `git clone "${spec.package}" "${cacheDir}"`;
|
|
2619
|
+
execSync(cloneCmd, { stdio: "pipe" });
|
|
2620
|
+
} catch (error) {
|
|
2621
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2622
|
+
throw new Error(`Failed to clone git repository '${spec.package}': ${message}`);
|
|
2623
|
+
}
|
|
2624
|
+
if (fs5.existsSync(path4.join(cacheDir, "package.json"))) {
|
|
2625
|
+
try {
|
|
2626
|
+
execSync("npm install", { cwd: cacheDir, stdio: "pipe" });
|
|
2627
|
+
} catch (error) {
|
|
2628
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2629
|
+
throw new Error(`Failed to install dependencies for '${spec.package}': ${message}`);
|
|
2630
|
+
}
|
|
2631
|
+
try {
|
|
2632
|
+
const packageJson = JSON.parse(fs5.readFileSync(path4.join(cacheDir, "package.json"), "utf-8"));
|
|
2633
|
+
if (packageJson.scripts?.build) {
|
|
2634
|
+
execSync("npm run build", { cwd: cacheDir, stdio: "pipe" });
|
|
2635
|
+
}
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2638
|
+
throw new Error(`Failed to build package '${spec.package}': ${message}`);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
function readManifest(packageDir) {
|
|
2644
|
+
const packageJsonPath = path4.join(packageDir, "package.json");
|
|
2645
|
+
if (!fs5.existsSync(packageJsonPath)) {
|
|
2646
|
+
return null;
|
|
2647
|
+
}
|
|
2648
|
+
try {
|
|
2649
|
+
const packageJson = JSON.parse(fs5.readFileSync(packageJsonPath, "utf-8"));
|
|
2650
|
+
return packageJson.llmist || null;
|
|
2651
|
+
} catch {
|
|
2652
|
+
return null;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
function getPackagePath(cacheDir, packageName) {
|
|
2656
|
+
const nodeModulesPath = path4.join(cacheDir, "node_modules", packageName);
|
|
2657
|
+
if (fs5.existsSync(nodeModulesPath)) {
|
|
2658
|
+
return nodeModulesPath;
|
|
2659
|
+
}
|
|
2660
|
+
return cacheDir;
|
|
2661
|
+
}
|
|
2662
|
+
async function loadExternalGadgets(specifier, forceInstall = false) {
|
|
2663
|
+
const spec = parseGadgetSpecifier(specifier);
|
|
2664
|
+
if (!spec) {
|
|
2665
|
+
throw new Error(`Invalid external package specifier: ${specifier}`);
|
|
2666
|
+
}
|
|
2667
|
+
const cacheDir = getCacheDir(spec);
|
|
2668
|
+
if (!isCached(cacheDir) || forceInstall) {
|
|
2669
|
+
if (spec.type === "npm") {
|
|
2670
|
+
await installNpmPackage(spec, cacheDir);
|
|
2671
|
+
} else {
|
|
2672
|
+
await installGitPackage(spec, cacheDir);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
const packagePath = getPackagePath(cacheDir, spec.package);
|
|
2676
|
+
const manifest = readManifest(packagePath);
|
|
2677
|
+
let entryPoint;
|
|
2678
|
+
let gadgetNames = null;
|
|
2679
|
+
if (spec.gadgetName) {
|
|
2680
|
+
gadgetNames = [spec.gadgetName];
|
|
2681
|
+
if (manifest?.subagents?.[spec.gadgetName]) {
|
|
2682
|
+
entryPoint = manifest.subagents[spec.gadgetName].entryPoint;
|
|
2683
|
+
} else {
|
|
2684
|
+
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
2685
|
+
}
|
|
2686
|
+
} else if (spec.preset) {
|
|
2687
|
+
if (!manifest?.presets?.[spec.preset]) {
|
|
2688
|
+
throw new Error(`Unknown preset '${spec.preset}' in package '${spec.package}'`);
|
|
2689
|
+
}
|
|
2690
|
+
const preset = manifest.presets[spec.preset];
|
|
2691
|
+
if (preset === "*") {
|
|
2692
|
+
gadgetNames = null;
|
|
2693
|
+
} else {
|
|
2694
|
+
gadgetNames = preset;
|
|
2695
|
+
}
|
|
2696
|
+
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
2697
|
+
} else {
|
|
2698
|
+
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
2699
|
+
}
|
|
2700
|
+
const resolvedEntryPoint = path4.resolve(packagePath, entryPoint);
|
|
2701
|
+
if (!fs5.existsSync(resolvedEntryPoint)) {
|
|
2702
|
+
throw new Error(
|
|
2703
|
+
`Entry point not found: ${resolvedEntryPoint}. Make sure the package is built (run 'npm run build' in the package directory).`
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2706
|
+
const moduleUrl = pathToFileURL(resolvedEntryPoint).href;
|
|
2707
|
+
let exports;
|
|
2708
|
+
try {
|
|
2709
|
+
exports = await import(moduleUrl);
|
|
2710
|
+
} catch (error) {
|
|
2711
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2712
|
+
throw new Error(`Failed to import '${specifier}': ${message}`);
|
|
2713
|
+
}
|
|
2714
|
+
let gadgets = extractGadgetsFromModule(exports);
|
|
2715
|
+
if (gadgetNames) {
|
|
2716
|
+
const gadgetSet = new Set(gadgetNames.map((n) => n.toLowerCase()));
|
|
2717
|
+
gadgets = gadgets.filter((g) => {
|
|
2718
|
+
const name = g.name?.toLowerCase() || "";
|
|
2719
|
+
return gadgetSet.has(name);
|
|
2720
|
+
});
|
|
2721
|
+
const foundNames = new Set(gadgets.map((g) => g.name?.toLowerCase() || ""));
|
|
2722
|
+
for (const requested of gadgetNames) {
|
|
2723
|
+
if (!foundNames.has(requested.toLowerCase())) {
|
|
2724
|
+
throw new Error(`Gadget '${requested}' not found in package '${spec.package}'`);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
if (gadgets.length === 0) {
|
|
2729
|
+
throw new Error(`No gadgets found in package '${spec.package}'`);
|
|
2730
|
+
}
|
|
2731
|
+
return gadgets;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2407
2734
|
// src/cli/gadgets.ts
|
|
2408
2735
|
var PATH_PREFIXES = [".", "/", "~"];
|
|
2409
2736
|
var BUILTIN_PREFIX = "builtin:";
|
|
@@ -2419,7 +2746,17 @@ function isGadgetConstructor(value) {
|
|
|
2419
2746
|
return false;
|
|
2420
2747
|
}
|
|
2421
2748
|
const prototype = value.prototype;
|
|
2422
|
-
|
|
2749
|
+
if (!prototype) {
|
|
2750
|
+
return false;
|
|
2751
|
+
}
|
|
2752
|
+
if (prototype instanceof AbstractGadget) {
|
|
2753
|
+
return true;
|
|
2754
|
+
}
|
|
2755
|
+
const proto = prototype;
|
|
2756
|
+
if (typeof proto.execute === "function") {
|
|
2757
|
+
return true;
|
|
2758
|
+
}
|
|
2759
|
+
return isGadgetLike(prototype);
|
|
2423
2760
|
}
|
|
2424
2761
|
function expandHomePath(input) {
|
|
2425
2762
|
if (!input.startsWith("~")) {
|
|
@@ -2429,10 +2766,10 @@ function expandHomePath(input) {
|
|
|
2429
2766
|
if (!home) {
|
|
2430
2767
|
return input;
|
|
2431
2768
|
}
|
|
2432
|
-
return
|
|
2769
|
+
return path5.join(home, input.slice(1));
|
|
2433
2770
|
}
|
|
2434
2771
|
function isFileLikeSpecifier(specifier) {
|
|
2435
|
-
return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(
|
|
2772
|
+
return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(path5.sep);
|
|
2436
2773
|
}
|
|
2437
2774
|
function tryResolveBuiltin(specifier) {
|
|
2438
2775
|
if (specifier.startsWith(BUILTIN_PREFIX)) {
|
|
@@ -2455,11 +2792,11 @@ function resolveGadgetSpecifier(specifier, cwd) {
|
|
|
2455
2792
|
return specifier;
|
|
2456
2793
|
}
|
|
2457
2794
|
const expanded = expandHomePath(specifier);
|
|
2458
|
-
const resolvedPath =
|
|
2459
|
-
if (!
|
|
2795
|
+
const resolvedPath = path5.resolve(cwd, expanded);
|
|
2796
|
+
if (!fs6.existsSync(resolvedPath)) {
|
|
2460
2797
|
throw new Error(`Gadget module not found at ${resolvedPath}`);
|
|
2461
2798
|
}
|
|
2462
|
-
return
|
|
2799
|
+
return pathToFileURL2(resolvedPath).href;
|
|
2463
2800
|
}
|
|
2464
2801
|
function extractGadgetsFromModule(moduleExports) {
|
|
2465
2802
|
const results = [];
|
|
@@ -2472,7 +2809,7 @@ function extractGadgetsFromModule(moduleExports) {
|
|
|
2472
2809
|
return;
|
|
2473
2810
|
}
|
|
2474
2811
|
visited.add(value);
|
|
2475
|
-
if (value instanceof
|
|
2812
|
+
if (value instanceof AbstractGadget || isGadgetLike(value)) {
|
|
2476
2813
|
results.push(value);
|
|
2477
2814
|
return;
|
|
2478
2815
|
}
|
|
@@ -2497,12 +2834,23 @@ function extractGadgetsFromModule(moduleExports) {
|
|
|
2497
2834
|
}
|
|
2498
2835
|
async function loadGadgets(specifiers, cwd, importer = (specifier) => import(specifier)) {
|
|
2499
2836
|
const gadgets = [];
|
|
2837
|
+
const usingDefaultImporter = importer.toString().includes("import(specifier)");
|
|
2500
2838
|
for (const specifier of specifiers) {
|
|
2501
2839
|
const builtin = tryResolveBuiltin(specifier);
|
|
2502
2840
|
if (builtin) {
|
|
2503
2841
|
gadgets.push(builtin);
|
|
2504
2842
|
continue;
|
|
2505
2843
|
}
|
|
2844
|
+
if (usingDefaultImporter && isExternalPackageSpecifier(specifier)) {
|
|
2845
|
+
try {
|
|
2846
|
+
const externalGadgets = await loadExternalGadgets(specifier);
|
|
2847
|
+
gadgets.push(...externalGadgets);
|
|
2848
|
+
continue;
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2851
|
+
throw new Error(`Failed to load external package '${specifier}': ${message}`);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2506
2854
|
const resolved = resolveGadgetSpecifier(specifier, cwd);
|
|
2507
2855
|
let exports;
|
|
2508
2856
|
try {
|
|
@@ -2545,7 +2893,7 @@ function formatLlmRequest(messages) {
|
|
|
2545
2893
|
const lines = [];
|
|
2546
2894
|
for (const msg of messages) {
|
|
2547
2895
|
lines.push(`=== ${msg.role.toUpperCase()} ===`);
|
|
2548
|
-
lines.push(msg.content ?
|
|
2896
|
+
lines.push(msg.content ? extractMessageText(msg.content) : "");
|
|
2549
2897
|
lines.push("");
|
|
2550
2898
|
}
|
|
2551
2899
|
return lines.join("\n");
|
|
@@ -2760,8 +3108,8 @@ function formatMediaLine(media) {
|
|
|
2760
3108
|
const id = chalk3.cyan(media.id);
|
|
2761
3109
|
const mimeType = chalk3.dim(media.mimeType);
|
|
2762
3110
|
const size = chalk3.yellow(formatBytes(media.sizeBytes));
|
|
2763
|
-
const
|
|
2764
|
-
return `${chalk3.dim("[")}${icon} ${id} ${mimeType} ${size}${chalk3.dim("]")} ${chalk3.dim("\u2192")} ${
|
|
3111
|
+
const path6 = chalk3.dim(media.path);
|
|
3112
|
+
return `${chalk3.dim("[")}${icon} ${id} ${mimeType} ${size}${chalk3.dim("]")} ${chalk3.dim("\u2192")} ${path6}`;
|
|
2765
3113
|
}
|
|
2766
3114
|
function formatGadgetSummary2(result) {
|
|
2767
3115
|
const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
|
|
@@ -3381,6 +3729,7 @@ function configToAgentOptions(config) {
|
|
|
3381
3729
|
if (config.docker !== void 0) result.docker = config.docker;
|
|
3382
3730
|
if (config["docker-cwd-permission"] !== void 0)
|
|
3383
3731
|
result.dockerCwdPermission = config["docker-cwd-permission"];
|
|
3732
|
+
if (config.subagents !== void 0) result.subagents = config.subagents;
|
|
3384
3733
|
return result;
|
|
3385
3734
|
}
|
|
3386
3735
|
|
|
@@ -3568,7 +3917,12 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3568
3917
|
return void 0;
|
|
3569
3918
|
}
|
|
3570
3919
|
};
|
|
3571
|
-
const
|
|
3920
|
+
const resolvedSubagentConfig = buildSubagentConfigMap(
|
|
3921
|
+
options.model,
|
|
3922
|
+
options.subagents,
|
|
3923
|
+
options.globalSubagents
|
|
3924
|
+
);
|
|
3925
|
+
const builder = new AgentBuilder(client).withModel(options.model).withSubagentConfig(resolvedSubagentConfig).withLogger(env.createLogger("llmist:cli:agent")).withHooks({
|
|
3572
3926
|
observers: {
|
|
3573
3927
|
// onLLMCallStart: Start progress indicator for each LLM call
|
|
3574
3928
|
// This showcases how to react to agent lifecycle events
|
|
@@ -3838,14 +4192,16 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
3838
4192
|
}
|
|
3839
4193
|
}
|
|
3840
4194
|
}
|
|
3841
|
-
function registerAgentCommand(program, env, config) {
|
|
4195
|
+
function registerAgentCommand(program, env, config, globalSubagents) {
|
|
3842
4196
|
const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument("[prompt]", "Prompt for the agent loop. Falls back to stdin when available.");
|
|
3843
4197
|
addAgentOptions(cmd, config);
|
|
3844
4198
|
cmd.action(
|
|
3845
4199
|
(prompt, options) => executeAction(() => {
|
|
3846
4200
|
const mergedOptions = {
|
|
3847
4201
|
...options,
|
|
3848
|
-
gadgetApproval: config?.["gadget-approval"]
|
|
4202
|
+
gadgetApproval: config?.["gadget-approval"],
|
|
4203
|
+
subagents: config?.subagents,
|
|
4204
|
+
globalSubagents
|
|
3849
4205
|
};
|
|
3850
4206
|
return executeAgent(prompt, mergedOptions, env);
|
|
3851
4207
|
}, env)
|
|
@@ -3945,6 +4301,104 @@ function registerCompleteCommand(program, env, config) {
|
|
|
3945
4301
|
);
|
|
3946
4302
|
}
|
|
3947
4303
|
|
|
4304
|
+
// src/cli/init-command.ts
|
|
4305
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4306
|
+
import { dirname as dirname2 } from "node:path";
|
|
4307
|
+
var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
4308
|
+
# llmist CLI configuration file
|
|
4309
|
+
#
|
|
4310
|
+
# This is a minimal starter config. For a comprehensive example with all options:
|
|
4311
|
+
# https://github.com/zbigniewsobiecki/llmist/blob/main/examples/cli.example.toml
|
|
4312
|
+
#
|
|
4313
|
+
# Key concepts:
|
|
4314
|
+
# - Any section can inherit from others using: inherits = "section-name"
|
|
4315
|
+
# - Prompts can use templates with Eta syntax: <%~ include("@prompt-name") %>
|
|
4316
|
+
# - Custom sections become CLI commands: [my-command] -> llmist my-command
|
|
4317
|
+
|
|
4318
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4319
|
+
# GLOBAL OPTIONS
|
|
4320
|
+
# These apply to all commands. CLI flags override these settings.
|
|
4321
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4322
|
+
[global]
|
|
4323
|
+
# log-level = "info" # silly, trace, debug, info, warn, error, fatal
|
|
4324
|
+
# log-file = "/tmp/llmist.log" # Enable file logging (JSON format)
|
|
4325
|
+
|
|
4326
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4327
|
+
# COMPLETE COMMAND DEFAULTS
|
|
4328
|
+
# For single LLM responses: llmist complete "prompt"
|
|
4329
|
+
# Model format: provider:model (e.g., openai:gpt-4o, anthropic:claude-sonnet-4-5)
|
|
4330
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4331
|
+
[complete]
|
|
4332
|
+
# model = "openai:gpt-4o"
|
|
4333
|
+
# temperature = 0.7 # 0-2, higher = more creative
|
|
4334
|
+
# max-tokens = 4096 # Maximum response length
|
|
4335
|
+
|
|
4336
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4337
|
+
# AGENT COMMAND DEFAULTS
|
|
4338
|
+
# For tool-using agents: llmist agent "prompt"
|
|
4339
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4340
|
+
[agent]
|
|
4341
|
+
# model = "anthropic:claude-sonnet-4-5"
|
|
4342
|
+
# max-iterations = 15 # Max tool-use loops before stopping
|
|
4343
|
+
# gadgets = [ # Tools the agent can use
|
|
4344
|
+
# "ListDirectory",
|
|
4345
|
+
# "ReadFile",
|
|
4346
|
+
# "WriteFile",
|
|
4347
|
+
# ]
|
|
4348
|
+
|
|
4349
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4350
|
+
# CUSTOM COMMANDS
|
|
4351
|
+
# Any other section becomes a new CLI command!
|
|
4352
|
+
# Uncomment below to create: llmist summarize "your text"
|
|
4353
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4354
|
+
# [summarize]
|
|
4355
|
+
# type = "complete" # "complete" or "agent"
|
|
4356
|
+
# description = "Summarize text concisely."
|
|
4357
|
+
# system = "Summarize the following text in 2-3 bullet points."
|
|
4358
|
+
# temperature = 0.3
|
|
4359
|
+
`;
|
|
4360
|
+
async function executeInit(_options, env) {
|
|
4361
|
+
const configPath = getConfigPath();
|
|
4362
|
+
const configDir = dirname2(configPath);
|
|
4363
|
+
if (existsSync5(configPath)) {
|
|
4364
|
+
env.stderr.write(`Configuration already exists at ${configPath}
|
|
4365
|
+
`);
|
|
4366
|
+
env.stderr.write("\n");
|
|
4367
|
+
env.stderr.write(`To view it: cat ${configPath}
|
|
4368
|
+
`);
|
|
4369
|
+
env.stderr.write(`To reset: rm ${configPath} && llmist init
|
|
4370
|
+
`);
|
|
4371
|
+
return;
|
|
4372
|
+
}
|
|
4373
|
+
if (!existsSync5(configDir)) {
|
|
4374
|
+
mkdirSync2(configDir, { recursive: true });
|
|
4375
|
+
}
|
|
4376
|
+
writeFileSync2(configPath, STARTER_CONFIG, "utf-8");
|
|
4377
|
+
env.stderr.write(`Created ${configPath}
|
|
4378
|
+
`);
|
|
4379
|
+
env.stderr.write("\n");
|
|
4380
|
+
env.stderr.write("Next steps:\n");
|
|
4381
|
+
env.stderr.write(" 1. Set your API key:\n");
|
|
4382
|
+
env.stderr.write(" export OPENAI_API_KEY=sk-...\n");
|
|
4383
|
+
env.stderr.write(" export ANTHROPIC_API_KEY=sk-...\n");
|
|
4384
|
+
env.stderr.write(" export GEMINI_API_KEY=...\n");
|
|
4385
|
+
env.stderr.write("\n");
|
|
4386
|
+
env.stderr.write(` 2. Customize your config:
|
|
4387
|
+
`);
|
|
4388
|
+
env.stderr.write(` $EDITOR ${configPath}
|
|
4389
|
+
`);
|
|
4390
|
+
env.stderr.write("\n");
|
|
4391
|
+
env.stderr.write(" 3. See all options:\n");
|
|
4392
|
+
env.stderr.write(
|
|
4393
|
+
" https://github.com/zbigniewsobiecki/llmist/blob/main/examples/cli.example.toml\n"
|
|
4394
|
+
);
|
|
4395
|
+
env.stderr.write("\n");
|
|
4396
|
+
env.stderr.write('Try it: llmist complete "Hello, world!"\n');
|
|
4397
|
+
}
|
|
4398
|
+
function registerInitCommand(program, env) {
|
|
4399
|
+
program.command(COMMANDS.init).description("Initialize llmist configuration at ~/.llmist/cli.toml").action((options) => executeAction(() => executeInit(options, env), env));
|
|
4400
|
+
}
|
|
4401
|
+
|
|
3948
4402
|
// src/cli/environment.ts
|
|
3949
4403
|
init_client();
|
|
3950
4404
|
init_logger();
|
|
@@ -4047,7 +4501,7 @@ function createCommandEnvironment(baseEnv, config) {
|
|
|
4047
4501
|
createLogger: createLoggerFactory(loggerConfig)
|
|
4048
4502
|
};
|
|
4049
4503
|
}
|
|
4050
|
-
function registerCustomCommand(program, name, config, env) {
|
|
4504
|
+
function registerCustomCommand(program, name, config, env, globalSubagents) {
|
|
4051
4505
|
const type = config.type ?? "agent";
|
|
4052
4506
|
const description = config.description ?? `Custom ${type} command`;
|
|
4053
4507
|
const cmd = program.command(name).description(description).argument("[prompt]", "Prompt for the command. Falls back to stdin when available.");
|
|
@@ -4072,7 +4526,8 @@ function registerCustomCommand(program, name, config, env) {
|
|
|
4072
4526
|
const configDefaults = configToAgentOptions(config);
|
|
4073
4527
|
const options = {
|
|
4074
4528
|
...configDefaults,
|
|
4075
|
-
...cliOptions
|
|
4529
|
+
...cliOptions,
|
|
4530
|
+
globalSubagents
|
|
4076
4531
|
};
|
|
4077
4532
|
await executeAgent(prompt, options, cmdEnv);
|
|
4078
4533
|
}, cmdEnv);
|
|
@@ -4515,7 +4970,7 @@ function registerGadgetCommand(program, env) {
|
|
|
4515
4970
|
}
|
|
4516
4971
|
|
|
4517
4972
|
// src/cli/image-command.ts
|
|
4518
|
-
import { writeFileSync as
|
|
4973
|
+
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
4519
4974
|
var DEFAULT_IMAGE_MODEL = "dall-e-3";
|
|
4520
4975
|
async function executeImage(promptArg, options, env) {
|
|
4521
4976
|
const prompt = await resolvePrompt(promptArg, env);
|
|
@@ -4539,7 +4994,7 @@ async function executeImage(promptArg, options, env) {
|
|
|
4539
4994
|
const imageData = result.images[0];
|
|
4540
4995
|
if (imageData.b64Json) {
|
|
4541
4996
|
const buffer = Buffer.from(imageData.b64Json, "base64");
|
|
4542
|
-
|
|
4997
|
+
writeFileSync3(options.output, buffer);
|
|
4543
4998
|
if (!options.quiet) {
|
|
4544
4999
|
env.stderr.write(`${SUMMARY_PREFIX} Image saved to ${options.output}
|
|
4545
5000
|
`);
|
|
@@ -4980,7 +5435,7 @@ function registerModelsCommand(program, env) {
|
|
|
4980
5435
|
}
|
|
4981
5436
|
|
|
4982
5437
|
// src/cli/speech-command.ts
|
|
4983
|
-
import { writeFileSync as
|
|
5438
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
4984
5439
|
var DEFAULT_SPEECH_MODEL = "tts-1";
|
|
4985
5440
|
var DEFAULT_VOICE = "nova";
|
|
4986
5441
|
async function executeSpeech(textArg, options, env) {
|
|
@@ -5003,7 +5458,7 @@ async function executeSpeech(textArg, options, env) {
|
|
|
5003
5458
|
});
|
|
5004
5459
|
const audioBuffer = Buffer.from(result.audio);
|
|
5005
5460
|
if (options.output) {
|
|
5006
|
-
|
|
5461
|
+
writeFileSync4(options.output, audioBuffer);
|
|
5007
5462
|
if (!options.quiet) {
|
|
5008
5463
|
env.stderr.write(`${SUMMARY_PREFIX} Audio saved to ${options.output}
|
|
5009
5464
|
`);
|
|
@@ -5077,17 +5532,18 @@ function createProgram(env, config) {
|
|
|
5077
5532
|
writeErr: (str) => env.stderr.write(str)
|
|
5078
5533
|
});
|
|
5079
5534
|
registerCompleteCommand(program, env, config?.complete);
|
|
5080
|
-
registerAgentCommand(program, env, config?.agent);
|
|
5535
|
+
registerAgentCommand(program, env, config?.agent, config?.subagents);
|
|
5081
5536
|
registerImageCommand(program, env, config?.image);
|
|
5082
5537
|
registerSpeechCommand(program, env, config?.speech);
|
|
5083
5538
|
registerVisionCommand(program, env);
|
|
5084
5539
|
registerModelsCommand(program, env);
|
|
5085
5540
|
registerGadgetCommand(program, env);
|
|
5541
|
+
registerInitCommand(program, env);
|
|
5086
5542
|
if (config) {
|
|
5087
5543
|
const customNames = getCustomCommandNames(config);
|
|
5088
5544
|
for (const name of customNames) {
|
|
5089
5545
|
const cmdConfig = config[name];
|
|
5090
|
-
registerCustomCommand(program, name, cmdConfig, env);
|
|
5546
|
+
registerCustomCommand(program, name, cmdConfig, env, config.subagents);
|
|
5091
5547
|
}
|
|
5092
5548
|
}
|
|
5093
5549
|
return program;
|