llmist 1.3.1 → 1.5.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.cjs CHANGED
@@ -172,7 +172,7 @@ Example fixes:
172
172
  );
173
173
  }
174
174
  }
175
- function findUnknownTypes(schema, path2 = []) {
175
+ function findUnknownTypes(schema, path5 = []) {
176
176
  const issues = [];
177
177
  if (!schema || typeof schema !== "object") {
178
178
  return issues;
@@ -184,7 +184,7 @@ function findUnknownTypes(schema, path2 = []) {
184
184
  }
185
185
  if (schema.properties) {
186
186
  for (const [propName, propSchema] of Object.entries(schema.properties)) {
187
- const propPath = [...path2, propName];
187
+ const propPath = [...path5, propName];
188
188
  if (hasNoType(propSchema)) {
189
189
  issues.push(propPath.join(".") || propName);
190
190
  }
@@ -192,7 +192,7 @@ function findUnknownTypes(schema, path2 = []) {
192
192
  }
193
193
  }
194
194
  if (schema.items) {
195
- const itemPath = [...path2, "[]"];
195
+ const itemPath = [...path5, "[]"];
196
196
  if (hasNoType(schema.items)) {
197
197
  issues.push(itemPath.join("."));
198
198
  }
@@ -200,17 +200,17 @@ function findUnknownTypes(schema, path2 = []) {
200
200
  }
201
201
  if (schema.anyOf) {
202
202
  schema.anyOf.forEach((subSchema, index) => {
203
- issues.push(...findUnknownTypes(subSchema, [...path2, `anyOf[${index}]`]));
203
+ issues.push(...findUnknownTypes(subSchema, [...path5, `anyOf[${index}]`]));
204
204
  });
205
205
  }
206
206
  if (schema.oneOf) {
207
207
  schema.oneOf.forEach((subSchema, index) => {
208
- issues.push(...findUnknownTypes(subSchema, [...path2, `oneOf[${index}]`]));
208
+ issues.push(...findUnknownTypes(subSchema, [...path5, `oneOf[${index}]`]));
209
209
  });
210
210
  }
211
211
  if (schema.allOf) {
212
212
  schema.allOf.forEach((subSchema, index) => {
213
- issues.push(...findUnknownTypes(subSchema, [...path2, `allOf[${index}]`]));
213
+ issues.push(...findUnknownTypes(subSchema, [...path5, `allOf[${index}]`]));
214
214
  });
215
215
  }
216
216
  return issues;
@@ -737,29 +737,29 @@ function schemaToJSONSchema(schema, options) {
737
737
  }
738
738
  function detectDescriptionMismatch(schema, jsonSchema) {
739
739
  const mismatches = [];
740
- function checkSchema(zodSchema, json, path2) {
740
+ function checkSchema(zodSchema, json, path5) {
741
741
  if (!zodSchema || typeof zodSchema !== "object") return;
742
742
  const def = zodSchema._def;
743
743
  const jsonObj = json;
744
744
  if (def?.description && !jsonObj?.description) {
745
- mismatches.push(path2 || "root");
745
+ mismatches.push(path5 || "root");
746
746
  }
747
747
  if (def?.typeName === "ZodObject" && def?.shape) {
748
748
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
749
749
  for (const [key, fieldSchema] of Object.entries(shape)) {
750
750
  const properties = jsonObj?.properties;
751
751
  const jsonProp = properties?.[key];
752
- checkSchema(fieldSchema, jsonProp, path2 ? `${path2}.${key}` : key);
752
+ checkSchema(fieldSchema, jsonProp, path5 ? `${path5}.${key}` : key);
753
753
  }
754
754
  }
755
755
  if (def?.typeName === "ZodArray" && def?.type) {
756
- checkSchema(def.type, jsonObj?.items, path2 ? `${path2}[]` : "[]");
756
+ checkSchema(def.type, jsonObj?.items, path5 ? `${path5}[]` : "[]");
757
757
  }
758
758
  if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
759
- checkSchema(def.innerType, json, path2);
759
+ checkSchema(def.innerType, json, path5);
760
760
  }
761
761
  if (def?.typeName === "ZodDefault" && def?.innerType) {
762
- checkSchema(def.innerType, json, path2);
762
+ checkSchema(def.innerType, json, path5);
763
763
  }
764
764
  }
765
765
  checkSchema(schema, jsonSchema, "");
@@ -830,38 +830,83 @@ function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX)
830
830
  }
831
831
  return lines.join("\n");
832
832
  }
833
- function formatSchemaAsPlainText(schema, indent = "") {
833
+ function formatParamLine(key, propObj, isRequired, indent = "") {
834
+ const type = propObj.type;
835
+ const description = propObj.description;
836
+ const enumValues = propObj.enum;
837
+ let line = `${indent}- ${key}`;
838
+ if (type === "array") {
839
+ const items = propObj.items;
840
+ const itemType = items?.type || "any";
841
+ line += ` (array of ${itemType})`;
842
+ } else if (type === "object" && propObj.properties) {
843
+ line += " (object)";
844
+ } else {
845
+ line += ` (${type})`;
846
+ }
847
+ if (isRequired && indent !== "") {
848
+ line += " [required]";
849
+ }
850
+ if (description) {
851
+ line += `: ${description}`;
852
+ }
853
+ if (enumValues) {
854
+ line += ` - one of: ${enumValues.map((v) => `"${v}"`).join(", ")}`;
855
+ }
856
+ return line;
857
+ }
858
+ function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
834
859
  const lines = [];
835
860
  const properties = schema.properties || {};
836
861
  const required = schema.required || [];
837
- for (const [key, prop] of Object.entries(properties)) {
838
- const propObj = prop;
839
- const type = propObj.type;
840
- const description = propObj.description;
841
- const isRequired = required.includes(key);
842
- const enumValues = propObj.enum;
843
- let line = `${indent}- ${key}`;
844
- if (type === "array") {
845
- const items = propObj.items;
846
- const itemType = items?.type || "any";
847
- line += ` (array of ${itemType})`;
848
- } else if (type === "object" && propObj.properties) {
849
- line += " (object)";
850
- } else {
851
- line += ` (${type})`;
862
+ if (atRoot && indent === "") {
863
+ const requiredProps = [];
864
+ const optionalProps = [];
865
+ for (const [key, prop] of Object.entries(properties)) {
866
+ if (required.includes(key)) {
867
+ requiredProps.push([key, prop]);
868
+ } else {
869
+ optionalProps.push([key, prop]);
870
+ }
852
871
  }
853
- if (isRequired) {
854
- line += " [required]";
872
+ const reqCount = requiredProps.length;
873
+ const optCount = optionalProps.length;
874
+ if (reqCount > 0 || optCount > 0) {
875
+ const parts = [];
876
+ if (reqCount > 0) parts.push(`${reqCount} required`);
877
+ if (optCount > 0) parts.push(`${optCount} optional`);
878
+ lines.push(parts.join(", "));
879
+ lines.push("");
855
880
  }
856
- if (description) {
857
- line += `: ${description}`;
881
+ if (reqCount > 0) {
882
+ lines.push("REQUIRED Parameters:");
883
+ for (const [key, prop] of requiredProps) {
884
+ lines.push(formatParamLine(key, prop, true, ""));
885
+ const propObj = prop;
886
+ if (propObj.type === "object" && propObj.properties) {
887
+ lines.push(formatSchemaAsPlainText(propObj, " ", false));
888
+ }
889
+ }
858
890
  }
859
- if (enumValues) {
860
- line += ` - one of: ${enumValues.map((v) => `"${v}"`).join(", ")}`;
891
+ if (optCount > 0) {
892
+ if (reqCount > 0) lines.push("");
893
+ lines.push("OPTIONAL Parameters:");
894
+ for (const [key, prop] of optionalProps) {
895
+ lines.push(formatParamLine(key, prop, false, ""));
896
+ const propObj = prop;
897
+ if (propObj.type === "object" && propObj.properties) {
898
+ lines.push(formatSchemaAsPlainText(propObj, " ", false));
899
+ }
900
+ }
861
901
  }
862
- lines.push(line);
863
- if (type === "object" && propObj.properties) {
864
- lines.push(formatSchemaAsPlainText(propObj, indent + " "));
902
+ return lines.join("\n");
903
+ }
904
+ for (const [key, prop] of Object.entries(properties)) {
905
+ const isRequired = required.includes(key);
906
+ lines.push(formatParamLine(key, prop, isRequired, indent));
907
+ const propObj = prop;
908
+ if (propObj.type === "object" && propObj.properties) {
909
+ lines.push(formatSchemaAsPlainText(propObj, indent + " ", false));
865
910
  }
866
911
  }
867
912
  return lines.join("\n");
@@ -912,10 +957,11 @@ var init_gadget = __esm({
912
957
  * Generate instruction text for the LLM.
913
958
  * Combines name, description, and parameter schema into a formatted instruction.
914
959
  *
915
- * @param argPrefix - Optional custom argument prefix for block format examples
960
+ * @param optionsOrArgPrefix - Optional custom prefixes for examples, or just argPrefix string for backwards compatibility
916
961
  * @returns Formatted instruction string
917
962
  */
918
- getInstruction(argPrefix) {
963
+ getInstruction(optionsOrArgPrefix) {
964
+ const options = typeof optionsOrArgPrefix === "string" ? { argPrefix: optionsOrArgPrefix } : optionsOrArgPrefix;
919
965
  const parts = [];
920
966
  parts.push(this.description);
921
967
  if (this.parameterSchema) {
@@ -929,18 +975,25 @@ var init_gadget = __esm({
929
975
  }
930
976
  if (this.examples && this.examples.length > 0) {
931
977
  parts.push("\n\nExamples:");
932
- const effectiveArgPrefix = argPrefix ?? GADGET_ARG_PREFIX;
978
+ const effectiveArgPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
979
+ const effectiveStartPrefix = options?.startPrefix ?? GADGET_START_PREFIX;
980
+ const effectiveEndPrefix = options?.endPrefix ?? GADGET_END_PREFIX;
981
+ const gadgetName = this.name || this.constructor.name;
933
982
  this.examples.forEach((example, index) => {
934
983
  if (index > 0) {
935
984
  parts.push("");
985
+ parts.push("---");
986
+ parts.push("");
936
987
  }
937
988
  if (example.comment) {
938
989
  parts.push(`# ${example.comment}`);
939
990
  }
940
- parts.push("Input:");
991
+ parts.push(`${effectiveStartPrefix}${gadgetName}`);
941
992
  parts.push(formatParamsAsBlock(example.params, "", effectiveArgPrefix));
993
+ parts.push(effectiveEndPrefix);
942
994
  if (example.output !== void 0) {
943
- parts.push("Output:");
995
+ parts.push("");
996
+ parts.push("Expected Output:");
944
997
  parts.push(example.output);
945
998
  }
946
999
  });
@@ -2268,8 +2321,8 @@ var init_error_formatter = __esm({
2268
2321
  const parts = [];
2269
2322
  parts.push(`Error: Invalid parameters for '${gadgetName}':`);
2270
2323
  for (const issue of zodError.issues) {
2271
- const path2 = issue.path.join(".") || "root";
2272
- parts.push(` - ${path2}: ${issue.message}`);
2324
+ const path5 = issue.path.join(".") || "root";
2325
+ parts.push(` - ${path5}: ${issue.message}`);
2273
2326
  }
2274
2327
  parts.push("");
2275
2328
  parts.push("Gadget Usage:");
@@ -3250,6 +3303,8 @@ var init_agent = __esm({
3250
3303
  outputLimitCharLimit;
3251
3304
  // Context compaction
3252
3305
  compactionManager;
3306
+ // Cancellation
3307
+ signal;
3253
3308
  /**
3254
3309
  * Creates a new Agent instance.
3255
3310
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -3317,6 +3372,7 @@ var init_agent = __esm({
3317
3372
  options.compactionConfig
3318
3373
  );
3319
3374
  }
3375
+ this.signal = options.signal;
3320
3376
  }
3321
3377
  /**
3322
3378
  * Get the gadget registry for this agent.
@@ -3434,7 +3490,8 @@ var init_agent = __esm({
3434
3490
  model: this.model,
3435
3491
  messages: this.conversation.getMessages(),
3436
3492
  temperature: this.temperature,
3437
- maxTokens: this.defaultMaxTokens
3493
+ maxTokens: this.defaultMaxTokens,
3494
+ signal: this.signal
3438
3495
  };
3439
3496
  await this.safeObserve(async () => {
3440
3497
  if (this.hooks.observers?.onLLMCallStart) {
@@ -4052,7 +4109,7 @@ var init_base_provider = __esm({
4052
4109
  async *stream(options, descriptor, spec) {
4053
4110
  const preparedMessages = this.prepareMessages(options.messages);
4054
4111
  const payload = this.buildRequestPayload(options, descriptor, spec, preparedMessages);
4055
- const rawStream = await this.executeStreamRequest(payload);
4112
+ const rawStream = await this.executeStreamRequest(payload, options.signal);
4056
4113
  yield* this.wrapStream(rawStream);
4057
4114
  }
4058
4115
  /**
@@ -4170,9 +4227,9 @@ var init_anthropic = __esm({
4170
4227
  };
4171
4228
  return payload;
4172
4229
  }
4173
- async executeStreamRequest(payload) {
4230
+ async executeStreamRequest(payload, signal) {
4174
4231
  const client = this.client;
4175
- const stream2 = await client.messages.create(payload);
4232
+ const stream2 = await client.messages.create(payload, signal ? { signal } : void 0);
4176
4233
  return stream2;
4177
4234
  }
4178
4235
  async *wrapStream(iterable) {
@@ -4504,9 +4561,15 @@ var init_gemini = __esm({
4504
4561
  config
4505
4562
  };
4506
4563
  }
4507
- async executeStreamRequest(payload) {
4564
+ async executeStreamRequest(payload, signal) {
4508
4565
  const client = this.client;
4509
- const streamResponse = await client.models.generateContentStream(payload);
4566
+ const streamResponse = await client.models.generateContentStream({
4567
+ ...payload,
4568
+ config: {
4569
+ ...payload.config,
4570
+ ...signal ? { abortSignal: signal } : {}
4571
+ }
4572
+ });
4510
4573
  return streamResponse;
4511
4574
  }
4512
4575
  /**
@@ -5095,9 +5158,9 @@ var init_openai = __esm({
5095
5158
  ...shouldIncludeTemperature ? { temperature } : {}
5096
5159
  };
5097
5160
  }
5098
- async executeStreamRequest(payload) {
5161
+ async executeStreamRequest(payload, signal) {
5099
5162
  const client = this.client;
5100
- const stream2 = await client.chat.completions.create(payload);
5163
+ const stream2 = await client.chat.completions.create(payload, signal ? { signal } : void 0);
5101
5164
  return stream2;
5102
5165
  }
5103
5166
  async *wrapStream(iterable) {
@@ -5760,6 +5823,7 @@ var init_builder = __esm({
5760
5823
  gadgetOutputLimit;
5761
5824
  gadgetOutputLimitPercent;
5762
5825
  compactionConfig;
5826
+ signal;
5763
5827
  constructor(client) {
5764
5828
  this.client = client;
5765
5829
  }
@@ -6206,6 +6270,35 @@ var init_builder = __esm({
6206
6270
  this.compactionConfig = { enabled: false };
6207
6271
  return this;
6208
6272
  }
6273
+ /**
6274
+ * Set an abort signal for cancelling requests mid-flight.
6275
+ *
6276
+ * When the signal is aborted, the current LLM request will be cancelled
6277
+ * and the agent loop will exit gracefully.
6278
+ *
6279
+ * @param signal - AbortSignal from an AbortController
6280
+ * @returns This builder for chaining
6281
+ *
6282
+ * @example
6283
+ * ```typescript
6284
+ * const controller = new AbortController();
6285
+ *
6286
+ * // Cancel after 30 seconds
6287
+ * setTimeout(() => controller.abort(), 30000);
6288
+ *
6289
+ * const agent = LLMist.createAgent()
6290
+ * .withModel("sonnet")
6291
+ * .withSignal(controller.signal)
6292
+ * .ask("Write a long story");
6293
+ *
6294
+ * // Or cancel on user action
6295
+ * document.getElementById("cancel").onclick = () => controller.abort();
6296
+ * ```
6297
+ */
6298
+ withSignal(signal) {
6299
+ this.signal = signal;
6300
+ return this;
6301
+ }
6209
6302
  /**
6210
6303
  * Add a synthetic gadget call to the conversation history.
6211
6304
  *
@@ -6322,7 +6415,8 @@ ${endPrefix}`
6322
6415
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
6323
6416
  gadgetOutputLimit: this.gadgetOutputLimit,
6324
6417
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
6325
- compactionConfig: this.compactionConfig
6418
+ compactionConfig: this.compactionConfig,
6419
+ signal: this.signal
6326
6420
  };
6327
6421
  return new Agent(AGENT_INTERNAL_KEY, options);
6328
6422
  }
@@ -6425,7 +6519,8 @@ ${endPrefix}`
6425
6519
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
6426
6520
  gadgetOutputLimit: this.gadgetOutputLimit,
6427
6521
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
6428
- compactionConfig: this.compactionConfig
6522
+ compactionConfig: this.compactionConfig,
6523
+ signal: this.signal
6429
6524
  };
6430
6525
  return new Agent(AGENT_INTERNAL_KEY, options);
6431
6526
  }
@@ -6484,7 +6579,7 @@ var import_commander2 = require("commander");
6484
6579
  // package.json
6485
6580
  var package_default = {
6486
6581
  name: "llmist",
6487
- version: "1.3.0",
6582
+ version: "1.4.0",
6488
6583
  description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
6489
6584
  type: "module",
6490
6585
  main: "dist/index.cjs",
@@ -6568,6 +6663,7 @@ var package_default = {
6568
6663
  "@google/genai": "^1.27.0",
6569
6664
  chalk: "^5.6.2",
6570
6665
  commander: "^12.1.0",
6666
+ diff: "^8.0.2",
6571
6667
  eta: "^4.4.1",
6572
6668
  "js-toml": "^1.0.2",
6573
6669
  "js-yaml": "^4.1.0",
@@ -6584,6 +6680,7 @@ var package_default = {
6584
6680
  "@commitlint/config-conventional": "^20.0.0",
6585
6681
  "@semantic-release/changelog": "^6.0.3",
6586
6682
  "@semantic-release/git": "^10.0.1",
6683
+ "@types/diff": "^8.0.0",
6587
6684
  "@types/js-yaml": "^4.0.9",
6588
6685
  "@types/marked-terminal": "^6.1.1",
6589
6686
  "@types/node": "^20.12.7",
@@ -6597,12 +6694,274 @@ var package_default = {
6597
6694
  };
6598
6695
 
6599
6696
  // src/cli/agent-command.ts
6600
- var import_promises2 = require("readline/promises");
6601
- var import_chalk3 = __toESM(require("chalk"), 1);
6697
+ var import_promises3 = require("readline/promises");
6698
+ var import_chalk5 = __toESM(require("chalk"), 1);
6602
6699
  init_builder();
6700
+
6701
+ // src/core/errors.ts
6702
+ function isAbortError(error) {
6703
+ if (!(error instanceof Error)) return false;
6704
+ if (error.name === "AbortError") return true;
6705
+ if (error.name === "APIConnectionAbortedError") return true;
6706
+ if (error.name === "APIUserAbortError") return true;
6707
+ const message = error.message.toLowerCase();
6708
+ if (message.includes("abort")) return true;
6709
+ if (message.includes("cancelled")) return true;
6710
+ if (message.includes("canceled")) return true;
6711
+ return false;
6712
+ }
6713
+
6714
+ // src/cli/agent-command.ts
6603
6715
  init_registry();
6604
6716
  init_constants2();
6605
6717
 
6718
+ // src/cli/approval/manager.ts
6719
+ var import_promises = require("readline/promises");
6720
+ var import_chalk2 = __toESM(require("chalk"), 1);
6721
+
6722
+ // src/cli/approval/context-providers.ts
6723
+ var import_node_fs2 = require("fs");
6724
+ var import_node_path2 = require("path");
6725
+ var import_diff = require("diff");
6726
+
6727
+ // src/cli/approval/diff-renderer.ts
6728
+ var import_chalk = __toESM(require("chalk"), 1);
6729
+ function renderColoredDiff(diff) {
6730
+ return diff.split("\n").map((line) => {
6731
+ if (line.startsWith("---") || line.startsWith("+++")) {
6732
+ return import_chalk.default.bold(line);
6733
+ }
6734
+ if (line.startsWith("+")) {
6735
+ return import_chalk.default.green(line);
6736
+ }
6737
+ if (line.startsWith("-")) {
6738
+ return import_chalk.default.red(line);
6739
+ }
6740
+ if (line.startsWith("@@")) {
6741
+ return import_chalk.default.cyan(line);
6742
+ }
6743
+ return import_chalk.default.dim(line);
6744
+ }).join("\n");
6745
+ }
6746
+ function formatNewFileDiff(filePath, content) {
6747
+ const lines = content.split("\n");
6748
+ const header = `+++ ${filePath} (new file)`;
6749
+ const addedLines = lines.map((line) => `+ ${line}`).join("\n");
6750
+ return `${header}
6751
+ ${addedLines}`;
6752
+ }
6753
+
6754
+ // src/cli/approval/context-providers.ts
6755
+ var WriteFileContextProvider = class {
6756
+ gadgetName = "WriteFile";
6757
+ async getContext(params) {
6758
+ const filePath = String(params.filePath ?? params.path ?? "");
6759
+ const newContent = String(params.content ?? "");
6760
+ const resolvedPath = (0, import_node_path2.resolve)(process.cwd(), filePath);
6761
+ if (!(0, import_node_fs2.existsSync)(resolvedPath)) {
6762
+ return {
6763
+ summary: `Create new file: ${filePath}`,
6764
+ details: formatNewFileDiff(filePath, newContent)
6765
+ };
6766
+ }
6767
+ const oldContent = (0, import_node_fs2.readFileSync)(resolvedPath, "utf-8");
6768
+ const diff = (0, import_diff.createPatch)(filePath, oldContent, newContent, "original", "modified");
6769
+ return {
6770
+ summary: `Modify: ${filePath}`,
6771
+ details: diff
6772
+ };
6773
+ }
6774
+ };
6775
+ var EditFileContextProvider = class {
6776
+ gadgetName = "EditFile";
6777
+ async getContext(params) {
6778
+ const filePath = String(params.filePath ?? params.path ?? "");
6779
+ const resolvedPath = (0, import_node_path2.resolve)(process.cwd(), filePath);
6780
+ if ("content" in params) {
6781
+ const newContent = String(params.content);
6782
+ if (!(0, import_node_fs2.existsSync)(resolvedPath)) {
6783
+ return {
6784
+ summary: `Create new file: ${filePath}`,
6785
+ details: formatNewFileDiff(filePath, newContent)
6786
+ };
6787
+ }
6788
+ const oldContent = (0, import_node_fs2.readFileSync)(resolvedPath, "utf-8");
6789
+ const diff = (0, import_diff.createPatch)(filePath, oldContent, newContent, "original", "modified");
6790
+ return {
6791
+ summary: `Modify: ${filePath}`,
6792
+ details: diff
6793
+ };
6794
+ }
6795
+ if ("commands" in params) {
6796
+ const commands = String(params.commands);
6797
+ return {
6798
+ summary: `Edit: ${filePath}`,
6799
+ details: `Commands:
6800
+ ${commands}`
6801
+ };
6802
+ }
6803
+ return {
6804
+ summary: `Edit: ${filePath}`
6805
+ };
6806
+ }
6807
+ };
6808
+ var RunCommandContextProvider = class {
6809
+ gadgetName = "RunCommand";
6810
+ async getContext(params) {
6811
+ const command = String(params.command ?? "");
6812
+ const cwd = params.cwd ? ` (in ${params.cwd})` : "";
6813
+ return {
6814
+ summary: `Execute: ${command}${cwd}`
6815
+ };
6816
+ }
6817
+ };
6818
+ var DefaultContextProvider = class {
6819
+ constructor(gadgetName) {
6820
+ this.gadgetName = gadgetName;
6821
+ }
6822
+ async getContext(params) {
6823
+ const paramEntries = Object.entries(params);
6824
+ if (paramEntries.length === 0) {
6825
+ return {
6826
+ summary: `${this.gadgetName}()`
6827
+ };
6828
+ }
6829
+ const formatValue = (value) => {
6830
+ const MAX_LEN = 50;
6831
+ const str = JSON.stringify(value);
6832
+ return str.length > MAX_LEN ? `${str.slice(0, MAX_LEN - 3)}...` : str;
6833
+ };
6834
+ const paramStr = paramEntries.map(([k, v]) => `${k}=${formatValue(v)}`).join(", ");
6835
+ return {
6836
+ summary: `${this.gadgetName}(${paramStr})`
6837
+ };
6838
+ }
6839
+ };
6840
+ var builtinContextProviders = [
6841
+ new WriteFileContextProvider(),
6842
+ new EditFileContextProvider(),
6843
+ new RunCommandContextProvider()
6844
+ ];
6845
+
6846
+ // src/cli/approval/manager.ts
6847
+ var ApprovalManager = class {
6848
+ /**
6849
+ * Creates a new ApprovalManager.
6850
+ *
6851
+ * @param config - Approval configuration with per-gadget modes
6852
+ * @param env - CLI environment for I/O operations
6853
+ * @param progress - Optional progress indicator to pause during prompts
6854
+ */
6855
+ constructor(config, env, progress) {
6856
+ this.config = config;
6857
+ this.env = env;
6858
+ this.progress = progress;
6859
+ for (const provider of builtinContextProviders) {
6860
+ this.registerProvider(provider);
6861
+ }
6862
+ }
6863
+ providers = /* @__PURE__ */ new Map();
6864
+ /**
6865
+ * Registers a custom context provider for a gadget.
6866
+ *
6867
+ * @param provider - The context provider to register
6868
+ */
6869
+ registerProvider(provider) {
6870
+ this.providers.set(provider.gadgetName.toLowerCase(), provider);
6871
+ }
6872
+ /**
6873
+ * Gets the approval mode for a gadget.
6874
+ *
6875
+ * Resolution order:
6876
+ * 1. Explicit configuration for the gadget name
6877
+ * 2. Wildcard "*" configuration
6878
+ * 3. Default mode from config
6879
+ *
6880
+ * @param gadgetName - Name of the gadget
6881
+ * @returns The approval mode to use
6882
+ */
6883
+ getApprovalMode(gadgetName) {
6884
+ const normalizedName = gadgetName.toLowerCase();
6885
+ for (const [configName, mode] of Object.entries(this.config.gadgetApprovals)) {
6886
+ if (configName.toLowerCase() === normalizedName) {
6887
+ return mode;
6888
+ }
6889
+ }
6890
+ if ("*" in this.config.gadgetApprovals) {
6891
+ return this.config.gadgetApprovals["*"];
6892
+ }
6893
+ return this.config.defaultMode;
6894
+ }
6895
+ /**
6896
+ * Requests approval for a gadget execution.
6897
+ *
6898
+ * Behavior depends on the gadget's approval mode:
6899
+ * - "allowed": Returns approved immediately
6900
+ * - "denied": Returns denied with configuration message
6901
+ * - "approval-required": Prompts user interactively
6902
+ *
6903
+ * @param gadgetName - Name of the gadget
6904
+ * @param params - The gadget's execution parameters
6905
+ * @returns Approval result indicating whether to proceed
6906
+ */
6907
+ async requestApproval(gadgetName, params) {
6908
+ const mode = this.getApprovalMode(gadgetName);
6909
+ if (mode === "allowed") {
6910
+ return { approved: true };
6911
+ }
6912
+ if (mode === "denied") {
6913
+ return {
6914
+ approved: false,
6915
+ reason: `${gadgetName} is denied by configuration`
6916
+ };
6917
+ }
6918
+ return this.promptForApproval(gadgetName, params);
6919
+ }
6920
+ /**
6921
+ * Prompts the user for approval interactively.
6922
+ */
6923
+ async promptForApproval(gadgetName, params) {
6924
+ const provider = this.providers.get(gadgetName.toLowerCase()) ?? new DefaultContextProvider(gadgetName);
6925
+ const context = await provider.getContext(params);
6926
+ this.progress?.pause();
6927
+ this.env.stderr.write(`
6928
+ ${import_chalk2.default.yellow("\u{1F512} Approval required:")} ${context.summary}
6929
+ `);
6930
+ if (context.details) {
6931
+ this.env.stderr.write(`
6932
+ ${renderColoredDiff(context.details)}
6933
+ `);
6934
+ }
6935
+ const response = await this.prompt(" \u23CE approve, or type to reject: ");
6936
+ const isApproved = response === "" || response.toLowerCase() === "y";
6937
+ if (isApproved) {
6938
+ this.env.stderr.write(` ${import_chalk2.default.green("\u2713 Approved")}
6939
+
6940
+ `);
6941
+ return { approved: true };
6942
+ }
6943
+ this.env.stderr.write(` ${import_chalk2.default.red("\u2717 Denied")}
6944
+
6945
+ `);
6946
+ return { approved: false, reason: response || "Rejected by user" };
6947
+ }
6948
+ /**
6949
+ * Prompts for user input.
6950
+ */
6951
+ async prompt(message) {
6952
+ const rl = (0, import_promises.createInterface)({
6953
+ input: this.env.stdin,
6954
+ output: this.env.stderr
6955
+ });
6956
+ try {
6957
+ const answer = await rl.question(message);
6958
+ return answer.trim();
6959
+ } finally {
6960
+ rl.close();
6961
+ }
6962
+ }
6963
+ };
6964
+
6606
6965
  // src/cli/builtin-gadgets.ts
6607
6966
  var import_zod2 = require("zod");
6608
6967
  init_create_gadget();
@@ -6682,11 +7041,483 @@ var finish = createGadget({
6682
7041
  var builtinGadgets = [askUser, tellUser, finish];
6683
7042
 
6684
7043
  // src/cli/gadgets.ts
6685
- var import_node_fs2 = __toESM(require("fs"), 1);
6686
- var import_node_path2 = __toESM(require("path"), 1);
7044
+ var import_node_fs7 = __toESM(require("fs"), 1);
7045
+ var import_node_path6 = __toESM(require("path"), 1);
6687
7046
  var import_node_url = require("url");
6688
7047
  init_gadget();
7048
+
7049
+ // src/cli/builtins/filesystem/list-directory.ts
7050
+ var import_node_fs4 = __toESM(require("fs"), 1);
7051
+ var import_node_path4 = __toESM(require("path"), 1);
7052
+ var import_zod4 = require("zod");
7053
+
7054
+ // src/index.ts
7055
+ var import_zod3 = require("zod");
7056
+ init_builder();
7057
+ init_event_handlers();
7058
+
7059
+ // src/agent/index.ts
7060
+ init_conversation_manager();
7061
+ init_stream_processor();
7062
+ init_gadget_output_store();
7063
+
7064
+ // src/agent/compaction/index.ts
7065
+ init_config();
7066
+ init_strategy();
7067
+ init_strategies();
7068
+ init_manager();
7069
+
7070
+ // src/agent/hints.ts
7071
+ init_prompt_config();
7072
+
7073
+ // src/index.ts
7074
+ init_client();
7075
+ init_messages();
7076
+ init_model_registry();
7077
+ init_model_shortcuts();
7078
+ init_options();
7079
+ init_prompt_config();
7080
+ init_quick_methods();
7081
+ init_create_gadget();
7082
+ init_output_viewer();
7083
+ init_exceptions();
7084
+ init_executor();
7085
+ init_gadget();
7086
+ init_parser();
7087
+ init_registry();
7088
+
7089
+ // src/gadgets/typed-gadget.ts
7090
+ init_gadget();
7091
+
7092
+ // src/index.ts
7093
+ init_logger();
7094
+ init_anthropic();
7095
+ init_discovery();
7096
+ init_gemini();
7097
+ init_openai();
7098
+
7099
+ // src/testing/mock-manager.ts
7100
+ init_logger();
7101
+
7102
+ // src/testing/mock-stream.ts
7103
+ init_constants();
7104
+
7105
+ // src/testing/mock-client.ts
7106
+ init_client();
7107
+
7108
+ // src/testing/mock-gadget.ts
7109
+ init_gadget();
7110
+
7111
+ // src/testing/cli-helpers.ts
7112
+ var import_node_stream = require("stream");
7113
+
7114
+ // src/cli/builtins/filesystem/utils.ts
7115
+ var import_node_fs3 = __toESM(require("fs"), 1);
7116
+ var import_node_path3 = __toESM(require("path"), 1);
7117
+ var PathSandboxException = class extends Error {
7118
+ constructor(inputPath, reason) {
7119
+ super(`Path access denied: ${inputPath}. ${reason}`);
7120
+ this.name = "PathSandboxException";
7121
+ }
7122
+ };
7123
+ function validatePathIsWithinCwd(inputPath) {
7124
+ const cwd = process.cwd();
7125
+ const resolvedPath = import_node_path3.default.resolve(cwd, inputPath);
7126
+ let finalPath;
7127
+ try {
7128
+ finalPath = import_node_fs3.default.realpathSync(resolvedPath);
7129
+ } catch (error) {
7130
+ const nodeError = error;
7131
+ if (nodeError.code === "ENOENT") {
7132
+ finalPath = resolvedPath;
7133
+ } else {
7134
+ throw error;
7135
+ }
7136
+ }
7137
+ const cwdWithSep = cwd + import_node_path3.default.sep;
7138
+ if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
7139
+ throw new PathSandboxException(inputPath, "Path is outside the current working directory");
7140
+ }
7141
+ return finalPath;
7142
+ }
7143
+
7144
+ // src/cli/builtins/filesystem/list-directory.ts
7145
+ function listFiles(dirPath, basePath = dirPath, maxDepth = 1, currentDepth = 1) {
7146
+ const entries = [];
7147
+ try {
7148
+ const items = import_node_fs4.default.readdirSync(dirPath);
7149
+ for (const item of items) {
7150
+ const fullPath = import_node_path4.default.join(dirPath, item);
7151
+ const relativePath = import_node_path4.default.relative(basePath, fullPath);
7152
+ try {
7153
+ const stats = import_node_fs4.default.lstatSync(fullPath);
7154
+ let type;
7155
+ let size;
7156
+ if (stats.isSymbolicLink()) {
7157
+ type = "symlink";
7158
+ size = 0;
7159
+ } else if (stats.isDirectory()) {
7160
+ type = "directory";
7161
+ size = 0;
7162
+ } else {
7163
+ type = "file";
7164
+ size = stats.size;
7165
+ }
7166
+ entries.push({
7167
+ name: item,
7168
+ relativePath,
7169
+ type,
7170
+ size,
7171
+ modified: Math.floor(stats.mtime.getTime() / 1e3)
7172
+ });
7173
+ if (type === "directory" && currentDepth < maxDepth) {
7174
+ try {
7175
+ validatePathIsWithinCwd(fullPath);
7176
+ const subEntries = listFiles(fullPath, basePath, maxDepth, currentDepth + 1);
7177
+ entries.push(...subEntries);
7178
+ } catch {
7179
+ }
7180
+ }
7181
+ } catch {
7182
+ }
7183
+ }
7184
+ } catch {
7185
+ return [];
7186
+ }
7187
+ return entries;
7188
+ }
7189
+ function formatAge(epochSeconds) {
7190
+ const now = Math.floor(Date.now() / 1e3);
7191
+ const seconds = now - epochSeconds;
7192
+ if (seconds < 60) return `${seconds}s`;
7193
+ const minutes = Math.floor(seconds / 60);
7194
+ if (minutes < 60) return `${minutes}m`;
7195
+ const hours = Math.floor(minutes / 60);
7196
+ if (hours < 24) return `${hours}h`;
7197
+ const days = Math.floor(hours / 24);
7198
+ if (days < 7) return `${days}d`;
7199
+ const weeks = Math.floor(days / 7);
7200
+ if (weeks < 4) return `${weeks}w`;
7201
+ const months = Math.floor(days / 30);
7202
+ if (months < 12) return `${months}mo`;
7203
+ const years = Math.floor(days / 365);
7204
+ return `${years}y`;
7205
+ }
7206
+ function formatEntriesAsString(entries) {
7207
+ if (entries.length === 0) {
7208
+ return "#empty";
7209
+ }
7210
+ const sortedEntries = [...entries].sort((a, b) => {
7211
+ const typeOrder = { directory: 0, file: 1, symlink: 2 };
7212
+ const typeCompare = typeOrder[a.type] - typeOrder[b.type];
7213
+ if (typeCompare !== 0) return typeCompare;
7214
+ return a.relativePath.localeCompare(b.relativePath);
7215
+ });
7216
+ const typeCode = {
7217
+ directory: "D",
7218
+ file: "F",
7219
+ symlink: "L"
7220
+ };
7221
+ const encodeName = (name) => name.replace(/\|/g, "%7C").replace(/\n/g, "%0A");
7222
+ const header = "#T|N|S|A";
7223
+ const rows = sortedEntries.map(
7224
+ (e) => `${typeCode[e.type]}|${encodeName(e.relativePath)}|${e.size}|${formatAge(e.modified)}`
7225
+ );
7226
+ return [header, ...rows].join("\n");
7227
+ }
7228
+ var listDirectory = createGadget({
7229
+ name: "ListDirectory",
7230
+ 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.",
7231
+ schema: import_zod4.z.object({
7232
+ directoryPath: import_zod4.z.string().default(".").describe("Path to the directory to list"),
7233
+ maxDepth: import_zod4.z.number().int().min(1).max(10).default(1).describe(
7234
+ "Maximum depth to recurse (1 = immediate children only, 2 = include grandchildren, etc.)"
7235
+ )
7236
+ }),
7237
+ examples: [
7238
+ {
7239
+ params: { directoryPath: ".", maxDepth: 1 },
7240
+ output: "path=. maxDepth=1\n\n#T|N|S|A\nD|src|0|2h\nD|tests|0|1d\nF|package.json|2841|3h",
7241
+ comment: "List current directory"
7242
+ },
7243
+ {
7244
+ params: { directoryPath: "src", maxDepth: 2 },
7245
+ output: "path=src maxDepth=2\n\n#T|N|S|A\nD|components|0|1d\nD|utils|0|2d\nF|index.ts|512|1h\nF|components/Button.tsx|1024|3h",
7246
+ comment: "List src directory recursively"
7247
+ }
7248
+ ],
7249
+ execute: ({ directoryPath, maxDepth }) => {
7250
+ const validatedPath = validatePathIsWithinCwd(directoryPath);
7251
+ const stats = import_node_fs4.default.statSync(validatedPath);
7252
+ if (!stats.isDirectory()) {
7253
+ throw new Error(`Path is not a directory: ${directoryPath}`);
7254
+ }
7255
+ const entries = listFiles(validatedPath, validatedPath, maxDepth);
7256
+ const formattedList = formatEntriesAsString(entries);
7257
+ return `path=${directoryPath} maxDepth=${maxDepth}
7258
+
7259
+ ${formattedList}`;
7260
+ }
7261
+ });
7262
+
7263
+ // src/cli/builtins/filesystem/read-file.ts
7264
+ var import_node_fs5 = __toESM(require("fs"), 1);
7265
+ var import_zod5 = require("zod");
7266
+ var readFile = createGadget({
7267
+ name: "ReadFile",
7268
+ 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.",
7269
+ schema: import_zod5.z.object({
7270
+ filePath: import_zod5.z.string().describe("Path to the file to read (relative or absolute)")
7271
+ }),
7272
+ examples: [
7273
+ {
7274
+ params: { filePath: "package.json" },
7275
+ output: 'path=package.json\n\n{\n "name": "my-project",\n "version": "1.0.0"\n ...\n}',
7276
+ comment: "Read a JSON config file"
7277
+ },
7278
+ {
7279
+ params: { filePath: "src/index.ts" },
7280
+ output: "path=src/index.ts\n\nexport function main() { ... }",
7281
+ comment: "Read a source file"
7282
+ }
7283
+ ],
7284
+ execute: ({ filePath }) => {
7285
+ const validatedPath = validatePathIsWithinCwd(filePath);
7286
+ const content = import_node_fs5.default.readFileSync(validatedPath, "utf-8");
7287
+ return `path=${filePath}
7288
+
7289
+ ${content}`;
7290
+ }
7291
+ });
7292
+
7293
+ // src/cli/builtins/filesystem/write-file.ts
7294
+ var import_node_fs6 = __toESM(require("fs"), 1);
7295
+ var import_node_path5 = __toESM(require("path"), 1);
7296
+ var import_zod6 = require("zod");
7297
+ var writeFile = createGadget({
7298
+ name: "WriteFile",
7299
+ 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.",
7300
+ schema: import_zod6.z.object({
7301
+ filePath: import_zod6.z.string().describe("Path to the file to write (relative or absolute)"),
7302
+ content: import_zod6.z.string().describe("Content to write to the file")
7303
+ }),
7304
+ examples: [
7305
+ {
7306
+ params: { filePath: "output.txt", content: "Hello, World!" },
7307
+ output: "path=output.txt\n\nWrote 13 bytes",
7308
+ comment: "Write a simple text file"
7309
+ },
7310
+ {
7311
+ params: {
7312
+ filePath: "src/server.ts",
7313
+ content: `import { serve } from "bun";
7314
+
7315
+ const port = 3000;
7316
+
7317
+ serve({
7318
+ port,
7319
+ fetch: (req) => new Response(\`Hello from \${req.url}\`),
7320
+ });
7321
+
7322
+ console.log(\`Server running on http://localhost:\${port}\`);`
7323
+ },
7324
+ output: "path=src/server.ts\n\nWrote 198 bytes (created directory: src)",
7325
+ comment: "Write code with template literals - NO escaping needed inside heredoc (use <<<EOF...EOF)"
7326
+ }
7327
+ ],
7328
+ execute: ({ filePath, content }) => {
7329
+ const validatedPath = validatePathIsWithinCwd(filePath);
7330
+ const parentDir = import_node_path5.default.dirname(validatedPath);
7331
+ let createdDir = false;
7332
+ if (!import_node_fs6.default.existsSync(parentDir)) {
7333
+ validatePathIsWithinCwd(parentDir);
7334
+ import_node_fs6.default.mkdirSync(parentDir, { recursive: true });
7335
+ createdDir = true;
7336
+ }
7337
+ import_node_fs6.default.writeFileSync(validatedPath, content, "utf-8");
7338
+ const bytesWritten = Buffer.byteLength(content, "utf-8");
7339
+ const dirNote = createdDir ? ` (created directory: ${import_node_path5.default.dirname(filePath)})` : "";
7340
+ return `path=${filePath}
7341
+
7342
+ Wrote ${bytesWritten} bytes${dirNote}`;
7343
+ }
7344
+ });
7345
+
7346
+ // src/cli/builtins/filesystem/edit-file.ts
7347
+ var import_zod7 = require("zod");
7348
+ function filterDangerousCommands(commands) {
7349
+ return commands.split("\n").filter((line) => !line.trimStart().startsWith("!")).join("\n");
7350
+ }
7351
+ var editFile = createGadget({
7352
+ name: "EditFile",
7353
+ description: "Edit a file using ed commands. Ed is a line-oriented text editor - pipe commands to it for precise file modifications. Commands are executed in sequence. Remember to end with 'w' (write) and 'q' (quit). Shell escape commands (!) are filtered for security.",
7354
+ schema: import_zod7.z.object({
7355
+ filePath: import_zod7.z.string().describe("Path to the file to edit (relative or absolute)"),
7356
+ commands: import_zod7.z.string().describe("Ed commands to execute, one per line")
7357
+ }),
7358
+ examples: [
7359
+ {
7360
+ params: {
7361
+ filePath: "config.txt",
7362
+ commands: `1,$p
7363
+ q`
7364
+ },
7365
+ output: "path=config.txt\n\n32\nkey=value\noption=true",
7366
+ comment: "Print entire file contents (ed shows byte count, then content)"
7367
+ },
7368
+ {
7369
+ params: {
7370
+ filePath: "data.txt",
7371
+ commands: `1,$s/foo/bar/g
7372
+ w
7373
+ q`
7374
+ },
7375
+ output: "path=data.txt\n\n42\n42",
7376
+ comment: "Replace all 'foo' with 'bar' (ed shows bytes read, then bytes written)"
7377
+ },
7378
+ {
7379
+ params: {
7380
+ filePath: "list.txt",
7381
+ commands: `3d
7382
+ w
7383
+ q`
7384
+ },
7385
+ output: "path=list.txt\n\n45\n28",
7386
+ comment: "Delete line 3, save and quit"
7387
+ },
7388
+ {
7389
+ params: {
7390
+ filePath: "readme.txt",
7391
+ commands: `$a
7392
+ New last line
7393
+ .
7394
+ w
7395
+ q`
7396
+ },
7397
+ output: "path=readme.txt\n\n40\n56",
7398
+ comment: "Append text after last line ($ = last line, . = end input mode)"
7399
+ }
7400
+ ],
7401
+ timeoutMs: 3e4,
7402
+ execute: async ({ filePath, commands }) => {
7403
+ const validatedPath = validatePathIsWithinCwd(filePath);
7404
+ const safeCommands = filterDangerousCommands(commands);
7405
+ try {
7406
+ const proc = Bun.spawn(["ed", validatedPath], {
7407
+ stdin: "pipe",
7408
+ stdout: "pipe",
7409
+ stderr: "pipe"
7410
+ });
7411
+ proc.stdin.write(`${safeCommands}
7412
+ `);
7413
+ proc.stdin.end();
7414
+ const timeoutPromise = new Promise((_, reject) => {
7415
+ setTimeout(() => {
7416
+ proc.kill();
7417
+ reject(new Error("ed command timed out after 30000ms"));
7418
+ }, 3e4);
7419
+ });
7420
+ const exitCode = await Promise.race([proc.exited, timeoutPromise]);
7421
+ const stdout = await new Response(proc.stdout).text();
7422
+ const stderr = await new Response(proc.stderr).text();
7423
+ const output = [stdout, stderr].filter(Boolean).join("\n").trim();
7424
+ if (exitCode !== 0) {
7425
+ return `path=${filePath}
7426
+
7427
+ ${output || "ed exited with non-zero status"}`;
7428
+ }
7429
+ return `path=${filePath}
7430
+
7431
+ ${output || "(no output)"}`;
7432
+ } catch (error) {
7433
+ const message = error instanceof Error ? error.message : String(error);
7434
+ return `path=${filePath}
7435
+
7436
+ error: ${message}`;
7437
+ }
7438
+ }
7439
+ });
7440
+
7441
+ // src/cli/builtins/run-command.ts
7442
+ var import_zod8 = require("zod");
7443
+ var runCommand = createGadget({
7444
+ name: "RunCommand",
7445
+ description: "Execute a shell command and return its output. Returns both stdout and stderr combined with the exit status.",
7446
+ schema: import_zod8.z.object({
7447
+ command: import_zod8.z.string().describe("The shell command to execute"),
7448
+ cwd: import_zod8.z.string().optional().describe("Working directory for the command (default: current directory)"),
7449
+ timeout: import_zod8.z.number().default(3e4).describe("Timeout in milliseconds (default: 30000)")
7450
+ }),
7451
+ examples: [
7452
+ {
7453
+ params: { command: "ls -la", timeout: 3e4 },
7454
+ output: "status=0\n\ntotal 24\ndrwxr-xr-x 5 user staff 160 Nov 27 10:00 .\ndrwxr-xr-x 3 user staff 96 Nov 27 09:00 ..\n-rw-r--r-- 1 user staff 1024 Nov 27 10:00 package.json",
7455
+ comment: "List directory contents with details"
7456
+ },
7457
+ {
7458
+ params: { command: "echo 'Hello World'", timeout: 3e4 },
7459
+ output: "status=0\n\nHello World",
7460
+ comment: "Simple echo command"
7461
+ },
7462
+ {
7463
+ params: { command: "cat nonexistent.txt", timeout: 3e4 },
7464
+ output: "status=1\n\ncat: nonexistent.txt: No such file or directory",
7465
+ comment: "Command that fails returns non-zero status"
7466
+ },
7467
+ {
7468
+ params: { command: "pwd", cwd: "/tmp", timeout: 3e4 },
7469
+ output: "status=0\n\n/tmp",
7470
+ comment: "Execute command in a specific directory"
7471
+ }
7472
+ ],
7473
+ execute: async ({ command, cwd, timeout }) => {
7474
+ const workingDir = cwd ?? process.cwd();
7475
+ try {
7476
+ const proc = Bun.spawn(["sh", "-c", command], {
7477
+ cwd: workingDir,
7478
+ stdout: "pipe",
7479
+ stderr: "pipe"
7480
+ });
7481
+ const timeoutPromise = new Promise((_, reject) => {
7482
+ setTimeout(() => {
7483
+ proc.kill();
7484
+ reject(new Error(`Command timed out after ${timeout}ms`));
7485
+ }, timeout);
7486
+ });
7487
+ const exitCode = await Promise.race([proc.exited, timeoutPromise]);
7488
+ const stdout = await new Response(proc.stdout).text();
7489
+ const stderr = await new Response(proc.stderr).text();
7490
+ const output = [stdout, stderr].filter(Boolean).join("\n").trim();
7491
+ return `status=${exitCode}
7492
+
7493
+ ${output || "(no output)"}`;
7494
+ } catch (error) {
7495
+ const message = error instanceof Error ? error.message : String(error);
7496
+ return `status=1
7497
+
7498
+ error: ${message}`;
7499
+ }
7500
+ }
7501
+ });
7502
+
7503
+ // src/cli/builtins/index.ts
7504
+ var builtinGadgetRegistry = {
7505
+ ListDirectory: listDirectory,
7506
+ ReadFile: readFile,
7507
+ WriteFile: writeFile,
7508
+ EditFile: editFile,
7509
+ RunCommand: runCommand
7510
+ };
7511
+ function getBuiltinGadget(name) {
7512
+ return builtinGadgetRegistry[name];
7513
+ }
7514
+ function isBuiltinGadgetName(name) {
7515
+ return name in builtinGadgetRegistry;
7516
+ }
7517
+
7518
+ // src/cli/gadgets.ts
6689
7519
  var PATH_PREFIXES = [".", "/", "~"];
7520
+ var BUILTIN_PREFIX = "builtin:";
6690
7521
  function isGadgetLike(value) {
6691
7522
  if (typeof value !== "object" || value === null) {
6692
7523
  return false;
@@ -6709,18 +7540,34 @@ function expandHomePath(input) {
6709
7540
  if (!home) {
6710
7541
  return input;
6711
7542
  }
6712
- return import_node_path2.default.join(home, input.slice(1));
7543
+ return import_node_path6.default.join(home, input.slice(1));
6713
7544
  }
6714
7545
  function isFileLikeSpecifier(specifier) {
6715
- return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path2.default.sep);
7546
+ return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path6.default.sep);
7547
+ }
7548
+ function tryResolveBuiltin(specifier) {
7549
+ if (specifier.startsWith(BUILTIN_PREFIX)) {
7550
+ const name = specifier.slice(BUILTIN_PREFIX.length);
7551
+ const gadget = getBuiltinGadget(name);
7552
+ if (!gadget) {
7553
+ throw new Error(
7554
+ `Unknown builtin gadget: ${name}. Available builtins: ListDirectory, ReadFile, WriteFile, EditFile, RunCommand`
7555
+ );
7556
+ }
7557
+ return gadget;
7558
+ }
7559
+ if (!isFileLikeSpecifier(specifier) && isBuiltinGadgetName(specifier)) {
7560
+ return getBuiltinGadget(specifier);
7561
+ }
7562
+ return null;
6716
7563
  }
6717
7564
  function resolveGadgetSpecifier(specifier, cwd) {
6718
7565
  if (!isFileLikeSpecifier(specifier)) {
6719
7566
  return specifier;
6720
7567
  }
6721
7568
  const expanded = expandHomePath(specifier);
6722
- const resolvedPath = import_node_path2.default.resolve(cwd, expanded);
6723
- if (!import_node_fs2.default.existsSync(resolvedPath)) {
7569
+ const resolvedPath = import_node_path6.default.resolve(cwd, expanded);
7570
+ if (!import_node_fs7.default.existsSync(resolvedPath)) {
6724
7571
  throw new Error(`Gadget module not found at ${resolvedPath}`);
6725
7572
  }
6726
7573
  return (0, import_node_url.pathToFileURL)(resolvedPath).href;
@@ -6762,6 +7609,11 @@ function extractGadgetsFromModule(moduleExports) {
6762
7609
  async function loadGadgets(specifiers, cwd, importer = (specifier) => import(specifier)) {
6763
7610
  const gadgets = [];
6764
7611
  for (const specifier of specifiers) {
7612
+ const builtin = tryResolveBuiltin(specifier);
7613
+ if (builtin) {
7614
+ gadgets.push(builtin);
7615
+ continue;
7616
+ }
6765
7617
  const resolved = resolveGadgetSpecifier(specifier, cwd);
6766
7618
  let exports2;
6767
7619
  try {
@@ -6786,13 +7638,13 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
6786
7638
  }
6787
7639
 
6788
7640
  // src/cli/llm-logging.ts
6789
- var import_promises = require("fs/promises");
7641
+ var import_promises2 = require("fs/promises");
6790
7642
  var import_node_os = require("os");
6791
- var import_node_path3 = require("path");
6792
- var DEFAULT_LLM_LOG_DIR = (0, import_node_path3.join)((0, import_node_os.homedir)(), ".llmist", "logs");
7643
+ var import_node_path7 = require("path");
7644
+ var DEFAULT_LLM_LOG_DIR = (0, import_node_path7.join)((0, import_node_os.homedir)(), ".llmist", "logs");
6793
7645
  function resolveLogDir(option, subdir) {
6794
7646
  if (option === true) {
6795
- return (0, import_node_path3.join)(DEFAULT_LLM_LOG_DIR, subdir);
7647
+ return (0, import_node_path7.join)(DEFAULT_LLM_LOG_DIR, subdir);
6796
7648
  }
6797
7649
  if (typeof option === "string") {
6798
7650
  return option;
@@ -6809,44 +7661,44 @@ function formatLlmRequest(messages) {
6809
7661
  return lines.join("\n");
6810
7662
  }
6811
7663
  async function writeLogFile(dir, filename, content) {
6812
- await (0, import_promises.mkdir)(dir, { recursive: true });
6813
- await (0, import_promises.writeFile)((0, import_node_path3.join)(dir, filename), content, "utf-8");
7664
+ await (0, import_promises2.mkdir)(dir, { recursive: true });
7665
+ await (0, import_promises2.writeFile)((0, import_node_path7.join)(dir, filename), content, "utf-8");
6814
7666
  }
6815
7667
 
6816
7668
  // src/cli/utils.ts
6817
- var import_chalk2 = __toESM(require("chalk"), 1);
7669
+ var import_chalk4 = __toESM(require("chalk"), 1);
6818
7670
  var import_commander = require("commander");
6819
7671
  init_constants2();
6820
7672
 
6821
7673
  // src/cli/ui/formatters.ts
6822
- var import_chalk = __toESM(require("chalk"), 1);
7674
+ var import_chalk3 = __toESM(require("chalk"), 1);
6823
7675
  var import_marked = require("marked");
6824
7676
  var import_marked_terminal = require("marked-terminal");
6825
7677
  var markedConfigured = false;
6826
7678
  function ensureMarkedConfigured() {
6827
7679
  if (!markedConfigured) {
6828
- import_chalk.default.level = process.env.NO_COLOR ? 0 : 3;
7680
+ import_chalk3.default.level = process.env.NO_COLOR ? 0 : 3;
6829
7681
  import_marked.marked.use(
6830
7682
  (0, import_marked_terminal.markedTerminal)({
6831
7683
  // Text styling
6832
- strong: import_chalk.default.bold,
6833
- em: import_chalk.default.italic,
6834
- del: import_chalk.default.dim.gray.strikethrough,
7684
+ strong: import_chalk3.default.bold,
7685
+ em: import_chalk3.default.italic,
7686
+ del: import_chalk3.default.dim.gray.strikethrough,
6835
7687
  // Code styling
6836
- code: import_chalk.default.yellow,
6837
- codespan: import_chalk.default.yellow,
7688
+ code: import_chalk3.default.yellow,
7689
+ codespan: import_chalk3.default.yellow,
6838
7690
  // Headings
6839
- heading: import_chalk.default.green.bold,
6840
- firstHeading: import_chalk.default.magenta.underline.bold,
7691
+ heading: import_chalk3.default.green.bold,
7692
+ firstHeading: import_chalk3.default.magenta.underline.bold,
6841
7693
  // Links
6842
- link: import_chalk.default.blue,
6843
- href: import_chalk.default.blue.underline,
7694
+ link: import_chalk3.default.blue,
7695
+ href: import_chalk3.default.blue.underline,
6844
7696
  // Block elements
6845
- blockquote: import_chalk.default.gray.italic,
7697
+ blockquote: import_chalk3.default.gray.italic,
6846
7698
  // List formatting - reduce indentation and add bullet styling
6847
7699
  tab: 2,
6848
7700
  // Reduce from default 4 to 2 spaces
6849
- listitem: import_chalk.default.reset
7701
+ listitem: import_chalk3.default.reset
6850
7702
  // Keep items readable (no dim)
6851
7703
  })
6852
7704
  );
@@ -6856,11 +7708,11 @@ function ensureMarkedConfigured() {
6856
7708
  function renderMarkdown(text) {
6857
7709
  ensureMarkedConfigured();
6858
7710
  let rendered = import_marked.marked.parse(text);
6859
- rendered = rendered.replace(/\*\*(.+?)\*\*/g, (_, content) => import_chalk.default.bold(content)).replace(/(?<!\*)\*(\S[^*]*)\*(?!\*)/g, (_, content) => import_chalk.default.italic(content));
7711
+ rendered = rendered.replace(/\*\*(.+?)\*\*/g, (_, content) => import_chalk3.default.bold(content)).replace(/(?<!\*)\*(\S[^*]*)\*(?!\*)/g, (_, content) => import_chalk3.default.italic(content));
6860
7712
  return rendered.trimEnd();
6861
7713
  }
6862
7714
  function createRainbowSeparator() {
6863
- const colors = [import_chalk.default.red, import_chalk.default.yellow, import_chalk.default.green, import_chalk.default.cyan, import_chalk.default.blue, import_chalk.default.magenta];
7715
+ const colors = [import_chalk3.default.red, import_chalk3.default.yellow, import_chalk3.default.green, import_chalk3.default.cyan, import_chalk3.default.blue, import_chalk3.default.magenta];
6864
7716
  const char = "\u2500";
6865
7717
  const width = process.stdout.columns || 80;
6866
7718
  let result = "";
@@ -6896,58 +7748,58 @@ function formatCost(cost) {
6896
7748
  function renderSummary(metadata) {
6897
7749
  const parts = [];
6898
7750
  if (metadata.iterations !== void 0) {
6899
- const iterPart = import_chalk.default.cyan(`#${metadata.iterations}`);
7751
+ const iterPart = import_chalk3.default.cyan(`#${metadata.iterations}`);
6900
7752
  if (metadata.model) {
6901
- parts.push(`${iterPart} ${import_chalk.default.magenta(metadata.model)}`);
7753
+ parts.push(`${iterPart} ${import_chalk3.default.magenta(metadata.model)}`);
6902
7754
  } else {
6903
7755
  parts.push(iterPart);
6904
7756
  }
6905
7757
  } else if (metadata.model) {
6906
- parts.push(import_chalk.default.magenta(metadata.model));
7758
+ parts.push(import_chalk3.default.magenta(metadata.model));
6907
7759
  }
6908
7760
  if (metadata.usage) {
6909
7761
  const { inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens } = metadata.usage;
6910
- parts.push(import_chalk.default.dim("\u2191") + import_chalk.default.yellow(` ${formatTokens(inputTokens)}`));
7762
+ parts.push(import_chalk3.default.dim("\u2191") + import_chalk3.default.yellow(` ${formatTokens(inputTokens)}`));
6911
7763
  if (cachedInputTokens && cachedInputTokens > 0) {
6912
- parts.push(import_chalk.default.dim("\u27F3") + import_chalk.default.blue(` ${formatTokens(cachedInputTokens)}`));
7764
+ parts.push(import_chalk3.default.dim("\u27F3") + import_chalk3.default.blue(` ${formatTokens(cachedInputTokens)}`));
6913
7765
  }
6914
7766
  if (cacheCreationInputTokens && cacheCreationInputTokens > 0) {
6915
- parts.push(import_chalk.default.dim("\u270E") + import_chalk.default.magenta(` ${formatTokens(cacheCreationInputTokens)}`));
7767
+ parts.push(import_chalk3.default.dim("\u270E") + import_chalk3.default.magenta(` ${formatTokens(cacheCreationInputTokens)}`));
6916
7768
  }
6917
- parts.push(import_chalk.default.dim("\u2193") + import_chalk.default.green(` ${formatTokens(outputTokens)}`));
7769
+ parts.push(import_chalk3.default.dim("\u2193") + import_chalk3.default.green(` ${formatTokens(outputTokens)}`));
6918
7770
  }
6919
7771
  if (metadata.elapsedSeconds !== void 0 && metadata.elapsedSeconds > 0) {
6920
- parts.push(import_chalk.default.dim(`${metadata.elapsedSeconds}s`));
7772
+ parts.push(import_chalk3.default.dim(`${metadata.elapsedSeconds}s`));
6921
7773
  }
6922
7774
  if (metadata.cost !== void 0 && metadata.cost > 0) {
6923
- parts.push(import_chalk.default.cyan(`$${formatCost(metadata.cost)}`));
7775
+ parts.push(import_chalk3.default.cyan(`$${formatCost(metadata.cost)}`));
6924
7776
  }
6925
7777
  if (metadata.finishReason) {
6926
- parts.push(import_chalk.default.dim(metadata.finishReason));
7778
+ parts.push(import_chalk3.default.dim(metadata.finishReason));
6927
7779
  }
6928
7780
  if (parts.length === 0) {
6929
7781
  return null;
6930
7782
  }
6931
- return parts.join(import_chalk.default.dim(" | "));
7783
+ return parts.join(import_chalk3.default.dim(" | "));
6932
7784
  }
6933
7785
  function renderOverallSummary(metadata) {
6934
7786
  const parts = [];
6935
7787
  if (metadata.totalTokens !== void 0 && metadata.totalTokens > 0) {
6936
- parts.push(import_chalk.default.dim("total:") + import_chalk.default.magenta(` ${formatTokens(metadata.totalTokens)}`));
7788
+ parts.push(import_chalk3.default.dim("total:") + import_chalk3.default.magenta(` ${formatTokens(metadata.totalTokens)}`));
6937
7789
  }
6938
7790
  if (metadata.iterations !== void 0 && metadata.iterations > 0) {
6939
- parts.push(import_chalk.default.cyan(`#${metadata.iterations}`));
7791
+ parts.push(import_chalk3.default.cyan(`#${metadata.iterations}`));
6940
7792
  }
6941
7793
  if (metadata.elapsedSeconds !== void 0 && metadata.elapsedSeconds > 0) {
6942
- parts.push(import_chalk.default.dim(`${metadata.elapsedSeconds}s`));
7794
+ parts.push(import_chalk3.default.dim(`${metadata.elapsedSeconds}s`));
6943
7795
  }
6944
7796
  if (metadata.cost !== void 0 && metadata.cost > 0) {
6945
- parts.push(import_chalk.default.cyan(`$${formatCost(metadata.cost)}`));
7797
+ parts.push(import_chalk3.default.cyan(`$${formatCost(metadata.cost)}`));
6946
7798
  }
6947
7799
  if (parts.length === 0) {
6948
7800
  return null;
6949
7801
  }
6950
- return parts.join(import_chalk.default.dim(" | "));
7802
+ return parts.join(import_chalk3.default.dim(" | "));
6951
7803
  }
6952
7804
  function formatParametersInline(params) {
6953
7805
  if (!params || Object.keys(params).length === 0) {
@@ -6963,8 +7815,8 @@ function formatParametersInline(params) {
6963
7815
  const json = JSON.stringify(value);
6964
7816
  formatted = json.length > 30 ? `${json.slice(0, 30)}\u2026` : json;
6965
7817
  }
6966
- return `${import_chalk.default.dim(key)}${import_chalk.default.dim("=")}${import_chalk.default.cyan(formatted)}`;
6967
- }).join(import_chalk.default.dim(", "));
7818
+ return `${import_chalk3.default.dim(key)}${import_chalk3.default.dim("=")}${import_chalk3.default.cyan(formatted)}`;
7819
+ }).join(import_chalk3.default.dim(", "));
6968
7820
  }
6969
7821
  function formatBytes(bytes) {
6970
7822
  if (bytes < 1024) {
@@ -6976,25 +7828,25 @@ function formatBytes(bytes) {
6976
7828
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6977
7829
  }
6978
7830
  function formatGadgetSummary(result) {
6979
- const gadgetLabel = import_chalk.default.magenta.bold(result.gadgetName);
6980
- const timeLabel = import_chalk.default.dim(`${Math.round(result.executionTimeMs)}ms`);
7831
+ const gadgetLabel = import_chalk3.default.magenta.bold(result.gadgetName);
7832
+ const timeLabel = import_chalk3.default.dim(`${Math.round(result.executionTimeMs)}ms`);
6981
7833
  const paramsStr = formatParametersInline(result.parameters);
6982
- const paramsLabel = paramsStr ? `${import_chalk.default.dim("(")}${paramsStr}${import_chalk.default.dim(")")}` : "";
7834
+ const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
6983
7835
  if (result.error) {
6984
7836
  const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
6985
- return `${import_chalk.default.red("\u2717")} ${gadgetLabel}${paramsLabel} ${import_chalk.default.red("error:")} ${errorMsg} ${timeLabel}`;
7837
+ return `${import_chalk3.default.red("\u2717")} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.red("error:")} ${errorMsg} ${timeLabel}`;
6986
7838
  }
6987
7839
  let outputLabel;
6988
7840
  if (result.tokenCount !== void 0 && result.tokenCount > 0) {
6989
- outputLabel = import_chalk.default.green(`${formatTokens(result.tokenCount)} tokens`);
7841
+ outputLabel = import_chalk3.default.green(`${formatTokens(result.tokenCount)} tokens`);
6990
7842
  } else if (result.result) {
6991
7843
  const outputBytes = Buffer.byteLength(result.result, "utf-8");
6992
- outputLabel = outputBytes > 0 ? import_chalk.default.green(formatBytes(outputBytes)) : import_chalk.default.dim("no output");
7844
+ outputLabel = outputBytes > 0 ? import_chalk3.default.green(formatBytes(outputBytes)) : import_chalk3.default.dim("no output");
6993
7845
  } else {
6994
- outputLabel = import_chalk.default.dim("no output");
7846
+ outputLabel = import_chalk3.default.dim("no output");
6995
7847
  }
6996
- const icon = result.breaksLoop ? import_chalk.default.yellow("\u23F9") : import_chalk.default.green("\u2713");
6997
- const summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${import_chalk.default.dim("\u2192")} ${outputLabel} ${timeLabel}`;
7848
+ const icon = result.breaksLoop ? import_chalk3.default.yellow("\u23F9") : import_chalk3.default.green("\u2713");
7849
+ const summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.dim("\u2192")} ${outputLabel} ${timeLabel}`;
6998
7850
  if (result.gadgetName === "TellUser" && result.parameters?.message) {
6999
7851
  const message = String(result.parameters.message);
7000
7852
  const rendered = renderMarkdownWithSeparators(message);
@@ -7058,6 +7910,66 @@ var StreamPrinter = class {
7058
7910
  function isInteractive(stream2) {
7059
7911
  return Boolean(stream2.isTTY);
7060
7912
  }
7913
+ var ESC_KEY = 27;
7914
+ var ESC_TIMEOUT_MS = 50;
7915
+ function createEscKeyListener(stdin, onEsc) {
7916
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function") {
7917
+ return null;
7918
+ }
7919
+ let escTimeout = null;
7920
+ const handleData = (data) => {
7921
+ if (data[0] === ESC_KEY) {
7922
+ if (data.length === 1) {
7923
+ escTimeout = setTimeout(() => {
7924
+ onEsc();
7925
+ }, ESC_TIMEOUT_MS);
7926
+ } else {
7927
+ if (escTimeout) {
7928
+ clearTimeout(escTimeout);
7929
+ escTimeout = null;
7930
+ }
7931
+ }
7932
+ } else {
7933
+ if (escTimeout) {
7934
+ clearTimeout(escTimeout);
7935
+ escTimeout = null;
7936
+ }
7937
+ }
7938
+ };
7939
+ stdin.setRawMode(true);
7940
+ stdin.resume();
7941
+ stdin.on("data", handleData);
7942
+ return () => {
7943
+ if (escTimeout) {
7944
+ clearTimeout(escTimeout);
7945
+ }
7946
+ stdin.removeListener("data", handleData);
7947
+ stdin.setRawMode(false);
7948
+ stdin.pause();
7949
+ };
7950
+ }
7951
+ var SIGINT_DOUBLE_PRESS_MS = 1e3;
7952
+ function createSigintListener(onCancel, onQuit, isOperationActive, stderr = process.stderr) {
7953
+ let lastSigintTime = 0;
7954
+ const handler = () => {
7955
+ const now = Date.now();
7956
+ if (isOperationActive()) {
7957
+ onCancel();
7958
+ lastSigintTime = now;
7959
+ return;
7960
+ }
7961
+ if (now - lastSigintTime < SIGINT_DOUBLE_PRESS_MS) {
7962
+ onQuit();
7963
+ return;
7964
+ }
7965
+ lastSigintTime = now;
7966
+ stderr.write(import_chalk4.default.dim("\n[Press Ctrl+C again to quit]\n"));
7967
+ };
7968
+ process.on("SIGINT", handler);
7969
+ return () => {
7970
+ process.removeListener("SIGINT", handler);
7971
+ };
7972
+ }
7061
7973
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7062
7974
  var SPINNER_DELAY_MS = 500;
7063
7975
  var StreamProgress = class {
@@ -7229,9 +8141,9 @@ var StreamProgress = class {
7229
8141
  const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
7230
8142
  const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
7231
8143
  const parts = [];
7232
- const iterPart = import_chalk2.default.cyan(`#${this.currentIteration}`);
8144
+ const iterPart = import_chalk4.default.cyan(`#${this.currentIteration}`);
7233
8145
  if (this.model) {
7234
- parts.push(`${iterPart} ${import_chalk2.default.magenta(this.model)}`);
8146
+ parts.push(`${iterPart} ${import_chalk4.default.magenta(this.model)}`);
7235
8147
  } else {
7236
8148
  parts.push(iterPart);
7237
8149
  }
@@ -7239,27 +8151,27 @@ var StreamProgress = class {
7239
8151
  if (usagePercent !== null) {
7240
8152
  const formatted = `${Math.round(usagePercent)}%`;
7241
8153
  if (usagePercent >= 80) {
7242
- parts.push(import_chalk2.default.red(formatted));
8154
+ parts.push(import_chalk4.default.red(formatted));
7243
8155
  } else if (usagePercent >= 50) {
7244
- parts.push(import_chalk2.default.yellow(formatted));
8156
+ parts.push(import_chalk4.default.yellow(formatted));
7245
8157
  } else {
7246
- parts.push(import_chalk2.default.green(formatted));
8158
+ parts.push(import_chalk4.default.green(formatted));
7247
8159
  }
7248
8160
  }
7249
8161
  if (this.callInputTokens > 0) {
7250
8162
  const prefix = this.callInputTokensEstimated ? "~" : "";
7251
- parts.push(import_chalk2.default.dim("\u2191") + import_chalk2.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
8163
+ parts.push(import_chalk4.default.dim("\u2191") + import_chalk4.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
7252
8164
  }
7253
8165
  if (this.isStreaming || outTokens > 0) {
7254
8166
  const prefix = this.callOutputTokensEstimated ? "~" : "";
7255
- parts.push(import_chalk2.default.dim("\u2193") + import_chalk2.default.green(` ${prefix}${formatTokens(outTokens)}`));
8167
+ parts.push(import_chalk4.default.dim("\u2193") + import_chalk4.default.green(` ${prefix}${formatTokens(outTokens)}`));
7256
8168
  }
7257
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
8169
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
7258
8170
  const callCost = this.calculateCurrentCallCost(outTokens);
7259
8171
  if (callCost > 0) {
7260
- parts.push(import_chalk2.default.cyan(`$${formatCost(callCost)}`));
8172
+ parts.push(import_chalk4.default.cyan(`$${formatCost(callCost)}`));
7261
8173
  }
7262
- this.target.write(`\r${parts.join(import_chalk2.default.dim(" | "))} ${import_chalk2.default.cyan(spinner)}`);
8174
+ this.target.write(`\r${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`);
7263
8175
  }
7264
8176
  /**
7265
8177
  * Calculates live cost estimate for the current streaming call.
@@ -7300,19 +8212,19 @@ var StreamProgress = class {
7300
8212
  const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
7301
8213
  const parts = [];
7302
8214
  if (this.model) {
7303
- parts.push(import_chalk2.default.cyan(this.model));
8215
+ parts.push(import_chalk4.default.cyan(this.model));
7304
8216
  }
7305
8217
  if (this.totalTokens > 0) {
7306
- parts.push(import_chalk2.default.dim("total:") + import_chalk2.default.magenta(` ${this.totalTokens}`));
8218
+ parts.push(import_chalk4.default.dim("total:") + import_chalk4.default.magenta(` ${this.totalTokens}`));
7307
8219
  }
7308
8220
  if (this.iterations > 0) {
7309
- parts.push(import_chalk2.default.dim("iter:") + import_chalk2.default.blue(` ${this.iterations}`));
8221
+ parts.push(import_chalk4.default.dim("iter:") + import_chalk4.default.blue(` ${this.iterations}`));
7310
8222
  }
7311
8223
  if (this.totalCost > 0) {
7312
- parts.push(import_chalk2.default.dim("cost:") + import_chalk2.default.cyan(` $${formatCost(this.totalCost)}`));
8224
+ parts.push(import_chalk4.default.dim("cost:") + import_chalk4.default.cyan(` $${formatCost(this.totalCost)}`));
7313
8225
  }
7314
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
7315
- this.target.write(`\r${parts.join(import_chalk2.default.dim(" | "))} ${import_chalk2.default.cyan(spinner)}`);
8226
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
8227
+ this.target.write(`\r${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`);
7316
8228
  }
7317
8229
  /**
7318
8230
  * Pauses the progress indicator and clears the line.
@@ -7346,6 +8258,25 @@ var StreamProgress = class {
7346
8258
  getTotalCost() {
7347
8259
  return this.totalCost;
7348
8260
  }
8261
+ /**
8262
+ * Returns a formatted stats string for cancellation messages.
8263
+ * Format: "↑ 1.2k | ↓ 300 | 5.0s"
8264
+ */
8265
+ formatStats() {
8266
+ const parts = [];
8267
+ const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
8268
+ const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
8269
+ if (this.callInputTokens > 0) {
8270
+ const prefix = this.callInputTokensEstimated ? "~" : "";
8271
+ parts.push(`\u2191 ${prefix}${formatTokens(this.callInputTokens)}`);
8272
+ }
8273
+ if (outTokens > 0) {
8274
+ const prefix = this.callOutputTokensEstimated ? "~" : "";
8275
+ parts.push(`\u2193 ${prefix}${formatTokens(outTokens)}`);
8276
+ }
8277
+ parts.push(`${elapsed}s`);
8278
+ return parts.join(" | ");
8279
+ }
7349
8280
  /**
7350
8281
  * Returns a formatted prompt string with stats (like bash PS1).
7351
8282
  * Shows current call stats during streaming, cumulative stats otherwise.
@@ -7360,28 +8291,28 @@ var StreamProgress = class {
7360
8291
  if (this.callInputTokens > 0) {
7361
8292
  const prefix = this.callInputTokensEstimated ? "~" : "";
7362
8293
  parts.push(
7363
- import_chalk2.default.dim("\u2191") + import_chalk2.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`)
8294
+ import_chalk4.default.dim("\u2191") + import_chalk4.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`)
7364
8295
  );
7365
8296
  }
7366
8297
  if (outTokens > 0) {
7367
8298
  const prefix = outEstimated ? "~" : "";
7368
- parts.push(import_chalk2.default.dim("\u2193") + import_chalk2.default.green(` ${prefix}${formatTokens(outTokens)}`));
8299
+ parts.push(import_chalk4.default.dim("\u2193") + import_chalk4.default.green(` ${prefix}${formatTokens(outTokens)}`));
7369
8300
  }
7370
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
8301
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
7371
8302
  } else {
7372
8303
  const elapsed = Math.round((Date.now() - this.totalStartTime) / 1e3);
7373
8304
  if (this.totalTokens > 0) {
7374
- parts.push(import_chalk2.default.magenta(formatTokens(this.totalTokens)));
8305
+ parts.push(import_chalk4.default.magenta(formatTokens(this.totalTokens)));
7375
8306
  }
7376
8307
  if (this.iterations > 0) {
7377
- parts.push(import_chalk2.default.blue(`i${this.iterations}`));
8308
+ parts.push(import_chalk4.default.blue(`i${this.iterations}`));
7378
8309
  }
7379
8310
  if (this.totalCost > 0) {
7380
- parts.push(import_chalk2.default.cyan(`$${formatCost(this.totalCost)}`));
8311
+ parts.push(import_chalk4.default.cyan(`$${formatCost(this.totalCost)}`));
7381
8312
  }
7382
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
8313
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
7383
8314
  }
7384
- return `${parts.join(import_chalk2.default.dim(" | "))} ${import_chalk2.default.green(">")} `;
8315
+ return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.green(">")} `;
7385
8316
  }
7386
8317
  };
7387
8318
  async function readStream(stream2) {
@@ -7416,7 +8347,7 @@ async function executeAction(action, env) {
7416
8347
  await action();
7417
8348
  } catch (error) {
7418
8349
  const message = error instanceof Error ? error.message : String(error);
7419
- env.stderr.write(`${import_chalk2.default.red.bold("Error:")} ${message}
8350
+ env.stderr.write(`${import_chalk4.default.red.bold("Error:")} ${message}
7420
8351
  `);
7421
8352
  env.setExitCode(1);
7422
8353
  }
@@ -7441,7 +8372,7 @@ function addAgentOptions(cmd, defaults) {
7441
8372
  ...previous,
7442
8373
  value
7443
8374
  ];
7444
- const defaultGadgets = defaults?.gadget ?? [];
8375
+ const defaultGadgets = defaults?.gadgets ?? defaults?.gadget ?? [];
7445
8376
  return cmd.option(OPTION_FLAGS.model, OPTION_DESCRIPTIONS.model, defaults?.model ?? DEFAULT_MODEL).option(OPTION_FLAGS.systemPrompt, OPTION_DESCRIPTIONS.systemPrompt, defaults?.system).option(
7446
8377
  OPTION_FLAGS.temperature,
7447
8378
  OPTION_DESCRIPTIONS.temperature,
@@ -7477,7 +8408,8 @@ function configToAgentOptions(config) {
7477
8408
  if (config.system !== void 0) result.system = config.system;
7478
8409
  if (config.temperature !== void 0) result.temperature = config.temperature;
7479
8410
  if (config["max-iterations"] !== void 0) result.maxIterations = config["max-iterations"];
7480
- if (config.gadget !== void 0) result.gadget = config.gadget;
8411
+ const gadgets = config.gadgets ?? config.gadget;
8412
+ if (gadgets !== void 0) result.gadget = gadgets;
7481
8413
  if (config.builtins !== void 0) result.builtins = config.builtins;
7482
8414
  if (config["builtin-interaction"] !== void 0)
7483
8415
  result.builtinInteraction = config["builtin-interaction"];
@@ -7487,6 +8419,8 @@ function configToAgentOptions(config) {
7487
8419
  result.gadgetEndPrefix = config["gadget-end-prefix"];
7488
8420
  if (config["gadget-arg-prefix"] !== void 0)
7489
8421
  result.gadgetArgPrefix = config["gadget-arg-prefix"];
8422
+ if (config["gadget-approval"] !== void 0)
8423
+ result.gadgetApproval = config["gadget-approval"];
7490
8424
  if (config.quiet !== void 0) result.quiet = config.quiet;
7491
8425
  if (config["log-llm-requests"] !== void 0) result.logLlmRequests = config["log-llm-requests"];
7492
8426
  if (config["log-llm-responses"] !== void 0) result.logLlmResponses = config["log-llm-responses"];
@@ -7494,23 +8428,18 @@ function configToAgentOptions(config) {
7494
8428
  }
7495
8429
 
7496
8430
  // src/cli/agent-command.ts
7497
- async function promptApproval(env, prompt) {
7498
- const rl = (0, import_promises2.createInterface)({ input: env.stdin, output: env.stderr });
7499
- try {
7500
- const answer = await rl.question(prompt);
7501
- return answer.trim();
7502
- } finally {
7503
- rl.close();
7504
- }
7505
- }
7506
- function createHumanInputHandler(env, progress) {
8431
+ function createHumanInputHandler(env, progress, keyboard) {
7507
8432
  const stdout = env.stdout;
7508
8433
  if (!isInteractive(env.stdin) || typeof stdout.isTTY !== "boolean" || !stdout.isTTY) {
7509
8434
  return void 0;
7510
8435
  }
7511
8436
  return async (question) => {
7512
8437
  progress.pause();
7513
- const rl = (0, import_promises2.createInterface)({ input: env.stdin, output: env.stdout });
8438
+ if (keyboard.cleanupEsc) {
8439
+ keyboard.cleanupEsc();
8440
+ keyboard.cleanupEsc = null;
8441
+ }
8442
+ const rl = (0, import_promises3.createInterface)({ input: env.stdin, output: env.stdout });
7514
8443
  try {
7515
8444
  const questionLine = question.trim() ? `
7516
8445
  ${renderMarkdownWithSeparators(question.trim())}` : "";
@@ -7528,6 +8457,7 @@ ${statsPrompt}` : statsPrompt;
7528
8457
  }
7529
8458
  } finally {
7530
8459
  rl.close();
8460
+ keyboard.restore();
7531
8461
  }
7532
8462
  };
7533
8463
  }
@@ -7554,6 +8484,77 @@ async function executeAgent(promptArg, options, env) {
7554
8484
  const printer = new StreamPrinter(env.stdout);
7555
8485
  const stderrTTY = env.stderr.isTTY === true;
7556
8486
  const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
8487
+ const abortController = new AbortController();
8488
+ let wasCancelled = false;
8489
+ let isStreaming = false;
8490
+ const stdinStream = env.stdin;
8491
+ const handleCancel = () => {
8492
+ if (!abortController.signal.aborted) {
8493
+ wasCancelled = true;
8494
+ abortController.abort();
8495
+ progress.pause();
8496
+ env.stderr.write(import_chalk5.default.yellow(`
8497
+ [Cancelled] ${progress.formatStats()}
8498
+ `));
8499
+ }
8500
+ };
8501
+ const keyboard = {
8502
+ cleanupEsc: null,
8503
+ cleanupSigint: null,
8504
+ restore: () => {
8505
+ if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
8506
+ keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel);
8507
+ }
8508
+ }
8509
+ };
8510
+ const handleQuit = () => {
8511
+ keyboard.cleanupEsc?.();
8512
+ keyboard.cleanupSigint?.();
8513
+ progress.complete();
8514
+ printer.ensureNewline();
8515
+ const summary = renderOverallSummary({
8516
+ totalTokens: usage?.totalTokens,
8517
+ iterations,
8518
+ elapsedSeconds: progress.getTotalElapsedSeconds(),
8519
+ cost: progress.getTotalCost()
8520
+ });
8521
+ if (summary) {
8522
+ env.stderr.write(`${import_chalk5.default.dim("\u2500".repeat(40))}
8523
+ `);
8524
+ env.stderr.write(`${summary}
8525
+ `);
8526
+ }
8527
+ env.stderr.write(import_chalk5.default.dim("[Quit]\n"));
8528
+ process.exit(130);
8529
+ };
8530
+ if (stdinIsInteractive && stdinStream.isTTY) {
8531
+ keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel);
8532
+ }
8533
+ keyboard.cleanupSigint = createSigintListener(
8534
+ handleCancel,
8535
+ handleQuit,
8536
+ () => isStreaming && !abortController.signal.aborted,
8537
+ env.stderr
8538
+ );
8539
+ const DEFAULT_APPROVAL_REQUIRED = ["RunCommand", "WriteFile", "EditFile"];
8540
+ const userApprovals = options.gadgetApproval ?? {};
8541
+ const gadgetApprovals = {
8542
+ ...userApprovals
8543
+ };
8544
+ for (const gadget of DEFAULT_APPROVAL_REQUIRED) {
8545
+ const normalizedGadget = gadget.toLowerCase();
8546
+ const isConfigured = Object.keys(userApprovals).some(
8547
+ (key) => key.toLowerCase() === normalizedGadget
8548
+ );
8549
+ if (!isConfigured) {
8550
+ gadgetApprovals[gadget] = "approval-required";
8551
+ }
8552
+ }
8553
+ const approvalConfig = {
8554
+ gadgetApprovals,
8555
+ defaultMode: "allowed"
8556
+ };
8557
+ const approvalManager = new ApprovalManager(approvalConfig, env, progress);
7557
8558
  let usage;
7558
8559
  let iterations = 0;
7559
8560
  const llmRequestsDir = resolveLogDir(options.logLlmRequests, "requests");
@@ -7581,6 +8582,7 @@ async function executeAgent(promptArg, options, env) {
7581
8582
  // onLLMCallStart: Start progress indicator for each LLM call
7582
8583
  // This showcases how to react to agent lifecycle events
7583
8584
  onLLMCallStart: async (context) => {
8585
+ isStreaming = true;
7584
8586
  llmCallCounter++;
7585
8587
  const inputTokens = await countMessagesTokens(
7586
8588
  context.options.model,
@@ -7614,6 +8616,7 @@ async function executeAgent(promptArg, options, env) {
7614
8616
  // onLLMCallComplete: Finalize metrics after each LLM call
7615
8617
  // This is where you'd typically log metrics or update dashboards
7616
8618
  onLLMCallComplete: async (context) => {
8619
+ isStreaming = false;
7617
8620
  usage = context.usage;
7618
8621
  iterations = Math.max(iterations, context.iteration + 1);
7619
8622
  if (context.usage) {
@@ -7661,47 +8664,53 @@ async function executeAgent(promptArg, options, env) {
7661
8664
  }
7662
8665
  }
7663
8666
  },
7664
- // SHOWCASE: Controller-based approval gating for dangerous gadgets
8667
+ // SHOWCASE: Controller-based approval gating for gadgets
7665
8668
  //
7666
8669
  // This demonstrates how to add safety layers WITHOUT modifying gadgets.
7667
- // The RunCommand gadget is simple - it just executes commands. The CLI
7668
- // adds the approval flow externally via beforeGadgetExecution controller.
8670
+ // The ApprovalManager handles approval flows externally via beforeGadgetExecution.
8671
+ // Approval modes are configurable via cli.toml:
8672
+ // - "allowed": auto-proceed
8673
+ // - "denied": auto-reject, return message to LLM
8674
+ // - "approval-required": prompt user interactively
7669
8675
  //
7670
- // This pattern is composable: you can apply the same gating logic to
7671
- // any gadget (DeleteFile, SendEmail, etc.) without changing the gadgets.
8676
+ // Default: RunCommand, WriteFile, EditFile require approval unless overridden.
7672
8677
  controllers: {
7673
8678
  beforeGadgetExecution: async (ctx) => {
7674
- if (ctx.gadgetName !== "RunCommand") {
8679
+ const mode = approvalManager.getApprovalMode(ctx.gadgetName);
8680
+ if (mode === "allowed") {
7675
8681
  return { action: "proceed" };
7676
8682
  }
7677
8683
  const stdinTTY = isInteractive(env.stdin);
7678
8684
  const stderrTTY2 = env.stderr.isTTY === true;
7679
- if (!stdinTTY || !stderrTTY2) {
7680
- return {
7681
- action: "skip",
7682
- syntheticResult: "status=denied\n\nRunCommand requires interactive approval. Run in a terminal to approve commands."
7683
- };
7684
- }
7685
- const command = ctx.parameters.command;
7686
- progress.pause();
7687
- env.stderr.write(`
7688
- \u{1F512} Execute: ${import_chalk3.default.cyan(command)}
7689
- `);
7690
- const response = await promptApproval(env, " \u23CE approve, or type to reject: ");
7691
- const isApproved = response === "" || response.toLowerCase() === "y";
7692
- if (!isApproved) {
7693
- env.stderr.write(` ${import_chalk3.default.red("\u2717 Denied")}
8685
+ const canPrompt = stdinTTY && stderrTTY2;
8686
+ if (!canPrompt) {
8687
+ if (mode === "approval-required") {
8688
+ return {
8689
+ action: "skip",
8690
+ syntheticResult: `status=denied
7694
8691
 
7695
- `);
8692
+ ${ctx.gadgetName} requires interactive approval. Run in a terminal to approve.`
8693
+ };
8694
+ }
8695
+ if (mode === "denied") {
8696
+ return {
8697
+ action: "skip",
8698
+ syntheticResult: `status=denied
8699
+
8700
+ ${ctx.gadgetName} is denied by configuration.`
8701
+ };
8702
+ }
8703
+ return { action: "proceed" };
8704
+ }
8705
+ const result = await approvalManager.requestApproval(ctx.gadgetName, ctx.parameters);
8706
+ if (!result.approved) {
7696
8707
  return {
7697
8708
  action: "skip",
7698
8709
  syntheticResult: `status=denied
7699
8710
 
7700
- Command rejected by user with message: "${response}"`
8711
+ Denied: ${result.reason ?? "by user"}`
7701
8712
  };
7702
8713
  }
7703
- env.stderr.write(` ${import_chalk3.default.green("\u2713 Approved")}
7704
- `);
7705
8714
  return { action: "proceed" };
7706
8715
  }
7707
8716
  }
@@ -7715,10 +8724,11 @@ Command rejected by user with message: "${response}"`
7715
8724
  if (options.temperature !== void 0) {
7716
8725
  builder.withTemperature(options.temperature);
7717
8726
  }
7718
- const humanInputHandler = createHumanInputHandler(env, progress);
8727
+ const humanInputHandler = createHumanInputHandler(env, progress, keyboard);
7719
8728
  if (humanInputHandler) {
7720
8729
  builder.onHumanInput(humanInputHandler);
7721
8730
  }
8731
+ builder.withSignal(abortController.signal);
7722
8732
  const gadgets = registry.getAll();
7723
8733
  if (gadgets.length > 0) {
7724
8734
  builder.withGadgets(...gadgets);
@@ -7756,31 +8766,41 @@ Command rejected by user with message: "${response}"`
7756
8766
  textBuffer = "";
7757
8767
  }
7758
8768
  };
7759
- for await (const event of agent.run()) {
7760
- if (event.type === "text") {
7761
- progress.pause();
7762
- textBuffer += event.content;
7763
- } else if (event.type === "gadget_result") {
7764
- flushTextBuffer();
7765
- progress.pause();
7766
- if (options.quiet) {
7767
- if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
7768
- const message = String(event.result.parameters.message);
7769
- env.stdout.write(`${message}
8769
+ try {
8770
+ for await (const event of agent.run()) {
8771
+ if (event.type === "text") {
8772
+ progress.pause();
8773
+ textBuffer += event.content;
8774
+ } else if (event.type === "gadget_result") {
8775
+ flushTextBuffer();
8776
+ progress.pause();
8777
+ if (options.quiet) {
8778
+ if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
8779
+ const message = String(event.result.parameters.message);
8780
+ env.stdout.write(`${message}
7770
8781
  `);
7771
- }
7772
- } else {
7773
- const tokenCount = await countGadgetOutputTokens(event.result.result);
7774
- env.stderr.write(`${formatGadgetSummary({ ...event.result, tokenCount })}
8782
+ }
8783
+ } else {
8784
+ const tokenCount = await countGadgetOutputTokens(event.result.result);
8785
+ env.stderr.write(`${formatGadgetSummary({ ...event.result, tokenCount })}
7775
8786
  `);
8787
+ }
7776
8788
  }
7777
8789
  }
8790
+ } catch (error) {
8791
+ if (!isAbortError(error)) {
8792
+ throw error;
8793
+ }
8794
+ } finally {
8795
+ isStreaming = false;
8796
+ keyboard.cleanupEsc?.();
8797
+ keyboard.cleanupSigint?.();
7778
8798
  }
7779
8799
  flushTextBuffer();
7780
8800
  progress.complete();
7781
8801
  printer.ensureNewline();
7782
8802
  if (!options.quiet && iterations > 1) {
7783
- env.stderr.write(`${import_chalk3.default.dim("\u2500".repeat(40))}
8803
+ env.stderr.write(`${import_chalk5.default.dim("\u2500".repeat(40))}
7784
8804
  `);
7785
8805
  const summary = renderOverallSummary({
7786
8806
  totalTokens: usage?.totalTokens,
@@ -7798,7 +8818,13 @@ function registerAgentCommand(program, env, config) {
7798
8818
  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.");
7799
8819
  addAgentOptions(cmd, config);
7800
8820
  cmd.action(
7801
- (prompt, options) => executeAction(() => executeAgent(prompt, options, env), env)
8821
+ (prompt, options) => executeAction(() => {
8822
+ const mergedOptions = {
8823
+ ...options,
8824
+ gadgetApproval: config?.["gadget-approval"]
8825
+ };
8826
+ return executeAgent(prompt, mergedOptions, env);
8827
+ }, env)
7802
8828
  );
7803
8829
  }
7804
8830
 
@@ -7882,9 +8908,9 @@ function registerCompleteCommand(program, env, config) {
7882
8908
  }
7883
8909
 
7884
8910
  // src/cli/config.ts
7885
- var import_node_fs3 = require("fs");
8911
+ var import_node_fs8 = require("fs");
7886
8912
  var import_node_os2 = require("os");
7887
- var import_node_path4 = require("path");
8913
+ var import_node_path8 = require("path");
7888
8914
  var import_js_toml = require("js-toml");
7889
8915
 
7890
8916
  // src/cli/templates.ts
@@ -7969,6 +8995,7 @@ function hasTemplateSyntax(str) {
7969
8995
  }
7970
8996
 
7971
8997
  // src/cli/config.ts
8998
+ var VALID_APPROVAL_MODES = ["allowed", "denied", "approval-required"];
7972
8999
  var GLOBAL_CONFIG_KEYS = /* @__PURE__ */ new Set(["log-level", "log-file", "log-reset"]);
7973
9000
  var VALID_LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
7974
9001
  var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
@@ -7991,12 +9018,20 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
7991
9018
  "system",
7992
9019
  "temperature",
7993
9020
  "max-iterations",
9021
+ "gadgets",
9022
+ // Full replacement (preferred)
9023
+ "gadget-add",
9024
+ // Add to inherited gadgets
9025
+ "gadget-remove",
9026
+ // Remove from inherited gadgets
7994
9027
  "gadget",
9028
+ // DEPRECATED: alias for gadgets
7995
9029
  "builtins",
7996
9030
  "builtin-interaction",
7997
9031
  "gadget-start-prefix",
7998
9032
  "gadget-end-prefix",
7999
9033
  "gadget-arg-prefix",
9034
+ "gadget-approval",
8000
9035
  "quiet",
8001
9036
  "inherits",
8002
9037
  "log-level",
@@ -8014,12 +9049,12 @@ var CUSTOM_CONFIG_KEYS = /* @__PURE__ */ new Set([
8014
9049
  "description"
8015
9050
  ]);
8016
9051
  function getConfigPath() {
8017
- return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".llmist", "cli.toml");
9052
+ return (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".llmist", "cli.toml");
8018
9053
  }
8019
9054
  var ConfigError = class extends Error {
8020
- constructor(message, path2) {
8021
- super(path2 ? `${path2}: ${message}` : message);
8022
- this.path = path2;
9055
+ constructor(message, path5) {
9056
+ super(path5 ? `${path5}: ${message}` : message);
9057
+ this.path = path5;
8023
9058
  this.name = "ConfigError";
8024
9059
  }
8025
9060
  };
@@ -8075,6 +9110,28 @@ function validateInherits(value, section) {
8075
9110
  }
8076
9111
  throw new ConfigError(`[${section}].inherits must be a string or array of strings`);
8077
9112
  }
9113
+ function validateGadgetApproval(value, section) {
9114
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
9115
+ throw new ConfigError(
9116
+ `[${section}].gadget-approval must be a table (e.g., { WriteFile = "approval-required" })`
9117
+ );
9118
+ }
9119
+ const result = {};
9120
+ for (const [gadgetName, mode] of Object.entries(value)) {
9121
+ if (typeof mode !== "string") {
9122
+ throw new ConfigError(
9123
+ `[${section}].gadget-approval.${gadgetName} must be a string`
9124
+ );
9125
+ }
9126
+ if (!VALID_APPROVAL_MODES.includes(mode)) {
9127
+ throw new ConfigError(
9128
+ `[${section}].gadget-approval.${gadgetName} must be one of: ${VALID_APPROVAL_MODES.join(", ")}`
9129
+ );
9130
+ }
9131
+ result[gadgetName] = mode;
9132
+ }
9133
+ return result;
9134
+ }
8078
9135
  function validateLoggingConfig(raw, section) {
8079
9136
  const result = {};
8080
9137
  if ("log-level" in raw) {
@@ -8184,6 +9241,15 @@ function validateAgentConfig(raw, section) {
8184
9241
  min: 1
8185
9242
  });
8186
9243
  }
9244
+ if ("gadgets" in rawObj) {
9245
+ result.gadgets = validateStringArray(rawObj.gadgets, "gadgets", section);
9246
+ }
9247
+ if ("gadget-add" in rawObj) {
9248
+ result["gadget-add"] = validateStringArray(rawObj["gadget-add"], "gadget-add", section);
9249
+ }
9250
+ if ("gadget-remove" in rawObj) {
9251
+ result["gadget-remove"] = validateStringArray(rawObj["gadget-remove"], "gadget-remove", section);
9252
+ }
8187
9253
  if ("gadget" in rawObj) {
8188
9254
  result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
8189
9255
  }
@@ -8218,6 +9284,9 @@ function validateAgentConfig(raw, section) {
8218
9284
  section
8219
9285
  );
8220
9286
  }
9287
+ if ("gadget-approval" in rawObj) {
9288
+ result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
9289
+ }
8221
9290
  if ("quiet" in rawObj) {
8222
9291
  result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
8223
9292
  }
@@ -8274,6 +9343,15 @@ function validateCustomConfig(raw, section) {
8274
9343
  min: 1
8275
9344
  });
8276
9345
  }
9346
+ if ("gadgets" in rawObj) {
9347
+ result.gadgets = validateStringArray(rawObj.gadgets, "gadgets", section);
9348
+ }
9349
+ if ("gadget-add" in rawObj) {
9350
+ result["gadget-add"] = validateStringArray(rawObj["gadget-add"], "gadget-add", section);
9351
+ }
9352
+ if ("gadget-remove" in rawObj) {
9353
+ result["gadget-remove"] = validateStringArray(rawObj["gadget-remove"], "gadget-remove", section);
9354
+ }
8277
9355
  if ("gadget" in rawObj) {
8278
9356
  result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
8279
9357
  }
@@ -8308,6 +9386,9 @@ function validateCustomConfig(raw, section) {
8308
9386
  section
8309
9387
  );
8310
9388
  }
9389
+ if ("gadget-approval" in rawObj) {
9390
+ result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
9391
+ }
8311
9392
  if ("max-tokens" in rawObj) {
8312
9393
  result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
8313
9394
  integer: true,
@@ -8363,12 +9444,12 @@ function validateConfig(raw, configPath) {
8363
9444
  }
8364
9445
  function loadConfig() {
8365
9446
  const configPath = getConfigPath();
8366
- if (!(0, import_node_fs3.existsSync)(configPath)) {
9447
+ if (!(0, import_node_fs8.existsSync)(configPath)) {
8367
9448
  return {};
8368
9449
  }
8369
9450
  let content;
8370
9451
  try {
8371
- content = (0, import_node_fs3.readFileSync)(configPath, "utf-8");
9452
+ content = (0, import_node_fs8.readFileSync)(configPath, "utf-8");
8372
9453
  } catch (error) {
8373
9454
  throw new ConfigError(
8374
9455
  `Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -8463,6 +9544,39 @@ function resolveTemplatesInConfig(config, configPath) {
8463
9544
  }
8464
9545
  return result;
8465
9546
  }
9547
+ function resolveGadgets(section, inheritedGadgets, sectionName, configPath) {
9548
+ const hasGadgets = "gadgets" in section;
9549
+ const hasGadgetLegacy = "gadget" in section;
9550
+ const hasGadgetAdd = "gadget-add" in section;
9551
+ const hasGadgetRemove = "gadget-remove" in section;
9552
+ if (hasGadgetLegacy && !hasGadgets) {
9553
+ console.warn(
9554
+ `[config] Warning: [${sectionName}].gadget is deprecated, use 'gadgets' (plural) instead`
9555
+ );
9556
+ }
9557
+ if ((hasGadgets || hasGadgetLegacy) && (hasGadgetAdd || hasGadgetRemove)) {
9558
+ throw new ConfigError(
9559
+ `[${sectionName}] Cannot use 'gadgets' with 'gadget-add'/'gadget-remove'. Use either full replacement (gadgets) OR modification (gadget-add/gadget-remove).`,
9560
+ configPath
9561
+ );
9562
+ }
9563
+ if (hasGadgets) {
9564
+ return section.gadgets;
9565
+ }
9566
+ if (hasGadgetLegacy) {
9567
+ return section.gadget;
9568
+ }
9569
+ let result = [...inheritedGadgets];
9570
+ if (hasGadgetRemove) {
9571
+ const toRemove = new Set(section["gadget-remove"]);
9572
+ result = result.filter((g) => !toRemove.has(g));
9573
+ }
9574
+ if (hasGadgetAdd) {
9575
+ const toAdd = section["gadget-add"];
9576
+ result.push(...toAdd);
9577
+ }
9578
+ return result;
9579
+ }
8466
9580
  function resolveInheritance(config, configPath) {
8467
9581
  const resolved = {};
8468
9582
  const resolving = /* @__PURE__ */ new Set();
@@ -8486,8 +9600,23 @@ function resolveInheritance(config, configPath) {
8486
9600
  const parentResolved = resolveSection(parent);
8487
9601
  merged = { ...merged, ...parentResolved };
8488
9602
  }
8489
- const { inherits: _inherits, ...ownValues } = sectionObj;
9603
+ const inheritedGadgets = merged.gadgets ?? [];
9604
+ const {
9605
+ inherits: _inherits,
9606
+ gadgets: _gadgets,
9607
+ gadget: _gadget,
9608
+ "gadget-add": _gadgetAdd,
9609
+ "gadget-remove": _gadgetRemove,
9610
+ ...ownValues
9611
+ } = sectionObj;
8490
9612
  merged = { ...merged, ...ownValues };
9613
+ const resolvedGadgets = resolveGadgets(sectionObj, inheritedGadgets, name, configPath);
9614
+ if (resolvedGadgets.length > 0) {
9615
+ merged.gadgets = resolvedGadgets;
9616
+ }
9617
+ delete merged["gadget"];
9618
+ delete merged["gadget-add"];
9619
+ delete merged["gadget-remove"];
8491
9620
  resolving.delete(name);
8492
9621
  resolved[name] = merged;
8493
9622
  return merged;
@@ -8499,13 +9628,13 @@ function resolveInheritance(config, configPath) {
8499
9628
  }
8500
9629
 
8501
9630
  // src/cli/gadget-command.ts
8502
- var import_chalk5 = __toESM(require("chalk"), 1);
9631
+ var import_chalk7 = __toESM(require("chalk"), 1);
8503
9632
  init_schema_to_json();
8504
9633
  init_schema_validator();
8505
9634
 
8506
9635
  // src/cli/gadget-prompts.ts
8507
- var import_promises3 = require("readline/promises");
8508
- var import_chalk4 = __toESM(require("chalk"), 1);
9636
+ var import_promises4 = require("readline/promises");
9637
+ var import_chalk6 = __toESM(require("chalk"), 1);
8509
9638
  init_schema_to_json();
8510
9639
  async function promptForParameters(schema, ctx) {
8511
9640
  if (!schema) {
@@ -8515,7 +9644,7 @@ async function promptForParameters(schema, ctx) {
8515
9644
  if (!jsonSchema.properties || Object.keys(jsonSchema.properties).length === 0) {
8516
9645
  return {};
8517
9646
  }
8518
- const rl = (0, import_promises3.createInterface)({ input: ctx.stdin, output: ctx.stdout });
9647
+ const rl = (0, import_promises4.createInterface)({ input: ctx.stdin, output: ctx.stdout });
8519
9648
  const params = {};
8520
9649
  try {
8521
9650
  for (const [key, prop] of Object.entries(jsonSchema.properties)) {
@@ -8538,16 +9667,16 @@ ${issues}`);
8538
9667
  async function promptForField(rl, key, prop, required) {
8539
9668
  const isRequired = required.includes(key);
8540
9669
  const typeHint = formatTypeHint(prop);
8541
- const defaultHint = prop.default !== void 0 ? import_chalk4.default.dim(` [default: ${JSON.stringify(prop.default)}]`) : "";
8542
- const requiredMarker = isRequired ? import_chalk4.default.red("*") : "";
9670
+ const defaultHint = prop.default !== void 0 ? import_chalk6.default.dim(` [default: ${JSON.stringify(prop.default)}]`) : "";
9671
+ const requiredMarker = isRequired ? import_chalk6.default.red("*") : "";
8543
9672
  let prompt = `
8544
- ${import_chalk4.default.cyan.bold(key)}${requiredMarker}`;
9673
+ ${import_chalk6.default.cyan.bold(key)}${requiredMarker}`;
8545
9674
  if (prop.description) {
8546
- prompt += import_chalk4.default.dim(` - ${prop.description}`);
9675
+ prompt += import_chalk6.default.dim(` - ${prop.description}`);
8547
9676
  }
8548
9677
  prompt += `
8549
9678
  ${typeHint}${defaultHint}
8550
- ${import_chalk4.default.green(">")} `;
9679
+ ${import_chalk6.default.green(">")} `;
8551
9680
  const answer = await rl.question(prompt);
8552
9681
  const trimmed = answer.trim();
8553
9682
  if (!trimmed) {
@@ -8563,20 +9692,20 @@ ${import_chalk4.default.cyan.bold(key)}${requiredMarker}`;
8563
9692
  }
8564
9693
  function formatTypeHint(prop) {
8565
9694
  if (prop.enum) {
8566
- return import_chalk4.default.yellow(`(${prop.enum.join(" | ")})`);
9695
+ return import_chalk6.default.yellow(`(${prop.enum.join(" | ")})`);
8567
9696
  }
8568
9697
  if (prop.type === "array") {
8569
9698
  const items = prop.items;
8570
9699
  if (items?.enum) {
8571
- return import_chalk4.default.yellow(`(${items.enum.join(" | ")})[] comma-separated`);
9700
+ return import_chalk6.default.yellow(`(${items.enum.join(" | ")})[] comma-separated`);
8572
9701
  }
8573
9702
  const itemType = items?.type ?? "any";
8574
- return import_chalk4.default.yellow(`(${itemType}[]) comma-separated`);
9703
+ return import_chalk6.default.yellow(`(${itemType}[]) comma-separated`);
8575
9704
  }
8576
9705
  if (prop.type === "object" && prop.properties) {
8577
- return import_chalk4.default.yellow("(object) enter as JSON");
9706
+ return import_chalk6.default.yellow("(object) enter as JSON");
8578
9707
  }
8579
- return import_chalk4.default.yellow(`(${prop.type ?? "any"})`);
9708
+ return import_chalk6.default.yellow(`(${prop.type ?? "any"})`);
8580
9709
  }
8581
9710
  function parseValue(input, prop, key) {
8582
9711
  const type = prop.type;
@@ -8687,7 +9816,7 @@ Available gadgets:
8687
9816
  async function executeGadgetRun(file, options, env) {
8688
9817
  const cwd = process.cwd();
8689
9818
  const { gadget, name } = await selectGadget(file, options.name, cwd);
8690
- env.stderr.write(import_chalk5.default.cyan.bold(`
9819
+ env.stderr.write(import_chalk7.default.cyan.bold(`
8691
9820
  \u{1F527} Running gadget: ${name}
8692
9821
  `));
8693
9822
  let params;
@@ -8698,7 +9827,7 @@ async function executeGadgetRun(file, options, env) {
8698
9827
  // Prompts go to stderr to keep stdout clean
8699
9828
  });
8700
9829
  } else {
8701
- env.stderr.write(import_chalk5.default.dim("Reading parameters from stdin...\n"));
9830
+ env.stderr.write(import_chalk7.default.dim("Reading parameters from stdin...\n"));
8702
9831
  const stdinParams = await readStdinJson(env.stdin);
8703
9832
  if (gadget.parameterSchema) {
8704
9833
  const result2 = gadget.parameterSchema.safeParse(stdinParams);
@@ -8712,7 +9841,7 @@ ${issues}`);
8712
9841
  params = stdinParams;
8713
9842
  }
8714
9843
  }
8715
- env.stderr.write(import_chalk5.default.dim("\nExecuting...\n"));
9844
+ env.stderr.write(import_chalk7.default.dim("\nExecuting...\n"));
8716
9845
  const startTime = Date.now();
8717
9846
  let result;
8718
9847
  try {
@@ -8734,7 +9863,7 @@ ${issues}`);
8734
9863
  throw new Error(`Execution failed: ${message}`);
8735
9864
  }
8736
9865
  const elapsed = Date.now() - startTime;
8737
- env.stderr.write(import_chalk5.default.green(`
9866
+ env.stderr.write(import_chalk7.default.green(`
8738
9867
  \u2713 Completed in ${elapsed}ms
8739
9868
 
8740
9869
  `));
@@ -8770,37 +9899,37 @@ async function executeGadgetInfo(file, options, env) {
8770
9899
  return;
8771
9900
  }
8772
9901
  env.stdout.write("\n");
8773
- env.stdout.write(import_chalk5.default.cyan.bold(`${name}
9902
+ env.stdout.write(import_chalk7.default.cyan.bold(`${name}
8774
9903
  `));
8775
- env.stdout.write(import_chalk5.default.cyan("\u2550".repeat(name.length)) + "\n\n");
8776
- env.stdout.write(import_chalk5.default.bold("Description:\n"));
9904
+ env.stdout.write(import_chalk7.default.cyan("\u2550".repeat(name.length)) + "\n\n");
9905
+ env.stdout.write(import_chalk7.default.bold("Description:\n"));
8777
9906
  env.stdout.write(` ${gadget.description}
8778
9907
 
8779
9908
  `);
8780
9909
  if (gadget.parameterSchema) {
8781
- env.stdout.write(import_chalk5.default.bold("Parameters:\n"));
9910
+ env.stdout.write(import_chalk7.default.bold("Parameters:\n"));
8782
9911
  const jsonSchema = schemaToJSONSchema(gadget.parameterSchema, { target: "draft-7" });
8783
9912
  env.stdout.write(formatSchemaAsText(jsonSchema, " ") + "\n\n");
8784
9913
  } else {
8785
- env.stdout.write(import_chalk5.default.dim("No parameters required.\n\n"));
9914
+ env.stdout.write(import_chalk7.default.dim("No parameters required.\n\n"));
8786
9915
  }
8787
9916
  if (gadget.timeoutMs) {
8788
- env.stdout.write(import_chalk5.default.bold("Timeout:\n"));
9917
+ env.stdout.write(import_chalk7.default.bold("Timeout:\n"));
8789
9918
  env.stdout.write(` ${gadget.timeoutMs}ms
8790
9919
 
8791
9920
  `);
8792
9921
  }
8793
9922
  if (gadget.examples && gadget.examples.length > 0) {
8794
- env.stdout.write(import_chalk5.default.bold("Examples:\n"));
9923
+ env.stdout.write(import_chalk7.default.bold("Examples:\n"));
8795
9924
  for (const example of gadget.examples) {
8796
9925
  if (example.comment) {
8797
- env.stdout.write(import_chalk5.default.dim(` # ${example.comment}
9926
+ env.stdout.write(import_chalk7.default.dim(` # ${example.comment}
8798
9927
  `));
8799
9928
  }
8800
- env.stdout.write(` Input: ${import_chalk5.default.cyan(JSON.stringify(example.params))}
9929
+ env.stdout.write(` Input: ${import_chalk7.default.cyan(JSON.stringify(example.params))}
8801
9930
  `);
8802
9931
  if (example.output !== void 0) {
8803
- env.stdout.write(` Output: ${import_chalk5.default.green(example.output)}
9932
+ env.stdout.write(` Output: ${import_chalk7.default.green(example.output)}
8804
9933
  `);
8805
9934
  }
8806
9935
  env.stdout.write("\n");
@@ -8833,27 +9962,27 @@ function formatSchemaAsText(schema, indent = "") {
8833
9962
  const isRequired = required.includes(key);
8834
9963
  const enumValues = prop.enum;
8835
9964
  const defaultValue = prop.default;
8836
- let line = `${indent}${import_chalk5.default.cyan(key)}`;
9965
+ let line = `${indent}${import_chalk7.default.cyan(key)}`;
8837
9966
  if (isRequired) {
8838
- line += import_chalk5.default.red("*");
9967
+ line += import_chalk7.default.red("*");
8839
9968
  }
8840
9969
  if (type === "array") {
8841
9970
  const items = prop.items;
8842
9971
  const itemType = items?.type || "any";
8843
- line += import_chalk5.default.dim(` (${itemType}[])`);
9972
+ line += import_chalk7.default.dim(` (${itemType}[])`);
8844
9973
  } else if (type === "object" && prop.properties) {
8845
- line += import_chalk5.default.dim(" (object)");
9974
+ line += import_chalk7.default.dim(" (object)");
8846
9975
  } else {
8847
- line += import_chalk5.default.dim(` (${type})`);
9976
+ line += import_chalk7.default.dim(` (${type})`);
8848
9977
  }
8849
9978
  if (defaultValue !== void 0) {
8850
- line += import_chalk5.default.dim(` [default: ${JSON.stringify(defaultValue)}]`);
9979
+ line += import_chalk7.default.dim(` [default: ${JSON.stringify(defaultValue)}]`);
8851
9980
  }
8852
9981
  if (description) {
8853
9982
  line += `: ${description}`;
8854
9983
  }
8855
9984
  if (enumValues) {
8856
- line += import_chalk5.default.yellow(` - one of: ${enumValues.join(", ")}`);
9985
+ line += import_chalk7.default.yellow(` - one of: ${enumValues.join(", ")}`);
8857
9986
  }
8858
9987
  lines.push(line);
8859
9988
  if (type === "object" && prop.properties) {
@@ -8893,20 +10022,20 @@ async function executeGadgetValidate(file, env) {
8893
10022
  throw new Error(`Validation issues:
8894
10023
  ${issues.map((i) => ` - ${i}`).join("\n")}`);
8895
10024
  }
8896
- env.stdout.write(import_chalk5.default.green.bold("\n\u2713 Valid\n\n"));
8897
- env.stdout.write(import_chalk5.default.bold("Gadgets found:\n"));
10025
+ env.stdout.write(import_chalk7.default.green.bold("\n\u2713 Valid\n\n"));
10026
+ env.stdout.write(import_chalk7.default.bold("Gadgets found:\n"));
8898
10027
  for (const gadget of gadgets) {
8899
10028
  const name = gadget.name ?? gadget.constructor.name;
8900
- const schemaInfo = gadget.parameterSchema ? import_chalk5.default.cyan("(with schema)") : import_chalk5.default.dim("(no schema)");
8901
- env.stdout.write(` ${import_chalk5.default.bold(name)} ${schemaInfo}
10029
+ const schemaInfo = gadget.parameterSchema ? import_chalk7.default.cyan("(with schema)") : import_chalk7.default.dim("(no schema)");
10030
+ env.stdout.write(` ${import_chalk7.default.bold(name)} ${schemaInfo}
8902
10031
  `);
8903
- env.stdout.write(import_chalk5.default.dim(` ${gadget.description}
10032
+ env.stdout.write(import_chalk7.default.dim(` ${gadget.description}
8904
10033
  `));
8905
10034
  }
8906
10035
  env.stdout.write("\n");
8907
10036
  } catch (error) {
8908
10037
  const message = error instanceof Error ? error.message : String(error);
8909
- env.stdout.write(import_chalk5.default.red.bold(`
10038
+ env.stdout.write(import_chalk7.default.red.bold(`
8910
10039
  \u2717 Invalid
8911
10040
 
8912
10041
  `));
@@ -8930,7 +10059,7 @@ function registerGadgetCommand(program, env) {
8930
10059
  }
8931
10060
 
8932
10061
  // src/cli/models-command.ts
8933
- var import_chalk6 = __toESM(require("chalk"), 1);
10062
+ var import_chalk8 = __toESM(require("chalk"), 1);
8934
10063
  init_model_shortcuts();
8935
10064
  async function handleModelsCommand(options, env) {
8936
10065
  const client = env.createClient();
@@ -8950,13 +10079,13 @@ function renderTable(models, verbose, stream2) {
8950
10079
  }
8951
10080
  grouped.get(provider).push(model);
8952
10081
  }
8953
- stream2.write(import_chalk6.default.bold.cyan("\nAvailable Models\n"));
8954
- stream2.write(import_chalk6.default.cyan("=".repeat(80)) + "\n\n");
10082
+ stream2.write(import_chalk8.default.bold.cyan("\nAvailable Models\n"));
10083
+ stream2.write(import_chalk8.default.cyan("=".repeat(80)) + "\n\n");
8955
10084
  const providers = Array.from(grouped.keys()).sort();
8956
10085
  for (const provider of providers) {
8957
10086
  const providerModels = grouped.get(provider);
8958
10087
  const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
8959
- stream2.write(import_chalk6.default.bold.yellow(`${providerName} Models
10088
+ stream2.write(import_chalk8.default.bold.yellow(`${providerName} Models
8960
10089
  `));
8961
10090
  if (verbose) {
8962
10091
  renderVerboseTable(providerModels, stream2);
@@ -8965,11 +10094,11 @@ function renderTable(models, verbose, stream2) {
8965
10094
  }
8966
10095
  stream2.write("\n");
8967
10096
  }
8968
- stream2.write(import_chalk6.default.bold.magenta("Model Shortcuts\n"));
8969
- stream2.write(import_chalk6.default.dim("\u2500".repeat(80)) + "\n");
10097
+ stream2.write(import_chalk8.default.bold.magenta("Model Shortcuts\n"));
10098
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(80)) + "\n");
8970
10099
  const shortcuts = Object.entries(MODEL_ALIASES).sort((a, b) => a[0].localeCompare(b[0]));
8971
10100
  for (const [shortcut, fullName] of shortcuts) {
8972
- stream2.write(import_chalk6.default.cyan(` ${shortcut.padEnd(15)}`) + import_chalk6.default.dim(" \u2192 ") + import_chalk6.default.white(fullName) + "\n");
10101
+ stream2.write(import_chalk8.default.cyan(` ${shortcut.padEnd(15)}`) + import_chalk8.default.dim(" \u2192 ") + import_chalk8.default.white(fullName) + "\n");
8973
10102
  }
8974
10103
  stream2.write("\n");
8975
10104
  }
@@ -8979,45 +10108,45 @@ function renderCompactTable(models, stream2) {
8979
10108
  const contextWidth = 13;
8980
10109
  const inputWidth = 10;
8981
10110
  const outputWidth = 10;
8982
- stream2.write(import_chalk6.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
10111
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
8983
10112
  stream2.write(
8984
- import_chalk6.default.bold(
10113
+ import_chalk8.default.bold(
8985
10114
  "Model ID".padEnd(idWidth) + " " + "Display Name".padEnd(nameWidth) + " " + "Context".padEnd(contextWidth) + " " + "Input".padEnd(inputWidth) + " " + "Output".padEnd(outputWidth)
8986
10115
  ) + "\n"
8987
10116
  );
8988
- stream2.write(import_chalk6.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
10117
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
8989
10118
  for (const model of models) {
8990
10119
  const contextFormatted = formatTokens2(model.contextWindow);
8991
10120
  const inputPrice = `$${model.pricing.input.toFixed(2)}`;
8992
10121
  const outputPrice = `$${model.pricing.output.toFixed(2)}`;
8993
10122
  stream2.write(
8994
- import_chalk6.default.green(model.modelId.padEnd(idWidth)) + " " + import_chalk6.default.white(model.displayName.padEnd(nameWidth)) + " " + import_chalk6.default.yellow(contextFormatted.padEnd(contextWidth)) + " " + import_chalk6.default.cyan(inputPrice.padEnd(inputWidth)) + " " + import_chalk6.default.cyan(outputPrice.padEnd(outputWidth)) + "\n"
10123
+ import_chalk8.default.green(model.modelId.padEnd(idWidth)) + " " + import_chalk8.default.white(model.displayName.padEnd(nameWidth)) + " " + import_chalk8.default.yellow(contextFormatted.padEnd(contextWidth)) + " " + import_chalk8.default.cyan(inputPrice.padEnd(inputWidth)) + " " + import_chalk8.default.cyan(outputPrice.padEnd(outputWidth)) + "\n"
8995
10124
  );
8996
10125
  }
8997
- stream2.write(import_chalk6.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
8998
- stream2.write(import_chalk6.default.dim(` * Prices are per 1M tokens
10126
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
10127
+ stream2.write(import_chalk8.default.dim(` * Prices are per 1M tokens
8999
10128
  `));
9000
10129
  }
9001
10130
  function renderVerboseTable(models, stream2) {
9002
10131
  for (const model of models) {
9003
- stream2.write(import_chalk6.default.bold.green(`
10132
+ stream2.write(import_chalk8.default.bold.green(`
9004
10133
  ${model.modelId}
9005
10134
  `));
9006
- stream2.write(import_chalk6.default.dim(" " + "\u2500".repeat(60)) + "\n");
9007
- stream2.write(` ${import_chalk6.default.dim("Name:")} ${import_chalk6.default.white(model.displayName)}
10135
+ stream2.write(import_chalk8.default.dim(" " + "\u2500".repeat(60)) + "\n");
10136
+ stream2.write(` ${import_chalk8.default.dim("Name:")} ${import_chalk8.default.white(model.displayName)}
9008
10137
  `);
9009
- stream2.write(` ${import_chalk6.default.dim("Context:")} ${import_chalk6.default.yellow(formatTokens2(model.contextWindow))}
10138
+ stream2.write(` ${import_chalk8.default.dim("Context:")} ${import_chalk8.default.yellow(formatTokens2(model.contextWindow))}
9010
10139
  `);
9011
- stream2.write(` ${import_chalk6.default.dim("Max Output:")} ${import_chalk6.default.yellow(formatTokens2(model.maxOutputTokens))}
10140
+ stream2.write(` ${import_chalk8.default.dim("Max Output:")} ${import_chalk8.default.yellow(formatTokens2(model.maxOutputTokens))}
9012
10141
  `);
9013
- stream2.write(` ${import_chalk6.default.dim("Pricing:")} ${import_chalk6.default.cyan(`$${model.pricing.input.toFixed(2)} input`)} ${import_chalk6.default.dim("/")} ${import_chalk6.default.cyan(`$${model.pricing.output.toFixed(2)} output`)} ${import_chalk6.default.dim("(per 1M tokens)")}
10142
+ stream2.write(` ${import_chalk8.default.dim("Pricing:")} ${import_chalk8.default.cyan(`$${model.pricing.input.toFixed(2)} input`)} ${import_chalk8.default.dim("/")} ${import_chalk8.default.cyan(`$${model.pricing.output.toFixed(2)} output`)} ${import_chalk8.default.dim("(per 1M tokens)")}
9014
10143
  `);
9015
10144
  if (model.pricing.cachedInput !== void 0) {
9016
- stream2.write(` ${import_chalk6.default.dim("Cached Input:")} ${import_chalk6.default.cyan(`$${model.pricing.cachedInput.toFixed(2)} per 1M tokens`)}
10145
+ stream2.write(` ${import_chalk8.default.dim("Cached Input:")} ${import_chalk8.default.cyan(`$${model.pricing.cachedInput.toFixed(2)} per 1M tokens`)}
9017
10146
  `);
9018
10147
  }
9019
10148
  if (model.knowledgeCutoff) {
9020
- stream2.write(` ${import_chalk6.default.dim("Knowledge:")} ${model.knowledgeCutoff}
10149
+ stream2.write(` ${import_chalk8.default.dim("Knowledge:")} ${model.knowledgeCutoff}
9021
10150
  `);
9022
10151
  }
9023
10152
  const features = [];
@@ -9028,20 +10157,20 @@ function renderVerboseTable(models, stream2) {
9028
10157
  if (model.features.structuredOutputs) features.push("structured-outputs");
9029
10158
  if (model.features.fineTuning) features.push("fine-tuning");
9030
10159
  if (features.length > 0) {
9031
- stream2.write(` ${import_chalk6.default.dim("Features:")} ${import_chalk6.default.blue(features.join(", "))}
10160
+ stream2.write(` ${import_chalk8.default.dim("Features:")} ${import_chalk8.default.blue(features.join(", "))}
9032
10161
  `);
9033
10162
  }
9034
10163
  if (model.metadata) {
9035
10164
  if (model.metadata.family) {
9036
- stream2.write(` ${import_chalk6.default.dim("Family:")} ${model.metadata.family}
10165
+ stream2.write(` ${import_chalk8.default.dim("Family:")} ${model.metadata.family}
9037
10166
  `);
9038
10167
  }
9039
10168
  if (model.metadata.releaseDate) {
9040
- stream2.write(` ${import_chalk6.default.dim("Released:")} ${model.metadata.releaseDate}
10169
+ stream2.write(` ${import_chalk8.default.dim("Released:")} ${model.metadata.releaseDate}
9041
10170
  `);
9042
10171
  }
9043
10172
  if (model.metadata.notes) {
9044
- stream2.write(` ${import_chalk6.default.dim("Notes:")} ${import_chalk6.default.italic(model.metadata.notes)}
10173
+ stream2.write(` ${import_chalk8.default.dim("Notes:")} ${import_chalk8.default.italic(model.metadata.notes)}
9045
10174
  `);
9046
10175
  }
9047
10176
  }
@@ -9091,7 +10220,7 @@ function registerModelsCommand(program, env) {
9091
10220
 
9092
10221
  // src/cli/environment.ts
9093
10222
  var import_node_readline = __toESM(require("readline"), 1);
9094
- var import_chalk7 = __toESM(require("chalk"), 1);
10223
+ var import_chalk9 = __toESM(require("chalk"), 1);
9095
10224
  init_client();
9096
10225
  init_logger();
9097
10226
  var LOG_LEVEL_MAP = {
@@ -9134,22 +10263,22 @@ function createLoggerFactory(config) {
9134
10263
  }
9135
10264
  function createPromptFunction(stdin, stdout) {
9136
10265
  return (question) => {
9137
- return new Promise((resolve) => {
10266
+ return new Promise((resolve2) => {
9138
10267
  const rl = import_node_readline.default.createInterface({
9139
10268
  input: stdin,
9140
10269
  output: stdout
9141
10270
  });
9142
10271
  stdout.write("\n");
9143
- stdout.write(`${import_chalk7.default.cyan("\u2500".repeat(60))}
10272
+ stdout.write(`${import_chalk9.default.cyan("\u2500".repeat(60))}
9144
10273
  `);
9145
- stdout.write(import_chalk7.default.cyan.bold("\u{1F916} Agent asks:\n"));
10274
+ stdout.write(import_chalk9.default.cyan.bold("\u{1F916} Agent asks:\n"));
9146
10275
  stdout.write(`${question}
9147
10276
  `);
9148
- stdout.write(`${import_chalk7.default.cyan("\u2500".repeat(60))}
10277
+ stdout.write(`${import_chalk9.default.cyan("\u2500".repeat(60))}
9149
10278
  `);
9150
- rl.question(import_chalk7.default.green.bold("You: "), (answer) => {
10279
+ rl.question(import_chalk9.default.green.bold("You: "), (answer) => {
9151
10280
  rl.close();
9152
- resolve(answer);
10281
+ resolve2(answer);
9153
10282
  });
9154
10283
  });
9155
10284
  };