micro-models-agent 0.61.1 → 0.63.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/main.js CHANGED
@@ -2945,7 +2945,9 @@ Excluded blocks: {count}`,
2945
2945
  "repl.provider_list": "List configured providers or hot-swap the active one",
2946
2946
  "repl.provider_current": "Current provider",
2947
2947
  "repl.provider_set": "Provider set to: {name}",
2948
- "repl.provider_usage": "/provider list - show entries (* = active, prio = fallback order) | /provider use <name> - switch without restart",
2948
+ "repl.provider_usage": "/provider list - show entries (* = active, prio = fallback order) | /provider use <name> - switch without restart | /provider add <name> [--url <url>] [--key <key>] [--priority <n>] [--context-window <n>] [--rpm <n>] [--parallel <n>] - add to config",
2949
+ "repl.provider_add_missing_value": "Missing value for {flag}",
2950
+ "repl.provider_switch_hint": "Switch to it with: /provider use {name}",
2949
2951
  "repl.model_list": "List models of the active provider or switch the model",
2950
2952
  "repl.model_current": "Current model",
2951
2953
  "repl.model_set": "Model set to: {name}",
@@ -3188,6 +3190,25 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
3188
3190
  "repl.cost": "Total cost: {cost}",
3189
3191
  "repl.cost_breakdown": "By provider: {breakdown}",
3190
3192
  "repl.tokens": "Tokens used: {tokens}",
3193
+ "repl.cache": "Cache: {hit}% hit · saved {saved}",
3194
+ "repl.cache_nosave": "Cache: {hit}% hit",
3195
+ "repl.prefix": "Prefix stable: {stable}% · broke: {cause}",
3196
+ "cache.cause.system": "system prompt",
3197
+ "cache.cause.tools": "tool set",
3198
+ "cache.cause.history": "history",
3199
+ "cache.cause.volatile": "volatile content",
3200
+ "cache.cause.unknown": "unknown",
3201
+ "cli.session_usage": "API usage: {prompt} prompt + {completion} completion = {total} tokens",
3202
+ "cli.session_cache": "Cache: {hit}% hit ({cached} cached / {uncached} uncached)",
3203
+ "cli.usage": "Show provider balance/usage (OpenRouter)",
3204
+ "cli.usage_unsupported": 'Provider "{provider}" does not expose an API balance. Only OpenRouter does; OpenCode Zen/Go show it in the web dashboard.',
3205
+ "cli.usage_no_key": "No API key configured for the active provider.",
3206
+ "cli.usage_error": "Failed to fetch balance: {error}",
3207
+ "cli.usage_key_usage": "Key usage: {usage}",
3208
+ "cli.usage_key_limit": "Key limit: {limit} · remaining {remaining}",
3209
+ "cli.usage_balance": "Balance: {balance}",
3210
+ "cli.usage_account": "Account: {credits} purchased · {used} used",
3211
+ "cli.usage_empty": "No balance information returned.",
3191
3212
  "repl.ctrl_c_interrupt": `
3192
3213
  [Ctrl+C] Stopping agent... (press again to force)`,
3193
3214
  "exec.stop_directive": 'STOP. Step {stepId} ("{description}") took {iterations} iterations with no progress. DO NOT continue this step. Immediately call: plan update step={stepId} status=done (if code works despite warnings) OR plan update step={stepId} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.',
@@ -3755,7 +3776,9 @@ var init_ru = __esm(() => {
3755
3776
  "repl.provider_list": "Список настроенных провайдеров или горячая смена активного",
3756
3777
  "repl.provider_current": "Текущий провайдер",
3757
3778
  "repl.provider_set": "Провайдер установлен: {name}",
3758
- "repl.provider_usage": "/provider list — записи (* = активный, prio = порядок fallback) | /provider use <имя> — смена без перезапуска",
3779
+ "repl.provider_usage": "/provider list — записи (* = активный, prio = порядок fallback) | /provider use <имя> — смена без перезапуска | /provider add <имя> [--url <url>] [--key <ключ>] [--priority <n>] [--context-window <n>] [--rpm <n>] [--parallel <n>] — добавить в конфиг",
3780
+ "repl.provider_add_missing_value": "Нет значения для {flag}",
3781
+ "repl.provider_switch_hint": "Переключитесь на него: /provider use {name}",
3759
3782
  "repl.model_list": "Список моделей активного провайдера или смена модели",
3760
3783
  "repl.model_current": "Текущая модель",
3761
3784
  "repl.model_set": "Модель установлена: {name}",
@@ -4005,6 +4028,25 @@ var init_ru = __esm(() => {
4005
4028
  "repl.cost": "Итого потрачено: {cost}",
4006
4029
  "repl.cost_breakdown": "По провайдерам: {breakdown}",
4007
4030
  "repl.tokens": "Потрачено токенов: {tokens}",
4031
+ "repl.cache": "Кеш: {hit}% попаданий · сэкономлено {saved}",
4032
+ "repl.cache_nosave": "Кеш: {hit}% попаданий",
4033
+ "repl.prefix": "Префикс стабилен: {stable}% · сломалось: {cause}",
4034
+ "cache.cause.system": "системный промпт",
4035
+ "cache.cause.tools": "набор инструментов",
4036
+ "cache.cause.history": "история",
4037
+ "cache.cause.volatile": "волатильное содержимое",
4038
+ "cache.cause.unknown": "неизвестно",
4039
+ "cli.session_usage": "API-использование: {prompt} prompt + {completion} completion = {total} токенов",
4040
+ "cli.session_cache": "Кеш: {hit}% попаданий ({cached} из кеша / {uncached} новых)",
4041
+ "cli.usage": "Показать баланс/расход провайдера (OpenRouter)",
4042
+ "cli.usage_unsupported": 'Провайдер "{provider}" не отдаёт баланс по API. Это умеет только OpenRouter; OpenCode Zen/Go показывают его в веб-дашборде.',
4043
+ "cli.usage_no_key": "Для активного провайдера не настроен API-ключ.",
4044
+ "cli.usage_error": "Не удалось получить баланс: {error}",
4045
+ "cli.usage_key_usage": "Расход по ключу: {usage}",
4046
+ "cli.usage_key_limit": "Лимит ключа: {limit} · осталось {remaining}",
4047
+ "cli.usage_balance": "Баланс: {balance}",
4048
+ "cli.usage_account": "Аккаунт: куплено {credits} · израсходовано {used}",
4049
+ "cli.usage_empty": "Провайдер не вернул данных о балансе.",
4008
4050
  "repl.ctrl_c_interrupt": `
4009
4051
  [Ctrl+C] Остановка агента... (ещё раз — принудительно)`,
4010
4052
  "exec.stop_directive": 'STOP. Step {stepId} ("{description}") took {iterations} iterations with no progress. DO NOT continue this step. Immediately call: plan update step={stepId} status=done (if code works despite warnings) OR plan update step={stepId} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.',
@@ -5893,6 +5935,73 @@ class StreamState {
5893
5935
  }
5894
5936
  }
5895
5937
 
5938
+ // src/llm/cache-usage.ts
5939
+ function num(value) {
5940
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
5941
+ }
5942
+ function isRecord(value) {
5943
+ return typeof value === "object" && value !== null;
5944
+ }
5945
+ function parseCacheUsage(usage, format, estimatedPromptTokens) {
5946
+ if (!isRecord(usage))
5947
+ return;
5948
+ switch (format) {
5949
+ case "openai": {
5950
+ const details = usage.prompt_tokens_details;
5951
+ if (!isRecord(details))
5952
+ return;
5953
+ const cached = num(details.cached_tokens);
5954
+ if (cached === undefined)
5955
+ return;
5956
+ const prompt = num(usage.prompt_tokens) ?? cached;
5957
+ const write = num(details.cache_write_tokens) ?? 0;
5958
+ return {
5959
+ cachedTokens: cached,
5960
+ cacheWriteTokens: write,
5961
+ uncachedTokens: Math.max(0, prompt - cached),
5962
+ source: "api"
5963
+ };
5964
+ }
5965
+ case "deepseek": {
5966
+ const hit = num(usage.prompt_cache_hit_tokens);
5967
+ const miss = num(usage.prompt_cache_miss_tokens);
5968
+ if (hit === undefined && miss === undefined)
5969
+ return;
5970
+ return {
5971
+ cachedTokens: hit ?? 0,
5972
+ cacheWriteTokens: 0,
5973
+ uncachedTokens: miss ?? 0,
5974
+ source: "api"
5975
+ };
5976
+ }
5977
+ case "anthropic": {
5978
+ const read = num(usage.cache_read_input_tokens);
5979
+ const creation = num(usage.cache_creation_input_tokens);
5980
+ if (read === undefined && creation === undefined)
5981
+ return;
5982
+ return {
5983
+ cachedTokens: read ?? 0,
5984
+ cacheWriteTokens: creation ?? 0,
5985
+ uncachedTokens: num(usage.input_tokens) ?? 0,
5986
+ source: "api"
5987
+ };
5988
+ }
5989
+ case "ollama": {
5990
+ const evaluated = num(usage.prompt_eval_count);
5991
+ if (evaluated === undefined || estimatedPromptTokens === undefined)
5992
+ return;
5993
+ return {
5994
+ cachedTokens: Math.max(0, estimatedPromptTokens - evaluated),
5995
+ cacheWriteTokens: 0,
5996
+ uncachedTokens: evaluated,
5997
+ source: "derived"
5998
+ };
5999
+ }
6000
+ default:
6001
+ return;
6002
+ }
6003
+ }
6004
+
5896
6005
  // src/llm/openai-compat.ts
5897
6006
  var exports_openai_compat = {};
5898
6007
  __export(exports_openai_compat, {
@@ -5911,6 +6020,14 @@ function buildRequestBody(opts) {
5911
6020
  };
5912
6021
  if (opts.maxTokens !== undefined)
5913
6022
  body.max_tokens = opts.maxTokens;
6023
+ if (opts.cachePrompt)
6024
+ body.cache_prompt = true;
6025
+ if (opts.promptCacheKey)
6026
+ body.prompt_cache_key = opts.promptCacheKey;
6027
+ if (opts.sessionId)
6028
+ body.session_id = opts.sessionId;
6029
+ if (opts.stream && opts.streamUsage)
6030
+ body.stream_options = { include_usage: true };
5914
6031
  const strategy = opts.reasoningStrategy ?? "openai-effort";
5915
6032
  const level = opts.reasoningEffort;
5916
6033
  if (strategy === "openai-effort" && level && level !== "default") {
@@ -5944,10 +6061,14 @@ class OpenAICompatProvider {
5944
6061
  debug;
5945
6062
  getSessionId;
5946
6063
  userAgent;
6064
+ cache;
6065
+ cacheReport;
5947
6066
  constructor(config) {
5948
6067
  this.config = config;
5949
6068
  this.model = config.model;
5950
6069
  this.contextWindow = config.contextWindow ?? 32768;
6070
+ this.cache = config.cache;
6071
+ this.cacheReport = config.cache?.report ?? "openai";
5951
6072
  this.tokenCounter = new TokenCounter;
5952
6073
  this.retryConfig = config.retry ?? {
5953
6074
  maxRetries: 3,
@@ -6050,7 +6171,8 @@ class OpenAICompatProvider {
6050
6171
  stream: true,
6051
6172
  maxTokens,
6052
6173
  reasoningEffort: options?.reasoningEffort,
6053
- reasoningStrategy: options?.reasoningStrategy
6174
+ reasoningStrategy: options?.reasoningStrategy,
6175
+ ...this.cacheHints()
6054
6176
  });
6055
6177
  this.debug?.("LLM stream request", {
6056
6178
  baseUrl: this.config.baseUrl,
@@ -6158,7 +6280,8 @@ class OpenAICompatProvider {
6158
6280
  st.usage = {
6159
6281
  promptTokens: parsed.usage.prompt_tokens ?? 0,
6160
6282
  completionTokens: parsed.usage.completion_tokens ?? 0,
6161
- totalTokens: parsed.usage.total_tokens ?? 0
6283
+ totalTokens: parsed.usage.total_tokens ?? 0,
6284
+ cache: parseCacheUsage(parsed.usage, this.cacheReport)
6162
6285
  };
6163
6286
  this.debug?.("LLM stream usage", { ...st.usage });
6164
6287
  } else if (parsed.error) {
@@ -6246,6 +6369,15 @@ class OpenAICompatProvider {
6246
6369
  }
6247
6370
  return st.sawDone;
6248
6371
  }
6372
+ cacheHints() {
6373
+ const sessionId = this.getSessionId?.();
6374
+ return {
6375
+ cachePrompt: this.cache?.requestCachePrompt === true,
6376
+ promptCacheKey: this.cache?.requestPromptCacheKey ? sessionId : undefined,
6377
+ sessionId: this.cache?.requestSessionId ? sessionId : undefined,
6378
+ streamUsage: this.cache?.requestStreamUsage === true
6379
+ };
6380
+ }
6249
6381
  buildRequestSetup(signal) {
6250
6382
  const headers = {
6251
6383
  "Content-Type": "application/json",
@@ -6255,7 +6387,8 @@ class OpenAICompatProvider {
6255
6387
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6256
6388
  }
6257
6389
  const sessionId = this.getSessionId?.();
6258
- if (sessionId)
6390
+ const sendSessionHeader = this.cache ? this.cache.sessionHeader : true;
6391
+ if (sessionId && sendSessionHeader)
6259
6392
  headers["x-opencode-session"] = sessionId;
6260
6393
  const controller = new AbortController;
6261
6394
  let timedOut = false;
@@ -6294,7 +6427,8 @@ class OpenAICompatProvider {
6294
6427
  stream: false,
6295
6428
  maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
6296
6429
  reasoningEffort: options?.reasoningEffort,
6297
- reasoningStrategy: options?.reasoningStrategy
6430
+ reasoningStrategy: options?.reasoningStrategy,
6431
+ ...this.cacheHints()
6298
6432
  });
6299
6433
  const { headers, abortSignal, cleanup, isTimeout } = this.buildRequestSetup(signal);
6300
6434
  try {
@@ -6347,7 +6481,8 @@ class OpenAICompatProvider {
6347
6481
  usage: {
6348
6482
  promptTokens: data.usage.prompt_tokens ?? 0,
6349
6483
  completionTokens: data.usage.completion_tokens ?? 0,
6350
- totalTokens: data.usage.total_tokens ?? 0
6484
+ totalTokens: data.usage.total_tokens ?? 0,
6485
+ cache: parseCacheUsage(data.usage, this.cacheReport)
6351
6486
  }
6352
6487
  });
6353
6488
  }
@@ -6469,6 +6604,7 @@ function openaiCompat(opts) {
6469
6604
  rateLimits,
6470
6605
  maxCompletionTokens: opts.maxCompletionTokens,
6471
6606
  getSessionId: opts.getSessionId,
6607
+ cache: opts.capabilities?.cache,
6472
6608
  logger: opts.logger
6473
6609
  });
6474
6610
  }
@@ -6486,7 +6622,17 @@ var init_presets = __esm(() => {
6486
6622
  reasoningStrategy: "prompt-tag",
6487
6623
  listModels: true,
6488
6624
  requiresKey: false,
6489
- auth: "bearer"
6625
+ auth: "bearer",
6626
+ cache: {
6627
+ mechanism: "local",
6628
+ report: "openai",
6629
+ sessionHeader: false,
6630
+ requestCachePrompt: false,
6631
+ requestPromptCacheKey: false,
6632
+ requestSessionId: false,
6633
+ requestCacheControl: false,
6634
+ requestStreamUsage: true
6635
+ }
6490
6636
  },
6491
6637
  create: openaiCompat
6492
6638
  };
@@ -6501,7 +6647,17 @@ var init_presets = __esm(() => {
6501
6647
  reasoningStrategy: "openai-effort",
6502
6648
  listModels: true,
6503
6649
  requiresKey: true,
6504
- auth: "bearer"
6650
+ auth: "bearer",
6651
+ cache: {
6652
+ mechanism: "auto",
6653
+ report: "openai",
6654
+ sessionHeader: false,
6655
+ requestCachePrompt: false,
6656
+ requestPromptCacheKey: false,
6657
+ requestSessionId: true,
6658
+ requestCacheControl: false,
6659
+ requestStreamUsage: true
6660
+ }
6505
6661
  },
6506
6662
  create: openaiCompat
6507
6663
  };
@@ -6516,7 +6672,17 @@ var init_presets = __esm(() => {
6516
6672
  reasoningStrategy: "openai-effort",
6517
6673
  listModels: true,
6518
6674
  requiresKey: true,
6519
- auth: "bearer"
6675
+ auth: "bearer",
6676
+ cache: {
6677
+ mechanism: "auto",
6678
+ report: "openai",
6679
+ sessionHeader: false,
6680
+ requestCachePrompt: false,
6681
+ requestPromptCacheKey: true,
6682
+ requestSessionId: false,
6683
+ requestCacheControl: false,
6684
+ requestStreamUsage: true
6685
+ }
6520
6686
  },
6521
6687
  create: openaiCompat
6522
6688
  };
@@ -6531,7 +6697,17 @@ var init_presets = __esm(() => {
6531
6697
  reasoningStrategy: "none",
6532
6698
  listModels: false,
6533
6699
  requiresKey: true,
6534
- auth: "header"
6700
+ auth: "header",
6701
+ cache: {
6702
+ mechanism: "explicit",
6703
+ report: "anthropic",
6704
+ sessionHeader: false,
6705
+ requestCachePrompt: false,
6706
+ requestPromptCacheKey: false,
6707
+ requestSessionId: false,
6708
+ requestCacheControl: true,
6709
+ requestStreamUsage: false
6710
+ }
6535
6711
  },
6536
6712
  create: openaiCompat
6537
6713
  };
@@ -6546,7 +6722,17 @@ var init_presets = __esm(() => {
6546
6722
  reasoningStrategy: "openai-effort",
6547
6723
  listModels: true,
6548
6724
  requiresKey: false,
6549
- auth: "bearer"
6725
+ auth: "bearer",
6726
+ cache: {
6727
+ mechanism: "auto",
6728
+ report: "openai",
6729
+ sessionHeader: true,
6730
+ requestCachePrompt: false,
6731
+ requestPromptCacheKey: false,
6732
+ requestSessionId: false,
6733
+ requestCacheControl: false,
6734
+ requestStreamUsage: true
6735
+ }
6550
6736
  },
6551
6737
  create: openaiCompat
6552
6738
  };
@@ -6561,7 +6747,17 @@ var init_presets = __esm(() => {
6561
6747
  reasoningStrategy: "openai-effort",
6562
6748
  listModels: true,
6563
6749
  requiresKey: false,
6564
- auth: "bearer"
6750
+ auth: "bearer",
6751
+ cache: {
6752
+ mechanism: "auto",
6753
+ report: "openai",
6754
+ sessionHeader: true,
6755
+ requestCachePrompt: false,
6756
+ requestPromptCacheKey: false,
6757
+ requestSessionId: false,
6758
+ requestCacheControl: false,
6759
+ requestStreamUsage: true
6760
+ }
6565
6761
  },
6566
6762
  create: openaiCompat
6567
6763
  };
@@ -6624,6 +6820,25 @@ var init_create = __esm(() => {
6624
6820
  init_presets();
6625
6821
  });
6626
6822
 
6823
+ // src/modules/providers/cache.ts
6824
+ function resolveCacheCapability(spec, override) {
6825
+ const base = spec?.capabilities.cache ?? NO_CACHE_CAPABILITY;
6826
+ return { ...base, ...override ?? {} };
6827
+ }
6828
+ var NO_CACHE_CAPABILITY;
6829
+ var init_cache = __esm(() => {
6830
+ NO_CACHE_CAPABILITY = {
6831
+ mechanism: "none",
6832
+ report: "none",
6833
+ sessionHeader: false,
6834
+ requestCachePrompt: false,
6835
+ requestPromptCacheKey: false,
6836
+ requestSessionId: false,
6837
+ requestCacheControl: false,
6838
+ requestStreamUsage: false
6839
+ };
6840
+ });
6841
+
6627
6842
  // src/modules/providers/manager.ts
6628
6843
  class ProviderManager {
6629
6844
  entries;
@@ -6747,17 +6962,25 @@ class ProviderManager {
6747
6962
  rateLimits: entry.rateLimits ?? this.opts.rateLimits,
6748
6963
  maxCompletionTokens: entry.maxCompletionTokens,
6749
6964
  getSessionId: this.opts.getSessionId,
6965
+ capabilities: this.resolveCapabilities(entry),
6750
6966
  logger: this.opts.logger
6751
6967
  }, this.registry);
6752
6968
  this.cache.set(key, provider);
6753
6969
  return provider;
6754
6970
  }
6971
+ resolveCapabilities(entry) {
6972
+ const spec = this.registry.get(entry.type);
6973
+ if (!spec)
6974
+ return { cache: resolveCacheCapability(undefined, entry.cache) };
6975
+ return { ...spec.capabilities, cache: resolveCacheCapability(spec, entry.cache) };
6976
+ }
6755
6977
  resetCache() {
6756
6978
  this.cache.clear();
6757
6979
  }
6758
6980
  }
6759
6981
  var init_manager = __esm(() => {
6760
6982
  init_create();
6983
+ init_cache();
6761
6984
  init_presets();
6762
6985
  });
6763
6986
 
@@ -8160,8 +8383,8 @@ function buildDiff(oldLines, newLines) {
8160
8383
  return result;
8161
8384
  }
8162
8385
  function formatLine(line, maxNumWidth) {
8163
- const num = line.type === "remove" ? line.oldNum : line.newNum;
8164
- const numStr = num !== null ? String(num).padStart(maxNumWidth) : " ".repeat(maxNumWidth);
8386
+ const num2 = line.type === "remove" ? line.oldNum : line.newNum;
8387
+ const numStr = num2 !== null ? String(num2).padStart(maxNumWidth) : " ".repeat(maxNumWidth);
8165
8388
  if (line.type === "remove") {
8166
8389
  return `${numStr} ${pc2.red("-")} ${line.content}`;
8167
8390
  } else if (line.type === "add") {
@@ -10296,7 +10519,12 @@ class SessionLogger {
10296
10519
  completionTokens: usage.completionTokens,
10297
10520
  totalTokens: usage.totalTokens,
10298
10521
  source: usage.source,
10299
- durationMs: usage.durationMs
10522
+ durationMs: usage.durationMs,
10523
+ cacheStable: usage.prefix ? Number(usage.prefix.stableRatio.toFixed(4)) : undefined,
10524
+ cacheCause: usage.prefix?.cause,
10525
+ cachedTokens: usage.cache?.cachedTokens,
10526
+ cacheWriteTokens: usage.cache?.cacheWriteTokens,
10527
+ cacheSource: usage.cache?.source
10300
10528
  });
10301
10529
  }
10302
10530
  logError(message) {
@@ -10946,52 +11174,52 @@ ${output}
10946
11174
  }
10947
11175
  function parseNumber() {
10948
11176
  const start = i;
10949
- let num = "";
11177
+ let num2 = "";
10950
11178
  let invalid = false;
10951
11179
  if (text[i] === "-") {
10952
- num += text[i];
11180
+ num2 += text[i];
10953
11181
  i++;
10954
11182
  if (!isDigit(text[i]) && atEndOfNumber()) {
10955
- num += "0";
11183
+ num2 += "0";
10956
11184
  }
10957
11185
  }
10958
11186
  if (text[i] === "0" && isDigit(text[i + 1])) {
10959
11187
  invalid = true;
10960
11188
  }
10961
11189
  while (isDigit(text[i])) {
10962
- num += text[i];
11190
+ num2 += text[i];
10963
11191
  i++;
10964
11192
  }
10965
11193
  if (text[i] === ".") {
10966
- if (num === "" || num === "-") {
10967
- num += "0";
11194
+ if (num2 === "" || num2 === "-") {
11195
+ num2 += "0";
10968
11196
  }
10969
- num += text[i];
11197
+ num2 += text[i];
10970
11198
  i++;
10971
11199
  if (!isDigit(text[i])) {
10972
- num += "0";
11200
+ num2 += "0";
10973
11201
  }
10974
11202
  while (isDigit(text[i])) {
10975
- num += text[i];
11203
+ num2 += text[i];
10976
11204
  i++;
10977
11205
  }
10978
11206
  }
10979
11207
  if (i > start) {
10980
11208
  if (text[i] === "e" || text[i] === "E") {
10981
- if (num === "-") {
11209
+ if (num2 === "-") {
10982
11210
  invalid = true;
10983
11211
  }
10984
- num += text[i];
11212
+ num2 += text[i];
10985
11213
  i++;
10986
11214
  if (text[i] === "-" || text[i] === "+") {
10987
- num += text[i];
11215
+ num2 += text[i];
10988
11216
  i++;
10989
11217
  }
10990
11218
  if (!isDigit(text[i])) {
10991
- num += "0";
11219
+ num2 += "0";
10992
11220
  }
10993
11221
  while (isDigit(text[i])) {
10994
- num += text[i];
11222
+ num2 += text[i];
10995
11223
  i++;
10996
11224
  }
10997
11225
  }
@@ -10999,7 +11227,7 @@ ${output}
10999
11227
  i = start;
11000
11228
  return false;
11001
11229
  }
11002
- output += invalid ? `"${text.substring(start, i)}"` : num;
11230
+ output += invalid ? `"${text.substring(start, i)}"` : num2;
11003
11231
  return true;
11004
11232
  }
11005
11233
  return false;
@@ -12791,6 +13019,24 @@ var init_moe_executor = __esm(() => {
12791
13019
  // src/modules/lsp/project-root.ts
12792
13020
  import { existsSync as existsSync24 } from "fs";
12793
13021
  import { dirname as dirname9, join as join14, relative as relative3, isAbsolute as isAbsolute3 } from "path";
13022
+ import { platform as platform4 } from "os";
13023
+ function resolveTscCommand(startDir, maxLevels = 8) {
13024
+ const runtime = process.execPath;
13025
+ let dir = startDir;
13026
+ for (let i = 0;i < maxLevels; i++) {
13027
+ const js = join14(dir, "node_modules", "typescript", "bin", "tsc");
13028
+ if (existsSync24(js))
13029
+ return `"${runtime}" "${js}"`;
13030
+ const stub = join14(dir, "node_modules", ".bin", platform4() === "win32" ? "tsc.cmd" : "tsc");
13031
+ if (existsSync24(stub))
13032
+ return `"${stub}"`;
13033
+ const parent = dirname9(dir);
13034
+ if (parent === dir)
13035
+ break;
13036
+ dir = parent;
13037
+ }
13038
+ return null;
13039
+ }
12794
13040
  function findProjectRoot(filePath, baseDir, markers) {
12795
13041
  if (!markers || markers.length === 0)
12796
13042
  return baseDir;
@@ -13163,7 +13409,10 @@ function findTypecheckRoot(baseDir, existingFiles = []) {
13163
13409
  return best?.root ?? null;
13164
13410
  }
13165
13411
  async function runTypecheck(baseDir) {
13166
- const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
13412
+ const tsc = resolveTscCommand(baseDir);
13413
+ if (!tsc)
13414
+ return null;
13415
+ const entry = processRegistry.start(`${tsc} --noEmit --skipLibCheck`, baseDir);
13167
13416
  const exited = await processRegistry.waitForExit(entry.id, 90000);
13168
13417
  const output = entry.log.join(`
13169
13418
  `);
@@ -13176,6 +13425,7 @@ var SKIP_DIRS, TEST_EXT_RE, PY_TEST_RE, TEST_STEP_RE;
13176
13425
  var init_audit_runners = __esm(() => {
13177
13426
  init_bash();
13178
13427
  init_processes();
13428
+ init_project_root();
13179
13429
  SKIP_DIRS = new Set([
13180
13430
  "node_modules",
13181
13431
  ".git",
@@ -13354,7 +13604,7 @@ var init_auditor = __esm(() => {
13354
13604
 
13355
13605
  // src/modules/execution/verifier.ts
13356
13606
  import { existsSync as existsSync27, readFileSync as readFileSync14 } from "fs";
13357
- import { resolve as resolve13, extname as extname3, join as join17 } from "path";
13607
+ import { resolve as resolve13, extname as extname3, dirname as dirname11, join as join17 } from "path";
13358
13608
  import { spawn as spawn4 } from "child_process";
13359
13609
 
13360
13610
  class StepVerifier {
@@ -13386,8 +13636,11 @@ class StepVerifier {
13386
13636
  if (!existsSync27(tsconfigPath)) {
13387
13637
  return { passed: true, message: "No tsconfig.json found — skipping type check" };
13388
13638
  }
13639
+ const tsc = resolveTscCommand(this.baseDir);
13640
+ if (!tsc)
13641
+ return { passed: true, message: "tsc not installed — skipping type check" };
13389
13642
  try {
13390
- await this.runAsync("npx tsc --noEmit", this.baseDir, 60000);
13643
+ await this.runAsync(`${tsc} --noEmit`, this.baseDir, 60000);
13391
13644
  return { passed: true, message: "TypeScript type check passed" };
13392
13645
  } catch (e) {
13393
13646
  const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
@@ -13400,8 +13653,11 @@ class StepVerifier {
13400
13653
  if (!existsSync27(tsconfigPath)) {
13401
13654
  return { passed: true, message: "No tsconfig.json found — skipping type check" };
13402
13655
  }
13656
+ const tsc = resolveTscCommand(projectRoot);
13657
+ if (!tsc)
13658
+ return { passed: true, message: "tsc not installed — skipping type check" };
13403
13659
  try {
13404
- await this.runAsync("npx tsc --noEmit --skipLibCheck", projectRoot, 60000);
13660
+ await this.runAsync(`${tsc} --noEmit --skipLibCheck`, projectRoot, 60000);
13405
13661
  return { passed: true, message: "TypeScript type check passed" };
13406
13662
  } catch (e) {
13407
13663
  const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
@@ -13554,8 +13810,11 @@ class StepVerifier {
13554
13810
  async validateSyntax(filePath) {
13555
13811
  const ext = extname3(filePath);
13556
13812
  if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
13813
+ const tsc = resolveTscCommand(dirname11(filePath));
13814
+ if (!tsc)
13815
+ return true;
13557
13816
  try {
13558
- await this.runAsync(`npx tsc --noEmit --skipLibCheck "${filePath}"`, this.baseDir, 1e4);
13817
+ await this.runAsync(`${tsc} --noEmit --skipLibCheck "${filePath}"`, this.baseDir, 20000);
13559
13818
  return true;
13560
13819
  } catch (err) {
13561
13820
  if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
@@ -13885,11 +14144,18 @@ function resolvePrice(model, config) {
13885
14144
  return direct;
13886
14145
  return ZEN_PRICES[bare];
13887
14146
  }
13888
- function calculateCost(model, promptTokens, completionTokens, config) {
14147
+ function calculateCostDetailed(model, promptTokens, completionTokens, config, cache) {
13889
14148
  const price = resolvePrice(model, config);
13890
14149
  if (!price)
13891
14150
  return;
13892
- return promptTokens / 1e6 * price.input + completionTokens / 1e6 * price.output;
14151
+ const cached = Math.max(0, cache?.cachedTokens ?? 0);
14152
+ const write = Math.max(0, cache?.cacheWriteTokens ?? 0);
14153
+ const uncached = Math.max(0, promptTokens - cached);
14154
+ const cachedRate = price.cachedInput ?? price.input;
14155
+ const writeRate = price.cacheWrite ?? 0;
14156
+ const cost = uncached / 1e6 * price.input + cached / 1e6 * cachedRate + write / 1e6 * writeRate + completionTokens / 1e6 * price.output;
14157
+ const saved = cached / 1e6 * (price.input - cachedRate);
14158
+ return { cost, saved };
13893
14159
  }
13894
14160
  function formatCost(cost) {
13895
14161
  if (cost === 0)
@@ -13909,12 +14175,12 @@ var init_prices = __esm(() => {
13909
14175
  "nemotron-3-ultra-free": { input: 0, output: 0 },
13910
14176
  "nemotron-3.5-lightning-free": { input: 0, output: 0 },
13911
14177
  "muse-spark-1.2-contributor-free": { input: 0, output: 0 },
13912
- "minimax-m3": { input: 0.3, output: 1.2 },
13913
- "minimax-m2.7": { input: 0.3, output: 1.2 },
13914
- "minimax-m2.5": { input: 0.3, output: 1.2 },
13915
- "glm-5.2": { input: 1.4, output: 4.4 },
13916
- "glm-5.1": { input: 1.4, output: 4.4 },
13917
- "glm-5": { input: 1, output: 3.2 },
14178
+ "minimax-m3": { input: 0.3, output: 1.2, cachedInput: 0.06 },
14179
+ "minimax-m2.7": { input: 0.3, output: 1.2, cachedInput: 0.06 },
14180
+ "minimax-m2.5": { input: 0.3, output: 1.2, cachedInput: 0.06 },
14181
+ "glm-5.2": { input: 1.4, output: 4.4, cachedInput: 0.26 },
14182
+ "glm-5.1": { input: 1.4, output: 4.4, cachedInput: 0.26 },
14183
+ "glm-5": { input: 1, output: 3.2, cachedInput: 0.2 },
13918
14184
  "glm-5.3": { input: 1.4, output: 4.4 },
13919
14185
  "kimi-k2.7-code": { input: 0.95, output: 4 },
13920
14186
  "kimi-k3": { input: 3, output: 15 },
@@ -13976,6 +14242,7 @@ class CostTracker {
13976
14242
  config;
13977
14243
  model;
13978
14244
  _total = 0;
14245
+ _saved = 0;
13979
14246
  _known = false;
13980
14247
  byProvider = new Map;
13981
14248
  constructor(model, config) {
@@ -13985,24 +14252,28 @@ class CostTracker {
13985
14252
  setModel(model) {
13986
14253
  this.model = model;
13987
14254
  }
13988
- record(promptTokens, completionTokens, provider) {
14255
+ record(promptTokens, completionTokens, provider, cache) {
13989
14256
  const key = provider || "unknown";
13990
- const cost = calculateCost(this.model, promptTokens, completionTokens, this.config);
13991
- if (cost === undefined)
14257
+ const result = calculateCostDetailed(this.model, promptTokens, completionTokens, this.config, cache);
14258
+ if (!result)
13992
14259
  return;
13993
14260
  this._known = true;
13994
- this._total += cost;
14261
+ this._total += result.cost;
14262
+ this._saved += result.saved;
13995
14263
  let entry = this.byProvider.get(key);
13996
14264
  if (!entry) {
13997
14265
  entry = { total: 0, known: false };
13998
14266
  this.byProvider.set(key, entry);
13999
14267
  }
14000
14268
  entry.known = true;
14001
- entry.total += cost;
14269
+ entry.total += result.cost;
14002
14270
  }
14003
14271
  get total() {
14004
14272
  return this._known ? this._total : undefined;
14005
14273
  }
14274
+ get saved() {
14275
+ return this._known ? this._saved : undefined;
14276
+ }
14006
14277
  totalFor(provider) {
14007
14278
  const entry = this.byProvider.get(provider);
14008
14279
  return entry && entry.known ? entry.total : undefined;
@@ -14083,6 +14354,10 @@ function createLoopState() {
14083
14354
  apiCompletionChars: 0,
14084
14355
  estimatedPromptTokensTotal: 0,
14085
14356
  totalLlmDuration: 0,
14357
+ cacheCachedTotal: 0,
14358
+ cacheUncachedTotal: 0,
14359
+ cacheWriteTotal: 0,
14360
+ cacheSource: "none",
14086
14361
  auditRetries: 0,
14087
14362
  lastAuditSummary: "",
14088
14363
  emptyResponseRetries: 0,
@@ -14189,6 +14464,7 @@ class TokenTracker {
14189
14464
  }
14190
14465
  beginIteration(state, estimatedPromptTokens) {
14191
14466
  state.estimatedPromptTokensTotal += estimatedPromptTokens;
14467
+ state.cacheUsage = undefined;
14192
14468
  return {
14193
14469
  promptBefore: state.apiPromptTokens,
14194
14470
  completionBefore: state.apiCompletionTokens
@@ -14197,6 +14473,7 @@ class TokenTracker {
14197
14473
  recordApiUsage(state, baseline, usage) {
14198
14474
  state.apiPromptTokens = baseline.promptBefore + usage.promptTokens;
14199
14475
  state.apiCompletionTokens = baseline.completionBefore + usage.completionTokens;
14476
+ state.cacheUsage = usage.cache;
14200
14477
  }
14201
14478
  addCompletionChars(state, chars) {
14202
14479
  state.apiCompletionChars += chars;
@@ -14207,14 +14484,22 @@ class TokenTracker {
14207
14484
  const source = usagePrompt > 0 || usageCompletion > 0 ? "api" : "estimate";
14208
14485
  const prompt = source === "api" ? usagePrompt : contextTokens;
14209
14486
  const completion = source === "api" ? usageCompletion : estimateTokens(textContent);
14487
+ if (state.cacheUsage) {
14488
+ state.cacheCachedTotal += state.cacheUsage.cachedTokens;
14489
+ state.cacheUncachedTotal += state.cacheUsage.uncachedTokens;
14490
+ state.cacheWriteTotal += state.cacheUsage.cacheWriteTokens;
14491
+ state.cacheSource = state.cacheUsage.source;
14492
+ }
14210
14493
  slog.logLlmUsage(iteration, {
14211
14494
  promptTokens: prompt,
14212
14495
  completionTokens: completion,
14213
14496
  totalTokens: prompt + completion,
14214
14497
  source,
14215
- durationMs
14498
+ durationMs,
14499
+ prefix: state.prefixDelta,
14500
+ cache: state.cacheUsage
14216
14501
  });
14217
- this.costTracker.record(prompt, completion, this.getProviderName());
14502
+ this.costTracker.record(prompt, completion, this.getProviderName(), source === "api" ? state.cacheUsage : undefined);
14218
14503
  }
14219
14504
  resolveFinal(state) {
14220
14505
  if (state.apiPromptTokens > 0 || state.apiCompletionTokens > 0) {
@@ -14233,6 +14518,75 @@ var init_token_tracker = __esm(() => {
14233
14518
  init_token_counter();
14234
14519
  });
14235
14520
 
14521
+ // src/core/agent/prefix-monitor.ts
14522
+ function serializeTools(tools) {
14523
+ return tools.map((t2) => `${t2.name}\x01${t2.description}\x01${JSON.stringify(t2.parameters)}`).join("\x02");
14524
+ }
14525
+ function serializeMessages(messages) {
14526
+ return messages.map((m) => `${m.role}\x01${typeof m.content === "string" ? m.content : JSON.stringify(m.content)}`).join("\x02");
14527
+ }
14528
+ function buildPromptSnapshot(messages, tools) {
14529
+ const system = [];
14530
+ const rest = [];
14531
+ for (const m of messages) {
14532
+ if (m.role === "system")
14533
+ system.push(m);
14534
+ else
14535
+ rest.push(m);
14536
+ }
14537
+ return {
14538
+ system: serializeMessages(system),
14539
+ tools: serializeTools(tools),
14540
+ history: serializeMessages(rest)
14541
+ };
14542
+ }
14543
+ function frame(s) {
14544
+ const full = s.system + SEP + s.tools + SEP + s.history;
14545
+ const toolsStart = s.system.length + SEP.length;
14546
+ const historyStart = toolsStart + s.tools.length + SEP.length;
14547
+ return { full, toolsStart, historyStart };
14548
+ }
14549
+ function diffPrompt(prev, curr, countTokens) {
14550
+ const c = frame(curr);
14551
+ const currTokens = countTokens(c.full);
14552
+ if (!prev) {
14553
+ return {
14554
+ prevTokens: 0,
14555
+ currTokens,
14556
+ commonPrefixTokens: 0,
14557
+ stableRatio: 0,
14558
+ cause: "unknown"
14559
+ };
14560
+ }
14561
+ const p = frame(prev);
14562
+ const prevTokens = countTokens(p.full);
14563
+ const maxCp = Math.min(p.full.length, c.full.length);
14564
+ let cp = 0;
14565
+ while (cp < maxCp && p.full[cp] === c.full[cp])
14566
+ cp++;
14567
+ const commonPrefixTokens = countTokens(c.full.slice(0, cp));
14568
+ const stableRatio = currTokens === 0 ? 0 : Math.min(1, commonPrefixTokens / currTokens);
14569
+ if (cp === maxCp) {
14570
+ return { prevTokens, currTokens, commonPrefixTokens, stableRatio, cause: "none" };
14571
+ }
14572
+ let cause;
14573
+ if (cp >= c.historyStart)
14574
+ cause = "history";
14575
+ else if (cp >= c.toolsStart)
14576
+ cause = "tools";
14577
+ else
14578
+ cause = "system";
14579
+ const window = c.full.slice(Math.max(0, cp - 40), cp + 160) + p.full.slice(Math.max(0, cp - 40), cp + 160);
14580
+ if (VOLATILE_RE.test(window))
14581
+ cause = "volatile";
14582
+ return { prevTokens, currTokens, commonPrefixTokens, stableRatio, cause };
14583
+ }
14584
+ var SEP = `
14585
+ `, VOLATILE_RE;
14586
+ var init_prefix_monitor = __esm(() => {
14587
+ VOLATILE_RE = /(\d{4}-\d{2}-\d{2}|\d{2}:\d{2}:\d{2}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}|[A-Za-z]:\\|\\AppData\\|\\Temp\\|\/tmp\/)/;
14588
+ });
14589
+
14236
14590
  // src/core/agent/reasoning-resolver.ts
14237
14591
  class ReasoningEffortResolver {
14238
14592
  resolve(deps) {
@@ -15261,6 +15615,12 @@ class Agent {
15261
15615
  compactionService.compactIfNeeded(state);
15262
15616
  this.refreshSystemPrompt();
15263
15617
  const history = contextManager.getActiveHistory();
15618
+ const promptSnapshot = buildPromptSnapshot(history, allToolsForBudget);
15619
+ state.prefixDelta = diffPrompt(state.prevPrompt, promptSnapshot, estimateTokens);
15620
+ state.prevPrompt = promptSnapshot;
15621
+ if (state.prefixDelta.cause === "system" || state.prefixDelta.cause === "tools" || state.prefixDelta.cause === "history" || state.prefixDelta.cause === "volatile") {
15622
+ state.lastPrefixBreak = state.prefixDelta;
15623
+ }
15264
15624
  slog.logToolDefs(allToolsForBudget.length, allToolsForBudget.map((t2) => t2.name), state.iteration);
15265
15625
  if (state.iteration === 1) {
15266
15626
  const { blocks } = this.buildSystemPrompt();
@@ -15509,6 +15869,7 @@ class Agent {
15509
15869
  const tokensUsed = contextManager.getEstimatedTokens();
15510
15870
  const budget = contextManager.getBudget();
15511
15871
  const usageTokens = tokenTracker.resolveFinal(state);
15872
+ const cacheStats = this.buildCacheStats(state);
15512
15873
  if (state.iteration >= config.maxToolIterations && !state.finalAnswerAccepted) {
15513
15874
  return {
15514
15875
  success: false,
@@ -15522,6 +15883,7 @@ class Agent {
15522
15883
  totalTokens: usageTokens.total,
15523
15884
  totalCost: this.costTracker.total,
15524
15885
  costBreakdown: this.costTracker.breakdown(),
15886
+ cache: cacheStats,
15525
15887
  compactionCount: contextManager.getCompactionCount(),
15526
15888
  contextQuality: contextManager.getQuality()
15527
15889
  };
@@ -15538,11 +15900,29 @@ class Agent {
15538
15900
  totalTokens: usageTokens.total,
15539
15901
  totalCost: this.costTracker.total,
15540
15902
  costBreakdown: this.costTracker.breakdown(),
15903
+ cache: cacheStats,
15541
15904
  compactionCount: contextManager.getCompactionCount(),
15542
15905
  contextQuality: contextManager.getQuality(),
15543
15906
  llmDurationMs: state.totalLlmDuration
15544
15907
  };
15545
15908
  }
15909
+ buildCacheStats(state) {
15910
+ const hasTokens = state.cacheCachedTotal > 0 || state.cacheUncachedTotal > 0 || state.cacheWriteTotal > 0;
15911
+ const prefix = state.lastPrefixBreak ?? state.prefixDelta;
15912
+ if (!hasTokens && prefix?.stableRatio === undefined)
15913
+ return;
15914
+ const denom = state.cacheCachedTotal + state.cacheUncachedTotal;
15915
+ return {
15916
+ cachedTokens: state.cacheCachedTotal,
15917
+ uncachedTokens: state.cacheUncachedTotal,
15918
+ cacheWriteTokens: state.cacheWriteTotal,
15919
+ hitRate: denom > 0 ? state.cacheCachedTotal / denom : 0,
15920
+ saved: this.costTracker.saved,
15921
+ prefixStable: prefix?.stableRatio,
15922
+ prefixCause: prefix?.cause,
15923
+ source: state.cacheSource
15924
+ };
15925
+ }
15546
15926
  clearContext() {
15547
15927
  this.deps.contextManager.clear();
15548
15928
  this.systemPromptAdded = false;
@@ -15647,6 +16027,7 @@ var init_agent = __esm(() => {
15647
16027
  init_loop_state();
15648
16028
  init_compaction();
15649
16029
  init_token_tracker();
16030
+ init_prefix_monitor();
15650
16031
  init_reasoning_resolver();
15651
16032
  init_context_renderer();
15652
16033
  init_tool_batch();
@@ -16538,8 +16919,8 @@ var init_detector = __esm(() => {
16538
16919
  // src/modules/lsp/command.ts
16539
16920
  import { delimiter, join as join21 } from "path";
16540
16921
  import { existsSync as existsSync30 } from "fs";
16541
- import { platform as platform4 } from "os";
16542
- function resolveSpawnCommand(command, platformName = platform4(), pathEnv = process.env.PATH ?? "") {
16922
+ import { platform as platform5 } from "os";
16923
+ function resolveSpawnCommand(command, platformName = platform5(), pathEnv = process.env.PATH ?? "") {
16543
16924
  if (platformName !== "win32")
16544
16925
  return command;
16545
16926
  if (command.includes("/") || command.includes("\\") || WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
@@ -16571,7 +16952,7 @@ var init_command = __esm(() => {
16571
16952
  });
16572
16953
 
16573
16954
  // src/modules/updater/checker.ts
16574
- import { platform as platform5 } from "os";
16955
+ import { platform as platform6 } from "os";
16575
16956
  function semverGt(a, b) {
16576
16957
  const pa = a.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
16577
16958
  const pb = b.replace(/^v/, "").split(/[.-]/).map((p) => Number.parseInt(p, 10) || 0);
@@ -16642,7 +17023,7 @@ class Updater {
16642
17023
  buildInstallCommand(latest) {
16643
17024
  const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
16644
17025
  const command = resolveSpawnCommand("npm");
16645
- if (platform5() === "win32" && /\.(cmd|bat)$/i.test(command)) {
17026
+ if (platform6() === "win32" && /\.(cmd|bat)$/i.test(command)) {
16646
17027
  return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
16647
17028
  }
16648
17029
  return { command, args: npmArgs };
@@ -20167,11 +20548,11 @@ async function readClipboardImage() {
20167
20548
  return readClipboardFallback();
20168
20549
  }
20169
20550
  async function readClipboardFallback() {
20170
- const { platform: platform6 } = await import("os");
20551
+ const { platform: platform7 } = await import("os");
20171
20552
  const { execSync } = await import("child_process");
20172
20553
  const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
20173
20554
  const { join: join30 } = await import("path");
20174
- if (platform6() !== "linux")
20555
+ if (platform7() !== "linux")
20175
20556
  return null;
20176
20557
  const tmpPath = join30(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
20177
20558
  const commands = [
@@ -20847,7 +21228,7 @@ var init_auto_fixer = __esm(() => {
20847
21228
  import { spawn as spawn7, execSync } from "child_process";
20848
21229
  import { existsSync as existsSync37, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
20849
21230
  import { resolve as resolve17, extname as extname6, join as join31 } from "path";
20850
- import { platform as platform6 } from "os";
21231
+ import { platform as platform7 } from "os";
20851
21232
  function lintCacheKey(baseDir, lintScript) {
20852
21233
  return `${baseDir}::${lintScript}`;
20853
21234
  }
@@ -20868,7 +21249,7 @@ function contentHash2(content) {
20868
21249
  function getWinDecoder() {
20869
21250
  if (_winDecoder !== undefined)
20870
21251
  return _winDecoder;
20871
- if (platform6() !== "win32") {
21252
+ if (platform7() !== "win32") {
20872
21253
  _winDecoder = new TextDecoder("utf-8");
20873
21254
  return _winDecoder;
20874
21255
  }
@@ -21029,6 +21410,10 @@ ${stdout}`;
21029
21410
  if (!existsSync37(tsconfigPath)) {
21030
21411
  return;
21031
21412
  }
21413
+ const tsc = resolveTscCommand(projectRoot);
21414
+ if (!tsc) {
21415
+ return;
21416
+ }
21032
21417
  const now = Date.now();
21033
21418
  if (this._checkPromise && now - this._checkTimestamp < TYPE_CHECK_DEBOUNCE_MS) {
21034
21419
  const error2 = await this._checkPromise;
@@ -21040,10 +21425,10 @@ ${stdout}`;
21040
21425
  return;
21041
21426
  }
21042
21427
  this._checkTimestamp = now;
21043
- this._checkPromise = this.runTscCheck(projectRoot, signal);
21428
+ this._checkPromise = this.runTscCheck(tsc, projectRoot, signal);
21044
21429
  const error = await this._checkPromise;
21045
21430
  if (error) {
21046
- const { stdout, stderr } = await runAsync(`npx tsc --noEmit --skipLibCheck 2>&1`, projectRoot, 30000, signal).catch(() => ({ stdout: "", stderr: error }));
21431
+ const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck 2>&1`, projectRoot, 30000, signal).catch(() => ({ stdout: "", stderr: error }));
21047
21432
  const output = stderr || stdout;
21048
21433
  const errors = parseTscOutput(output);
21049
21434
  if (errors.length > 0) {
@@ -21072,9 +21457,9 @@ ${stdout}`;
21072
21457
  }
21073
21458
  }
21074
21459
  }
21075
- async runTscCheck(baseDir, signal) {
21460
+ async runTscCheck(tsc, baseDir, signal) {
21076
21461
  try {
21077
- const { stdout, stderr } = await runAsync(`npx tsc --noEmit --skipLibCheck`, baseDir, 30000, signal);
21462
+ const { stdout, stderr } = await runAsync(`${tsc} --noEmit --skipLibCheck`, baseDir, 30000, signal);
21078
21463
  return null;
21079
21464
  } catch (err) {
21080
21465
  if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
@@ -21560,7 +21945,7 @@ function switchStepToDelete(plan, step, save) {
21560
21945
  }
21561
21946
 
21562
21947
  // src/modules/execution/execution-plugin.ts
21563
- import { platform as platform7 } from "os";
21948
+ import { platform as platform8 } from "os";
21564
21949
  function normalizeBrokenPath(p) {
21565
21950
  return toForwardSlash(p).replace(/^\.\//, "");
21566
21951
  }
@@ -21793,7 +22178,7 @@ Last compile error: ${first[1]}`;
21793
22178
  if (call.name === "bash") {
21794
22179
  deps.stuckDetector.recordBashAttempt(false);
21795
22180
  }
21796
- if (call.name === "bash" && platform7() === "win32") {
22181
+ if (call.name === "bash" && platform8() === "win32") {
21797
22182
  const cmd = String(call.arguments?.command ?? "");
21798
22183
  const forbidden = forbiddenWindowsCommand(cmd);
21799
22184
  if (forbidden) {
@@ -23687,7 +24072,7 @@ class ProfileCompressor {
23687
24072
  // src/modules/user-profile/profile.ts
23688
24073
  import { readFileSync as readFileSync26, writeFileSync as writeFileSync17, existsSync as existsSync42, mkdirSync as mkdirSync18 } from "fs";
23689
24074
  import { join as join35 } from "path";
23690
- import { homedir as homedir10, hostname, platform as platform8, type } from "os";
24075
+ import { homedir as homedir10, hostname, platform as platform9, type } from "os";
23691
24076
  import { env } from "process";
23692
24077
 
23693
24078
  class UserProfile {
@@ -23699,7 +24084,7 @@ class UserProfile {
23699
24084
  }
23700
24085
  collect() {
23701
24086
  this.info = {
23702
- platform: platform8(),
24087
+ platform: platform9(),
23703
24088
  os: `${type()} ${hostname()}`,
23704
24089
  hostname: hostname(),
23705
24090
  shell: env.SHELL || env.ComSpec || "unknown",
@@ -24021,7 +24406,7 @@ var init_browser2 = __esm(() => {
24021
24406
  // src/modules/lsp/client.ts
24022
24407
  import { spawn as spawn8, execSync as execSync2 } from "child_process";
24023
24408
  import { resolve as resolve19 } from "path";
24024
- import { platform as platform9 } from "os";
24409
+ import { platform as platform10 } from "os";
24025
24410
 
24026
24411
  class FileOnlyLogger {
24027
24412
  logger;
@@ -24141,7 +24526,7 @@ class LspClient {
24141
24526
  if (config.command === "npx" && effectiveArgs.length > 0) {
24142
24527
  const binaryName = this.extractBinaryFromNpxArgs(effectiveArgs);
24143
24528
  if (binaryName) {
24144
- const whichCmd = platform9() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
24529
+ const whichCmd = platform10() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
24145
24530
  try {
24146
24531
  execSync2(whichCmd, { stdio: "pipe", timeout: 3000 });
24147
24532
  effectiveCommand = binaryName;
@@ -24154,7 +24539,7 @@ class LspClient {
24154
24539
  }
24155
24540
  return new Promise((resolve20, reject) => {
24156
24541
  const args = effectiveArgs;
24157
- const isWin = platform9() === "win32";
24542
+ const isWin = platform10() === "win32";
24158
24543
  let spawnCommand = resolveSpawnCommand(effectiveCommand);
24159
24544
  let spawnArgs = args;
24160
24545
  const spawnOpts = {
@@ -24760,8 +25145,11 @@ async function runCheck(config, baseDir, deps) {
24760
25145
  return { lines: lines.slice(0, STARTUP_CHECK_ERROR_CAP) };
24761
25146
  }
24762
25147
  async function runTscDefault(projectRoot, timeoutMs) {
25148
+ const tsc = resolveTscCommand(projectRoot);
25149
+ if (!tsc)
25150
+ return [];
24763
25151
  return new Promise((resolve22) => {
24764
- const child = spawn9("npx", ["tsc", "--noEmit", "--skipLibCheck"], {
25152
+ const child = spawn9(`${tsc} --noEmit --skipLibCheck`, {
24765
25153
  cwd: projectRoot,
24766
25154
  shell: true,
24767
25155
  windowsHide: true,
@@ -25165,7 +25553,7 @@ class IndexCache {
25165
25553
  }
25166
25554
  }
25167
25555
  }
25168
- var init_cache = () => {};
25556
+ var init_cache2 = () => {};
25169
25557
 
25170
25558
  // src/modules/indexer/map-select.ts
25171
25559
  function isNoiseFile(file) {
@@ -25637,7 +26025,7 @@ ${stackLine}` : summary;
25637
26025
  var MAP_FILE_LIMIT = 80;
25638
26026
  var init_module6 = __esm(() => {
25639
26027
  init_walker();
25640
- init_cache();
26028
+ init_cache2();
25641
26029
  init_map_select();
25642
26030
  init_project_profile();
25643
26031
  init_i18n();
@@ -25648,7 +26036,7 @@ var init_module6 = __esm(() => {
25648
26036
  // src/modules/indexer/index.ts
25649
26037
  var init_indexer = __esm(() => {
25650
26038
  init_walker();
25651
- init_cache();
26039
+ init_cache2();
25652
26040
  init_module6();
25653
26041
  });
25654
26042
 
@@ -25993,7 +26381,7 @@ import { spawnSync as spawnSync3 } from "child_process";
25993
26381
  import { createRequire as createRequire2 } from "module";
25994
26382
  import { join as join43, dirname as dirname16 } from "path";
25995
26383
  import { fileURLToPath as fileURLToPath3 } from "url";
25996
- import { arch, homedir as homedir12, hostname as hostname2, platform as platform10, release } from "os";
26384
+ import { arch, homedir as homedir12, hostname as hostname2, platform as platform11, release } from "os";
25997
26385
  import { env as env2 } from "process";
25998
26386
  function readEngineRequirement() {
25999
26387
  const here = dirname16(fileURLToPath3(import.meta.url));
@@ -26139,7 +26527,7 @@ function collectEnvironment(opts) {
26139
26527
  mmaVersion: readMmaVersion(),
26140
26528
  runtime,
26141
26529
  os: {
26142
- platform: platform10(),
26530
+ platform: platform11(),
26143
26531
  arch: arch(),
26144
26532
  release: release(),
26145
26533
  hostname: hostname2(),
@@ -26496,6 +26884,7 @@ var init_set_thinking = __esm(() => {
26496
26884
  // src/core/bootstrap.ts
26497
26885
  var exports_bootstrap = {};
26498
26886
  __export(exports_bootstrap, {
26887
+ setOneShotMode: () => setOneShotMode,
26499
26888
  buildSystemInfo: () => buildSystemInfo,
26500
26889
  bootstrap: () => bootstrap
26501
26890
  });
@@ -26611,6 +27000,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
26611
27000
  return lines.join(`
26612
27001
  `);
26613
27002
  }
27003
+ function setOneShotMode(value) {
27004
+ oneShotMode = value;
27005
+ }
26614
27006
  async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reasoningLevel) {
26615
27007
  const dir = configDir || process.env.MMA_CONFIG_DIR || join46(homedir15(), ".mma");
26616
27008
  const projectConfigPath = projectDir ? join46(projectDir, ".mmrc") : join46(process.cwd(), ".mmrc");
@@ -26846,7 +27238,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
26846
27238
  moduleRegistry.register(lspModule);
26847
27239
  registerBuiltinPlugin(pluginManager, lspModule.getPlugin());
26848
27240
  let startupCheckBlock = null;
26849
- const startupCheckPromise = !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir, { logger: lspLogger }) : Promise.resolve(null);
27241
+ const startupCheckPromise = !oneShotMode && !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir, { logger: lspLogger }) : Promise.resolve(null);
26850
27242
  const moduleTools = moduleRegistry.collectToolDefinitions();
26851
27243
  for (const tool of moduleTools) {
26852
27244
  toolRegistry.register(tool);
@@ -27001,6 +27393,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
27001
27393
  contextProbe: contextProbePromise
27002
27394
  };
27003
27395
  }
27396
+ var oneShotMode = false;
27004
27397
  var init_bootstrap = __esm(() => {
27005
27398
  init_config2();
27006
27399
  init_app_logger();
@@ -27476,9 +27869,9 @@ class Spinner {
27476
27869
  tick() {
27477
27870
  if (!this.timer)
27478
27871
  return;
27479
- const frame = FRAMES[this.frame % FRAMES.length];
27872
+ const frame2 = FRAMES[this.frame % FRAMES.length];
27480
27873
  this.frame++;
27481
- this.stream.write("\r" + pc2.cyan(frame) + " " + this.message + "\x1B[K");
27874
+ this.stream.write("\r" + pc2.cyan(frame2) + " " + this.message + "\x1B[K");
27482
27875
  }
27483
27876
  }
27484
27877
  var FRAMES;
@@ -30074,9 +30467,9 @@ var require_stringifyNumber = __commonJS((exports) => {
30074
30467
  function stringifyNumber({ format, minFractionDigits, tag, value }) {
30075
30468
  if (typeof value === "bigint")
30076
30469
  return String(value);
30077
- const num = typeof value === "number" ? value : Number(value);
30078
- if (!isFinite(num))
30079
- return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
30470
+ const num3 = typeof value === "number" ? value : Number(value);
30471
+ if (!isFinite(num3))
30472
+ return isNaN(num3) ? ".nan" : num3 < 0 ? "-.inf" : ".inf";
30080
30473
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
30081
30474
  if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
30082
30475
  let i = n.indexOf(".");
@@ -30113,8 +30506,8 @@ var require_float = __commonJS((exports) => {
30113
30506
  test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
30114
30507
  resolve: (str) => parseFloat(str),
30115
30508
  stringify(node) {
30116
- const num = Number(node.value);
30117
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
30509
+ const num3 = Number(node.value);
30510
+ return isFinite(num3) ? num3.toExponential() : stringifyNumber.stringifyNumber(node);
30118
30511
  }
30119
30512
  };
30120
30513
  var float = {
@@ -30518,8 +30911,8 @@ var require_float2 = __commonJS((exports) => {
30518
30911
  test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
30519
30912
  resolve: (str) => parseFloat(str.replace(/_/g, "")),
30520
30913
  stringify(node) {
30521
- const num = Number(node.value);
30522
- return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
30914
+ const num3 = Number(node.value);
30915
+ return isFinite(num3) ? num3.toExponential() : stringifyNumber.stringifyNumber(node);
30523
30916
  }
30524
30917
  };
30525
30918
  var float = {
@@ -30709,23 +31102,23 @@ var require_timestamp = __commonJS((exports) => {
30709
31102
  function parseSexagesimal(str, asBigInt) {
30710
31103
  const sign = str[0];
30711
31104
  const parts = sign === "-" || sign === "+" ? str.substring(1) : str;
30712
- const num = (n) => asBigInt ? BigInt(n) : Number(n);
30713
- const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0));
30714
- return sign === "-" ? num(-1) * res : res;
31105
+ const num3 = (n) => asBigInt ? BigInt(n) : Number(n);
31106
+ const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num3(60) + num3(p), num3(0));
31107
+ return sign === "-" ? num3(-1) * res : res;
30715
31108
  }
30716
31109
  function stringifySexagesimal(node) {
30717
31110
  let { value } = node;
30718
- let num = (n) => n;
31111
+ let num3 = (n) => n;
30719
31112
  if (typeof value === "bigint")
30720
- num = (n) => BigInt(n);
31113
+ num3 = (n) => BigInt(n);
30721
31114
  else if (isNaN(value) || !isFinite(value))
30722
31115
  return stringifyNumber.stringifyNumber(node);
30723
31116
  let sign = "";
30724
31117
  if (value < 0) {
30725
31118
  sign = "-";
30726
- value *= num(-1);
31119
+ value *= num3(-1);
30727
31120
  }
30728
- const _60 = num(60);
31121
+ const _60 = num3(60);
30729
31122
  const parts = [value % _60];
30730
31123
  if (value < 60) {
30731
31124
  parts.unshift(0);
@@ -36316,100 +36709,172 @@ function registerConfigCommands(ctx) {
36316
36709
  });
36317
36710
  }
36318
36711
  function registerProviderCommands(ctx) {
36712
+ const list = () => {
36713
+ const providers = ctx.agent.listProviders();
36714
+ if (providers.length === 0) {
36715
+ console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
36716
+ console.log(` ${ctx.config.provider.baseUrl}`);
36717
+ return;
36718
+ }
36719
+ console.log(t("repl.provider_current"));
36720
+ for (const p of providers) {
36721
+ const marker = p.active ? pc2.green("* ") : " ";
36722
+ console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}`);
36723
+ }
36724
+ };
36725
+ const use = async (args) => {
36726
+ const name = args[0];
36727
+ if (!name) {
36728
+ console.log(t("repl.provider_usage"));
36729
+ return;
36730
+ }
36731
+ try {
36732
+ await ctx.agent.setProvider(name);
36733
+ ctx.refreshModelCache();
36734
+ console.log(pc2.green(t("repl.provider_set", { name })));
36735
+ } catch (e) {
36736
+ console.log(pc2.red(e.message));
36737
+ }
36738
+ };
36739
+ const add = async (args) => {
36740
+ const flags = {};
36741
+ let name;
36742
+ for (let i = 0;i < args.length; i++) {
36743
+ const arg = args[i];
36744
+ if (arg.startsWith("--")) {
36745
+ const value = args[i + 1];
36746
+ if (value === undefined || value.startsWith("--")) {
36747
+ console.log(pc2.red(t("repl.provider_add_missing_value", { flag: arg })));
36748
+ return;
36749
+ }
36750
+ flags[arg.slice(2)] = value;
36751
+ i++;
36752
+ } else if (name === undefined) {
36753
+ name = arg;
36754
+ }
36755
+ }
36756
+ if (!name) {
36757
+ console.log(t("repl.provider_usage"));
36758
+ return;
36759
+ }
36760
+ const entries = Array.isArray(ctx.config.provider.entries) ? ctx.config.provider.entries : [];
36761
+ if (entries.length === 0) {
36762
+ entries.push({
36763
+ type: ctx.config.provider.type,
36764
+ label: ctx.config.provider.type,
36765
+ baseUrl: ctx.config.provider.baseUrl,
36766
+ apiKey: ctx.config.provider.apiKey
36767
+ });
36768
+ }
36769
+ const baseUrl = flags.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
36770
+ if (!baseUrl) {
36771
+ console.log(pc2.red(t("cli.provider_no_url", { name })));
36772
+ return;
36773
+ }
36774
+ entries.push({
36775
+ type: name,
36776
+ label: name,
36777
+ baseUrl,
36778
+ apiKey: flags.key,
36779
+ ...flags.priority !== undefined ? { priority: Number(flags.priority) } : {},
36780
+ ...flags["context-window"] !== undefined ? { contextWindow: Number(flags["context-window"]) } : {},
36781
+ ...flags.rpm !== undefined || flags.parallel !== undefined ? {
36782
+ rateLimits: {
36783
+ maxRequestsPerMinute: Number(flags.rpm ?? 0) || 60,
36784
+ maxParallelTasks: Number(flags.parallel ?? 0) || 1
36785
+ }
36786
+ } : {}
36787
+ });
36788
+ ctx.config.provider.entries = entries;
36789
+ ctx.config.provider.active = ctx.config.provider.active || ctx.config.provider.type;
36790
+ const configPath = join54(ctx.configDir, "config.json");
36791
+ saveConfig(ctx.config, configPath, dirname24(configPath));
36792
+ await ctx.agent.reconfigure(ctx.config);
36793
+ console.log(pc2.green(t("cli.provider_added", { name })));
36794
+ console.log(t("repl.provider_switch_hint", { name }));
36795
+ };
36796
+ const handlers = {
36797
+ list,
36798
+ use,
36799
+ add
36800
+ };
36319
36801
  ctx.registerCommand({
36320
36802
  name: "provider",
36321
36803
  description: t("repl.provider_list"),
36322
36804
  usage: t("repl.provider_usage"),
36323
36805
  action: async (args) => {
36324
36806
  const subcmd = args[0];
36325
- if (!subcmd || subcmd === "list") {
36326
- const providers = ctx.agent.listProviders();
36327
- if (providers.length === 0) {
36328
- console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
36329
- console.log(` ${ctx.config.provider.baseUrl}`);
36330
- } else {
36331
- console.log(t("repl.provider_current"));
36332
- for (const p of providers) {
36333
- const marker = p.active ? pc2.green("* ") : " ";
36334
- console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}`);
36335
- }
36336
- }
36807
+ const handler = subcmd ? handlers[subcmd] : handlers.list;
36808
+ if (!handler) {
36809
+ console.log(t("repl.provider_usage"));
36337
36810
  return;
36338
36811
  }
36339
- if (subcmd === "use") {
36340
- const name = args[1];
36341
- if (!name) {
36342
- console.log(t("repl.provider_usage"));
36343
- return;
36344
- }
36345
- try {
36346
- await ctx.agent.setProvider(name);
36347
- ctx.refreshModelCache();
36348
- console.log(pc2.green(t("repl.provider_set", { name })));
36349
- } catch (e) {
36350
- console.log(pc2.red(e.message));
36812
+ await handler(args.slice(1));
36813
+ }
36814
+ });
36815
+ const listModels = async () => {
36816
+ console.log(`${t("repl.model_current")} ${ctx.config.model}`);
36817
+ const { OpenAICompatProvider: OpenAICompatProvider2 } = await Promise.resolve().then(() => (init_openai_compat(), exports_openai_compat));
36818
+ const provider = new OpenAICompatProvider2({
36819
+ model: ctx.config.model,
36820
+ baseUrl: ctx.config.provider.baseUrl,
36821
+ apiKey: ctx.config.provider.apiKey,
36822
+ contextWindow: ctx.config.contextWindow
36823
+ });
36824
+ const { Spinner: Spinner2 } = await Promise.resolve().then(() => (init_spinner(), exports_spinner));
36825
+ const s = new Spinner2;
36826
+ s.start(t("cli.fetching_models"));
36827
+ try {
36828
+ const models = await provider.listModels();
36829
+ s.stop();
36830
+ if (models.length > 0) {
36831
+ ctx.refreshModelCache();
36832
+ console.log(t("cli.available_models"));
36833
+ const { getCertMark: getCertMark2 } = await Promise.resolve().then(() => (init_manifest(), exports_manifest));
36834
+ for (const m of models) {
36835
+ const marker = m === ctx.config.model ? pc2.green("* ") : " ";
36836
+ const mark = getCertMark2(m, ctx.config.provider.baseUrl, version2, process.cwd());
36837
+ const cert = mark === "certified" ? pc2.green("✔") : mark === "stale" ? pc2.yellow("○") : pc2.dim("·");
36838
+ const label = mark === "certified" ? ` ${pc2.green(t("cli.cert_label"))}` : mark === "stale" ? ` ${pc2.yellow(t("cli.cert_stale_label"))}` : "";
36839
+ console.log(` ${marker}${cert} ${m}${label}`);
36351
36840
  }
36352
- return;
36841
+ } else {
36842
+ console.log(t("cli.no_models_found"));
36353
36843
  }
36354
- console.log(t("repl.provider_usage"));
36844
+ } catch (err) {
36845
+ s.stop();
36846
+ console.log(t("cli.model_fetch_failed", { error: String(err) }));
36355
36847
  }
36356
- });
36848
+ console.log(t("cli.model_hint"));
36849
+ };
36850
+ const useModel = async (args) => {
36851
+ const name = args[0];
36852
+ if (!name) {
36853
+ console.log(t("repl.model_usage"));
36854
+ return;
36855
+ }
36856
+ ctx.config.model = name;
36857
+ const configPath = join54(ctx.configDir, "config.json");
36858
+ saveConfig(ctx.config, configPath, dirname24(configPath));
36859
+ await ctx.agent.reconfigure(ctx.config);
36860
+ console.log(pc2.green(t("repl.model_set", { name })));
36861
+ };
36862
+ const modelHandlers = {
36863
+ list: listModels,
36864
+ use: useModel
36865
+ };
36357
36866
  ctx.registerCommand({
36358
36867
  name: "model",
36359
36868
  description: t("repl.model_list"),
36360
36869
  usage: t("repl.model_usage"),
36361
36870
  action: async (args) => {
36362
36871
  const subcmd = args[0];
36363
- if (!subcmd || subcmd === "list") {
36364
- console.log(`${t("repl.model_current")} ${ctx.config.model}`);
36365
- const { OpenAICompatProvider: OpenAICompatProvider2 } = await Promise.resolve().then(() => (init_openai_compat(), exports_openai_compat));
36366
- const provider = new OpenAICompatProvider2({
36367
- model: ctx.config.model,
36368
- baseUrl: ctx.config.provider.baseUrl,
36369
- apiKey: ctx.config.provider.apiKey,
36370
- contextWindow: ctx.config.contextWindow
36371
- });
36372
- const { Spinner: Spinner2 } = await Promise.resolve().then(() => (init_spinner(), exports_spinner));
36373
- const s = new Spinner2;
36374
- s.start(t("cli.fetching_models"));
36375
- try {
36376
- const models = await provider.listModels();
36377
- s.stop();
36378
- if (models.length > 0) {
36379
- ctx.refreshModelCache();
36380
- console.log(t("cli.available_models"));
36381
- const { getCertMark: getCertMark2 } = await Promise.resolve().then(() => (init_manifest(), exports_manifest));
36382
- for (const m of models) {
36383
- const marker = m === ctx.config.model ? pc2.green("* ") : " ";
36384
- const mark = getCertMark2(m, ctx.config.provider.baseUrl, version2, process.cwd());
36385
- const cert = mark === "certified" ? pc2.green("✔") : mark === "stale" ? pc2.yellow("○") : pc2.dim("·");
36386
- const label = mark === "certified" ? ` ${pc2.green(t("cli.cert_label"))}` : mark === "stale" ? ` ${pc2.yellow(t("cli.cert_stale_label"))}` : "";
36387
- console.log(` ${marker}${cert} ${m}${label}`);
36388
- }
36389
- } else {
36390
- console.log(t("cli.no_models_found"));
36391
- }
36392
- } catch (err) {
36393
- s.stop();
36394
- console.log(t("cli.model_fetch_failed", { error: String(err) }));
36395
- }
36396
- console.log(t("cli.model_hint"));
36397
- return;
36398
- }
36399
- if (subcmd === "use") {
36400
- const name = args[1];
36401
- if (!name) {
36402
- console.log(t("repl.model_usage"));
36403
- return;
36404
- }
36405
- ctx.config.model = name;
36406
- const configPath = join54(ctx.configDir, "config.json");
36407
- saveConfig(ctx.config, configPath, dirname24(configPath));
36408
- await ctx.agent.reconfigure(ctx.config);
36409
- console.log(pc2.green(t("repl.model_set", { name })));
36872
+ const handler = subcmd ? modelHandlers[subcmd] : modelHandlers.list;
36873
+ if (!handler) {
36874
+ console.log(t("repl.model_usage"));
36410
36875
  return;
36411
36876
  }
36412
- console.log(t("repl.model_usage"));
36877
+ await handler(args.slice(1));
36413
36878
  }
36414
36879
  });
36415
36880
  ctx.registerCommand({
@@ -36718,88 +37183,97 @@ function registerSessionCommands(ctx) {
36718
37183
  function registerSkillCommands(ctx) {
36719
37184
  if (!ctx.skillsModule)
36720
37185
  return;
37186
+ const listSkills = () => {
37187
+ const available = ctx.skillsModule.getAvailable();
37188
+ if (available.length === 0) {
37189
+ console.log(t("repl.no_skills"));
37190
+ return;
37191
+ }
37192
+ console.log(pc2.bold(t("repl.available_skills")));
37193
+ for (const skill of available) {
37194
+ const tokens = estimateTokens(skill.content);
37195
+ const loaded = ctx.skillsModule.getLoaded().some((s) => s.name === skill.name);
37196
+ const marker = loaded ? pc2.green(" [loaded]") : "";
37197
+ console.log(` ${pc2.cyan(skill.name)}${marker} ${pc2.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
37198
+ }
37199
+ };
37200
+ const listLoaded = () => {
37201
+ const loaded = ctx.skillsModule.getLoaded();
37202
+ const budget = ctx.skillsModule.getBudget();
37203
+ if (loaded.length === 0) {
37204
+ console.log(t("repl.no_loaded"));
37205
+ return;
37206
+ }
37207
+ console.log(pc2.bold(t("repl.loaded_skills")));
37208
+ for (const skill of loaded) {
37209
+ const tokens = estimateTokens(skill.content);
37210
+ console.log(` ${pc2.cyan(skill.name)} ${pc2.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
37211
+ }
37212
+ console.log(pc2.dim(`
37213
+ ${t("repl.budget", { used: budget.used, total: budget.total, remaining: budget.remaining })}`));
37214
+ };
37215
+ const loadSkill = (args) => {
37216
+ const arg = args.join(" ");
37217
+ if (!arg) {
37218
+ console.log(t("repl.skill_load_usage"));
37219
+ return;
37220
+ }
37221
+ const result = ctx.skillsModule.loadByName(arg);
37222
+ if (result.success) {
37223
+ console.log(pc2.green(result.message));
37224
+ } else {
37225
+ console.log(pc2.red(result.message));
37226
+ }
37227
+ };
37228
+ const unloadSkill = (args) => {
37229
+ const arg = args.join(" ");
37230
+ if (!arg) {
37231
+ console.log(t("repl.skill_unload_usage"));
37232
+ return;
37233
+ }
37234
+ if (ctx.skillsModule.unload(arg)) {
37235
+ console.log(pc2.green(t("repl.skill_unloaded", { name: arg })));
37236
+ } else {
37237
+ console.log(pc2.red(t("repl.skill_not_loaded", { name: arg })));
37238
+ }
37239
+ };
37240
+ const searchSkills = (args) => {
37241
+ const arg = args.join(" ");
37242
+ if (!arg) {
37243
+ console.log(t("repl.skill_search_usage"));
37244
+ return;
37245
+ }
37246
+ const results = ctx.skillsModule.search(arg);
37247
+ if (results.length === 0) {
37248
+ console.log(t("repl.no_skill_match", { query: arg }));
37249
+ return;
37250
+ }
37251
+ console.log(pc2.bold(t("repl.skills_matching", { query: arg })));
37252
+ for (const skill of results) {
37253
+ const tokens = estimateTokens(skill.content);
37254
+ console.log(` ${pc2.cyan(skill.name)} ${pc2.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
37255
+ }
37256
+ };
37257
+ const skillHandlers = {
37258
+ list: listSkills,
37259
+ loaded: listLoaded,
37260
+ load: loadSkill,
37261
+ unload: unloadSkill,
37262
+ search: searchSkills
37263
+ };
36721
37264
  ctx.registerCommand({
36722
37265
  name: "skill",
36723
37266
  description: t("repl.skill"),
36724
37267
  usage: t("repl.skill_usage"),
36725
37268
  action: (args) => {
36726
37269
  const subcmd = args[0];
36727
- const arg = args.slice(1).join(" ");
36728
- if (!subcmd || subcmd === "list") {
36729
- const available = ctx.skillsModule.getAvailable();
36730
- if (available.length === 0) {
36731
- console.log(t("repl.no_skills"));
36732
- return;
36733
- }
36734
- console.log(pc2.bold(t("repl.available_skills")));
36735
- for (const skill of available) {
36736
- const tokens = estimateTokens(skill.content);
36737
- const loaded = ctx.skillsModule.getLoaded().some((s) => s.name === skill.name);
36738
- const marker = loaded ? pc2.green(" [loaded]") : "";
36739
- console.log(` ${pc2.cyan(skill.name)}${marker} ${pc2.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
36740
- }
36741
- return;
36742
- }
36743
- if (subcmd === "loaded") {
36744
- const loaded = ctx.skillsModule.getLoaded();
36745
- const budget = ctx.skillsModule.getBudget();
36746
- if (loaded.length === 0) {
36747
- console.log(t("repl.no_loaded"));
36748
- return;
36749
- }
36750
- console.log(pc2.bold(t("repl.loaded_skills")));
36751
- for (const skill of loaded) {
36752
- const tokens = estimateTokens(skill.content);
36753
- console.log(` ${pc2.cyan(skill.name)} ${pc2.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
36754
- }
36755
- console.log(pc2.dim(`
36756
- ${t("repl.budget", { used: budget.used, total: budget.total, remaining: budget.remaining })}`));
37270
+ const handler = subcmd ? skillHandlers[subcmd] : skillHandlers.list;
37271
+ if (!handler) {
37272
+ console.log(pc2.red(t("repl.skill_unknown_sub", { subcmd })));
37273
+ console.log(t("repl.skill_usage"));
36757
37274
  return;
36758
37275
  }
36759
- if (subcmd === "load") {
36760
- if (!arg) {
36761
- console.log(t("repl.skill_load_usage"));
36762
- return;
36763
- }
36764
- const result = ctx.skillsModule.loadByName(arg);
36765
- if (result.success) {
36766
- console.log(pc2.green(result.message));
36767
- } else {
36768
- console.log(pc2.red(result.message));
36769
- }
36770
- return;
36771
- }
36772
- if (subcmd === "unload") {
36773
- if (!arg) {
36774
- console.log(t("repl.skill_unload_usage"));
36775
- return;
36776
- }
36777
- if (ctx.skillsModule.unload(arg)) {
36778
- console.log(pc2.green(t("repl.skill_unloaded", { name: arg })));
36779
- } else {
36780
- console.log(pc2.red(t("repl.skill_not_loaded", { name: arg })));
36781
- }
36782
- return;
36783
- }
36784
- if (subcmd === "search") {
36785
- if (!arg) {
36786
- console.log(t("repl.skill_search_usage"));
36787
- return;
36788
- }
36789
- const results = ctx.skillsModule.search(arg);
36790
- if (results.length === 0) {
36791
- console.log(t("repl.no_skill_match", { query: arg }));
36792
- return;
36793
- }
36794
- console.log(pc2.bold(t("repl.skills_matching", { query: arg })));
36795
- for (const skill of results) {
36796
- const tokens = estimateTokens(skill.content);
36797
- console.log(` ${pc2.cyan(skill.name)} ${pc2.dim(`(${tokens} tokens)`)} ${skill.description.slice(0, 60)}`);
36798
- }
36799
- return;
36800
- }
36801
- console.log(pc2.red(t("repl.skill_unknown_sub", { subcmd })));
36802
- console.log(t("repl.skill_usage"));
37276
+ handler(args.slice(1));
36803
37277
  }
36804
37278
  });
36805
37279
  }
@@ -36832,6 +37306,7 @@ var init_repl_commands = __esm(() => {
36832
37306
  init_i18n();
36833
37307
  init_setup();
36834
37308
  init_config2();
37309
+ init_presets();
36835
37310
  init_budget();
36836
37311
  init_token_counter();
36837
37312
  init_map_command();
@@ -37566,6 +38041,63 @@ init_version();
37566
38041
  init_map_command();
37567
38042
  init_budget();
37568
38043
 
38044
+ // src/llm/provider-budget.ts
38045
+ var DEFAULT_TIMEOUT_MS = 5000;
38046
+ function num2(value) {
38047
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
38048
+ }
38049
+ async function getJson(url, apiKey, timeoutMs) {
38050
+ const response = await fetch(url, {
38051
+ headers: { Authorization: `Bearer ${apiKey}` },
38052
+ signal: AbortSignal.timeout(timeoutMs)
38053
+ });
38054
+ if (!response.ok) {
38055
+ throw new Error(`HTTP ${response.status}`);
38056
+ }
38057
+ return response.json();
38058
+ }
38059
+ async function fetchProviderBudget(provider, baseUrl, apiKey, options) {
38060
+ if (provider !== "openrouter") {
38061
+ options.log("debug", `budget: provider "${provider}" does not expose an API balance`);
38062
+ return { budget: null, reason: "unsupported" };
38063
+ }
38064
+ if (!apiKey) {
38065
+ options.log("debug", "budget: no api key configured");
38066
+ return { budget: null, reason: "no-key" };
38067
+ }
38068
+ const base = baseUrl.replace(/\/$/, "");
38069
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
38070
+ try {
38071
+ const keyBody = await getJson(`${base}/key`, apiKey, timeoutMs);
38072
+ const data = keyBody?.data ?? {};
38073
+ const budget = { provider, source: "openrouter" };
38074
+ budget.keyLabel = typeof data.label === "string" ? data.label : undefined;
38075
+ budget.keyUsageUsd = num2(data.usage);
38076
+ budget.keyLimitUsd = num2(data.limit);
38077
+ budget.keyRemainingUsd = num2(data.limit_remaining);
38078
+ budget.isFreeTier = typeof data.is_free_tier === "boolean" ? data.is_free_tier : undefined;
38079
+ try {
38080
+ const creditsBody = await getJson(`${base}/credits`, apiKey, timeoutMs);
38081
+ const c = creditsBody?.data ?? {};
38082
+ budget.totalCreditsUsd = num2(c.total_credits);
38083
+ budget.totalUsageUsd = num2(c.total_usage);
38084
+ if (budget.totalCreditsUsd !== undefined && budget.totalUsageUsd !== undefined) {
38085
+ budget.balanceUsd = budget.totalCreditsUsd - budget.totalUsageUsd;
38086
+ }
38087
+ } catch (creditsErr) {
38088
+ options.log("debug", `budget: /credits unavailable (${creditsErr instanceof Error ? creditsErr.message : String(creditsErr)})`);
38089
+ }
38090
+ return { budget, reason: "ok" };
38091
+ } catch (err) {
38092
+ const message = err instanceof Error ? err.message : String(err);
38093
+ options.log("warn", `budget: request failed (${message})`);
38094
+ return { budget: null, reason: "error", error: message };
38095
+ }
38096
+ }
38097
+
38098
+ // src/cli/commands.ts
38099
+ init_prices();
38100
+
37569
38101
  // src/modules/updater/changelog-reader.ts
37570
38102
  import { readFileSync as readFileSync35, existsSync as existsSync53 } from "fs";
37571
38103
  import { dirname as dirname20, join as join48 } from "path";
@@ -37854,6 +38386,53 @@ function buildContextCommand(program2) {
37854
38386
  console.log(t("cli.context_set", { size: contextWindow }));
37855
38387
  });
37856
38388
  }
38389
+ function buildUsageCommand(program2) {
38390
+ program2.command("usage").description(t("cli.usage")).action(async () => {
38391
+ const { config, logger: logger4 } = await bootstrap();
38392
+ const { type: provider, baseUrl, apiKey } = config.provider;
38393
+ const result = await fetchProviderBudget(provider, baseUrl, apiKey, {
38394
+ log: (level, message) => level === "warn" ? logger4.warn(message) : logger4.debug(message)
38395
+ });
38396
+ if (result.reason === "unsupported") {
38397
+ console.log(t("cli.usage_unsupported", { provider }));
38398
+ return;
38399
+ }
38400
+ if (result.reason === "no-key") {
38401
+ console.log(t("cli.usage_no_key"));
38402
+ return;
38403
+ }
38404
+ if (result.reason === "error" || !result.budget) {
38405
+ console.log(t("cli.usage_error", { error: result.error ?? "" }));
38406
+ return;
38407
+ }
38408
+ const b = result.budget;
38409
+ let printed = false;
38410
+ if (b.keyUsageUsd !== undefined) {
38411
+ console.log(t("cli.usage_key_usage", { usage: formatCost(b.keyUsageUsd) }));
38412
+ printed = true;
38413
+ }
38414
+ if (b.keyLimitUsd !== undefined && b.keyRemainingUsd !== undefined) {
38415
+ console.log(t("cli.usage_key_limit", {
38416
+ limit: formatCost(b.keyLimitUsd),
38417
+ remaining: formatCost(b.keyRemainingUsd)
38418
+ }));
38419
+ printed = true;
38420
+ }
38421
+ if (b.balanceUsd !== undefined) {
38422
+ console.log(t("cli.usage_balance", { balance: formatCost(b.balanceUsd) }));
38423
+ printed = true;
38424
+ }
38425
+ if (b.totalCreditsUsd !== undefined && b.totalUsageUsd !== undefined) {
38426
+ console.log(t("cli.usage_account", {
38427
+ credits: formatCost(b.totalCreditsUsd),
38428
+ used: formatCost(b.totalUsageUsd)
38429
+ }));
38430
+ printed = true;
38431
+ }
38432
+ if (!printed)
38433
+ console.log(t("cli.usage_empty"));
38434
+ });
38435
+ }
37857
38436
  function buildMapCommand(program2) {
37858
38437
  program2.command("map").description(t("cli.map.description")).argument("[action]", t("cli.map.action"), "summary").argument("[query]", t("cli.map.query")).action(async (action, query) => {
37859
38438
  const { agent } = await bootstrap();
@@ -38010,6 +38589,31 @@ function buildSessionCommands(program2) {
38010
38589
  }
38011
38590
  }
38012
38591
  }
38592
+ const usageEvents = sessionManager.loadSessionLog(id).filter((e) => e.type === "llm_usage");
38593
+ if (usageEvents.length > 0) {
38594
+ let prompt = 0;
38595
+ let completion = 0;
38596
+ let cached = 0;
38597
+ for (const e of usageEvents) {
38598
+ prompt += e.promptTokens ?? 0;
38599
+ completion += e.completionTokens ?? 0;
38600
+ cached += e.cachedTokens ?? 0;
38601
+ }
38602
+ console.log("");
38603
+ console.log(pc2.cyan(t("cli.session_usage", {
38604
+ prompt,
38605
+ completion,
38606
+ total: prompt + completion
38607
+ })));
38608
+ if (cached > 0 && prompt > 0) {
38609
+ const hit = Math.round(cached / prompt * 100);
38610
+ console.log(pc2.dim(t("cli.session_cache", {
38611
+ hit,
38612
+ cached,
38613
+ uncached: Math.max(0, prompt - cached)
38614
+ })));
38615
+ }
38616
+ }
38013
38617
  });
38014
38618
  session2.command("delete").argument("<id>", "Session id").description(t("cli.delete_session")).action(async (id) => {
38015
38619
  const { sessionManager } = await bootstrap();
@@ -38049,6 +38653,7 @@ function createProgram() {
38049
38653
  buildConfigCommands(program2);
38050
38654
  buildModelCommands(program2);
38051
38655
  buildContextCommand(program2);
38656
+ buildUsageCommand(program2);
38052
38657
  buildMapCommand(program2);
38053
38658
  buildProviderCommands(program2);
38054
38659
  buildSessionCommands(program2);
@@ -38056,11 +38661,15 @@ function createProgram() {
38056
38661
  createPluginCommand(program2);
38057
38662
  buildChangelogCommand(program2);
38058
38663
  program2.argument("[prompt...]", "Prompt to execute").description("Run a single prompt").action((prompt) => {});
38059
- for (const cmd of program2.commands) {
38664
+ const attachOneShotExit = (cmd) => {
38060
38665
  cmd.hook("postAction", () => {
38061
38666
  process.exit(0);
38062
38667
  });
38063
- }
38668
+ for (const sub of cmd.commands)
38669
+ attachOneShotExit(sub);
38670
+ };
38671
+ for (const cmd of program2.commands)
38672
+ attachOneShotExit(cmd);
38064
38673
  return program2;
38065
38674
  }
38066
38675
 
@@ -39026,22 +39635,17 @@ class SkillNameProvider {
39026
39635
  return ctx.tokenIndex >= 2 && ctx.tokens[0] === "/skill" && (ctx.tokens[1] === "load" || ctx.tokens[1] === "unload");
39027
39636
  }
39028
39637
  complete(ctx) {
39029
- const subcmd = ctx.tokens[1];
39030
- if (subcmd === "load") {
39031
- const available = this.skillsModule.getAvailable();
39032
- const names = available.map((s) => s.name);
39033
- if (!ctx.partial)
39034
- return names;
39035
- return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
39036
- }
39037
- if (subcmd === "unload") {
39038
- const loaded = this.skillsModule.getLoaded();
39039
- const names = loaded.map((s) => s.name);
39040
- if (!ctx.partial)
39041
- return names;
39042
- return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
39043
- }
39044
- return [];
39638
+ const sources = {
39639
+ load: () => this.skillsModule.getAvailable().map((s) => s.name),
39640
+ unload: () => this.skillsModule.getLoaded().map((s) => s.name)
39641
+ };
39642
+ const source = sources[ctx.tokens[1]];
39643
+ if (!source)
39644
+ return [];
39645
+ const names = source();
39646
+ if (!ctx.partial)
39647
+ return names;
39648
+ return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
39045
39649
  }
39046
39650
  }
39047
39651
 
@@ -39693,6 +40297,28 @@ function stepContextForTool(plan, tool, args) {
39693
40297
  // src/cli/repl.ts
39694
40298
  init_prices();
39695
40299
 
40300
+ // src/cli/cache-line.ts
40301
+ init_i18n();
40302
+ init_prices();
40303
+ function formatCacheLine(cache) {
40304
+ if (!cache)
40305
+ return;
40306
+ const total = cache.cachedTokens + cache.uncachedTokens;
40307
+ const hit = Math.round(cache.hitRate * 100);
40308
+ if (total > 0) {
40309
+ if (cache.saved !== undefined && cache.saved > 0) {
40310
+ return t("repl.cache", { hit, saved: formatCost(cache.saved) });
40311
+ }
40312
+ return t("repl.cache_nosave", { hit });
40313
+ }
40314
+ const broken = cache.prefixCause !== undefined && cache.prefixCause !== "none" && cache.prefixCause !== "unknown";
40315
+ if (broken && cache.prefixStable !== undefined && cache.prefixStable < 0.98) {
40316
+ const stable = Math.round(cache.prefixStable * 100);
40317
+ return t("repl.prefix", { stable, cause: t(`cache.cause.${cache.prefixCause}`) });
40318
+ }
40319
+ return;
40320
+ }
40321
+
39696
40322
  // src/ui/output.ts
39697
40323
  function writeWarning(text) {
39698
40324
  process.stdout.write(formatWarning(text) + `
@@ -39872,7 +40498,7 @@ class Repl {
39872
40498
  this.completer.registerProvider(new SubcommandProvider("skill", ["list", "loaded", "load", "unload", "search"]));
39873
40499
  this.completer.registerProvider(new SkillNameProvider(this.skillsModule));
39874
40500
  }
39875
- this.completer.registerProvider(new SubcommandProvider("provider", ["list", "use"]));
40501
+ this.completer.registerProvider(new SubcommandProvider("provider", ["list", "use", "add"]));
39876
40502
  this.completer.registerProvider(new SubcommandProvider("model", ["list", "use"]));
39877
40503
  const entryLabels = this.config.provider?.entries?.map((e) => e.label || e.type) ?? [];
39878
40504
  if (entryLabels.length > 0) {
@@ -40199,6 +40825,10 @@ ${t("image.clipboard_empty")}`));
40199
40825
  if (result.totalCost !== undefined && result.totalCost > 0) {
40200
40826
  console.log(pc2.yellow(` ${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
40201
40827
  }
40828
+ const cacheLine = formatCacheLine(result.cache);
40829
+ if (cacheLine) {
40830
+ console.log(pc2.dim(` ${cacheLine}`));
40831
+ }
40202
40832
  } else if (ui?.showCompaction && result.compactionCount !== undefined) {
40203
40833
  if (result.compactionCount > this.lastCompactionShown) {
40204
40834
  this.lastCompactionShown = result.compactionCount;
@@ -40383,6 +41013,10 @@ function printRunResult(result, flush) {
40383
41013
  } else if (result.totalTokens !== undefined && result.totalTokens > 0) {
40384
41014
  console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
40385
41015
  }
41016
+ const cacheLine = formatCacheLine(result.cache);
41017
+ if (cacheLine) {
41018
+ console.log(pc2.dim(` ${cacheLine}`));
41019
+ }
40386
41020
  if (!result.text) {
40387
41021
  console.log(pc2.yellow(t("cli.no_output")));
40388
41022
  }
@@ -40621,6 +41255,22 @@ function startAutoUpdate(config) {
40621
41255
  async function main() {
40622
41256
  installCrashHandlers();
40623
41257
  const program2 = createProgram();
41258
+ const valueOpts = new Set(["-d", "--dir", "--reasoning"]);
41259
+ const argv = process.argv.slice(2);
41260
+ let firstPositional;
41261
+ for (let i = 0;i < argv.length; i++) {
41262
+ const a = argv[i];
41263
+ if (a.startsWith("-")) {
41264
+ if (valueOpts.has(a))
41265
+ i++;
41266
+ continue;
41267
+ }
41268
+ firstPositional = a;
41269
+ break;
41270
+ }
41271
+ if (firstPositional && program2.commands.some((c) => c.name() === firstPositional)) {
41272
+ setOneShotMode(true);
41273
+ }
40624
41274
  program2.parse(process.argv);
40625
41275
  const cmdNames = new Set(program2.commands.map((c) => c.name()));
40626
41276
  const opts = program2.opts();
@@ -40672,7 +41322,8 @@ async function main() {
40672
41322
  completionTokens: result2.completionTokens ?? null,
40673
41323
  totalTokens: result2.totalTokens ?? null,
40674
41324
  totalCost: result2.totalCost ?? null,
40675
- costBreakdown: result2.costBreakdown ?? []
41325
+ costBreakdown: result2.costBreakdown ?? [],
41326
+ cache: result2.cache ?? null
40676
41327
  }, null, 2));
40677
41328
  process.stdout.write(`
40678
41329
  `);