llmist 1.3.0 → 1.4.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, "");
@@ -2268,8 +2268,8 @@ var init_error_formatter = __esm({
2268
2268
  const parts = [];
2269
2269
  parts.push(`Error: Invalid parameters for '${gadgetName}':`);
2270
2270
  for (const issue of zodError.issues) {
2271
- const path2 = issue.path.join(".") || "root";
2272
- parts.push(` - ${path2}: ${issue.message}`);
2271
+ const path5 = issue.path.join(".") || "root";
2272
+ parts.push(` - ${path5}: ${issue.message}`);
2273
2273
  }
2274
2274
  parts.push("");
2275
2275
  parts.push("Gadget Usage:");
@@ -3250,6 +3250,8 @@ var init_agent = __esm({
3250
3250
  outputLimitCharLimit;
3251
3251
  // Context compaction
3252
3252
  compactionManager;
3253
+ // Cancellation
3254
+ signal;
3253
3255
  /**
3254
3256
  * Creates a new Agent instance.
3255
3257
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -3317,6 +3319,7 @@ var init_agent = __esm({
3317
3319
  options.compactionConfig
3318
3320
  );
3319
3321
  }
3322
+ this.signal = options.signal;
3320
3323
  }
3321
3324
  /**
3322
3325
  * Get the gadget registry for this agent.
@@ -3434,7 +3437,8 @@ var init_agent = __esm({
3434
3437
  model: this.model,
3435
3438
  messages: this.conversation.getMessages(),
3436
3439
  temperature: this.temperature,
3437
- maxTokens: this.defaultMaxTokens
3440
+ maxTokens: this.defaultMaxTokens,
3441
+ signal: this.signal
3438
3442
  };
3439
3443
  await this.safeObserve(async () => {
3440
3444
  if (this.hooks.observers?.onLLMCallStart) {
@@ -4052,7 +4056,7 @@ var init_base_provider = __esm({
4052
4056
  async *stream(options, descriptor, spec) {
4053
4057
  const preparedMessages = this.prepareMessages(options.messages);
4054
4058
  const payload = this.buildRequestPayload(options, descriptor, spec, preparedMessages);
4055
- const rawStream = await this.executeStreamRequest(payload);
4059
+ const rawStream = await this.executeStreamRequest(payload, options.signal);
4056
4060
  yield* this.wrapStream(rawStream);
4057
4061
  }
4058
4062
  /**
@@ -4170,9 +4174,9 @@ var init_anthropic = __esm({
4170
4174
  };
4171
4175
  return payload;
4172
4176
  }
4173
- async executeStreamRequest(payload) {
4177
+ async executeStreamRequest(payload, signal) {
4174
4178
  const client = this.client;
4175
- const stream2 = await client.messages.create(payload);
4179
+ const stream2 = await client.messages.create(payload, signal ? { signal } : void 0);
4176
4180
  return stream2;
4177
4181
  }
4178
4182
  async *wrapStream(iterable) {
@@ -4504,9 +4508,15 @@ var init_gemini = __esm({
4504
4508
  config
4505
4509
  };
4506
4510
  }
4507
- async executeStreamRequest(payload) {
4511
+ async executeStreamRequest(payload, signal) {
4508
4512
  const client = this.client;
4509
- const streamResponse = await client.models.generateContentStream(payload);
4513
+ const streamResponse = await client.models.generateContentStream({
4514
+ ...payload,
4515
+ config: {
4516
+ ...payload.config,
4517
+ ...signal ? { abortSignal: signal } : {}
4518
+ }
4519
+ });
4510
4520
  return streamResponse;
4511
4521
  }
4512
4522
  /**
@@ -5095,9 +5105,9 @@ var init_openai = __esm({
5095
5105
  ...shouldIncludeTemperature ? { temperature } : {}
5096
5106
  };
5097
5107
  }
5098
- async executeStreamRequest(payload) {
5108
+ async executeStreamRequest(payload, signal) {
5099
5109
  const client = this.client;
5100
- const stream2 = await client.chat.completions.create(payload);
5110
+ const stream2 = await client.chat.completions.create(payload, signal ? { signal } : void 0);
5101
5111
  return stream2;
5102
5112
  }
5103
5113
  async *wrapStream(iterable) {
@@ -5760,6 +5770,7 @@ var init_builder = __esm({
5760
5770
  gadgetOutputLimit;
5761
5771
  gadgetOutputLimitPercent;
5762
5772
  compactionConfig;
5773
+ signal;
5763
5774
  constructor(client) {
5764
5775
  this.client = client;
5765
5776
  }
@@ -6206,6 +6217,35 @@ var init_builder = __esm({
6206
6217
  this.compactionConfig = { enabled: false };
6207
6218
  return this;
6208
6219
  }
6220
+ /**
6221
+ * Set an abort signal for cancelling requests mid-flight.
6222
+ *
6223
+ * When the signal is aborted, the current LLM request will be cancelled
6224
+ * and the agent loop will exit gracefully.
6225
+ *
6226
+ * @param signal - AbortSignal from an AbortController
6227
+ * @returns This builder for chaining
6228
+ *
6229
+ * @example
6230
+ * ```typescript
6231
+ * const controller = new AbortController();
6232
+ *
6233
+ * // Cancel after 30 seconds
6234
+ * setTimeout(() => controller.abort(), 30000);
6235
+ *
6236
+ * const agent = LLMist.createAgent()
6237
+ * .withModel("sonnet")
6238
+ * .withSignal(controller.signal)
6239
+ * .ask("Write a long story");
6240
+ *
6241
+ * // Or cancel on user action
6242
+ * document.getElementById("cancel").onclick = () => controller.abort();
6243
+ * ```
6244
+ */
6245
+ withSignal(signal) {
6246
+ this.signal = signal;
6247
+ return this;
6248
+ }
6209
6249
  /**
6210
6250
  * Add a synthetic gadget call to the conversation history.
6211
6251
  *
@@ -6322,7 +6362,8 @@ ${endPrefix}`
6322
6362
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
6323
6363
  gadgetOutputLimit: this.gadgetOutputLimit,
6324
6364
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
6325
- compactionConfig: this.compactionConfig
6365
+ compactionConfig: this.compactionConfig,
6366
+ signal: this.signal
6326
6367
  };
6327
6368
  return new Agent(AGENT_INTERNAL_KEY, options);
6328
6369
  }
@@ -6425,7 +6466,8 @@ ${endPrefix}`
6425
6466
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
6426
6467
  gadgetOutputLimit: this.gadgetOutputLimit,
6427
6468
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
6428
- compactionConfig: this.compactionConfig
6469
+ compactionConfig: this.compactionConfig,
6470
+ signal: this.signal
6429
6471
  };
6430
6472
  return new Agent(AGENT_INTERNAL_KEY, options);
6431
6473
  }
@@ -6484,7 +6526,7 @@ var import_commander2 = require("commander");
6484
6526
  // package.json
6485
6527
  var package_default = {
6486
6528
  name: "llmist",
6487
- version: "1.2.0",
6529
+ version: "1.3.1",
6488
6530
  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
6531
  type: "module",
6490
6532
  main: "dist/index.cjs",
@@ -6568,6 +6610,7 @@ var package_default = {
6568
6610
  "@google/genai": "^1.27.0",
6569
6611
  chalk: "^5.6.2",
6570
6612
  commander: "^12.1.0",
6613
+ diff: "^8.0.2",
6571
6614
  eta: "^4.4.1",
6572
6615
  "js-toml": "^1.0.2",
6573
6616
  "js-yaml": "^4.1.0",
@@ -6584,6 +6627,7 @@ var package_default = {
6584
6627
  "@commitlint/config-conventional": "^20.0.0",
6585
6628
  "@semantic-release/changelog": "^6.0.3",
6586
6629
  "@semantic-release/git": "^10.0.1",
6630
+ "@types/diff": "^8.0.0",
6587
6631
  "@types/js-yaml": "^4.0.9",
6588
6632
  "@types/marked-terminal": "^6.1.1",
6589
6633
  "@types/node": "^20.12.7",
@@ -6597,12 +6641,274 @@ var package_default = {
6597
6641
  };
6598
6642
 
6599
6643
  // src/cli/agent-command.ts
6600
- var import_promises2 = require("readline/promises");
6601
- var import_chalk3 = __toESM(require("chalk"), 1);
6644
+ var import_promises3 = require("readline/promises");
6645
+ var import_chalk5 = __toESM(require("chalk"), 1);
6602
6646
  init_builder();
6647
+
6648
+ // src/core/errors.ts
6649
+ function isAbortError(error) {
6650
+ if (!(error instanceof Error)) return false;
6651
+ if (error.name === "AbortError") return true;
6652
+ if (error.name === "APIConnectionAbortedError") return true;
6653
+ if (error.name === "APIUserAbortError") return true;
6654
+ const message = error.message.toLowerCase();
6655
+ if (message.includes("abort")) return true;
6656
+ if (message.includes("cancelled")) return true;
6657
+ if (message.includes("canceled")) return true;
6658
+ return false;
6659
+ }
6660
+
6661
+ // src/cli/agent-command.ts
6603
6662
  init_registry();
6604
6663
  init_constants2();
6605
6664
 
6665
+ // src/cli/approval/manager.ts
6666
+ var import_promises = require("readline/promises");
6667
+ var import_chalk2 = __toESM(require("chalk"), 1);
6668
+
6669
+ // src/cli/approval/context-providers.ts
6670
+ var import_node_fs2 = require("fs");
6671
+ var import_node_path2 = require("path");
6672
+ var import_diff = require("diff");
6673
+
6674
+ // src/cli/approval/diff-renderer.ts
6675
+ var import_chalk = __toESM(require("chalk"), 1);
6676
+ function renderColoredDiff(diff) {
6677
+ return diff.split("\n").map((line) => {
6678
+ if (line.startsWith("---") || line.startsWith("+++")) {
6679
+ return import_chalk.default.bold(line);
6680
+ }
6681
+ if (line.startsWith("+")) {
6682
+ return import_chalk.default.green(line);
6683
+ }
6684
+ if (line.startsWith("-")) {
6685
+ return import_chalk.default.red(line);
6686
+ }
6687
+ if (line.startsWith("@@")) {
6688
+ return import_chalk.default.cyan(line);
6689
+ }
6690
+ return import_chalk.default.dim(line);
6691
+ }).join("\n");
6692
+ }
6693
+ function formatNewFileDiff(filePath, content) {
6694
+ const lines = content.split("\n");
6695
+ const header = `+++ ${filePath} (new file)`;
6696
+ const addedLines = lines.map((line) => `+ ${line}`).join("\n");
6697
+ return `${header}
6698
+ ${addedLines}`;
6699
+ }
6700
+
6701
+ // src/cli/approval/context-providers.ts
6702
+ var WriteFileContextProvider = class {
6703
+ gadgetName = "WriteFile";
6704
+ async getContext(params) {
6705
+ const filePath = String(params.filePath ?? params.path ?? "");
6706
+ const newContent = String(params.content ?? "");
6707
+ const resolvedPath = (0, import_node_path2.resolve)(process.cwd(), filePath);
6708
+ if (!(0, import_node_fs2.existsSync)(resolvedPath)) {
6709
+ return {
6710
+ summary: `Create new file: ${filePath}`,
6711
+ details: formatNewFileDiff(filePath, newContent)
6712
+ };
6713
+ }
6714
+ const oldContent = (0, import_node_fs2.readFileSync)(resolvedPath, "utf-8");
6715
+ const diff = (0, import_diff.createPatch)(filePath, oldContent, newContent, "original", "modified");
6716
+ return {
6717
+ summary: `Modify: ${filePath}`,
6718
+ details: diff
6719
+ };
6720
+ }
6721
+ };
6722
+ var EditFileContextProvider = class {
6723
+ gadgetName = "EditFile";
6724
+ async getContext(params) {
6725
+ const filePath = String(params.filePath ?? params.path ?? "");
6726
+ const resolvedPath = (0, import_node_path2.resolve)(process.cwd(), filePath);
6727
+ if ("content" in params) {
6728
+ const newContent = String(params.content);
6729
+ if (!(0, import_node_fs2.existsSync)(resolvedPath)) {
6730
+ return {
6731
+ summary: `Create new file: ${filePath}`,
6732
+ details: formatNewFileDiff(filePath, newContent)
6733
+ };
6734
+ }
6735
+ const oldContent = (0, import_node_fs2.readFileSync)(resolvedPath, "utf-8");
6736
+ const diff = (0, import_diff.createPatch)(filePath, oldContent, newContent, "original", "modified");
6737
+ return {
6738
+ summary: `Modify: ${filePath}`,
6739
+ details: diff
6740
+ };
6741
+ }
6742
+ if ("commands" in params) {
6743
+ const commands = String(params.commands);
6744
+ return {
6745
+ summary: `Edit: ${filePath}`,
6746
+ details: `Commands:
6747
+ ${commands}`
6748
+ };
6749
+ }
6750
+ return {
6751
+ summary: `Edit: ${filePath}`
6752
+ };
6753
+ }
6754
+ };
6755
+ var RunCommandContextProvider = class {
6756
+ gadgetName = "RunCommand";
6757
+ async getContext(params) {
6758
+ const command = String(params.command ?? "");
6759
+ const cwd = params.cwd ? ` (in ${params.cwd})` : "";
6760
+ return {
6761
+ summary: `Execute: ${command}${cwd}`
6762
+ };
6763
+ }
6764
+ };
6765
+ var DefaultContextProvider = class {
6766
+ constructor(gadgetName) {
6767
+ this.gadgetName = gadgetName;
6768
+ }
6769
+ async getContext(params) {
6770
+ const paramEntries = Object.entries(params);
6771
+ if (paramEntries.length === 0) {
6772
+ return {
6773
+ summary: `${this.gadgetName}()`
6774
+ };
6775
+ }
6776
+ const formatValue = (value) => {
6777
+ const MAX_LEN = 50;
6778
+ const str = JSON.stringify(value);
6779
+ return str.length > MAX_LEN ? `${str.slice(0, MAX_LEN - 3)}...` : str;
6780
+ };
6781
+ const paramStr = paramEntries.map(([k, v]) => `${k}=${formatValue(v)}`).join(", ");
6782
+ return {
6783
+ summary: `${this.gadgetName}(${paramStr})`
6784
+ };
6785
+ }
6786
+ };
6787
+ var builtinContextProviders = [
6788
+ new WriteFileContextProvider(),
6789
+ new EditFileContextProvider(),
6790
+ new RunCommandContextProvider()
6791
+ ];
6792
+
6793
+ // src/cli/approval/manager.ts
6794
+ var ApprovalManager = class {
6795
+ /**
6796
+ * Creates a new ApprovalManager.
6797
+ *
6798
+ * @param config - Approval configuration with per-gadget modes
6799
+ * @param env - CLI environment for I/O operations
6800
+ * @param progress - Optional progress indicator to pause during prompts
6801
+ */
6802
+ constructor(config, env, progress) {
6803
+ this.config = config;
6804
+ this.env = env;
6805
+ this.progress = progress;
6806
+ for (const provider of builtinContextProviders) {
6807
+ this.registerProvider(provider);
6808
+ }
6809
+ }
6810
+ providers = /* @__PURE__ */ new Map();
6811
+ /**
6812
+ * Registers a custom context provider for a gadget.
6813
+ *
6814
+ * @param provider - The context provider to register
6815
+ */
6816
+ registerProvider(provider) {
6817
+ this.providers.set(provider.gadgetName.toLowerCase(), provider);
6818
+ }
6819
+ /**
6820
+ * Gets the approval mode for a gadget.
6821
+ *
6822
+ * Resolution order:
6823
+ * 1. Explicit configuration for the gadget name
6824
+ * 2. Wildcard "*" configuration
6825
+ * 3. Default mode from config
6826
+ *
6827
+ * @param gadgetName - Name of the gadget
6828
+ * @returns The approval mode to use
6829
+ */
6830
+ getApprovalMode(gadgetName) {
6831
+ const normalizedName = gadgetName.toLowerCase();
6832
+ for (const [configName, mode] of Object.entries(this.config.gadgetApprovals)) {
6833
+ if (configName.toLowerCase() === normalizedName) {
6834
+ return mode;
6835
+ }
6836
+ }
6837
+ if ("*" in this.config.gadgetApprovals) {
6838
+ return this.config.gadgetApprovals["*"];
6839
+ }
6840
+ return this.config.defaultMode;
6841
+ }
6842
+ /**
6843
+ * Requests approval for a gadget execution.
6844
+ *
6845
+ * Behavior depends on the gadget's approval mode:
6846
+ * - "allowed": Returns approved immediately
6847
+ * - "denied": Returns denied with configuration message
6848
+ * - "approval-required": Prompts user interactively
6849
+ *
6850
+ * @param gadgetName - Name of the gadget
6851
+ * @param params - The gadget's execution parameters
6852
+ * @returns Approval result indicating whether to proceed
6853
+ */
6854
+ async requestApproval(gadgetName, params) {
6855
+ const mode = this.getApprovalMode(gadgetName);
6856
+ if (mode === "allowed") {
6857
+ return { approved: true };
6858
+ }
6859
+ if (mode === "denied") {
6860
+ return {
6861
+ approved: false,
6862
+ reason: `${gadgetName} is denied by configuration`
6863
+ };
6864
+ }
6865
+ return this.promptForApproval(gadgetName, params);
6866
+ }
6867
+ /**
6868
+ * Prompts the user for approval interactively.
6869
+ */
6870
+ async promptForApproval(gadgetName, params) {
6871
+ const provider = this.providers.get(gadgetName.toLowerCase()) ?? new DefaultContextProvider(gadgetName);
6872
+ const context = await provider.getContext(params);
6873
+ this.progress?.pause();
6874
+ this.env.stderr.write(`
6875
+ ${import_chalk2.default.yellow("\u{1F512} Approval required:")} ${context.summary}
6876
+ `);
6877
+ if (context.details) {
6878
+ this.env.stderr.write(`
6879
+ ${renderColoredDiff(context.details)}
6880
+ `);
6881
+ }
6882
+ const response = await this.prompt(" \u23CE approve, or type to reject: ");
6883
+ const isApproved = response === "" || response.toLowerCase() === "y";
6884
+ if (isApproved) {
6885
+ this.env.stderr.write(` ${import_chalk2.default.green("\u2713 Approved")}
6886
+
6887
+ `);
6888
+ return { approved: true };
6889
+ }
6890
+ this.env.stderr.write(` ${import_chalk2.default.red("\u2717 Denied")}
6891
+
6892
+ `);
6893
+ return { approved: false, reason: response || "Rejected by user" };
6894
+ }
6895
+ /**
6896
+ * Prompts for user input.
6897
+ */
6898
+ async prompt(message) {
6899
+ const rl = (0, import_promises.createInterface)({
6900
+ input: this.env.stdin,
6901
+ output: this.env.stderr
6902
+ });
6903
+ try {
6904
+ const answer = await rl.question(message);
6905
+ return answer.trim();
6906
+ } finally {
6907
+ rl.close();
6908
+ }
6909
+ }
6910
+ };
6911
+
6606
6912
  // src/cli/builtin-gadgets.ts
6607
6913
  var import_zod2 = require("zod");
6608
6914
  init_create_gadget();
@@ -6682,11 +6988,483 @@ var finish = createGadget({
6682
6988
  var builtinGadgets = [askUser, tellUser, finish];
6683
6989
 
6684
6990
  // src/cli/gadgets.ts
6685
- var import_node_fs2 = __toESM(require("fs"), 1);
6686
- var import_node_path2 = __toESM(require("path"), 1);
6991
+ var import_node_fs7 = __toESM(require("fs"), 1);
6992
+ var import_node_path6 = __toESM(require("path"), 1);
6687
6993
  var import_node_url = require("url");
6688
6994
  init_gadget();
6995
+
6996
+ // src/cli/builtins/filesystem/list-directory.ts
6997
+ var import_node_fs4 = __toESM(require("fs"), 1);
6998
+ var import_node_path4 = __toESM(require("path"), 1);
6999
+ var import_zod4 = require("zod");
7000
+
7001
+ // src/index.ts
7002
+ var import_zod3 = require("zod");
7003
+ init_builder();
7004
+ init_event_handlers();
7005
+
7006
+ // src/agent/index.ts
7007
+ init_conversation_manager();
7008
+ init_stream_processor();
7009
+ init_gadget_output_store();
7010
+
7011
+ // src/agent/compaction/index.ts
7012
+ init_config();
7013
+ init_strategy();
7014
+ init_strategies();
7015
+ init_manager();
7016
+
7017
+ // src/agent/hints.ts
7018
+ init_prompt_config();
7019
+
7020
+ // src/index.ts
7021
+ init_client();
7022
+ init_messages();
7023
+ init_model_registry();
7024
+ init_model_shortcuts();
7025
+ init_options();
7026
+ init_prompt_config();
7027
+ init_quick_methods();
7028
+ init_create_gadget();
7029
+ init_output_viewer();
7030
+ init_exceptions();
7031
+ init_executor();
7032
+ init_gadget();
7033
+ init_parser();
7034
+ init_registry();
7035
+
7036
+ // src/gadgets/typed-gadget.ts
7037
+ init_gadget();
7038
+
7039
+ // src/index.ts
7040
+ init_logger();
7041
+ init_anthropic();
7042
+ init_discovery();
7043
+ init_gemini();
7044
+ init_openai();
7045
+
7046
+ // src/testing/mock-manager.ts
7047
+ init_logger();
7048
+
7049
+ // src/testing/mock-stream.ts
7050
+ init_constants();
7051
+
7052
+ // src/testing/mock-client.ts
7053
+ init_client();
7054
+
7055
+ // src/testing/mock-gadget.ts
7056
+ init_gadget();
7057
+
7058
+ // src/testing/cli-helpers.ts
7059
+ var import_node_stream = require("stream");
7060
+
7061
+ // src/cli/builtins/filesystem/utils.ts
7062
+ var import_node_fs3 = __toESM(require("fs"), 1);
7063
+ var import_node_path3 = __toESM(require("path"), 1);
7064
+ var PathSandboxException = class extends Error {
7065
+ constructor(inputPath, reason) {
7066
+ super(`Path access denied: ${inputPath}. ${reason}`);
7067
+ this.name = "PathSandboxException";
7068
+ }
7069
+ };
7070
+ function validatePathIsWithinCwd(inputPath) {
7071
+ const cwd = process.cwd();
7072
+ const resolvedPath = import_node_path3.default.resolve(cwd, inputPath);
7073
+ let finalPath;
7074
+ try {
7075
+ finalPath = import_node_fs3.default.realpathSync(resolvedPath);
7076
+ } catch (error) {
7077
+ const nodeError = error;
7078
+ if (nodeError.code === "ENOENT") {
7079
+ finalPath = resolvedPath;
7080
+ } else {
7081
+ throw error;
7082
+ }
7083
+ }
7084
+ const cwdWithSep = cwd + import_node_path3.default.sep;
7085
+ if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
7086
+ throw new PathSandboxException(inputPath, "Path is outside the current working directory");
7087
+ }
7088
+ return finalPath;
7089
+ }
7090
+
7091
+ // src/cli/builtins/filesystem/list-directory.ts
7092
+ function listFiles(dirPath, basePath = dirPath, maxDepth = 1, currentDepth = 1) {
7093
+ const entries = [];
7094
+ try {
7095
+ const items = import_node_fs4.default.readdirSync(dirPath);
7096
+ for (const item of items) {
7097
+ const fullPath = import_node_path4.default.join(dirPath, item);
7098
+ const relativePath = import_node_path4.default.relative(basePath, fullPath);
7099
+ try {
7100
+ const stats = import_node_fs4.default.lstatSync(fullPath);
7101
+ let type;
7102
+ let size;
7103
+ if (stats.isSymbolicLink()) {
7104
+ type = "symlink";
7105
+ size = 0;
7106
+ } else if (stats.isDirectory()) {
7107
+ type = "directory";
7108
+ size = 0;
7109
+ } else {
7110
+ type = "file";
7111
+ size = stats.size;
7112
+ }
7113
+ entries.push({
7114
+ name: item,
7115
+ relativePath,
7116
+ type,
7117
+ size,
7118
+ modified: Math.floor(stats.mtime.getTime() / 1e3)
7119
+ });
7120
+ if (type === "directory" && currentDepth < maxDepth) {
7121
+ try {
7122
+ validatePathIsWithinCwd(fullPath);
7123
+ const subEntries = listFiles(fullPath, basePath, maxDepth, currentDepth + 1);
7124
+ entries.push(...subEntries);
7125
+ } catch {
7126
+ }
7127
+ }
7128
+ } catch {
7129
+ }
7130
+ }
7131
+ } catch {
7132
+ return [];
7133
+ }
7134
+ return entries;
7135
+ }
7136
+ function formatAge(epochSeconds) {
7137
+ const now = Math.floor(Date.now() / 1e3);
7138
+ const seconds = now - epochSeconds;
7139
+ if (seconds < 60) return `${seconds}s`;
7140
+ const minutes = Math.floor(seconds / 60);
7141
+ if (minutes < 60) return `${minutes}m`;
7142
+ const hours = Math.floor(minutes / 60);
7143
+ if (hours < 24) return `${hours}h`;
7144
+ const days = Math.floor(hours / 24);
7145
+ if (days < 7) return `${days}d`;
7146
+ const weeks = Math.floor(days / 7);
7147
+ if (weeks < 4) return `${weeks}w`;
7148
+ const months = Math.floor(days / 30);
7149
+ if (months < 12) return `${months}mo`;
7150
+ const years = Math.floor(days / 365);
7151
+ return `${years}y`;
7152
+ }
7153
+ function formatEntriesAsString(entries) {
7154
+ if (entries.length === 0) {
7155
+ return "#empty";
7156
+ }
7157
+ const sortedEntries = [...entries].sort((a, b) => {
7158
+ const typeOrder = { directory: 0, file: 1, symlink: 2 };
7159
+ const typeCompare = typeOrder[a.type] - typeOrder[b.type];
7160
+ if (typeCompare !== 0) return typeCompare;
7161
+ return a.relativePath.localeCompare(b.relativePath);
7162
+ });
7163
+ const typeCode = {
7164
+ directory: "D",
7165
+ file: "F",
7166
+ symlink: "L"
7167
+ };
7168
+ const encodeName = (name) => name.replace(/\|/g, "%7C").replace(/\n/g, "%0A");
7169
+ const header = "#T|N|S|A";
7170
+ const rows = sortedEntries.map(
7171
+ (e) => `${typeCode[e.type]}|${encodeName(e.relativePath)}|${e.size}|${formatAge(e.modified)}`
7172
+ );
7173
+ return [header, ...rows].join("\n");
7174
+ }
7175
+ var listDirectory = createGadget({
7176
+ name: "ListDirectory",
7177
+ 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.",
7178
+ schema: import_zod4.z.object({
7179
+ directoryPath: import_zod4.z.string().default(".").describe("Path to the directory to list"),
7180
+ maxDepth: import_zod4.z.number().int().min(1).max(10).default(1).describe(
7181
+ "Maximum depth to recurse (1 = immediate children only, 2 = include grandchildren, etc.)"
7182
+ )
7183
+ }),
7184
+ examples: [
7185
+ {
7186
+ params: { directoryPath: ".", maxDepth: 1 },
7187
+ output: "path=. maxDepth=1\n\n#T|N|S|A\nD|src|0|2h\nD|tests|0|1d\nF|package.json|2841|3h",
7188
+ comment: "List current directory"
7189
+ },
7190
+ {
7191
+ params: { directoryPath: "src", maxDepth: 2 },
7192
+ 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",
7193
+ comment: "List src directory recursively"
7194
+ }
7195
+ ],
7196
+ execute: ({ directoryPath, maxDepth }) => {
7197
+ const validatedPath = validatePathIsWithinCwd(directoryPath);
7198
+ const stats = import_node_fs4.default.statSync(validatedPath);
7199
+ if (!stats.isDirectory()) {
7200
+ throw new Error(`Path is not a directory: ${directoryPath}`);
7201
+ }
7202
+ const entries = listFiles(validatedPath, validatedPath, maxDepth);
7203
+ const formattedList = formatEntriesAsString(entries);
7204
+ return `path=${directoryPath} maxDepth=${maxDepth}
7205
+
7206
+ ${formattedList}`;
7207
+ }
7208
+ });
7209
+
7210
+ // src/cli/builtins/filesystem/read-file.ts
7211
+ var import_node_fs5 = __toESM(require("fs"), 1);
7212
+ var import_zod5 = require("zod");
7213
+ var readFile = createGadget({
7214
+ name: "ReadFile",
7215
+ 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.",
7216
+ schema: import_zod5.z.object({
7217
+ filePath: import_zod5.z.string().describe("Path to the file to read (relative or absolute)")
7218
+ }),
7219
+ examples: [
7220
+ {
7221
+ params: { filePath: "package.json" },
7222
+ output: 'path=package.json\n\n{\n "name": "my-project",\n "version": "1.0.0"\n ...\n}',
7223
+ comment: "Read a JSON config file"
7224
+ },
7225
+ {
7226
+ params: { filePath: "src/index.ts" },
7227
+ output: "path=src/index.ts\n\nexport function main() { ... }",
7228
+ comment: "Read a source file"
7229
+ }
7230
+ ],
7231
+ execute: ({ filePath }) => {
7232
+ const validatedPath = validatePathIsWithinCwd(filePath);
7233
+ const content = import_node_fs5.default.readFileSync(validatedPath, "utf-8");
7234
+ return `path=${filePath}
7235
+
7236
+ ${content}`;
7237
+ }
7238
+ });
7239
+
7240
+ // src/cli/builtins/filesystem/write-file.ts
7241
+ var import_node_fs6 = __toESM(require("fs"), 1);
7242
+ var import_node_path5 = __toESM(require("path"), 1);
7243
+ var import_zod6 = require("zod");
7244
+ var writeFile = createGadget({
7245
+ name: "WriteFile",
7246
+ 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.",
7247
+ schema: import_zod6.z.object({
7248
+ filePath: import_zod6.z.string().describe("Path to the file to write (relative or absolute)"),
7249
+ content: import_zod6.z.string().describe("Content to write to the file")
7250
+ }),
7251
+ examples: [
7252
+ {
7253
+ params: { filePath: "output.txt", content: "Hello, World!" },
7254
+ output: "path=output.txt\n\nWrote 13 bytes",
7255
+ comment: "Write a simple text file"
7256
+ },
7257
+ {
7258
+ params: {
7259
+ filePath: "src/server.ts",
7260
+ content: `import { serve } from "bun";
7261
+
7262
+ const port = 3000;
7263
+
7264
+ serve({
7265
+ port,
7266
+ fetch: (req) => new Response(\`Hello from \${req.url}\`),
7267
+ });
7268
+
7269
+ console.log(\`Server running on http://localhost:\${port}\`);`
7270
+ },
7271
+ output: "path=src/server.ts\n\nWrote 198 bytes (created directory: src)",
7272
+ comment: "Write code with template literals - NO escaping needed inside heredoc (use <<<EOF...EOF)"
7273
+ }
7274
+ ],
7275
+ execute: ({ filePath, content }) => {
7276
+ const validatedPath = validatePathIsWithinCwd(filePath);
7277
+ const parentDir = import_node_path5.default.dirname(validatedPath);
7278
+ let createdDir = false;
7279
+ if (!import_node_fs6.default.existsSync(parentDir)) {
7280
+ validatePathIsWithinCwd(parentDir);
7281
+ import_node_fs6.default.mkdirSync(parentDir, { recursive: true });
7282
+ createdDir = true;
7283
+ }
7284
+ import_node_fs6.default.writeFileSync(validatedPath, content, "utf-8");
7285
+ const bytesWritten = Buffer.byteLength(content, "utf-8");
7286
+ const dirNote = createdDir ? ` (created directory: ${import_node_path5.default.dirname(filePath)})` : "";
7287
+ return `path=${filePath}
7288
+
7289
+ Wrote ${bytesWritten} bytes${dirNote}`;
7290
+ }
7291
+ });
7292
+
7293
+ // src/cli/builtins/filesystem/edit-file.ts
7294
+ var import_zod7 = require("zod");
7295
+ function filterDangerousCommands(commands) {
7296
+ return commands.split("\n").filter((line) => !line.trimStart().startsWith("!")).join("\n");
7297
+ }
7298
+ var editFile = createGadget({
7299
+ name: "EditFile",
7300
+ 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.",
7301
+ schema: import_zod7.z.object({
7302
+ filePath: import_zod7.z.string().describe("Path to the file to edit (relative or absolute)"),
7303
+ commands: import_zod7.z.string().describe("Ed commands to execute, one per line")
7304
+ }),
7305
+ examples: [
7306
+ {
7307
+ params: {
7308
+ filePath: "config.txt",
7309
+ commands: `1,$p
7310
+ q`
7311
+ },
7312
+ output: "path=config.txt\n\n32\nkey=value\noption=true",
7313
+ comment: "Print entire file contents (ed shows byte count, then content)"
7314
+ },
7315
+ {
7316
+ params: {
7317
+ filePath: "data.txt",
7318
+ commands: `1,$s/foo/bar/g
7319
+ w
7320
+ q`
7321
+ },
7322
+ output: "path=data.txt\n\n42\n42",
7323
+ comment: "Replace all 'foo' with 'bar' (ed shows bytes read, then bytes written)"
7324
+ },
7325
+ {
7326
+ params: {
7327
+ filePath: "list.txt",
7328
+ commands: `3d
7329
+ w
7330
+ q`
7331
+ },
7332
+ output: "path=list.txt\n\n45\n28",
7333
+ comment: "Delete line 3, save and quit"
7334
+ },
7335
+ {
7336
+ params: {
7337
+ filePath: "readme.txt",
7338
+ commands: `$a
7339
+ New last line
7340
+ .
7341
+ w
7342
+ q`
7343
+ },
7344
+ output: "path=readme.txt\n\n40\n56",
7345
+ comment: "Append text after last line ($ = last line, . = end input mode)"
7346
+ }
7347
+ ],
7348
+ timeoutMs: 3e4,
7349
+ execute: async ({ filePath, commands }) => {
7350
+ const validatedPath = validatePathIsWithinCwd(filePath);
7351
+ const safeCommands = filterDangerousCommands(commands);
7352
+ try {
7353
+ const proc = Bun.spawn(["ed", validatedPath], {
7354
+ stdin: "pipe",
7355
+ stdout: "pipe",
7356
+ stderr: "pipe"
7357
+ });
7358
+ proc.stdin.write(`${safeCommands}
7359
+ `);
7360
+ proc.stdin.end();
7361
+ const timeoutPromise = new Promise((_, reject) => {
7362
+ setTimeout(() => {
7363
+ proc.kill();
7364
+ reject(new Error("ed command timed out after 30000ms"));
7365
+ }, 3e4);
7366
+ });
7367
+ const exitCode = await Promise.race([proc.exited, timeoutPromise]);
7368
+ const stdout = await new Response(proc.stdout).text();
7369
+ const stderr = await new Response(proc.stderr).text();
7370
+ const output = [stdout, stderr].filter(Boolean).join("\n").trim();
7371
+ if (exitCode !== 0) {
7372
+ return `path=${filePath}
7373
+
7374
+ ${output || "ed exited with non-zero status"}`;
7375
+ }
7376
+ return `path=${filePath}
7377
+
7378
+ ${output || "(no output)"}`;
7379
+ } catch (error) {
7380
+ const message = error instanceof Error ? error.message : String(error);
7381
+ return `path=${filePath}
7382
+
7383
+ error: ${message}`;
7384
+ }
7385
+ }
7386
+ });
7387
+
7388
+ // src/cli/builtins/run-command.ts
7389
+ var import_zod8 = require("zod");
7390
+ var runCommand = createGadget({
7391
+ name: "RunCommand",
7392
+ description: "Execute a shell command and return its output. Returns both stdout and stderr combined with the exit status.",
7393
+ schema: import_zod8.z.object({
7394
+ command: import_zod8.z.string().describe("The shell command to execute"),
7395
+ cwd: import_zod8.z.string().optional().describe("Working directory for the command (default: current directory)"),
7396
+ timeout: import_zod8.z.number().default(3e4).describe("Timeout in milliseconds (default: 30000)")
7397
+ }),
7398
+ examples: [
7399
+ {
7400
+ params: { command: "ls -la", timeout: 3e4 },
7401
+ 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",
7402
+ comment: "List directory contents with details"
7403
+ },
7404
+ {
7405
+ params: { command: "echo 'Hello World'", timeout: 3e4 },
7406
+ output: "status=0\n\nHello World",
7407
+ comment: "Simple echo command"
7408
+ },
7409
+ {
7410
+ params: { command: "cat nonexistent.txt", timeout: 3e4 },
7411
+ output: "status=1\n\ncat: nonexistent.txt: No such file or directory",
7412
+ comment: "Command that fails returns non-zero status"
7413
+ },
7414
+ {
7415
+ params: { command: "pwd", cwd: "/tmp", timeout: 3e4 },
7416
+ output: "status=0\n\n/tmp",
7417
+ comment: "Execute command in a specific directory"
7418
+ }
7419
+ ],
7420
+ execute: async ({ command, cwd, timeout }) => {
7421
+ const workingDir = cwd ?? process.cwd();
7422
+ try {
7423
+ const proc = Bun.spawn(["sh", "-c", command], {
7424
+ cwd: workingDir,
7425
+ stdout: "pipe",
7426
+ stderr: "pipe"
7427
+ });
7428
+ const timeoutPromise = new Promise((_, reject) => {
7429
+ setTimeout(() => {
7430
+ proc.kill();
7431
+ reject(new Error(`Command timed out after ${timeout}ms`));
7432
+ }, timeout);
7433
+ });
7434
+ const exitCode = await Promise.race([proc.exited, timeoutPromise]);
7435
+ const stdout = await new Response(proc.stdout).text();
7436
+ const stderr = await new Response(proc.stderr).text();
7437
+ const output = [stdout, stderr].filter(Boolean).join("\n").trim();
7438
+ return `status=${exitCode}
7439
+
7440
+ ${output || "(no output)"}`;
7441
+ } catch (error) {
7442
+ const message = error instanceof Error ? error.message : String(error);
7443
+ return `status=1
7444
+
7445
+ error: ${message}`;
7446
+ }
7447
+ }
7448
+ });
7449
+
7450
+ // src/cli/builtins/index.ts
7451
+ var builtinGadgetRegistry = {
7452
+ ListDirectory: listDirectory,
7453
+ ReadFile: readFile,
7454
+ WriteFile: writeFile,
7455
+ EditFile: editFile,
7456
+ RunCommand: runCommand
7457
+ };
7458
+ function getBuiltinGadget(name) {
7459
+ return builtinGadgetRegistry[name];
7460
+ }
7461
+ function isBuiltinGadgetName(name) {
7462
+ return name in builtinGadgetRegistry;
7463
+ }
7464
+
7465
+ // src/cli/gadgets.ts
6689
7466
  var PATH_PREFIXES = [".", "/", "~"];
7467
+ var BUILTIN_PREFIX = "builtin:";
6690
7468
  function isGadgetLike(value) {
6691
7469
  if (typeof value !== "object" || value === null) {
6692
7470
  return false;
@@ -6709,18 +7487,34 @@ function expandHomePath(input) {
6709
7487
  if (!home) {
6710
7488
  return input;
6711
7489
  }
6712
- return import_node_path2.default.join(home, input.slice(1));
7490
+ return import_node_path6.default.join(home, input.slice(1));
6713
7491
  }
6714
7492
  function isFileLikeSpecifier(specifier) {
6715
- return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path2.default.sep);
7493
+ return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path6.default.sep);
7494
+ }
7495
+ function tryResolveBuiltin(specifier) {
7496
+ if (specifier.startsWith(BUILTIN_PREFIX)) {
7497
+ const name = specifier.slice(BUILTIN_PREFIX.length);
7498
+ const gadget = getBuiltinGadget(name);
7499
+ if (!gadget) {
7500
+ throw new Error(
7501
+ `Unknown builtin gadget: ${name}. Available builtins: ListDirectory, ReadFile, WriteFile, EditFile, RunCommand`
7502
+ );
7503
+ }
7504
+ return gadget;
7505
+ }
7506
+ if (!isFileLikeSpecifier(specifier) && isBuiltinGadgetName(specifier)) {
7507
+ return getBuiltinGadget(specifier);
7508
+ }
7509
+ return null;
6716
7510
  }
6717
7511
  function resolveGadgetSpecifier(specifier, cwd) {
6718
7512
  if (!isFileLikeSpecifier(specifier)) {
6719
7513
  return specifier;
6720
7514
  }
6721
7515
  const expanded = expandHomePath(specifier);
6722
- const resolvedPath = import_node_path2.default.resolve(cwd, expanded);
6723
- if (!import_node_fs2.default.existsSync(resolvedPath)) {
7516
+ const resolvedPath = import_node_path6.default.resolve(cwd, expanded);
7517
+ if (!import_node_fs7.default.existsSync(resolvedPath)) {
6724
7518
  throw new Error(`Gadget module not found at ${resolvedPath}`);
6725
7519
  }
6726
7520
  return (0, import_node_url.pathToFileURL)(resolvedPath).href;
@@ -6762,6 +7556,11 @@ function extractGadgetsFromModule(moduleExports) {
6762
7556
  async function loadGadgets(specifiers, cwd, importer = (specifier) => import(specifier)) {
6763
7557
  const gadgets = [];
6764
7558
  for (const specifier of specifiers) {
7559
+ const builtin = tryResolveBuiltin(specifier);
7560
+ if (builtin) {
7561
+ gadgets.push(builtin);
7562
+ continue;
7563
+ }
6765
7564
  const resolved = resolveGadgetSpecifier(specifier, cwd);
6766
7565
  let exports2;
6767
7566
  try {
@@ -6786,13 +7585,13 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
6786
7585
  }
6787
7586
 
6788
7587
  // src/cli/llm-logging.ts
6789
- var import_promises = require("fs/promises");
7588
+ var import_promises2 = require("fs/promises");
6790
7589
  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");
7590
+ var import_node_path7 = require("path");
7591
+ var DEFAULT_LLM_LOG_DIR = (0, import_node_path7.join)((0, import_node_os.homedir)(), ".llmist", "logs");
6793
7592
  function resolveLogDir(option, subdir) {
6794
7593
  if (option === true) {
6795
- return (0, import_node_path3.join)(DEFAULT_LLM_LOG_DIR, subdir);
7594
+ return (0, import_node_path7.join)(DEFAULT_LLM_LOG_DIR, subdir);
6796
7595
  }
6797
7596
  if (typeof option === "string") {
6798
7597
  return option;
@@ -6809,44 +7608,44 @@ function formatLlmRequest(messages) {
6809
7608
  return lines.join("\n");
6810
7609
  }
6811
7610
  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");
7611
+ await (0, import_promises2.mkdir)(dir, { recursive: true });
7612
+ await (0, import_promises2.writeFile)((0, import_node_path7.join)(dir, filename), content, "utf-8");
6814
7613
  }
6815
7614
 
6816
7615
  // src/cli/utils.ts
6817
- var import_chalk2 = __toESM(require("chalk"), 1);
7616
+ var import_chalk4 = __toESM(require("chalk"), 1);
6818
7617
  var import_commander = require("commander");
6819
7618
  init_constants2();
6820
7619
 
6821
7620
  // src/cli/ui/formatters.ts
6822
- var import_chalk = __toESM(require("chalk"), 1);
7621
+ var import_chalk3 = __toESM(require("chalk"), 1);
6823
7622
  var import_marked = require("marked");
6824
7623
  var import_marked_terminal = require("marked-terminal");
6825
7624
  var markedConfigured = false;
6826
7625
  function ensureMarkedConfigured() {
6827
7626
  if (!markedConfigured) {
6828
- import_chalk.default.level = process.env.NO_COLOR ? 0 : 3;
7627
+ import_chalk3.default.level = process.env.NO_COLOR ? 0 : 3;
6829
7628
  import_marked.marked.use(
6830
7629
  (0, import_marked_terminal.markedTerminal)({
6831
7630
  // Text styling
6832
- strong: import_chalk.default.bold,
6833
- em: import_chalk.default.italic,
6834
- del: import_chalk.default.dim.gray.strikethrough,
7631
+ strong: import_chalk3.default.bold,
7632
+ em: import_chalk3.default.italic,
7633
+ del: import_chalk3.default.dim.gray.strikethrough,
6835
7634
  // Code styling
6836
- code: import_chalk.default.yellow,
6837
- codespan: import_chalk.default.yellow,
7635
+ code: import_chalk3.default.yellow,
7636
+ codespan: import_chalk3.default.yellow,
6838
7637
  // Headings
6839
- heading: import_chalk.default.green.bold,
6840
- firstHeading: import_chalk.default.magenta.underline.bold,
7638
+ heading: import_chalk3.default.green.bold,
7639
+ firstHeading: import_chalk3.default.magenta.underline.bold,
6841
7640
  // Links
6842
- link: import_chalk.default.blue,
6843
- href: import_chalk.default.blue.underline,
7641
+ link: import_chalk3.default.blue,
7642
+ href: import_chalk3.default.blue.underline,
6844
7643
  // Block elements
6845
- blockquote: import_chalk.default.gray.italic,
7644
+ blockquote: import_chalk3.default.gray.italic,
6846
7645
  // List formatting - reduce indentation and add bullet styling
6847
7646
  tab: 2,
6848
7647
  // Reduce from default 4 to 2 spaces
6849
- listitem: import_chalk.default.reset
7648
+ listitem: import_chalk3.default.reset
6850
7649
  // Keep items readable (no dim)
6851
7650
  })
6852
7651
  );
@@ -6856,11 +7655,11 @@ function ensureMarkedConfigured() {
6856
7655
  function renderMarkdown(text) {
6857
7656
  ensureMarkedConfigured();
6858
7657
  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));
7658
+ rendered = rendered.replace(/\*\*(.+?)\*\*/g, (_, content) => import_chalk3.default.bold(content)).replace(/(?<!\*)\*(\S[^*]*)\*(?!\*)/g, (_, content) => import_chalk3.default.italic(content));
6860
7659
  return rendered.trimEnd();
6861
7660
  }
6862
7661
  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];
7662
+ 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
7663
  const char = "\u2500";
6865
7664
  const width = process.stdout.columns || 80;
6866
7665
  let result = "";
@@ -6896,58 +7695,58 @@ function formatCost(cost) {
6896
7695
  function renderSummary(metadata) {
6897
7696
  const parts = [];
6898
7697
  if (metadata.iterations !== void 0) {
6899
- const iterPart = import_chalk.default.cyan(`#${metadata.iterations}`);
7698
+ const iterPart = import_chalk3.default.cyan(`#${metadata.iterations}`);
6900
7699
  if (metadata.model) {
6901
- parts.push(`${iterPart} ${import_chalk.default.magenta(metadata.model)}`);
7700
+ parts.push(`${iterPart} ${import_chalk3.default.magenta(metadata.model)}`);
6902
7701
  } else {
6903
7702
  parts.push(iterPart);
6904
7703
  }
6905
7704
  } else if (metadata.model) {
6906
- parts.push(import_chalk.default.magenta(metadata.model));
7705
+ parts.push(import_chalk3.default.magenta(metadata.model));
6907
7706
  }
6908
7707
  if (metadata.usage) {
6909
7708
  const { inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens } = metadata.usage;
6910
- parts.push(import_chalk.default.dim("\u2191") + import_chalk.default.yellow(` ${formatTokens(inputTokens)}`));
7709
+ parts.push(import_chalk3.default.dim("\u2191") + import_chalk3.default.yellow(` ${formatTokens(inputTokens)}`));
6911
7710
  if (cachedInputTokens && cachedInputTokens > 0) {
6912
- parts.push(import_chalk.default.dim("\u27F3") + import_chalk.default.blue(` ${formatTokens(cachedInputTokens)}`));
7711
+ parts.push(import_chalk3.default.dim("\u27F3") + import_chalk3.default.blue(` ${formatTokens(cachedInputTokens)}`));
6913
7712
  }
6914
7713
  if (cacheCreationInputTokens && cacheCreationInputTokens > 0) {
6915
- parts.push(import_chalk.default.dim("\u270E") + import_chalk.default.magenta(` ${formatTokens(cacheCreationInputTokens)}`));
7714
+ parts.push(import_chalk3.default.dim("\u270E") + import_chalk3.default.magenta(` ${formatTokens(cacheCreationInputTokens)}`));
6916
7715
  }
6917
- parts.push(import_chalk.default.dim("\u2193") + import_chalk.default.green(` ${formatTokens(outputTokens)}`));
7716
+ parts.push(import_chalk3.default.dim("\u2193") + import_chalk3.default.green(` ${formatTokens(outputTokens)}`));
6918
7717
  }
6919
7718
  if (metadata.elapsedSeconds !== void 0 && metadata.elapsedSeconds > 0) {
6920
- parts.push(import_chalk.default.dim(`${metadata.elapsedSeconds}s`));
7719
+ parts.push(import_chalk3.default.dim(`${metadata.elapsedSeconds}s`));
6921
7720
  }
6922
7721
  if (metadata.cost !== void 0 && metadata.cost > 0) {
6923
- parts.push(import_chalk.default.cyan(`$${formatCost(metadata.cost)}`));
7722
+ parts.push(import_chalk3.default.cyan(`$${formatCost(metadata.cost)}`));
6924
7723
  }
6925
7724
  if (metadata.finishReason) {
6926
- parts.push(import_chalk.default.dim(metadata.finishReason));
7725
+ parts.push(import_chalk3.default.dim(metadata.finishReason));
6927
7726
  }
6928
7727
  if (parts.length === 0) {
6929
7728
  return null;
6930
7729
  }
6931
- return parts.join(import_chalk.default.dim(" | "));
7730
+ return parts.join(import_chalk3.default.dim(" | "));
6932
7731
  }
6933
7732
  function renderOverallSummary(metadata) {
6934
7733
  const parts = [];
6935
7734
  if (metadata.totalTokens !== void 0 && metadata.totalTokens > 0) {
6936
- parts.push(import_chalk.default.dim("total:") + import_chalk.default.magenta(` ${formatTokens(metadata.totalTokens)}`));
7735
+ parts.push(import_chalk3.default.dim("total:") + import_chalk3.default.magenta(` ${formatTokens(metadata.totalTokens)}`));
6937
7736
  }
6938
7737
  if (metadata.iterations !== void 0 && metadata.iterations > 0) {
6939
- parts.push(import_chalk.default.cyan(`#${metadata.iterations}`));
7738
+ parts.push(import_chalk3.default.cyan(`#${metadata.iterations}`));
6940
7739
  }
6941
7740
  if (metadata.elapsedSeconds !== void 0 && metadata.elapsedSeconds > 0) {
6942
- parts.push(import_chalk.default.dim(`${metadata.elapsedSeconds}s`));
7741
+ parts.push(import_chalk3.default.dim(`${metadata.elapsedSeconds}s`));
6943
7742
  }
6944
7743
  if (metadata.cost !== void 0 && metadata.cost > 0) {
6945
- parts.push(import_chalk.default.cyan(`$${formatCost(metadata.cost)}`));
7744
+ parts.push(import_chalk3.default.cyan(`$${formatCost(metadata.cost)}`));
6946
7745
  }
6947
7746
  if (parts.length === 0) {
6948
7747
  return null;
6949
7748
  }
6950
- return parts.join(import_chalk.default.dim(" | "));
7749
+ return parts.join(import_chalk3.default.dim(" | "));
6951
7750
  }
6952
7751
  function formatParametersInline(params) {
6953
7752
  if (!params || Object.keys(params).length === 0) {
@@ -6963,8 +7762,8 @@ function formatParametersInline(params) {
6963
7762
  const json = JSON.stringify(value);
6964
7763
  formatted = json.length > 30 ? `${json.slice(0, 30)}\u2026` : json;
6965
7764
  }
6966
- return `${import_chalk.default.dim(key)}${import_chalk.default.dim("=")}${import_chalk.default.cyan(formatted)}`;
6967
- }).join(import_chalk.default.dim(", "));
7765
+ return `${import_chalk3.default.dim(key)}${import_chalk3.default.dim("=")}${import_chalk3.default.cyan(formatted)}`;
7766
+ }).join(import_chalk3.default.dim(", "));
6968
7767
  }
6969
7768
  function formatBytes(bytes) {
6970
7769
  if (bytes < 1024) {
@@ -6976,25 +7775,25 @@ function formatBytes(bytes) {
6976
7775
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6977
7776
  }
6978
7777
  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`);
7778
+ const gadgetLabel = import_chalk3.default.magenta.bold(result.gadgetName);
7779
+ const timeLabel = import_chalk3.default.dim(`${Math.round(result.executionTimeMs)}ms`);
6981
7780
  const paramsStr = formatParametersInline(result.parameters);
6982
- const paramsLabel = paramsStr ? `${import_chalk.default.dim("(")}${paramsStr}${import_chalk.default.dim(")")}` : "";
7781
+ const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
6983
7782
  if (result.error) {
6984
7783
  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}`;
7784
+ return `${import_chalk3.default.red("\u2717")} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.red("error:")} ${errorMsg} ${timeLabel}`;
6986
7785
  }
6987
7786
  let outputLabel;
6988
7787
  if (result.tokenCount !== void 0 && result.tokenCount > 0) {
6989
- outputLabel = import_chalk.default.green(`${formatTokens(result.tokenCount)} tokens`);
7788
+ outputLabel = import_chalk3.default.green(`${formatTokens(result.tokenCount)} tokens`);
6990
7789
  } else if (result.result) {
6991
7790
  const outputBytes = Buffer.byteLength(result.result, "utf-8");
6992
- outputLabel = outputBytes > 0 ? import_chalk.default.green(formatBytes(outputBytes)) : import_chalk.default.dim("no output");
7791
+ outputLabel = outputBytes > 0 ? import_chalk3.default.green(formatBytes(outputBytes)) : import_chalk3.default.dim("no output");
6993
7792
  } else {
6994
- outputLabel = import_chalk.default.dim("no output");
7793
+ outputLabel = import_chalk3.default.dim("no output");
6995
7794
  }
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}`;
7795
+ const icon = result.breaksLoop ? import_chalk3.default.yellow("\u23F9") : import_chalk3.default.green("\u2713");
7796
+ const summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${import_chalk3.default.dim("\u2192")} ${outputLabel} ${timeLabel}`;
6998
7797
  if (result.gadgetName === "TellUser" && result.parameters?.message) {
6999
7798
  const message = String(result.parameters.message);
7000
7799
  const rendered = renderMarkdownWithSeparators(message);
@@ -7058,6 +7857,66 @@ var StreamPrinter = class {
7058
7857
  function isInteractive(stream2) {
7059
7858
  return Boolean(stream2.isTTY);
7060
7859
  }
7860
+ var ESC_KEY = 27;
7861
+ var ESC_TIMEOUT_MS = 50;
7862
+ function createEscKeyListener(stdin, onEsc) {
7863
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function") {
7864
+ return null;
7865
+ }
7866
+ let escTimeout = null;
7867
+ const handleData = (data) => {
7868
+ if (data[0] === ESC_KEY) {
7869
+ if (data.length === 1) {
7870
+ escTimeout = setTimeout(() => {
7871
+ onEsc();
7872
+ }, ESC_TIMEOUT_MS);
7873
+ } else {
7874
+ if (escTimeout) {
7875
+ clearTimeout(escTimeout);
7876
+ escTimeout = null;
7877
+ }
7878
+ }
7879
+ } else {
7880
+ if (escTimeout) {
7881
+ clearTimeout(escTimeout);
7882
+ escTimeout = null;
7883
+ }
7884
+ }
7885
+ };
7886
+ stdin.setRawMode(true);
7887
+ stdin.resume();
7888
+ stdin.on("data", handleData);
7889
+ return () => {
7890
+ if (escTimeout) {
7891
+ clearTimeout(escTimeout);
7892
+ }
7893
+ stdin.removeListener("data", handleData);
7894
+ stdin.setRawMode(false);
7895
+ stdin.pause();
7896
+ };
7897
+ }
7898
+ var SIGINT_DOUBLE_PRESS_MS = 1e3;
7899
+ function createSigintListener(onCancel, onQuit, isOperationActive, stderr = process.stderr) {
7900
+ let lastSigintTime = 0;
7901
+ const handler = () => {
7902
+ const now = Date.now();
7903
+ if (isOperationActive()) {
7904
+ onCancel();
7905
+ lastSigintTime = now;
7906
+ return;
7907
+ }
7908
+ if (now - lastSigintTime < SIGINT_DOUBLE_PRESS_MS) {
7909
+ onQuit();
7910
+ return;
7911
+ }
7912
+ lastSigintTime = now;
7913
+ stderr.write(import_chalk4.default.dim("\n[Press Ctrl+C again to quit]\n"));
7914
+ };
7915
+ process.on("SIGINT", handler);
7916
+ return () => {
7917
+ process.removeListener("SIGINT", handler);
7918
+ };
7919
+ }
7061
7920
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7062
7921
  var SPINNER_DELAY_MS = 500;
7063
7922
  var StreamProgress = class {
@@ -7229,9 +8088,9 @@ var StreamProgress = class {
7229
8088
  const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
7230
8089
  const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
7231
8090
  const parts = [];
7232
- const iterPart = import_chalk2.default.cyan(`#${this.currentIteration}`);
8091
+ const iterPart = import_chalk4.default.cyan(`#${this.currentIteration}`);
7233
8092
  if (this.model) {
7234
- parts.push(`${iterPart} ${import_chalk2.default.magenta(this.model)}`);
8093
+ parts.push(`${iterPart} ${import_chalk4.default.magenta(this.model)}`);
7235
8094
  } else {
7236
8095
  parts.push(iterPart);
7237
8096
  }
@@ -7239,27 +8098,27 @@ var StreamProgress = class {
7239
8098
  if (usagePercent !== null) {
7240
8099
  const formatted = `${Math.round(usagePercent)}%`;
7241
8100
  if (usagePercent >= 80) {
7242
- parts.push(import_chalk2.default.red(formatted));
8101
+ parts.push(import_chalk4.default.red(formatted));
7243
8102
  } else if (usagePercent >= 50) {
7244
- parts.push(import_chalk2.default.yellow(formatted));
8103
+ parts.push(import_chalk4.default.yellow(formatted));
7245
8104
  } else {
7246
- parts.push(import_chalk2.default.green(formatted));
8105
+ parts.push(import_chalk4.default.green(formatted));
7247
8106
  }
7248
8107
  }
7249
8108
  if (this.callInputTokens > 0) {
7250
8109
  const prefix = this.callInputTokensEstimated ? "~" : "";
7251
- parts.push(import_chalk2.default.dim("\u2191") + import_chalk2.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
8110
+ parts.push(import_chalk4.default.dim("\u2191") + import_chalk4.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
7252
8111
  }
7253
8112
  if (this.isStreaming || outTokens > 0) {
7254
8113
  const prefix = this.callOutputTokensEstimated ? "~" : "";
7255
- parts.push(import_chalk2.default.dim("\u2193") + import_chalk2.default.green(` ${prefix}${formatTokens(outTokens)}`));
8114
+ parts.push(import_chalk4.default.dim("\u2193") + import_chalk4.default.green(` ${prefix}${formatTokens(outTokens)}`));
7256
8115
  }
7257
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
8116
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
7258
8117
  const callCost = this.calculateCurrentCallCost(outTokens);
7259
8118
  if (callCost > 0) {
7260
- parts.push(import_chalk2.default.cyan(`$${formatCost(callCost)}`));
8119
+ parts.push(import_chalk4.default.cyan(`$${formatCost(callCost)}`));
7261
8120
  }
7262
- this.target.write(`\r${parts.join(import_chalk2.default.dim(" | "))} ${import_chalk2.default.cyan(spinner)}`);
8121
+ this.target.write(`\r${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`);
7263
8122
  }
7264
8123
  /**
7265
8124
  * Calculates live cost estimate for the current streaming call.
@@ -7300,19 +8159,19 @@ var StreamProgress = class {
7300
8159
  const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
7301
8160
  const parts = [];
7302
8161
  if (this.model) {
7303
- parts.push(import_chalk2.default.cyan(this.model));
8162
+ parts.push(import_chalk4.default.cyan(this.model));
7304
8163
  }
7305
8164
  if (this.totalTokens > 0) {
7306
- parts.push(import_chalk2.default.dim("total:") + import_chalk2.default.magenta(` ${this.totalTokens}`));
8165
+ parts.push(import_chalk4.default.dim("total:") + import_chalk4.default.magenta(` ${this.totalTokens}`));
7307
8166
  }
7308
8167
  if (this.iterations > 0) {
7309
- parts.push(import_chalk2.default.dim("iter:") + import_chalk2.default.blue(` ${this.iterations}`));
8168
+ parts.push(import_chalk4.default.dim("iter:") + import_chalk4.default.blue(` ${this.iterations}`));
7310
8169
  }
7311
8170
  if (this.totalCost > 0) {
7312
- parts.push(import_chalk2.default.dim("cost:") + import_chalk2.default.cyan(` $${formatCost(this.totalCost)}`));
8171
+ parts.push(import_chalk4.default.dim("cost:") + import_chalk4.default.cyan(` $${formatCost(this.totalCost)}`));
7313
8172
  }
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)}`);
8173
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
8174
+ this.target.write(`\r${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`);
7316
8175
  }
7317
8176
  /**
7318
8177
  * Pauses the progress indicator and clears the line.
@@ -7346,6 +8205,25 @@ var StreamProgress = class {
7346
8205
  getTotalCost() {
7347
8206
  return this.totalCost;
7348
8207
  }
8208
+ /**
8209
+ * Returns a formatted stats string for cancellation messages.
8210
+ * Format: "↑ 1.2k | ↓ 300 | 5.0s"
8211
+ */
8212
+ formatStats() {
8213
+ const parts = [];
8214
+ const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
8215
+ const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
8216
+ if (this.callInputTokens > 0) {
8217
+ const prefix = this.callInputTokensEstimated ? "~" : "";
8218
+ parts.push(`\u2191 ${prefix}${formatTokens(this.callInputTokens)}`);
8219
+ }
8220
+ if (outTokens > 0) {
8221
+ const prefix = this.callOutputTokensEstimated ? "~" : "";
8222
+ parts.push(`\u2193 ${prefix}${formatTokens(outTokens)}`);
8223
+ }
8224
+ parts.push(`${elapsed}s`);
8225
+ return parts.join(" | ");
8226
+ }
7349
8227
  /**
7350
8228
  * Returns a formatted prompt string with stats (like bash PS1).
7351
8229
  * Shows current call stats during streaming, cumulative stats otherwise.
@@ -7360,28 +8238,28 @@ var StreamProgress = class {
7360
8238
  if (this.callInputTokens > 0) {
7361
8239
  const prefix = this.callInputTokensEstimated ? "~" : "";
7362
8240
  parts.push(
7363
- import_chalk2.default.dim("\u2191") + import_chalk2.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`)
8241
+ import_chalk4.default.dim("\u2191") + import_chalk4.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`)
7364
8242
  );
7365
8243
  }
7366
8244
  if (outTokens > 0) {
7367
8245
  const prefix = outEstimated ? "~" : "";
7368
- parts.push(import_chalk2.default.dim("\u2193") + import_chalk2.default.green(` ${prefix}${formatTokens(outTokens)}`));
8246
+ parts.push(import_chalk4.default.dim("\u2193") + import_chalk4.default.green(` ${prefix}${formatTokens(outTokens)}`));
7369
8247
  }
7370
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
8248
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
7371
8249
  } else {
7372
8250
  const elapsed = Math.round((Date.now() - this.totalStartTime) / 1e3);
7373
8251
  if (this.totalTokens > 0) {
7374
- parts.push(import_chalk2.default.magenta(formatTokens(this.totalTokens)));
8252
+ parts.push(import_chalk4.default.magenta(formatTokens(this.totalTokens)));
7375
8253
  }
7376
8254
  if (this.iterations > 0) {
7377
- parts.push(import_chalk2.default.blue(`i${this.iterations}`));
8255
+ parts.push(import_chalk4.default.blue(`i${this.iterations}`));
7378
8256
  }
7379
8257
  if (this.totalCost > 0) {
7380
- parts.push(import_chalk2.default.cyan(`$${formatCost(this.totalCost)}`));
8258
+ parts.push(import_chalk4.default.cyan(`$${formatCost(this.totalCost)}`));
7381
8259
  }
7382
- parts.push(import_chalk2.default.dim(`${elapsed}s`));
8260
+ parts.push(import_chalk4.default.dim(`${elapsed}s`));
7383
8261
  }
7384
- return `${parts.join(import_chalk2.default.dim(" | "))} ${import_chalk2.default.green(">")} `;
8262
+ return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.green(">")} `;
7385
8263
  }
7386
8264
  };
7387
8265
  async function readStream(stream2) {
@@ -7416,7 +8294,7 @@ async function executeAction(action, env) {
7416
8294
  await action();
7417
8295
  } catch (error) {
7418
8296
  const message = error instanceof Error ? error.message : String(error);
7419
- env.stderr.write(`${import_chalk2.default.red.bold("Error:")} ${message}
8297
+ env.stderr.write(`${import_chalk4.default.red.bold("Error:")} ${message}
7420
8298
  `);
7421
8299
  env.setExitCode(1);
7422
8300
  }
@@ -7441,7 +8319,7 @@ function addAgentOptions(cmd, defaults) {
7441
8319
  ...previous,
7442
8320
  value
7443
8321
  ];
7444
- const defaultGadgets = defaults?.gadget ?? [];
8322
+ const defaultGadgets = defaults?.gadgets ?? defaults?.gadget ?? [];
7445
8323
  return cmd.option(OPTION_FLAGS.model, OPTION_DESCRIPTIONS.model, defaults?.model ?? DEFAULT_MODEL).option(OPTION_FLAGS.systemPrompt, OPTION_DESCRIPTIONS.systemPrompt, defaults?.system).option(
7446
8324
  OPTION_FLAGS.temperature,
7447
8325
  OPTION_DESCRIPTIONS.temperature,
@@ -7477,7 +8355,8 @@ function configToAgentOptions(config) {
7477
8355
  if (config.system !== void 0) result.system = config.system;
7478
8356
  if (config.temperature !== void 0) result.temperature = config.temperature;
7479
8357
  if (config["max-iterations"] !== void 0) result.maxIterations = config["max-iterations"];
7480
- if (config.gadget !== void 0) result.gadget = config.gadget;
8358
+ const gadgets = config.gadgets ?? config.gadget;
8359
+ if (gadgets !== void 0) result.gadget = gadgets;
7481
8360
  if (config.builtins !== void 0) result.builtins = config.builtins;
7482
8361
  if (config["builtin-interaction"] !== void 0)
7483
8362
  result.builtinInteraction = config["builtin-interaction"];
@@ -7487,6 +8366,8 @@ function configToAgentOptions(config) {
7487
8366
  result.gadgetEndPrefix = config["gadget-end-prefix"];
7488
8367
  if (config["gadget-arg-prefix"] !== void 0)
7489
8368
  result.gadgetArgPrefix = config["gadget-arg-prefix"];
8369
+ if (config["gadget-approval"] !== void 0)
8370
+ result.gadgetApproval = config["gadget-approval"];
7490
8371
  if (config.quiet !== void 0) result.quiet = config.quiet;
7491
8372
  if (config["log-llm-requests"] !== void 0) result.logLlmRequests = config["log-llm-requests"];
7492
8373
  if (config["log-llm-responses"] !== void 0) result.logLlmResponses = config["log-llm-responses"];
@@ -7494,23 +8375,18 @@ function configToAgentOptions(config) {
7494
8375
  }
7495
8376
 
7496
8377
  // 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) {
8378
+ function createHumanInputHandler(env, progress, keyboard) {
7507
8379
  const stdout = env.stdout;
7508
8380
  if (!isInteractive(env.stdin) || typeof stdout.isTTY !== "boolean" || !stdout.isTTY) {
7509
8381
  return void 0;
7510
8382
  }
7511
8383
  return async (question) => {
7512
8384
  progress.pause();
7513
- const rl = (0, import_promises2.createInterface)({ input: env.stdin, output: env.stdout });
8385
+ if (keyboard.cleanupEsc) {
8386
+ keyboard.cleanupEsc();
8387
+ keyboard.cleanupEsc = null;
8388
+ }
8389
+ const rl = (0, import_promises3.createInterface)({ input: env.stdin, output: env.stdout });
7514
8390
  try {
7515
8391
  const questionLine = question.trim() ? `
7516
8392
  ${renderMarkdownWithSeparators(question.trim())}` : "";
@@ -7528,6 +8404,7 @@ ${statsPrompt}` : statsPrompt;
7528
8404
  }
7529
8405
  } finally {
7530
8406
  rl.close();
8407
+ keyboard.restore();
7531
8408
  }
7532
8409
  };
7533
8410
  }
@@ -7554,6 +8431,77 @@ async function executeAgent(promptArg, options, env) {
7554
8431
  const printer = new StreamPrinter(env.stdout);
7555
8432
  const stderrTTY = env.stderr.isTTY === true;
7556
8433
  const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
8434
+ const abortController = new AbortController();
8435
+ let wasCancelled = false;
8436
+ let isStreaming = false;
8437
+ const stdinStream = env.stdin;
8438
+ const handleCancel = () => {
8439
+ if (!abortController.signal.aborted) {
8440
+ wasCancelled = true;
8441
+ abortController.abort();
8442
+ progress.pause();
8443
+ env.stderr.write(import_chalk5.default.yellow(`
8444
+ [Cancelled] ${progress.formatStats()}
8445
+ `));
8446
+ }
8447
+ };
8448
+ const keyboard = {
8449
+ cleanupEsc: null,
8450
+ cleanupSigint: null,
8451
+ restore: () => {
8452
+ if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
8453
+ keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel);
8454
+ }
8455
+ }
8456
+ };
8457
+ const handleQuit = () => {
8458
+ keyboard.cleanupEsc?.();
8459
+ keyboard.cleanupSigint?.();
8460
+ progress.complete();
8461
+ printer.ensureNewline();
8462
+ const summary = renderOverallSummary({
8463
+ totalTokens: usage?.totalTokens,
8464
+ iterations,
8465
+ elapsedSeconds: progress.getTotalElapsedSeconds(),
8466
+ cost: progress.getTotalCost()
8467
+ });
8468
+ if (summary) {
8469
+ env.stderr.write(`${import_chalk5.default.dim("\u2500".repeat(40))}
8470
+ `);
8471
+ env.stderr.write(`${summary}
8472
+ `);
8473
+ }
8474
+ env.stderr.write(import_chalk5.default.dim("[Quit]\n"));
8475
+ process.exit(130);
8476
+ };
8477
+ if (stdinIsInteractive && stdinStream.isTTY) {
8478
+ keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel);
8479
+ }
8480
+ keyboard.cleanupSigint = createSigintListener(
8481
+ handleCancel,
8482
+ handleQuit,
8483
+ () => isStreaming && !abortController.signal.aborted,
8484
+ env.stderr
8485
+ );
8486
+ const DEFAULT_APPROVAL_REQUIRED = ["RunCommand", "WriteFile", "EditFile"];
8487
+ const userApprovals = options.gadgetApproval ?? {};
8488
+ const gadgetApprovals = {
8489
+ ...userApprovals
8490
+ };
8491
+ for (const gadget of DEFAULT_APPROVAL_REQUIRED) {
8492
+ const normalizedGadget = gadget.toLowerCase();
8493
+ const isConfigured = Object.keys(userApprovals).some(
8494
+ (key) => key.toLowerCase() === normalizedGadget
8495
+ );
8496
+ if (!isConfigured) {
8497
+ gadgetApprovals[gadget] = "approval-required";
8498
+ }
8499
+ }
8500
+ const approvalConfig = {
8501
+ gadgetApprovals,
8502
+ defaultMode: "allowed"
8503
+ };
8504
+ const approvalManager = new ApprovalManager(approvalConfig, env, progress);
7557
8505
  let usage;
7558
8506
  let iterations = 0;
7559
8507
  const llmRequestsDir = resolveLogDir(options.logLlmRequests, "requests");
@@ -7581,6 +8529,7 @@ async function executeAgent(promptArg, options, env) {
7581
8529
  // onLLMCallStart: Start progress indicator for each LLM call
7582
8530
  // This showcases how to react to agent lifecycle events
7583
8531
  onLLMCallStart: async (context) => {
8532
+ isStreaming = true;
7584
8533
  llmCallCounter++;
7585
8534
  const inputTokens = await countMessagesTokens(
7586
8535
  context.options.model,
@@ -7614,6 +8563,7 @@ async function executeAgent(promptArg, options, env) {
7614
8563
  // onLLMCallComplete: Finalize metrics after each LLM call
7615
8564
  // This is where you'd typically log metrics or update dashboards
7616
8565
  onLLMCallComplete: async (context) => {
8566
+ isStreaming = false;
7617
8567
  usage = context.usage;
7618
8568
  iterations = Math.max(iterations, context.iteration + 1);
7619
8569
  if (context.usage) {
@@ -7661,47 +8611,53 @@ async function executeAgent(promptArg, options, env) {
7661
8611
  }
7662
8612
  }
7663
8613
  },
7664
- // SHOWCASE: Controller-based approval gating for dangerous gadgets
8614
+ // SHOWCASE: Controller-based approval gating for gadgets
7665
8615
  //
7666
8616
  // 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.
8617
+ // The ApprovalManager handles approval flows externally via beforeGadgetExecution.
8618
+ // Approval modes are configurable via cli.toml:
8619
+ // - "allowed": auto-proceed
8620
+ // - "denied": auto-reject, return message to LLM
8621
+ // - "approval-required": prompt user interactively
7669
8622
  //
7670
- // This pattern is composable: you can apply the same gating logic to
7671
- // any gadget (DeleteFile, SendEmail, etc.) without changing the gadgets.
8623
+ // Default: RunCommand, WriteFile, EditFile require approval unless overridden.
7672
8624
  controllers: {
7673
8625
  beforeGadgetExecution: async (ctx) => {
7674
- if (ctx.gadgetName !== "RunCommand") {
8626
+ const mode = approvalManager.getApprovalMode(ctx.gadgetName);
8627
+ if (mode === "allowed") {
7675
8628
  return { action: "proceed" };
7676
8629
  }
7677
8630
  const stdinTTY = isInteractive(env.stdin);
7678
8631
  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")}
8632
+ const canPrompt = stdinTTY && stderrTTY2;
8633
+ if (!canPrompt) {
8634
+ if (mode === "approval-required") {
8635
+ return {
8636
+ action: "skip",
8637
+ syntheticResult: `status=denied
7694
8638
 
7695
- `);
8639
+ ${ctx.gadgetName} requires interactive approval. Run in a terminal to approve.`
8640
+ };
8641
+ }
8642
+ if (mode === "denied") {
8643
+ return {
8644
+ action: "skip",
8645
+ syntheticResult: `status=denied
8646
+
8647
+ ${ctx.gadgetName} is denied by configuration.`
8648
+ };
8649
+ }
8650
+ return { action: "proceed" };
8651
+ }
8652
+ const result = await approvalManager.requestApproval(ctx.gadgetName, ctx.parameters);
8653
+ if (!result.approved) {
7696
8654
  return {
7697
8655
  action: "skip",
7698
8656
  syntheticResult: `status=denied
7699
8657
 
7700
- Command rejected by user with message: "${response}"`
8658
+ Denied: ${result.reason ?? "by user"}`
7701
8659
  };
7702
8660
  }
7703
- env.stderr.write(` ${import_chalk3.default.green("\u2713 Approved")}
7704
- `);
7705
8661
  return { action: "proceed" };
7706
8662
  }
7707
8663
  }
@@ -7715,10 +8671,11 @@ Command rejected by user with message: "${response}"`
7715
8671
  if (options.temperature !== void 0) {
7716
8672
  builder.withTemperature(options.temperature);
7717
8673
  }
7718
- const humanInputHandler = createHumanInputHandler(env, progress);
8674
+ const humanInputHandler = createHumanInputHandler(env, progress, keyboard);
7719
8675
  if (humanInputHandler) {
7720
8676
  builder.onHumanInput(humanInputHandler);
7721
8677
  }
8678
+ builder.withSignal(abortController.signal);
7722
8679
  const gadgets = registry.getAll();
7723
8680
  if (gadgets.length > 0) {
7724
8681
  builder.withGadgets(...gadgets);
@@ -7756,31 +8713,41 @@ Command rejected by user with message: "${response}"`
7756
8713
  textBuffer = "";
7757
8714
  }
7758
8715
  };
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}
8716
+ try {
8717
+ for await (const event of agent.run()) {
8718
+ if (event.type === "text") {
8719
+ progress.pause();
8720
+ textBuffer += event.content;
8721
+ } else if (event.type === "gadget_result") {
8722
+ flushTextBuffer();
8723
+ progress.pause();
8724
+ if (options.quiet) {
8725
+ if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
8726
+ const message = String(event.result.parameters.message);
8727
+ env.stdout.write(`${message}
7770
8728
  `);
7771
- }
7772
- } else {
7773
- const tokenCount = await countGadgetOutputTokens(event.result.result);
7774
- env.stderr.write(`${formatGadgetSummary({ ...event.result, tokenCount })}
8729
+ }
8730
+ } else {
8731
+ const tokenCount = await countGadgetOutputTokens(event.result.result);
8732
+ env.stderr.write(`${formatGadgetSummary({ ...event.result, tokenCount })}
7775
8733
  `);
8734
+ }
7776
8735
  }
7777
8736
  }
8737
+ } catch (error) {
8738
+ if (!isAbortError(error)) {
8739
+ throw error;
8740
+ }
8741
+ } finally {
8742
+ isStreaming = false;
8743
+ keyboard.cleanupEsc?.();
8744
+ keyboard.cleanupSigint?.();
7778
8745
  }
7779
8746
  flushTextBuffer();
7780
8747
  progress.complete();
7781
8748
  printer.ensureNewline();
7782
8749
  if (!options.quiet && iterations > 1) {
7783
- env.stderr.write(`${import_chalk3.default.dim("\u2500".repeat(40))}
8750
+ env.stderr.write(`${import_chalk5.default.dim("\u2500".repeat(40))}
7784
8751
  `);
7785
8752
  const summary = renderOverallSummary({
7786
8753
  totalTokens: usage?.totalTokens,
@@ -7798,7 +8765,13 @@ function registerAgentCommand(program, env, config) {
7798
8765
  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
8766
  addAgentOptions(cmd, config);
7800
8767
  cmd.action(
7801
- (prompt, options) => executeAction(() => executeAgent(prompt, options, env), env)
8768
+ (prompt, options) => executeAction(() => {
8769
+ const mergedOptions = {
8770
+ ...options,
8771
+ gadgetApproval: config?.["gadget-approval"]
8772
+ };
8773
+ return executeAgent(prompt, mergedOptions, env);
8774
+ }, env)
7802
8775
  );
7803
8776
  }
7804
8777
 
@@ -7882,9 +8855,9 @@ function registerCompleteCommand(program, env, config) {
7882
8855
  }
7883
8856
 
7884
8857
  // src/cli/config.ts
7885
- var import_node_fs3 = require("fs");
8858
+ var import_node_fs8 = require("fs");
7886
8859
  var import_node_os2 = require("os");
7887
- var import_node_path4 = require("path");
8860
+ var import_node_path8 = require("path");
7888
8861
  var import_js_toml = require("js-toml");
7889
8862
 
7890
8863
  // src/cli/templates.ts
@@ -7969,6 +8942,7 @@ function hasTemplateSyntax(str) {
7969
8942
  }
7970
8943
 
7971
8944
  // src/cli/config.ts
8945
+ var VALID_APPROVAL_MODES = ["allowed", "denied", "approval-required"];
7972
8946
  var GLOBAL_CONFIG_KEYS = /* @__PURE__ */ new Set(["log-level", "log-file", "log-reset"]);
7973
8947
  var VALID_LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
7974
8948
  var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
@@ -7991,12 +8965,20 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
7991
8965
  "system",
7992
8966
  "temperature",
7993
8967
  "max-iterations",
8968
+ "gadgets",
8969
+ // Full replacement (preferred)
8970
+ "gadget-add",
8971
+ // Add to inherited gadgets
8972
+ "gadget-remove",
8973
+ // Remove from inherited gadgets
7994
8974
  "gadget",
8975
+ // DEPRECATED: alias for gadgets
7995
8976
  "builtins",
7996
8977
  "builtin-interaction",
7997
8978
  "gadget-start-prefix",
7998
8979
  "gadget-end-prefix",
7999
8980
  "gadget-arg-prefix",
8981
+ "gadget-approval",
8000
8982
  "quiet",
8001
8983
  "inherits",
8002
8984
  "log-level",
@@ -8014,12 +8996,12 @@ var CUSTOM_CONFIG_KEYS = /* @__PURE__ */ new Set([
8014
8996
  "description"
8015
8997
  ]);
8016
8998
  function getConfigPath() {
8017
- return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".llmist", "cli.toml");
8999
+ return (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".llmist", "cli.toml");
8018
9000
  }
8019
9001
  var ConfigError = class extends Error {
8020
- constructor(message, path2) {
8021
- super(path2 ? `${path2}: ${message}` : message);
8022
- this.path = path2;
9002
+ constructor(message, path5) {
9003
+ super(path5 ? `${path5}: ${message}` : message);
9004
+ this.path = path5;
8023
9005
  this.name = "ConfigError";
8024
9006
  }
8025
9007
  };
@@ -8075,6 +9057,28 @@ function validateInherits(value, section) {
8075
9057
  }
8076
9058
  throw new ConfigError(`[${section}].inherits must be a string or array of strings`);
8077
9059
  }
9060
+ function validateGadgetApproval(value, section) {
9061
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
9062
+ throw new ConfigError(
9063
+ `[${section}].gadget-approval must be a table (e.g., { WriteFile = "approval-required" })`
9064
+ );
9065
+ }
9066
+ const result = {};
9067
+ for (const [gadgetName, mode] of Object.entries(value)) {
9068
+ if (typeof mode !== "string") {
9069
+ throw new ConfigError(
9070
+ `[${section}].gadget-approval.${gadgetName} must be a string`
9071
+ );
9072
+ }
9073
+ if (!VALID_APPROVAL_MODES.includes(mode)) {
9074
+ throw new ConfigError(
9075
+ `[${section}].gadget-approval.${gadgetName} must be one of: ${VALID_APPROVAL_MODES.join(", ")}`
9076
+ );
9077
+ }
9078
+ result[gadgetName] = mode;
9079
+ }
9080
+ return result;
9081
+ }
8078
9082
  function validateLoggingConfig(raw, section) {
8079
9083
  const result = {};
8080
9084
  if ("log-level" in raw) {
@@ -8184,6 +9188,15 @@ function validateAgentConfig(raw, section) {
8184
9188
  min: 1
8185
9189
  });
8186
9190
  }
9191
+ if ("gadgets" in rawObj) {
9192
+ result.gadgets = validateStringArray(rawObj.gadgets, "gadgets", section);
9193
+ }
9194
+ if ("gadget-add" in rawObj) {
9195
+ result["gadget-add"] = validateStringArray(rawObj["gadget-add"], "gadget-add", section);
9196
+ }
9197
+ if ("gadget-remove" in rawObj) {
9198
+ result["gadget-remove"] = validateStringArray(rawObj["gadget-remove"], "gadget-remove", section);
9199
+ }
8187
9200
  if ("gadget" in rawObj) {
8188
9201
  result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
8189
9202
  }
@@ -8218,6 +9231,9 @@ function validateAgentConfig(raw, section) {
8218
9231
  section
8219
9232
  );
8220
9233
  }
9234
+ if ("gadget-approval" in rawObj) {
9235
+ result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
9236
+ }
8221
9237
  if ("quiet" in rawObj) {
8222
9238
  result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
8223
9239
  }
@@ -8274,6 +9290,15 @@ function validateCustomConfig(raw, section) {
8274
9290
  min: 1
8275
9291
  });
8276
9292
  }
9293
+ if ("gadgets" in rawObj) {
9294
+ result.gadgets = validateStringArray(rawObj.gadgets, "gadgets", section);
9295
+ }
9296
+ if ("gadget-add" in rawObj) {
9297
+ result["gadget-add"] = validateStringArray(rawObj["gadget-add"], "gadget-add", section);
9298
+ }
9299
+ if ("gadget-remove" in rawObj) {
9300
+ result["gadget-remove"] = validateStringArray(rawObj["gadget-remove"], "gadget-remove", section);
9301
+ }
8277
9302
  if ("gadget" in rawObj) {
8278
9303
  result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
8279
9304
  }
@@ -8308,6 +9333,9 @@ function validateCustomConfig(raw, section) {
8308
9333
  section
8309
9334
  );
8310
9335
  }
9336
+ if ("gadget-approval" in rawObj) {
9337
+ result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
9338
+ }
8311
9339
  if ("max-tokens" in rawObj) {
8312
9340
  result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
8313
9341
  integer: true,
@@ -8363,12 +9391,12 @@ function validateConfig(raw, configPath) {
8363
9391
  }
8364
9392
  function loadConfig() {
8365
9393
  const configPath = getConfigPath();
8366
- if (!(0, import_node_fs3.existsSync)(configPath)) {
9394
+ if (!(0, import_node_fs8.existsSync)(configPath)) {
8367
9395
  return {};
8368
9396
  }
8369
9397
  let content;
8370
9398
  try {
8371
- content = (0, import_node_fs3.readFileSync)(configPath, "utf-8");
9399
+ content = (0, import_node_fs8.readFileSync)(configPath, "utf-8");
8372
9400
  } catch (error) {
8373
9401
  throw new ConfigError(
8374
9402
  `Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -8463,6 +9491,39 @@ function resolveTemplatesInConfig(config, configPath) {
8463
9491
  }
8464
9492
  return result;
8465
9493
  }
9494
+ function resolveGadgets(section, inheritedGadgets, sectionName, configPath) {
9495
+ const hasGadgets = "gadgets" in section;
9496
+ const hasGadgetLegacy = "gadget" in section;
9497
+ const hasGadgetAdd = "gadget-add" in section;
9498
+ const hasGadgetRemove = "gadget-remove" in section;
9499
+ if (hasGadgetLegacy && !hasGadgets) {
9500
+ console.warn(
9501
+ `[config] Warning: [${sectionName}].gadget is deprecated, use 'gadgets' (plural) instead`
9502
+ );
9503
+ }
9504
+ if ((hasGadgets || hasGadgetLegacy) && (hasGadgetAdd || hasGadgetRemove)) {
9505
+ throw new ConfigError(
9506
+ `[${sectionName}] Cannot use 'gadgets' with 'gadget-add'/'gadget-remove'. Use either full replacement (gadgets) OR modification (gadget-add/gadget-remove).`,
9507
+ configPath
9508
+ );
9509
+ }
9510
+ if (hasGadgets) {
9511
+ return section.gadgets;
9512
+ }
9513
+ if (hasGadgetLegacy) {
9514
+ return section.gadget;
9515
+ }
9516
+ let result = [...inheritedGadgets];
9517
+ if (hasGadgetRemove) {
9518
+ const toRemove = new Set(section["gadget-remove"]);
9519
+ result = result.filter((g) => !toRemove.has(g));
9520
+ }
9521
+ if (hasGadgetAdd) {
9522
+ const toAdd = section["gadget-add"];
9523
+ result.push(...toAdd);
9524
+ }
9525
+ return result;
9526
+ }
8466
9527
  function resolveInheritance(config, configPath) {
8467
9528
  const resolved = {};
8468
9529
  const resolving = /* @__PURE__ */ new Set();
@@ -8486,8 +9547,23 @@ function resolveInheritance(config, configPath) {
8486
9547
  const parentResolved = resolveSection(parent);
8487
9548
  merged = { ...merged, ...parentResolved };
8488
9549
  }
8489
- const { inherits: _inherits, ...ownValues } = sectionObj;
9550
+ const inheritedGadgets = merged.gadgets ?? [];
9551
+ const {
9552
+ inherits: _inherits,
9553
+ gadgets: _gadgets,
9554
+ gadget: _gadget,
9555
+ "gadget-add": _gadgetAdd,
9556
+ "gadget-remove": _gadgetRemove,
9557
+ ...ownValues
9558
+ } = sectionObj;
8490
9559
  merged = { ...merged, ...ownValues };
9560
+ const resolvedGadgets = resolveGadgets(sectionObj, inheritedGadgets, name, configPath);
9561
+ if (resolvedGadgets.length > 0) {
9562
+ merged.gadgets = resolvedGadgets;
9563
+ }
9564
+ delete merged["gadget"];
9565
+ delete merged["gadget-add"];
9566
+ delete merged["gadget-remove"];
8491
9567
  resolving.delete(name);
8492
9568
  resolved[name] = merged;
8493
9569
  return merged;
@@ -8499,13 +9575,13 @@ function resolveInheritance(config, configPath) {
8499
9575
  }
8500
9576
 
8501
9577
  // src/cli/gadget-command.ts
8502
- var import_chalk5 = __toESM(require("chalk"), 1);
9578
+ var import_chalk7 = __toESM(require("chalk"), 1);
8503
9579
  init_schema_to_json();
8504
9580
  init_schema_validator();
8505
9581
 
8506
9582
  // src/cli/gadget-prompts.ts
8507
- var import_promises3 = require("readline/promises");
8508
- var import_chalk4 = __toESM(require("chalk"), 1);
9583
+ var import_promises4 = require("readline/promises");
9584
+ var import_chalk6 = __toESM(require("chalk"), 1);
8509
9585
  init_schema_to_json();
8510
9586
  async function promptForParameters(schema, ctx) {
8511
9587
  if (!schema) {
@@ -8515,7 +9591,7 @@ async function promptForParameters(schema, ctx) {
8515
9591
  if (!jsonSchema.properties || Object.keys(jsonSchema.properties).length === 0) {
8516
9592
  return {};
8517
9593
  }
8518
- const rl = (0, import_promises3.createInterface)({ input: ctx.stdin, output: ctx.stdout });
9594
+ const rl = (0, import_promises4.createInterface)({ input: ctx.stdin, output: ctx.stdout });
8519
9595
  const params = {};
8520
9596
  try {
8521
9597
  for (const [key, prop] of Object.entries(jsonSchema.properties)) {
@@ -8538,16 +9614,16 @@ ${issues}`);
8538
9614
  async function promptForField(rl, key, prop, required) {
8539
9615
  const isRequired = required.includes(key);
8540
9616
  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("*") : "";
9617
+ const defaultHint = prop.default !== void 0 ? import_chalk6.default.dim(` [default: ${JSON.stringify(prop.default)}]`) : "";
9618
+ const requiredMarker = isRequired ? import_chalk6.default.red("*") : "";
8543
9619
  let prompt = `
8544
- ${import_chalk4.default.cyan.bold(key)}${requiredMarker}`;
9620
+ ${import_chalk6.default.cyan.bold(key)}${requiredMarker}`;
8545
9621
  if (prop.description) {
8546
- prompt += import_chalk4.default.dim(` - ${prop.description}`);
9622
+ prompt += import_chalk6.default.dim(` - ${prop.description}`);
8547
9623
  }
8548
9624
  prompt += `
8549
9625
  ${typeHint}${defaultHint}
8550
- ${import_chalk4.default.green(">")} `;
9626
+ ${import_chalk6.default.green(">")} `;
8551
9627
  const answer = await rl.question(prompt);
8552
9628
  const trimmed = answer.trim();
8553
9629
  if (!trimmed) {
@@ -8563,20 +9639,20 @@ ${import_chalk4.default.cyan.bold(key)}${requiredMarker}`;
8563
9639
  }
8564
9640
  function formatTypeHint(prop) {
8565
9641
  if (prop.enum) {
8566
- return import_chalk4.default.yellow(`(${prop.enum.join(" | ")})`);
9642
+ return import_chalk6.default.yellow(`(${prop.enum.join(" | ")})`);
8567
9643
  }
8568
9644
  if (prop.type === "array") {
8569
9645
  const items = prop.items;
8570
9646
  if (items?.enum) {
8571
- return import_chalk4.default.yellow(`(${items.enum.join(" | ")})[] comma-separated`);
9647
+ return import_chalk6.default.yellow(`(${items.enum.join(" | ")})[] comma-separated`);
8572
9648
  }
8573
9649
  const itemType = items?.type ?? "any";
8574
- return import_chalk4.default.yellow(`(${itemType}[]) comma-separated`);
9650
+ return import_chalk6.default.yellow(`(${itemType}[]) comma-separated`);
8575
9651
  }
8576
9652
  if (prop.type === "object" && prop.properties) {
8577
- return import_chalk4.default.yellow("(object) enter as JSON");
9653
+ return import_chalk6.default.yellow("(object) enter as JSON");
8578
9654
  }
8579
- return import_chalk4.default.yellow(`(${prop.type ?? "any"})`);
9655
+ return import_chalk6.default.yellow(`(${prop.type ?? "any"})`);
8580
9656
  }
8581
9657
  function parseValue(input, prop, key) {
8582
9658
  const type = prop.type;
@@ -8687,7 +9763,7 @@ Available gadgets:
8687
9763
  async function executeGadgetRun(file, options, env) {
8688
9764
  const cwd = process.cwd();
8689
9765
  const { gadget, name } = await selectGadget(file, options.name, cwd);
8690
- env.stderr.write(import_chalk5.default.cyan.bold(`
9766
+ env.stderr.write(import_chalk7.default.cyan.bold(`
8691
9767
  \u{1F527} Running gadget: ${name}
8692
9768
  `));
8693
9769
  let params;
@@ -8698,7 +9774,7 @@ async function executeGadgetRun(file, options, env) {
8698
9774
  // Prompts go to stderr to keep stdout clean
8699
9775
  });
8700
9776
  } else {
8701
- env.stderr.write(import_chalk5.default.dim("Reading parameters from stdin...\n"));
9777
+ env.stderr.write(import_chalk7.default.dim("Reading parameters from stdin...\n"));
8702
9778
  const stdinParams = await readStdinJson(env.stdin);
8703
9779
  if (gadget.parameterSchema) {
8704
9780
  const result2 = gadget.parameterSchema.safeParse(stdinParams);
@@ -8712,7 +9788,7 @@ ${issues}`);
8712
9788
  params = stdinParams;
8713
9789
  }
8714
9790
  }
8715
- env.stderr.write(import_chalk5.default.dim("\nExecuting...\n"));
9791
+ env.stderr.write(import_chalk7.default.dim("\nExecuting...\n"));
8716
9792
  const startTime = Date.now();
8717
9793
  let result;
8718
9794
  try {
@@ -8734,7 +9810,7 @@ ${issues}`);
8734
9810
  throw new Error(`Execution failed: ${message}`);
8735
9811
  }
8736
9812
  const elapsed = Date.now() - startTime;
8737
- env.stderr.write(import_chalk5.default.green(`
9813
+ env.stderr.write(import_chalk7.default.green(`
8738
9814
  \u2713 Completed in ${elapsed}ms
8739
9815
 
8740
9816
  `));
@@ -8770,37 +9846,37 @@ async function executeGadgetInfo(file, options, env) {
8770
9846
  return;
8771
9847
  }
8772
9848
  env.stdout.write("\n");
8773
- env.stdout.write(import_chalk5.default.cyan.bold(`${name}
9849
+ env.stdout.write(import_chalk7.default.cyan.bold(`${name}
8774
9850
  `));
8775
- env.stdout.write(import_chalk5.default.cyan("\u2550".repeat(name.length)) + "\n\n");
8776
- env.stdout.write(import_chalk5.default.bold("Description:\n"));
9851
+ env.stdout.write(import_chalk7.default.cyan("\u2550".repeat(name.length)) + "\n\n");
9852
+ env.stdout.write(import_chalk7.default.bold("Description:\n"));
8777
9853
  env.stdout.write(` ${gadget.description}
8778
9854
 
8779
9855
  `);
8780
9856
  if (gadget.parameterSchema) {
8781
- env.stdout.write(import_chalk5.default.bold("Parameters:\n"));
9857
+ env.stdout.write(import_chalk7.default.bold("Parameters:\n"));
8782
9858
  const jsonSchema = schemaToJSONSchema(gadget.parameterSchema, { target: "draft-7" });
8783
9859
  env.stdout.write(formatSchemaAsText(jsonSchema, " ") + "\n\n");
8784
9860
  } else {
8785
- env.stdout.write(import_chalk5.default.dim("No parameters required.\n\n"));
9861
+ env.stdout.write(import_chalk7.default.dim("No parameters required.\n\n"));
8786
9862
  }
8787
9863
  if (gadget.timeoutMs) {
8788
- env.stdout.write(import_chalk5.default.bold("Timeout:\n"));
9864
+ env.stdout.write(import_chalk7.default.bold("Timeout:\n"));
8789
9865
  env.stdout.write(` ${gadget.timeoutMs}ms
8790
9866
 
8791
9867
  `);
8792
9868
  }
8793
9869
  if (gadget.examples && gadget.examples.length > 0) {
8794
- env.stdout.write(import_chalk5.default.bold("Examples:\n"));
9870
+ env.stdout.write(import_chalk7.default.bold("Examples:\n"));
8795
9871
  for (const example of gadget.examples) {
8796
9872
  if (example.comment) {
8797
- env.stdout.write(import_chalk5.default.dim(` # ${example.comment}
9873
+ env.stdout.write(import_chalk7.default.dim(` # ${example.comment}
8798
9874
  `));
8799
9875
  }
8800
- env.stdout.write(` Input: ${import_chalk5.default.cyan(JSON.stringify(example.params))}
9876
+ env.stdout.write(` Input: ${import_chalk7.default.cyan(JSON.stringify(example.params))}
8801
9877
  `);
8802
9878
  if (example.output !== void 0) {
8803
- env.stdout.write(` Output: ${import_chalk5.default.green(example.output)}
9879
+ env.stdout.write(` Output: ${import_chalk7.default.green(example.output)}
8804
9880
  `);
8805
9881
  }
8806
9882
  env.stdout.write("\n");
@@ -8833,27 +9909,27 @@ function formatSchemaAsText(schema, indent = "") {
8833
9909
  const isRequired = required.includes(key);
8834
9910
  const enumValues = prop.enum;
8835
9911
  const defaultValue = prop.default;
8836
- let line = `${indent}${import_chalk5.default.cyan(key)}`;
9912
+ let line = `${indent}${import_chalk7.default.cyan(key)}`;
8837
9913
  if (isRequired) {
8838
- line += import_chalk5.default.red("*");
9914
+ line += import_chalk7.default.red("*");
8839
9915
  }
8840
9916
  if (type === "array") {
8841
9917
  const items = prop.items;
8842
9918
  const itemType = items?.type || "any";
8843
- line += import_chalk5.default.dim(` (${itemType}[])`);
9919
+ line += import_chalk7.default.dim(` (${itemType}[])`);
8844
9920
  } else if (type === "object" && prop.properties) {
8845
- line += import_chalk5.default.dim(" (object)");
9921
+ line += import_chalk7.default.dim(" (object)");
8846
9922
  } else {
8847
- line += import_chalk5.default.dim(` (${type})`);
9923
+ line += import_chalk7.default.dim(` (${type})`);
8848
9924
  }
8849
9925
  if (defaultValue !== void 0) {
8850
- line += import_chalk5.default.dim(` [default: ${JSON.stringify(defaultValue)}]`);
9926
+ line += import_chalk7.default.dim(` [default: ${JSON.stringify(defaultValue)}]`);
8851
9927
  }
8852
9928
  if (description) {
8853
9929
  line += `: ${description}`;
8854
9930
  }
8855
9931
  if (enumValues) {
8856
- line += import_chalk5.default.yellow(` - one of: ${enumValues.join(", ")}`);
9932
+ line += import_chalk7.default.yellow(` - one of: ${enumValues.join(", ")}`);
8857
9933
  }
8858
9934
  lines.push(line);
8859
9935
  if (type === "object" && prop.properties) {
@@ -8893,20 +9969,20 @@ async function executeGadgetValidate(file, env) {
8893
9969
  throw new Error(`Validation issues:
8894
9970
  ${issues.map((i) => ` - ${i}`).join("\n")}`);
8895
9971
  }
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"));
9972
+ env.stdout.write(import_chalk7.default.green.bold("\n\u2713 Valid\n\n"));
9973
+ env.stdout.write(import_chalk7.default.bold("Gadgets found:\n"));
8898
9974
  for (const gadget of gadgets) {
8899
9975
  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}
9976
+ const schemaInfo = gadget.parameterSchema ? import_chalk7.default.cyan("(with schema)") : import_chalk7.default.dim("(no schema)");
9977
+ env.stdout.write(` ${import_chalk7.default.bold(name)} ${schemaInfo}
8902
9978
  `);
8903
- env.stdout.write(import_chalk5.default.dim(` ${gadget.description}
9979
+ env.stdout.write(import_chalk7.default.dim(` ${gadget.description}
8904
9980
  `));
8905
9981
  }
8906
9982
  env.stdout.write("\n");
8907
9983
  } catch (error) {
8908
9984
  const message = error instanceof Error ? error.message : String(error);
8909
- env.stdout.write(import_chalk5.default.red.bold(`
9985
+ env.stdout.write(import_chalk7.default.red.bold(`
8910
9986
  \u2717 Invalid
8911
9987
 
8912
9988
  `));
@@ -8930,7 +10006,7 @@ function registerGadgetCommand(program, env) {
8930
10006
  }
8931
10007
 
8932
10008
  // src/cli/models-command.ts
8933
- var import_chalk6 = __toESM(require("chalk"), 1);
10009
+ var import_chalk8 = __toESM(require("chalk"), 1);
8934
10010
  init_model_shortcuts();
8935
10011
  async function handleModelsCommand(options, env) {
8936
10012
  const client = env.createClient();
@@ -8950,13 +10026,13 @@ function renderTable(models, verbose, stream2) {
8950
10026
  }
8951
10027
  grouped.get(provider).push(model);
8952
10028
  }
8953
- stream2.write(import_chalk6.default.bold.cyan("\nAvailable Models\n"));
8954
- stream2.write(import_chalk6.default.cyan("=".repeat(80)) + "\n\n");
10029
+ stream2.write(import_chalk8.default.bold.cyan("\nAvailable Models\n"));
10030
+ stream2.write(import_chalk8.default.cyan("=".repeat(80)) + "\n\n");
8955
10031
  const providers = Array.from(grouped.keys()).sort();
8956
10032
  for (const provider of providers) {
8957
10033
  const providerModels = grouped.get(provider);
8958
10034
  const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
8959
- stream2.write(import_chalk6.default.bold.yellow(`${providerName} Models
10035
+ stream2.write(import_chalk8.default.bold.yellow(`${providerName} Models
8960
10036
  `));
8961
10037
  if (verbose) {
8962
10038
  renderVerboseTable(providerModels, stream2);
@@ -8965,11 +10041,11 @@ function renderTable(models, verbose, stream2) {
8965
10041
  }
8966
10042
  stream2.write("\n");
8967
10043
  }
8968
- stream2.write(import_chalk6.default.bold.magenta("Model Shortcuts\n"));
8969
- stream2.write(import_chalk6.default.dim("\u2500".repeat(80)) + "\n");
10044
+ stream2.write(import_chalk8.default.bold.magenta("Model Shortcuts\n"));
10045
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(80)) + "\n");
8970
10046
  const shortcuts = Object.entries(MODEL_ALIASES).sort((a, b) => a[0].localeCompare(b[0]));
8971
10047
  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");
10048
+ stream2.write(import_chalk8.default.cyan(` ${shortcut.padEnd(15)}`) + import_chalk8.default.dim(" \u2192 ") + import_chalk8.default.white(fullName) + "\n");
8973
10049
  }
8974
10050
  stream2.write("\n");
8975
10051
  }
@@ -8979,45 +10055,45 @@ function renderCompactTable(models, stream2) {
8979
10055
  const contextWidth = 13;
8980
10056
  const inputWidth = 10;
8981
10057
  const outputWidth = 10;
8982
- stream2.write(import_chalk6.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
10058
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
8983
10059
  stream2.write(
8984
- import_chalk6.default.bold(
10060
+ import_chalk8.default.bold(
8985
10061
  "Model ID".padEnd(idWidth) + " " + "Display Name".padEnd(nameWidth) + " " + "Context".padEnd(contextWidth) + " " + "Input".padEnd(inputWidth) + " " + "Output".padEnd(outputWidth)
8986
10062
  ) + "\n"
8987
10063
  );
8988
- stream2.write(import_chalk6.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
10064
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
8989
10065
  for (const model of models) {
8990
10066
  const contextFormatted = formatTokens2(model.contextWindow);
8991
10067
  const inputPrice = `$${model.pricing.input.toFixed(2)}`;
8992
10068
  const outputPrice = `$${model.pricing.output.toFixed(2)}`;
8993
10069
  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"
10070
+ 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
10071
  );
8996
10072
  }
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
10073
+ stream2.write(import_chalk8.default.dim("\u2500".repeat(idWidth + nameWidth + contextWidth + inputWidth + outputWidth + 8)) + "\n");
10074
+ stream2.write(import_chalk8.default.dim(` * Prices are per 1M tokens
8999
10075
  `));
9000
10076
  }
9001
10077
  function renderVerboseTable(models, stream2) {
9002
10078
  for (const model of models) {
9003
- stream2.write(import_chalk6.default.bold.green(`
10079
+ stream2.write(import_chalk8.default.bold.green(`
9004
10080
  ${model.modelId}
9005
10081
  `));
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)}
10082
+ stream2.write(import_chalk8.default.dim(" " + "\u2500".repeat(60)) + "\n");
10083
+ stream2.write(` ${import_chalk8.default.dim("Name:")} ${import_chalk8.default.white(model.displayName)}
9008
10084
  `);
9009
- stream2.write(` ${import_chalk6.default.dim("Context:")} ${import_chalk6.default.yellow(formatTokens2(model.contextWindow))}
10085
+ stream2.write(` ${import_chalk8.default.dim("Context:")} ${import_chalk8.default.yellow(formatTokens2(model.contextWindow))}
9010
10086
  `);
9011
- stream2.write(` ${import_chalk6.default.dim("Max Output:")} ${import_chalk6.default.yellow(formatTokens2(model.maxOutputTokens))}
10087
+ stream2.write(` ${import_chalk8.default.dim("Max Output:")} ${import_chalk8.default.yellow(formatTokens2(model.maxOutputTokens))}
9012
10088
  `);
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)")}
10089
+ 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
10090
  `);
9015
10091
  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`)}
10092
+ stream2.write(` ${import_chalk8.default.dim("Cached Input:")} ${import_chalk8.default.cyan(`$${model.pricing.cachedInput.toFixed(2)} per 1M tokens`)}
9017
10093
  `);
9018
10094
  }
9019
10095
  if (model.knowledgeCutoff) {
9020
- stream2.write(` ${import_chalk6.default.dim("Knowledge:")} ${model.knowledgeCutoff}
10096
+ stream2.write(` ${import_chalk8.default.dim("Knowledge:")} ${model.knowledgeCutoff}
9021
10097
  `);
9022
10098
  }
9023
10099
  const features = [];
@@ -9028,20 +10104,20 @@ function renderVerboseTable(models, stream2) {
9028
10104
  if (model.features.structuredOutputs) features.push("structured-outputs");
9029
10105
  if (model.features.fineTuning) features.push("fine-tuning");
9030
10106
  if (features.length > 0) {
9031
- stream2.write(` ${import_chalk6.default.dim("Features:")} ${import_chalk6.default.blue(features.join(", "))}
10107
+ stream2.write(` ${import_chalk8.default.dim("Features:")} ${import_chalk8.default.blue(features.join(", "))}
9032
10108
  `);
9033
10109
  }
9034
10110
  if (model.metadata) {
9035
10111
  if (model.metadata.family) {
9036
- stream2.write(` ${import_chalk6.default.dim("Family:")} ${model.metadata.family}
10112
+ stream2.write(` ${import_chalk8.default.dim("Family:")} ${model.metadata.family}
9037
10113
  `);
9038
10114
  }
9039
10115
  if (model.metadata.releaseDate) {
9040
- stream2.write(` ${import_chalk6.default.dim("Released:")} ${model.metadata.releaseDate}
10116
+ stream2.write(` ${import_chalk8.default.dim("Released:")} ${model.metadata.releaseDate}
9041
10117
  `);
9042
10118
  }
9043
10119
  if (model.metadata.notes) {
9044
- stream2.write(` ${import_chalk6.default.dim("Notes:")} ${import_chalk6.default.italic(model.metadata.notes)}
10120
+ stream2.write(` ${import_chalk8.default.dim("Notes:")} ${import_chalk8.default.italic(model.metadata.notes)}
9045
10121
  `);
9046
10122
  }
9047
10123
  }
@@ -9091,7 +10167,7 @@ function registerModelsCommand(program, env) {
9091
10167
 
9092
10168
  // src/cli/environment.ts
9093
10169
  var import_node_readline = __toESM(require("readline"), 1);
9094
- var import_chalk7 = __toESM(require("chalk"), 1);
10170
+ var import_chalk9 = __toESM(require("chalk"), 1);
9095
10171
  init_client();
9096
10172
  init_logger();
9097
10173
  var LOG_LEVEL_MAP = {
@@ -9134,22 +10210,22 @@ function createLoggerFactory(config) {
9134
10210
  }
9135
10211
  function createPromptFunction(stdin, stdout) {
9136
10212
  return (question) => {
9137
- return new Promise((resolve) => {
10213
+ return new Promise((resolve2) => {
9138
10214
  const rl = import_node_readline.default.createInterface({
9139
10215
  input: stdin,
9140
10216
  output: stdout
9141
10217
  });
9142
10218
  stdout.write("\n");
9143
- stdout.write(`${import_chalk7.default.cyan("\u2500".repeat(60))}
10219
+ stdout.write(`${import_chalk9.default.cyan("\u2500".repeat(60))}
9144
10220
  `);
9145
- stdout.write(import_chalk7.default.cyan.bold("\u{1F916} Agent asks:\n"));
10221
+ stdout.write(import_chalk9.default.cyan.bold("\u{1F916} Agent asks:\n"));
9146
10222
  stdout.write(`${question}
9147
10223
  `);
9148
- stdout.write(`${import_chalk7.default.cyan("\u2500".repeat(60))}
10224
+ stdout.write(`${import_chalk9.default.cyan("\u2500".repeat(60))}
9149
10225
  `);
9150
- rl.question(import_chalk7.default.green.bold("You: "), (answer) => {
10226
+ rl.question(import_chalk9.default.green.bold("You: "), (answer) => {
9151
10227
  rl.close();
9152
- resolve(answer);
10228
+ resolve2(answer);
9153
10229
  });
9154
10230
  });
9155
10231
  };