gearbox-code 0.1.15 → 0.1.18

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +773 -241
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -106134,7 +106134,7 @@ var init_catalog = __esm(() => {
106134
106134
  { id: "deepseek", label: "DeepSeek", group: "native", exec: "in-loop", authKind: "api-key", envVars: ["DEEPSEEK_API_KEY"], baseUrl: "https://api.deepseek.com/v1", signupUrl: "https://platform.deepseek.com/api_keys", defaultModels: ["deepseek-v4-pro", "deepseek-v4-flash"] },
106135
106135
  { id: "xai", label: "xAI (Grok)", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["XAI_API_KEY"], keyPrefix: ["xai-"], baseUrl: "https://api.x.ai/v1", signupUrl: "https://console.x.ai", defaultModels: ["grok-4.3", "grok-4.1-fast"] },
106136
106136
  { id: "mistral", label: "Mistral", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["MISTRAL_API_KEY"], baseUrl: "https://api.mistral.ai/v1", signupUrl: "https://console.mistral.ai/api-keys", defaultModels: ["mistral-large-latest", "codestral-latest"] },
106137
- { id: "groq", label: "Groq", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["GROQ_API_KEY"], keyPrefix: ["gsk_"], baseUrl: "https://api.groq.com/openai/v1", signupUrl: "https://console.groq.com/keys", defaultModels: ["llama-3.3-70b-versatile", "moonshotai/kimi-k2-instruct"] },
106137
+ { id: "groq", label: "Groq", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["GROQ_API_KEY"], keyPrefix: ["gsk_"], baseUrl: "https://api.groq.com/openai/v1", signupUrl: "https://console.groq.com/keys", defaultModels: ["llama-3.3-70b-versatile", "qwen-qwq-32b", "gemma2-9b-it"] },
106138
106138
  { id: "together", label: "Together AI", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["TOGETHER_API_KEY"], baseUrl: "https://api.together.xyz/v1", signupUrl: "https://api.together.ai/settings/api-keys", defaultModels: ["deepseek-ai/DeepSeek-V3", "Qwen/Qwen2.5-Coder-32B-Instruct"] },
106139
106139
  { id: "fireworks", label: "Fireworks", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["FIREWORKS_API_KEY"], keyPrefix: ["fw_"], baseUrl: "https://api.fireworks.ai/inference/v1", signupUrl: "https://fireworks.ai/account/api-keys", defaultModels: ["accounts/fireworks/models/deepseek-v3"] },
106140
106140
  { id: "deepinfra", label: "DeepInfra", group: "openai-compat", exec: "in-loop", authKind: "openai-compat", envVars: ["DEEPINFRA_API_KEY"], baseUrl: "https://api.deepinfra.com/v1/openai", signupUrl: "https://deepinfra.com/dash/api_keys" },
@@ -106153,9 +106153,9 @@ var init_catalog = __esm(() => {
106153
106153
  { id: "portkey", label: "Portkey", group: "gateway", exec: "in-loop", authKind: "openai-compat", envVars: ["PORTKEY_API_KEY"], baseUrl: "https://api.portkey.ai/v1", signupUrl: "https://app.portkey.ai", notes: "Config-driven routing via x-portkey-* headers." },
106154
106154
  { id: "litellm", label: "LiteLLM proxy", group: "gateway", exec: "in-loop", authKind: "openai-compat", envVars: ["LITELLM_API_KEY"], signupUrl: "https://docs.litellm.ai/docs/simple_proxy", notes: "Self-hosted; set baseUrl to your proxy." },
106155
106155
  { id: "azure-foundry", label: "Azure AI Foundry", group: "gateway", exec: "in-loop", authKind: "openai-compat", envVars: ["AZURE_AI_FOUNDRY_API_KEY", "AZURE_AI_INFERENCE_API_KEY"], signupUrl: "https://ai.azure.com", defaultModels: ["gpt-5.5", "gpt-5.5-mini", "gpt-4.1", "o4-mini"], notes: "OpenAI-compatible Foundry endpoint. Use baseUrl ending in /openai/v1." },
106156
- { id: "bedrock", label: "Amazon Bedrock", group: "cloud", exec: "in-loop", authKind: "aws", envVars: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_PROFILE"], keyPrefix: ["AKIA", "ASIA"], signupUrl: "https://console.aws.amazon.com/bedrock", defaultModels: ["anthropic.claude-sonnet-4-20250514-v1:0"], notes: "Needs @ai-sdk/amazon-bedrock (P2)." },
106157
- { id: "vertex", label: "Google Vertex AI", group: "cloud", exec: "in-loop", authKind: "vertex", envVars: ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], signupUrl: "https://console.cloud.google.com/vertex-ai", defaultModels: ["gemini-3.1-pro-preview"], notes: "ADC or a service-account JSON." },
106158
- { id: "azure", label: "Azure OpenAI", group: "cloud", exec: "in-loop", authKind: "azure", envVars: ["AZURE_API_KEY", "AZURE_RESOURCE_NAME"], signupUrl: "https://oai.azure.com", notes: "Needs @ai-sdk/azure (P2); resourceName + deployment." },
106156
+ { id: "bedrock", label: "Amazon Bedrock", group: "cloud", exec: "in-loop", authKind: "aws", envVars: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_PROFILE"], keyPrefix: ["AKIA", "ASIA"], signupUrl: "https://console.aws.amazon.com/bedrock", defaultModels: ["anthropic.claude-sonnet-4-20250514-v1:0", "anthropic.claude-haiku-4-5-20251001-v1:0", "anthropic.claude-opus-4-20250514-v1:0", "amazon.nova-pro-v1:0", "amazon.nova-lite-v1:0", "amazon.nova-micro-v1:0", "meta.llama4-maverick-17b-instruct-v1:0", "meta.llama4-scout-17b-instruct-v1:0"], notes: "AWS IAM credentials or ~/.aws profile. Enable models in the Bedrock console first." },
106157
+ { id: "vertex", label: "Google Vertex AI", group: "cloud", exec: "in-loop", authKind: "vertex", envVars: ["GOOGLE_VERTEX_PROJECT", "GOOGLE_VERTEX_LOCATION", "GOOGLE_APPLICATION_CREDENTIALS"], signupUrl: "https://console.cloud.google.com/vertex-ai", defaultModels: ["gemini-3.1-pro-preview", "gemini-3.5-flash", "gemini-3.1-flash-lite"], notes: "ADC (gcloud auth application-default login) or a service-account JSON." },
106158
+ { id: "azure", label: "Azure OpenAI", group: "cloud", exec: "in-loop", authKind: "azure", envVars: ["AZURE_API_KEY", "AZURE_RESOURCE_NAME"], signupUrl: "https://oai.azure.com", defaultModels: ["gpt-5.5", "gpt-5.5-mini", "gpt-4.1"], notes: "resourceName (e.g. my-resource) + API key. Model IDs are your deployment names." },
106159
106159
  { id: "ollama", label: "Ollama (local)", group: "local", exec: "in-loop", authKind: "openai-compat", envVars: [], baseUrl: "http://localhost:11434/v1", signupUrl: "https://ollama.com", defaultModels: ["qwen2.5-coder:7b", "llama3.3"], notes: "No key; runs on your machine." },
106160
106160
  { id: "lmstudio", label: "LM Studio (local)", group: "local", exec: "in-loop", authKind: "openai-compat", envVars: [], baseUrl: "http://localhost:1234/v1", signupUrl: "https://lmstudio.ai" },
106161
106161
  { id: "vllm", label: "vLLM (local/self-host)", group: "local", exec: "in-loop", authKind: "openai-compat", envVars: [], baseUrl: "http://localhost:8000/v1", signupUrl: "https://docs.vllm.ai" },
@@ -106180,6 +106180,41 @@ function generatedModels() {
106180
106180
  }
106181
106181
  return out;
106182
106182
  }
106183
+ function accountModelSpecs() {
106184
+ const out = [];
106185
+ for (const account of listAccounts()) {
106186
+ if (!account.enabled || account.exec === "cli")
106187
+ continue;
106188
+ for (const sdkId of account.models ?? []) {
106189
+ if (!sdkId)
106190
+ continue;
106191
+ if (MODELS.some((m2) => m2.provider === account.provider && m2.sdkId === sdkId))
106192
+ continue;
106193
+ const id = `${account.provider}/${sdkId}`;
106194
+ out.push({
106195
+ id,
106196
+ provider: account.provider,
106197
+ sdkId,
106198
+ label: sdkId.length > 24 ? sdkId.slice(0, 24) : sdkId,
106199
+ contextWindow: 128000,
106200
+ capabilities: { source: "user-configured", tools: "unknown", images: "unknown", jsonSchema: "unknown", usage: "partial" }
106201
+ });
106202
+ }
106203
+ }
106204
+ return out;
106205
+ }
106206
+ function modelRegistry() {
106207
+ const seen = new Set;
106208
+ const out = [];
106209
+ for (const m2 of [...MODELS, ...accountModelSpecs()]) {
106210
+ const key = `${m2.provider}\x00${m2.sdkId}`;
106211
+ if (seen.has(key))
106212
+ continue;
106213
+ seen.add(key);
106214
+ out.push(m2);
106215
+ }
106216
+ return out;
106217
+ }
106183
106218
  function envVarFor(provider) {
106184
106219
  return ENV_KEY[provider] ?? catalogProvider(provider)?.envVars[0];
106185
106220
  }
@@ -106210,12 +106245,12 @@ function providerAvailable(p) {
106210
106245
  return ev ? Boolean(process.env[ev]) : false;
106211
106246
  }
106212
106247
  function findModel(idOrLabel) {
106213
- return MODELS.find((m2) => m2.id === idOrLabel || m2.label === idOrLabel);
106248
+ return modelRegistry().find((m2) => m2.id === idOrLabel || m2.label === idOrLabel);
106214
106249
  }
106215
106250
  function estimateCost(turns) {
106216
106251
  let usd2 = 0;
106217
106252
  for (const t2 of turns) {
106218
- const c = MODELS.find((m2) => m2.id === t2.model)?.cost;
106253
+ const c = modelRegistry().find((m2) => m2.id === t2.model)?.cost;
106219
106254
  if (!c)
106220
106255
  continue;
106221
106256
  usd2 += t2.inputTokens / 1e6 * c.inUSDPerMtok + t2.outputTokens / 1e6 * c.outUSDPerMtok;
@@ -106281,14 +106316,25 @@ var init_providers = __esm(() => {
106281
106316
  NATIVE = new Set(["anthropic", "openai", "google", "deepseek"]);
106282
106317
  CURATED = [
106283
106318
  { id: "claude-opus-4-8", provider: "anthropic", sdkId: "claude-opus-4-8", label: "opus-4.8", contextWindow: 1e6, cost: { inUSDPerMtok: 5, outUSDPerMtok: 25 }, reasoning: true, efforts: ["low", "medium", "high", "xhigh", "max"] },
106284
- { id: "claude-sonnet-4-6", provider: "anthropic", sdkId: "claude-sonnet-4-6", label: "sonnet-4.6", contextWindow: 1e6, cost: { inUSDPerMtok: 3, outUSDPerMtok: 15 }, reasoning: true, efforts: ["low", "medium", "high", "xhigh", "max"] },
106319
+ { id: "claude-sonnet-4-6", provider: "anthropic", sdkId: "claude-sonnet-4-6", label: "sonnet-4.6", contextWindow: 1e6, cost: { inUSDPerMtok: 3, outUSDPerMtok: 15 }, reasoning: true, efforts: ["low", "medium", "high", "max"] },
106285
106320
  { id: "claude-haiku-4-5", provider: "anthropic", sdkId: "claude-haiku-4-5", label: "haiku-4.5", contextWindow: 200000, cost: { inUSDPerMtok: 1, outUSDPerMtok: 5 } },
106286
106321
  { id: "gpt-5.5", provider: "openai", sdkId: "gpt-5.5", label: "gpt-5.5", contextWindow: 400000, cost: { inUSDPerMtok: 2.5, outUSDPerMtok: 10 }, reasoning: true, efforts: ["none", "minimal", "low", "medium", "high", "xhigh"] },
106287
106322
  { id: "gpt-5.5-pro", provider: "openai", sdkId: "gpt-5.5-pro", label: "gpt-5.5-pro", contextWindow: 400000, cost: { inUSDPerMtok: 15, outUSDPerMtok: 120 }, reasoning: true, efforts: ["none", "minimal", "low", "medium", "high", "xhigh"] },
106288
106323
  { id: "gemini-3.1-pro-preview", provider: "google", sdkId: "gemini-3.1-pro-preview", label: "gemini-3.1-pro", contextWindow: 1e6, cost: { inUSDPerMtok: 2, outUSDPerMtok: 12 }, reasoning: true, efforts: ["minimal", "low", "medium", "high"] },
106289
106324
  { id: "gemini-3.5-flash", provider: "google", sdkId: "gemini-3.5-flash", label: "gemini-3.5-flash", contextWindow: 1e6, cost: { inUSDPerMtok: 0.3, outUSDPerMtok: 2.5 }, reasoning: true, efforts: ["minimal", "low", "medium", "high"] },
106290
- { id: "deepseek-v4-pro", provider: "deepseek", sdkId: "deepseek-v4-pro", label: "deepseek-v4-pro", contextWindow: 128000, cost: { inUSDPerMtok: 0.4, outUSDPerMtok: 1.75 }, reasoning: true },
106291
- { id: "deepseek-v4-flash", provider: "deepseek", sdkId: "deepseek-v4-flash", label: "deepseek-v4-flash", contextWindow: 128000, cost: { inUSDPerMtok: 0.27, outUSDPerMtok: 1.1 } }
106325
+ { id: "deepseek-v4-pro", provider: "deepseek", sdkId: "deepseek-v4-pro", label: "deepseek-v4-pro", contextWindow: 128000, cost: { inUSDPerMtok: 0.4, outUSDPerMtok: 1.75 } },
106326
+ { id: "deepseek-v4-flash", provider: "deepseek", sdkId: "deepseek-v4-flash", label: "deepseek-v4-flash", contextWindow: 128000, cost: { inUSDPerMtok: 0.27, outUSDPerMtok: 1.1 } },
106327
+ { id: "bedrock/anthropic.claude-sonnet-4-20250514-v1:0", provider: "bedrock", sdkId: "anthropic.claude-sonnet-4-20250514-v1:0", label: "bedrock/sonnet-4", contextWindow: 200000, cost: { inUSDPerMtok: 3.3, outUSDPerMtok: 16.5 }, reasoning: true, efforts: ["low", "medium", "high", "max"] },
106328
+ { id: "bedrock/anthropic.claude-haiku-4-5-20251001-v1:0", provider: "bedrock", sdkId: "anthropic.claude-haiku-4-5-20251001-v1:0", label: "bedrock/haiku-4.5", contextWindow: 200000, cost: { inUSDPerMtok: 1.1, outUSDPerMtok: 5.5 }, reasoning: true, efforts: ["low", "medium", "high", "max"] },
106329
+ { id: "bedrock/anthropic.claude-opus-4-20250514-v1:0", provider: "bedrock", sdkId: "anthropic.claude-opus-4-20250514-v1:0", label: "bedrock/opus-4", contextWindow: 200000, cost: { inUSDPerMtok: 5.5, outUSDPerMtok: 27.5 }, reasoning: true, efforts: ["low", "medium", "high", "max"] },
106330
+ { id: "bedrock/amazon.nova-pro-v1:0", provider: "bedrock", sdkId: "amazon.nova-pro-v1:0", label: "bedrock/nova-pro", contextWindow: 300000, cost: { inUSDPerMtok: 0.8, outUSDPerMtok: 3.2 } },
106331
+ { id: "bedrock/amazon.nova-lite-v1:0", provider: "bedrock", sdkId: "amazon.nova-lite-v1:0", label: "bedrock/nova-lite", contextWindow: 300000, cost: { inUSDPerMtok: 0.06, outUSDPerMtok: 0.24 } },
106332
+ { id: "bedrock/amazon.nova-micro-v1:0", provider: "bedrock", sdkId: "amazon.nova-micro-v1:0", label: "bedrock/nova-micro", contextWindow: 128000, cost: { inUSDPerMtok: 0.035, outUSDPerMtok: 0.14 } },
106333
+ { id: "bedrock/meta.llama4-maverick-17b-instruct-v1:0", provider: "bedrock", sdkId: "meta.llama4-maverick-17b-instruct-v1:0", label: "bedrock/llama-4-mav", contextWindow: 128000, cost: { inUSDPerMtok: 0.24, outUSDPerMtok: 0.97 } },
106334
+ { id: "bedrock/meta.llama4-scout-17b-instruct-v1:0", provider: "bedrock", sdkId: "meta.llama4-scout-17b-instruct-v1:0", label: "bedrock/llama-4-scout", contextWindow: 128000, cost: { inUSDPerMtok: 0.17, outUSDPerMtok: 0.66 } },
106335
+ { id: "vertex/gemini-3.1-pro-preview", provider: "vertex", sdkId: "gemini-3.1-pro-preview", label: "vertex/gemini-3.1-pro", contextWindow: 1e6, cost: { inUSDPerMtok: 2, outUSDPerMtok: 12 }, reasoning: true, efforts: ["minimal", "low", "medium", "high"] },
106336
+ { id: "vertex/gemini-3.5-flash", provider: "vertex", sdkId: "gemini-3.5-flash", label: "vertex/gemini-3.5-flash", contextWindow: 1e6, cost: { inUSDPerMtok: 0.3, outUSDPerMtok: 2.5 }, reasoning: true, efforts: ["minimal", "low", "medium", "high"] },
106337
+ { id: "vertex/gemini-3.1-flash-lite", provider: "vertex", sdkId: "gemini-3.1-flash-lite", label: "vertex/gemini-3.1-flash-lite", contextWindow: 1e6, cost: { inUSDPerMtok: 0.1, outUSDPerMtok: 0.4 } }
106292
106338
  ];
106293
106339
  MODELS = [...CURATED, ...generatedModels()];
106294
106340
  ENV_KEY = {
@@ -106336,12 +106382,13 @@ var init_permission = __esm(() => {
106336
106382
  function profileFor(id) {
106337
106383
  return BY_ID2.get(id);
106338
106384
  }
106339
- var CLAUDE_TOK, TIKTOKEN, GEMINI_TOK, DEEPSEEK_TOK, PROFILES, BY_ID2;
106385
+ var CLAUDE_TOK, TIKTOKEN, GEMINI_TOK, DEEPSEEK_TOK, LLAMA_TOK, PROFILES, BY_ID2;
106340
106386
  var init_profiles = __esm(() => {
106341
106387
  CLAUDE_TOK = { family: "claude", calibration: 1.35, calibrationSrc: "measured" };
106342
106388
  TIKTOKEN = { family: "tiktoken-o200k", calibration: 1, calibrationSrc: "measured" };
106343
106389
  GEMINI_TOK = { family: "gemini", calibration: 1.1, calibrationSrc: "seeded" };
106344
106390
  DEEPSEEK_TOK = { family: "deepseek", calibration: 1.05, calibrationSrc: "seeded" };
106391
+ LLAMA_TOK = { family: "llama", calibration: 1.1, calibrationSrc: "seeded" };
106345
106392
  PROFILES = [
106346
106393
  {
106347
106394
  id: "claude-opus-4-8",
@@ -106433,12 +106480,171 @@ var init_profiles = __esm(() => {
106433
106480
  strengths: ["far cheaper than frontier", "strong coding for the price", "reasoning"],
106434
106481
  weaknesses: ["smaller context", "slower hosted latency"],
106435
106482
  asOf: "2026-06"
106483
+ },
106484
+ {
106485
+ id: "bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
106486
+ provider: "bedrock",
106487
+ contextWindow: 200000,
106488
+ maxOutput: 64000,
106489
+ tokenizer: CLAUDE_TOK,
106490
+ cost: { inUSDPerMtok: 3.3, outUSDPerMtok: 16.5, src: "seeded" },
106491
+ latency: { ttftMs: 1900, tps: 90, src: "seeded" },
106492
+ quality: { sweBenchVerified: 0.77, intelligenceIndex: 50, src: "seeded" },
106493
+ strengths: ["agentic coding", "tool use", "instruction-following", "AWS-native deployment", "extended thinking"],
106494
+ weaknesses: ["10% pricier than Anthropic direct", "Bedrock model enablement required"],
106495
+ asOf: "2026-06"
106496
+ },
106497
+ {
106498
+ id: "bedrock/anthropic.claude-haiku-4-5-20251001-v1:0",
106499
+ provider: "bedrock",
106500
+ contextWindow: 200000,
106501
+ maxOutput: 32000,
106502
+ tokenizer: CLAUDE_TOK,
106503
+ cost: { inUSDPerMtok: 1.1, outUSDPerMtok: 5.5, src: "seeded" },
106504
+ latency: { ttftMs: 1400, tps: 170, src: "seeded" },
106505
+ quality: { intelligenceIndex: 38, src: "seeded" },
106506
+ strengths: ["fast", "cheap", "AWS-native", "good for bounded sub-tasks"],
106507
+ weaknesses: ["weaker on hard reasoning", "10% premium over Anthropic direct"],
106508
+ asOf: "2026-06"
106509
+ },
106510
+ {
106511
+ id: "bedrock/anthropic.claude-opus-4-20250514-v1:0",
106512
+ provider: "bedrock",
106513
+ contextWindow: 200000,
106514
+ maxOutput: 128000,
106515
+ tokenizer: CLAUDE_TOK,
106516
+ cost: { inUSDPerMtok: 5.5, outUSDPerMtok: 27.5, src: "seeded" },
106517
+ latency: { ttftMs: 2500, tps: 65, src: "seeded" },
106518
+ quality: { sweBenchVerified: 0.83, intelligenceIndex: 64, src: "seeded" },
106519
+ strengths: ["most capable", "long-horizon agentic work", "AWS-native deployment", "extended thinking"],
106520
+ weaknesses: ["most expensive", "slower", "10% premium over Anthropic direct"],
106521
+ asOf: "2026-06"
106522
+ },
106523
+ {
106524
+ id: "bedrock/amazon.nova-pro-v1:0",
106525
+ provider: "bedrock",
106526
+ contextWindow: 300000,
106527
+ maxOutput: 5000,
106528
+ tokenizer: TIKTOKEN,
106529
+ cost: { inUSDPerMtok: 0.8, outUSDPerMtok: 3.2, src: "seeded" },
106530
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106531
+ quality: { intelligenceIndex: 42, src: "seeded" },
106532
+ strengths: ["native AWS model", "multimodal (text+image+video)", "large context", "reasonable price"],
106533
+ weaknesses: ["weaker than Claude on hard coding/reasoning"],
106534
+ asOf: "2026-06"
106535
+ },
106536
+ {
106537
+ id: "bedrock/amazon.nova-lite-v1:0",
106538
+ provider: "bedrock",
106539
+ contextWindow: 300000,
106540
+ maxOutput: 5000,
106541
+ tokenizer: TIKTOKEN,
106542
+ cost: { inUSDPerMtok: 0.06, outUSDPerMtok: 0.24, src: "seeded" },
106543
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106544
+ quality: { intelligenceIndex: 32, src: "seeded" },
106545
+ strengths: ["very cheap", "native AWS", "large context", "good for bulk tasks"],
106546
+ weaknesses: ["lower ceiling on complex tasks"],
106547
+ asOf: "2026-06"
106548
+ },
106549
+ {
106550
+ id: "bedrock/amazon.nova-micro-v1:0",
106551
+ provider: "bedrock",
106552
+ contextWindow: 128000,
106553
+ maxOutput: 5000,
106554
+ tokenizer: TIKTOKEN,
106555
+ cost: { inUSDPerMtok: 0.035, outUSDPerMtok: 0.14, src: "seeded" },
106556
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106557
+ quality: { intelligenceIndex: 25, src: "seeded" },
106558
+ strengths: ["cheapest AWS model", "text-only, fast", "classify/summarize"],
106559
+ weaknesses: ["no image input", "lower quality ceiling"],
106560
+ asOf: "2026-06"
106561
+ },
106562
+ {
106563
+ id: "bedrock/meta.llama4-maverick-17b-instruct-v1:0",
106564
+ provider: "bedrock",
106565
+ contextWindow: 128000,
106566
+ maxOutput: 8000,
106567
+ tokenizer: LLAMA_TOK,
106568
+ cost: { inUSDPerMtok: 0.24, outUSDPerMtok: 0.97, src: "seeded" },
106569
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106570
+ quality: { intelligenceIndex: 44, src: "seeded" },
106571
+ strengths: ["multimodal (text+image)", "competitive quality per dollar", "AWS-native"],
106572
+ weaknesses: ["smaller context than Nova", "OSS model quality ceiling"],
106573
+ asOf: "2026-06"
106574
+ },
106575
+ {
106576
+ id: "bedrock/meta.llama4-scout-17b-instruct-v1:0",
106577
+ provider: "bedrock",
106578
+ contextWindow: 128000,
106579
+ maxOutput: 8000,
106580
+ tokenizer: LLAMA_TOK,
106581
+ cost: { inUSDPerMtok: 0.17, outUSDPerMtok: 0.66, src: "seeded" },
106582
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106583
+ quality: { intelligenceIndex: 38, src: "seeded" },
106584
+ strengths: ["cheap", "fast", "multimodal", "AWS-native"],
106585
+ weaknesses: ["lower quality than Maverick", "OSS model quality ceiling"],
106586
+ asOf: "2026-06"
106587
+ },
106588
+ {
106589
+ id: "vertex/gemini-3.1-pro-preview",
106590
+ provider: "vertex",
106591
+ contextWindow: 1e6,
106592
+ maxOutput: 64000,
106593
+ tokenizer: GEMINI_TOK,
106594
+ cost: { inUSDPerMtok: 2, outUSDPerMtok: 12, src: "seeded" },
106595
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106596
+ quality: { sweBenchVerified: 0.76, intelligenceIndex: 60, src: "seeded" },
106597
+ strengths: ["1M context", "strong reasoning", "thinking config", "GCP-native deployment"],
106598
+ weaknesses: ["preview", "agentic tool-use behind Claude"],
106599
+ asOf: "2026-06"
106600
+ },
106601
+ {
106602
+ id: "vertex/gemini-3.5-flash",
106603
+ provider: "vertex",
106604
+ contextWindow: 1e6,
106605
+ maxOutput: 64000,
106606
+ tokenizer: GEMINI_TOK,
106607
+ cost: { inUSDPerMtok: 0.3, outUSDPerMtok: 2.5, src: "seeded" },
106608
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106609
+ quality: { intelligenceIndex: 48, src: "seeded" },
106610
+ strengths: ["very cheap", "fast", "1M context", "thinking config", "GCP-native"],
106611
+ weaknesses: ["lower ceiling on hard tasks"],
106612
+ asOf: "2026-06"
106613
+ },
106614
+ {
106615
+ id: "vertex/gemini-3.1-flash-lite",
106616
+ provider: "vertex",
106617
+ contextWindow: 1e6,
106618
+ maxOutput: 8000,
106619
+ tokenizer: GEMINI_TOK,
106620
+ cost: { inUSDPerMtok: 0.1, outUSDPerMtok: 0.4, src: "seeded" },
106621
+ latency: { ttftMs: 0, tps: 0, src: "seeded" },
106622
+ quality: { intelligenceIndex: 30, src: "seeded" },
106623
+ strengths: ["cheapest Vertex model", "1M context", "fast", "classify/summarize"],
106624
+ weaknesses: ["no thinking config", "lower quality ceiling"],
106625
+ asOf: "2026-06"
106436
106626
  }
106437
106627
  ];
106438
106628
  BY_ID2 = new Map(PROFILES.map((p) => [p.id, p]));
106439
106629
  });
106440
106630
 
106441
106631
  // src/model/reasoning.ts
106632
+ function clampEffort(current, allowed) {
106633
+ if (!allowed.length)
106634
+ return { level: "medium", clamped: current !== "medium" };
106635
+ if (allowed.includes(current))
106636
+ return { level: current, clamped: false };
106637
+ const idx = EFFORT_ORDER.indexOf(current);
106638
+ for (let d = 1;d <= EFFORT_ORDER.length; d++) {
106639
+ const hi = EFFORT_ORDER[idx + d];
106640
+ if (hi && allowed.includes(hi))
106641
+ return { level: hi, clamped: true };
106642
+ const lo = EFFORT_ORDER[idx - d];
106643
+ if (lo && allowed.includes(lo))
106644
+ return { level: lo, clamped: true };
106645
+ }
106646
+ return { level: allowed[allowed.length - 1], clamped: true };
106647
+ }
106442
106648
  function effortLevels(spec6) {
106443
106649
  if (spec6.efforts)
106444
106650
  return spec6.efforts;
@@ -106446,6 +106652,8 @@ function effortLevels(spec6) {
106446
106652
  return [];
106447
106653
  if (spec6.provider === "openai")
106448
106654
  return OPENAI_EFFORTS;
106655
+ if (spec6.provider === "azure" || spec6.provider === "azure-foundry")
106656
+ return OPENAI_EFFORTS;
106449
106657
  if (spec6.provider === "anthropic")
106450
106658
  return ANTHROPIC_EFFORTS;
106451
106659
  if (spec6.provider === "google" || spec6.provider === "vertex")
@@ -106470,7 +106678,7 @@ function reasoningOptions(spec6, effort) {
106470
106678
  if (!level)
106471
106679
  return {};
106472
106680
  const p = spec6.provider;
106473
- if (p === "openai") {
106681
+ if (p === "openai" || p === "azure" || p === "azure-foundry") {
106474
106682
  return { openai: { reasoningEffort: level } };
106475
106683
  }
106476
106684
  if (p === "google" || p === "vertex") {
@@ -106481,11 +106689,12 @@ function reasoningOptions(spec6, effort) {
106481
106689
  }
106482
106690
  return {};
106483
106691
  }
106484
- var OPENAI_EFFORTS, ANTHROPIC_EFFORTS, GOOGLE_EFFORTS;
106692
+ var OPENAI_EFFORTS, ANTHROPIC_EFFORTS, GOOGLE_EFFORTS, EFFORT_ORDER;
106485
106693
  var init_reasoning = __esm(() => {
106486
106694
  OPENAI_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh"];
106487
106695
  ANTHROPIC_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
106488
106696
  GOOGLE_EFFORTS = ["minimal", "low", "medium", "high"];
106697
+ EFFORT_ORDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
106489
106698
  });
106490
106699
 
106491
106700
  // src/model/capabilities.ts
@@ -106497,12 +106706,16 @@ __export(exports_capabilities, {
106497
106706
  capabilitiesFor: () => capabilitiesFor
106498
106707
  });
106499
106708
  function providerSource(spec6) {
106709
+ if (spec6.capabilities?.source)
106710
+ return spec6.capabilities.source;
106500
106711
  const group = catalogProvider(spec6.provider)?.group;
106501
106712
  if (group === "gateway" || group === "openai-compat" || group === "local")
106502
106713
  return "seeded";
106503
106714
  return "seeded";
106504
106715
  }
106505
106716
  function exactUsage(spec6) {
106717
+ if (spec6.capabilities?.usage)
106718
+ return spec6.capabilities.usage;
106506
106719
  if (spec6.provider === "anthropic" || spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "deepseek")
106507
106720
  return "exact";
106508
106721
  const group = catalogProvider(spec6.provider)?.group;
@@ -106511,25 +106724,41 @@ function exactUsage(spec6) {
106511
106724
  return "none";
106512
106725
  }
106513
106726
  function toolSupport(spec6) {
106727
+ if (spec6.capabilities?.tools != null)
106728
+ return spec6.capabilities.tools;
106514
106729
  const group = catalogProvider(spec6.provider)?.group;
106515
106730
  if (spec6.provider === "anthropic" || spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "deepseek")
106516
106731
  return true;
106517
- if (spec6.provider === "bedrock" || spec6.provider === "vertex" || spec6.provider === "azure")
106518
- return "unknown";
106732
+ if (spec6.provider === "bedrock")
106733
+ return !spec6.sdkId.includes("nova-micro");
106734
+ if (spec6.provider === "vertex" || spec6.provider === "azure" || spec6.provider === "azure-foundry")
106735
+ return true;
106519
106736
  if (group === "gateway" || group === "openai-compat" || group === "local")
106520
106737
  return "unknown";
106521
106738
  return "unknown";
106522
106739
  }
106523
106740
  function imageSupport(spec6) {
106741
+ if (spec6.capabilities?.images != null)
106742
+ return spec6.capabilities.images;
106524
106743
  if (spec6.provider === "anthropic" || spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "vertex")
106525
106744
  return true;
106526
106745
  if (spec6.provider === "deepseek")
106527
106746
  return false;
106747
+ if (spec6.provider === "bedrock")
106748
+ return !spec6.sdkId.includes("nova-micro");
106749
+ if (spec6.provider === "azure" || spec6.provider === "azure-foundry")
106750
+ return true;
106528
106751
  return "unknown";
106529
106752
  }
106530
106753
  function schemaSupport(spec6) {
106754
+ if (spec6.capabilities?.jsonSchema != null)
106755
+ return spec6.capabilities.jsonSchema;
106531
106756
  if (spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "anthropic")
106532
106757
  return true;
106758
+ if (spec6.provider === "bedrock")
106759
+ return spec6.sdkId.startsWith("anthropic.") ? true : "unknown";
106760
+ if (spec6.provider === "vertex" || spec6.provider === "azure" || spec6.provider === "azure-foundry")
106761
+ return true;
106533
106762
  if (spec6.provider === "deepseek")
106534
106763
  return "unknown";
106535
106764
  return "unknown";
@@ -106551,7 +106780,7 @@ function capabilitiesFor(spec6) {
106551
106780
  images: imageSupport(spec6),
106552
106781
  jsonSchema: schemaSupport(spec6),
106553
106782
  reasoningEffort: efforts.length ? efforts : false,
106554
- systemPrompt: true,
106783
+ systemPrompt: spec6.capabilities?.systemPrompt ?? true,
106555
106784
  usage: exactUsage(spec6),
106556
106785
  contextWindow: profile?.contextWindow ?? spec6.contextWindow,
106557
106786
  maxOutputTokens: profile?.maxOutput,
@@ -106583,7 +106812,7 @@ function cell(v) {
106583
106812
  return "?";
106584
106813
  return String(v ?? "");
106585
106814
  }
106586
- function formatCapabilityMatrix(models = MODELS) {
106815
+ function formatCapabilityMatrix(models = modelRegistry()) {
106587
106816
  const rows = models.map((m2) => {
106588
106817
  const c = capabilitiesFor(m2);
106589
106818
  return {
@@ -128749,12 +128978,19 @@ var init_stdio2 = __esm(() => {
128749
128978
  // src/mcp.ts
128750
128979
  var exports_mcp = {};
128751
128980
  __export(exports_mcp, {
128981
+ shellSplit: () => shellSplit,
128982
+ removeMcpServer: () => removeMcpServer,
128983
+ reloadMcpConnections: () => reloadMcpConnections,
128752
128984
  mcpTools: () => mcpTools,
128753
128985
  mcpToolSummary: () => mcpToolSummary,
128754
- mcpConfigPaths: () => mcpConfigPaths
128986
+ mcpConfigPaths: () => mcpConfigPaths,
128987
+ mcpConfigPath: () => mcpConfigPath,
128988
+ formatMcpConfigList: () => formatMcpConfigList,
128989
+ configuredMcpServers: () => configuredMcpServers,
128990
+ addMcpServer: () => addMcpServer
128755
128991
  });
128756
- import { existsSync as existsSync3, readFileSync as readFileSync7 } from "node:fs";
128757
- import { join as join7 } from "node:path";
128992
+ import { existsSync as existsSync3, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
128993
+ import { dirname as dirname3, join as join7 } from "node:path";
128758
128994
  import { homedir as homedir6 } from "node:os";
128759
128995
  function readConfigFile(path) {
128760
128996
  if (!existsSync3(path))
@@ -128765,6 +129001,11 @@ function readConfigFile(path) {
128765
129001
  return {};
128766
129002
  }
128767
129003
  }
129004
+ function writeConfigFile(path, config3) {
129005
+ mkdirSync6(dirname3(path), { recursive: true });
129006
+ writeFileSync6(path, JSON.stringify(config3, null, 2) + `
129007
+ `, { mode: 384 });
129008
+ }
128768
129009
  function expandEnv(value) {
128769
129010
  return value.replace(/\$\{([A-Z0-9_]+)\}/gi, (_, name31) => process.env[name31] ?? "");
128770
129011
  }
@@ -128813,6 +129054,9 @@ async function connected() {
128813
129054
  connectedPromise ??= connectAll();
128814
129055
  return connectedPromise;
128815
129056
  }
129057
+ function reloadMcpConnections() {
129058
+ connectedPromise = null;
129059
+ }
128816
129060
  function formatMcpResult(result2) {
128817
129061
  const content = result2?.content ?? [];
128818
129062
  if (!Array.isArray(content) || !content.length)
@@ -128839,6 +129083,122 @@ async function mcpToolSummary() {
128839
129083
  return rows.flatMap((s2) => s2.tools.map((t2) => `${safeToolName(s2.name, t2.name).padEnd(34)} ${t2.description ?? ""}`)).join(`
128840
129084
  `);
128841
129085
  }
129086
+ function mcpConfigPath(scope = "project", cwd2 = process.cwd()) {
129087
+ return scope === "global" ? join7(HOME(), "mcp.json") : join7(cwd2, ".gearbox", "mcp.json");
129088
+ }
129089
+ function configuredMcpServers(cwd2 = process.cwd()) {
129090
+ const paths = [
129091
+ { scope: "global", path: join7(HOME(), "mcp.json") },
129092
+ { scope: "compat", path: join7(cwd2, ".mcp.json") },
129093
+ { scope: "project", path: join7(cwd2, ".gearbox", "mcp.json") }
129094
+ ];
129095
+ const rows = [];
129096
+ for (const p of paths) {
129097
+ const file5 = readConfigFile(p.path);
129098
+ for (const [name31, config3] of Object.entries(file5.mcpServers ?? file5.servers ?? {})) {
129099
+ if (!config3?.command)
129100
+ continue;
129101
+ rows.push({ name: name31, scope: p.scope, config: config3 });
129102
+ }
129103
+ }
129104
+ return rows;
129105
+ }
129106
+ function formatMcpConfigList(cwd2 = process.cwd()) {
129107
+ const rows = configuredMcpServers(cwd2);
129108
+ if (!rows.length) {
129109
+ return [
129110
+ "MCP servers",
129111
+ " none configured",
129112
+ "",
129113
+ "Add one:",
129114
+ " /mcp add github npx -y @modelcontextprotocol/server-github",
129115
+ " /mcp add --global linear npx -y @modelcontextprotocol/server-linear"
129116
+ ].join(`
129117
+ `);
129118
+ }
129119
+ return [
129120
+ "MCP servers",
129121
+ ...rows.map((r2) => {
129122
+ const args = r2.config.args?.length ? " " + r2.config.args.join(" ") : "";
129123
+ const off = r2.config.disabled ? " · disabled" : "";
129124
+ return ` ${r2.name.padEnd(18)} ${r2.scope.padEnd(7)} ${r2.config.command}${args}${off}`;
129125
+ }),
129126
+ "",
129127
+ "Tools: /mcp tools",
129128
+ "Remove: /mcp remove <name>"
129129
+ ].join(`
129130
+ `);
129131
+ }
129132
+ function shellSplit(input) {
129133
+ const out = [];
129134
+ let cur = "";
129135
+ let quote = null;
129136
+ let esc2 = false;
129137
+ for (const ch of input) {
129138
+ if (esc2) {
129139
+ cur += ch;
129140
+ esc2 = false;
129141
+ continue;
129142
+ }
129143
+ if (ch === "\\") {
129144
+ esc2 = true;
129145
+ continue;
129146
+ }
129147
+ if (quote) {
129148
+ if (ch === quote)
129149
+ quote = null;
129150
+ else
129151
+ cur += ch;
129152
+ continue;
129153
+ }
129154
+ if (ch === "'" || ch === '"') {
129155
+ quote = ch;
129156
+ continue;
129157
+ }
129158
+ if (/\s/.test(ch)) {
129159
+ if (cur) {
129160
+ out.push(cur);
129161
+ cur = "";
129162
+ }
129163
+ continue;
129164
+ }
129165
+ cur += ch;
129166
+ }
129167
+ if (cur)
129168
+ out.push(cur);
129169
+ return out;
129170
+ }
129171
+ function cleanServerName(name31) {
129172
+ return name31.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
129173
+ }
129174
+ function addMcpServer(name31, command, args = [], opts = {}) {
129175
+ const serverName = cleanServerName(name31);
129176
+ if (!serverName || !command)
129177
+ throw new Error("usage: /mcp add <name> <command> [args...]");
129178
+ const path = mcpConfigPath(opts.scope ?? "project", opts.cwd ?? process.cwd());
129179
+ const file5 = readConfigFile(path);
129180
+ const servers = { ...file5.mcpServers ?? file5.servers ?? {} };
129181
+ servers[serverName] = { command, args };
129182
+ writeConfigFile(path, { mcpServers: servers });
129183
+ reloadMcpConnections();
129184
+ return `connected ${serverName} (${opts.scope ?? "project"})`;
129185
+ }
129186
+ function removeMcpServer(name31, opts = {}) {
129187
+ const serverName = cleanServerName(name31);
129188
+ const scopes = opts.scope ? [opts.scope] : ["project", "global"];
129189
+ for (const scope of scopes) {
129190
+ const path = mcpConfigPath(scope, opts.cwd ?? process.cwd());
129191
+ const file5 = readConfigFile(path);
129192
+ const servers = { ...file5.mcpServers ?? file5.servers ?? {} };
129193
+ if (!(serverName in servers))
129194
+ continue;
129195
+ delete servers[serverName];
129196
+ writeConfigFile(path, { mcpServers: servers });
129197
+ reloadMcpConnections();
129198
+ return `removed ${serverName} (${scope})`;
129199
+ }
129200
+ return `no MCP server named ${serverName}`;
129201
+ }
128842
129202
  async function mcpTools(onEvent, readOnly = false) {
128843
129203
  const rows = await connected();
128844
129204
  const out = {};
@@ -128993,9 +129353,10 @@ function awsIni(file5, profile = "default") {
128993
129353
  }
128994
129354
  function detectCloudCreds() {
128995
129355
  const out = [];
129356
+ const awsProfile = process.env.AWS_PROFILE ?? "default";
128996
129357
  const home5 = homedir8();
128997
- const creds = awsIni(join10(home5, ".aws", "credentials"));
128998
- const conf = awsIni(join10(home5, ".aws", "config"));
129358
+ const creds = awsIni(join10(home5, ".aws", "credentials"), awsProfile);
129359
+ const conf = awsIni(join10(home5, ".aws", "config"), awsProfile);
128999
129360
  const akid = process.env.AWS_ACCESS_KEY_ID ?? creds.aws_access_key_id;
129000
129361
  const secret = process.env.AWS_SECRET_ACCESS_KEY ?? creds.aws_secret_access_key;
129001
129362
  const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? conf.region;
@@ -129415,15 +129776,19 @@ __export(exports_onboard, {
129415
129776
  cliLoginArgs: () => cliLoginArgs,
129416
129777
  cliAuthStatus: () => cliAuthStatus,
129417
129778
  addableProviders: () => addableProviders,
129779
+ addOpenAICompatAccount: () => addOpenAICompatAccount,
129418
129780
  addCliAccount: () => addCliAccount,
129419
129781
  addByPastedKey: () => addByPastedKey,
129420
129782
  addAzureFoundryAccount: () => addAzureFoundryAccount,
129421
129783
  addAzureAccount: () => addAzureAccount,
129422
129784
  addApiKeyAccount: () => addApiKeyAccount
129423
129785
  });
129424
- import { mkdirSync as mkdirSync7 } from "node:fs";
129786
+ import { mkdirSync as mkdirSync8 } from "node:fs";
129425
129787
  import { join as join11 } from "node:path";
129426
129788
  import { homedir as homedir9 } from "node:os";
129789
+ function slugify(input) {
129790
+ return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || shortId();
129791
+ }
129427
129792
  async function addApiKeyAccount(provider, key, opts = {}) {
129428
129793
  provider = normalizeProviderId(provider);
129429
129794
  const cat = catalogProvider(provider);
@@ -129450,6 +129815,29 @@ async function addApiKeyAccount(provider, key, opts = {}) {
129450
129815
  putAccount(account);
129451
129816
  return { ok: true, account, message: `added ${account.label} (${id})` };
129452
129817
  }
129818
+ async function addOpenAICompatAccount(name31, baseUrl, key, models, opts = {}) {
129819
+ if (!/^https?:\/\//i.test(baseUrl) || !models.length) {
129820
+ return { ok: false, message: "usage: /account add openai-compat <name> <base-url> <api-key> <model> [model...]" };
129821
+ }
129822
+ const known = catalogProvider(normalizeProviderId(name31));
129823
+ const provider = known?.authKind === "openai-compat" ? known.id : `custom-${slugify(name31)}`;
129824
+ const id = opts.id ?? `${provider}-${shortId()}`;
129825
+ const ref = `${id}:api-key`;
129826
+ await setSecret(ref, key.trim());
129827
+ const account = {
129828
+ id,
129829
+ label: opts.label ?? (known?.label ?? (name31.trim() || "OpenAI-compatible")),
129830
+ provider,
129831
+ exec: "in-loop",
129832
+ auth: { kind: "openai-compat", ref },
129833
+ baseUrl: baseUrl.replace(/\/+$/, ""),
129834
+ models,
129835
+ enabled: true,
129836
+ addedAt: Date.now()
129837
+ };
129838
+ putAccount(account);
129839
+ return { ok: true, account, message: `added ${account.label} (${models.length} model${models.length === 1 ? "" : "s"})` };
129840
+ }
129453
129841
  function azureResourceName(input) {
129454
129842
  const s2 = input.trim();
129455
129843
  try {
@@ -129492,7 +129880,7 @@ async function addAzureAccount(resourceOrEndpoint, key, opts = {}) {
129492
129880
  const resourceName = azureResourceName(resourceOrEndpoint);
129493
129881
  if (!resourceName || !key.trim())
129494
129882
  return { ok: false, message: "usage: /account add azure <resource-or-endpoint> <api-key> [api-version]" };
129495
- const id = opts.id ?? `azure-${resourceName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || shortId()}`;
129883
+ const id = opts.id ?? `azure-${slugify(resourceName)}`;
129496
129884
  const ref = `${id}:api-key`;
129497
129885
  await setSecret(ref, key.trim());
129498
129886
  const account = {
@@ -129521,7 +129909,7 @@ function addCliAccount(provider, name31) {
129521
129909
  const id = slug3 ? `${provider}-${slug3}` : provider;
129522
129910
  const profile = slug3 ? cliProfileDir(id) : undefined;
129523
129911
  if (profile)
129524
- mkdirSync7(profile, { recursive: true });
129912
+ mkdirSync8(profile, { recursive: true });
129525
129913
  const account = {
129526
129914
  id,
129527
129915
  label: slug3 ? `${cat.label.replace(/ \(.*\)$/, "")} (${name31.trim()})` : cat.label,
@@ -129599,7 +129987,8 @@ async function addByPastedKey(key) {
129599
129987
  }
129600
129988
  async function testAccount(a) {
129601
129989
  const creds = await resolveCreds(a);
129602
- if (!creds.apiKey && a.auth.kind !== "cli")
129990
+ const isCloud = a.auth.kind === "aws" || a.auth.kind === "azure" || a.auth.kind === "vertex";
129991
+ if (!creds.apiKey && !isCloud && a.auth.kind !== "cli")
129603
129992
  return { ok: false, message: "no key stored" };
129604
129993
  try {
129605
129994
  if (a.provider === "anthropic") {
@@ -129614,6 +130003,32 @@ async function testAccount(a) {
129614
130003
  const r3 = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${creds.apiKey ?? ""}`);
129615
130004
  return r3.ok ? { ok: true, message: "credential works" } : { ok: false, message: await errMessage(r3) };
129616
130005
  }
130006
+ if (a.provider === "azure" && creds.azure) {
130007
+ const { resourceName, apiKey, apiVersion = "2024-08-01-preview" } = creds.azure;
130008
+ if (!resourceName || !apiKey)
130009
+ return { ok: false, message: "azure: resourceName and apiKey are required" };
130010
+ const r3 = await fetch(`https://${resourceName}.openai.azure.com/openai/models?api-version=${apiVersion}`, { headers: { "api-key": apiKey } });
130011
+ return r3.ok ? { ok: true, message: "credential works" } : { ok: false, message: await errMessage(r3) };
130012
+ }
130013
+ if (a.provider === "bedrock" && creds.aws) {
130014
+ const { accessKeyId, secretAccessKey, region } = creds.aws;
130015
+ if (!accessKeyId || !secretAccessKey)
130016
+ return { ok: false, message: "bedrock: AWS access key and secret are required" };
130017
+ if (!region)
130018
+ return { ok: false, message: "bedrock: AWS_REGION is required" };
130019
+ const keyOk = /^(AKIA|ASIA)[A-Z0-9]{16}$/.test(accessKeyId);
130020
+ if (!keyOk)
130021
+ return { ok: false, message: `bedrock: access key ID looks malformed (expected AKIA… or ASIA…, got ${accessKeyId.slice(0, 8)}…)` };
130022
+ return { ok: true, message: "credential fields present (Bedrock connectivity verified on first use)" };
130023
+ }
130024
+ if (a.provider === "vertex" && creds.vertex) {
130025
+ const { project, location } = creds.vertex;
130026
+ if (!project)
130027
+ return { ok: false, message: "vertex: GOOGLE_VERTEX_PROJECT is required" };
130028
+ if (!location)
130029
+ return { ok: false, message: "vertex: GOOGLE_VERTEX_LOCATION is required" };
130030
+ return { ok: true, message: "credential fields present (Vertex connectivity verified on first use — run `gcloud auth application-default login` if not done)" };
130031
+ }
129617
130032
  const base2 = creds.baseURL ?? "https://api.openai.com/v1";
129618
130033
  const r2 = await fetch(`${base2.replace(/\/$/, "")}/models`, {
129619
130034
  headers: { Authorization: `Bearer ${creds.apiKey ?? ""}`, ...creds.headers ?? {} }
@@ -135173,7 +135588,7 @@ var import_react21 = __toESM(require_react(), 1);
135173
135588
  // src/cli.tsx
135174
135589
  import { createInterface } from "node:readline/promises";
135175
135590
  import { execFileSync as execFileSync4, spawnSync } from "node:child_process";
135176
- import { resolve as resolve12 } from "node:path";
135591
+ import { resolve as resolve13 } from "node:path";
135177
135592
  import { existsSync as existsSync12 } from "node:fs";
135178
135593
 
135179
135594
  // src/ui/App.tsx
@@ -139383,6 +139798,7 @@ var COMMANDS = [
139383
139798
  { name: "/memory", usage: "/memory [note]", desc: "show or add facts to remember (or start a line with #)", group: "chat" },
139384
139799
  { name: "/account", usage: "/account", desc: "list accounts; /account <number> to switch, /account add to add one", group: "accounts" },
139385
139800
  { name: "/onboard", usage: "/onboard", desc: "first-run setup; provider list and import/add commands", group: "accounts" },
139801
+ { name: "/mcp", usage: "/mcp", desc: "list or connect MCP servers: /mcp add <name> <command> [args]", group: "accounts" },
139386
139802
  { name: "/cost", usage: "/cost", desc: "see what you've spent per account", group: "accounts" },
139387
139803
  { name: "/copy", usage: "/copy", desc: "copy the last reply to the clipboard", group: "output" },
139388
139804
  { name: "/export", usage: "/export [file]", desc: "save the conversation to a file", group: "output" },
@@ -139413,7 +139829,7 @@ var GROUP_TITLES = [
139413
139829
  { id: "accounts", title: "accounts & cost" },
139414
139830
  { id: "output", title: "save & copy" },
139415
139831
  { id: "modes", title: "modes" },
139416
- { id: "settings", title: "look & settings" },
139832
+ { id: "settings", title: "settings" },
139417
139833
  { id: "other", title: "other" }
139418
139834
  ];
139419
139835
  function helpText() {
@@ -139503,9 +139919,10 @@ var ENV_LABEL = {
139503
139919
  deepseek: "DEEPSEEK_API_KEY"
139504
139920
  };
139505
139921
  function formatModelList(currentId, showAll = false) {
139922
+ const MODELS2 = modelRegistry();
139506
139923
  const line = (m2) => ` ${m2.id === currentId ? glyph.on : glyph.off} ${m2.label.padEnd(18)} ${m2.provider}`;
139507
- const usable = MODELS.filter((m2) => providerAvailable(m2.provider));
139508
- const rest2 = MODELS.filter((m2) => !providerAvailable(m2.provider));
139924
+ const usable = MODELS2.filter((m2) => providerAvailable(m2.provider));
139925
+ const rest2 = MODELS2.filter((m2) => !providerAvailable(m2.provider));
139509
139926
  const rows = ["models · /model <name> pins one · /model auto routes per task"];
139510
139927
  if (usable.length) {
139511
139928
  rows.push("", "ready to use");
@@ -139528,7 +139945,8 @@ function resolveModelSwitch(query) {
139528
139945
  const q = query.trim().toLowerCase();
139529
139946
  if (!q)
139530
139947
  return { ok: false, message: "usage: /model <name>" };
139531
- const matches2 = MODELS.filter((m3) => m3.label.toLowerCase().includes(q) || m3.id.toLowerCase().includes(q));
139948
+ const MODELS2 = modelRegistry();
139949
+ const matches2 = MODELS2.filter((m3) => m3.label.toLowerCase().includes(q) || m3.id.toLowerCase().includes(q));
139532
139950
  if (matches2.length === 0)
139533
139951
  return { ok: false, message: `no model matching “${query}” — /model to list` };
139534
139952
  const exact = matches2.find((m3) => m3.label.toLowerCase() === q || m3.id.toLowerCase() === q);
@@ -142120,10 +142538,10 @@ function pickDefaultModel(preferredId) {
142120
142538
  const wanted = findModel(pref);
142121
142539
  if (wanted && providerAvailable(wanted.provider))
142122
142540
  return wanted;
142123
- return MODELS.find((m2) => providerAvailable(m2.provider));
142541
+ return modelRegistry().find((m2) => providerAvailable(m2.provider));
142124
142542
  }
142125
142543
  function anyProviderAvailable() {
142126
- return MODELS.some((m2) => providerAvailable(m2.provider));
142544
+ return modelRegistry().some((m2) => providerAvailable(m2.provider));
142127
142545
  }
142128
142546
 
142129
142547
  // src/model/selector.ts
@@ -142239,7 +142657,7 @@ class RoutingSelector {
142239
142657
  const kind = task.kind ?? classify(task.prompt);
142240
142658
  const bar = BAR[kind];
142241
142659
  const required2 = task.requires ?? [];
142242
- const available = MODELS.filter((m2) => providerAvailable(m2.provider) && profileFor(m2.id));
142660
+ const available = modelRegistry().filter((m2) => providerAvailable(m2.provider));
142243
142661
  const capable = required2.length ? available.filter((m2) => supportsRequirements(m2, required2)) : available;
142244
142662
  if (available.length > 0 && capable.length === 0) {
142245
142663
  const missing = available.slice(0, 4).map((m2) => `${m2.label}: ${missingRequirements(m2, required2).join(", ")}`).join("; ");
@@ -143204,7 +143622,7 @@ function countTokens(text2, modelId) {
143204
143622
  }
143205
143623
 
143206
143624
  // src/context/memory.ts
143207
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync5 } from "node:fs";
143625
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync5 } from "node:fs";
143208
143626
  import { join as join8 } from "node:path";
143209
143627
  import { homedir as homedir7 } from "node:os";
143210
143628
  var DOC_CANDIDATES = ["GEARBOX.md", "CLAUDE.md", "AGENTS.md"];
@@ -143240,9 +143658,9 @@ function appendFact(text2, cwd2 = process.cwd()) {
143240
143658
  if (!fact)
143241
143659
  return false;
143242
143660
  try {
143243
- mkdirSync6(memDir(cwd2), { recursive: true });
143661
+ mkdirSync7(memDir(cwd2), { recursive: true });
143244
143662
  const stamp = new Date().toISOString().slice(0, 10);
143245
- writeFileSync6(factsFile(cwd2), `- [${stamp}] ${fact}
143663
+ writeFileSync7(factsFile(cwd2), `- [${stamp}] ${fact}
143246
143664
  `, { flag: "a" });
143247
143665
  return true;
143248
143666
  } catch {
@@ -144109,7 +144527,7 @@ ${summary}` },
144109
144527
 
144110
144528
  // src/image.ts
144111
144529
  import { existsSync as existsSync8, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
144112
- import { isAbsolute as isAbsolute2, resolve as resolve11 } from "node:path";
144530
+ import { basename as basename2, isAbsolute as isAbsolute2, resolve as resolve11 } from "node:path";
144113
144531
  var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
144114
144532
  var IMAGE_EXT = {
144115
144533
  ".png": "image/png",
@@ -144121,6 +144539,39 @@ var IMAGE_EXT = {
144121
144539
  function unquotePath(raw) {
144122
144540
  return raw.trim().replace(/^file:\/\//, "").replace(/^['"]|['"]$/g, "").replace(/\\ /g, " ");
144123
144541
  }
144542
+ function imageMimeForPath(path) {
144543
+ const ext = path.toLowerCase().match(/\.[^.]+$/)?.[0] ?? "";
144544
+ return IMAGE_EXT[ext];
144545
+ }
144546
+ function isImageFilePath(path) {
144547
+ return Boolean(imageMimeForPath(unquotePath(path)));
144548
+ }
144549
+ function shortName(path, max2 = 42) {
144550
+ const name31 = basename2(path);
144551
+ if (name31.length <= max2)
144552
+ return name31;
144553
+ const ext = name31.match(/\.[^.]+$/)?.[0] ?? "";
144554
+ const stem = ext ? name31.slice(0, -ext.length) : name31;
144555
+ return `${stem.slice(0, Math.max(8, max2 - ext.length - 1))}…${ext}`;
144556
+ }
144557
+ function imageChipLabel(path, index2) {
144558
+ return `[image${index2 && index2 > 1 ? ` ${index2}` : ""}: ${shortName(path)}]`;
144559
+ }
144560
+ function escapeRegExp2(s2) {
144561
+ return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
144562
+ }
144563
+ function replaceImagePathWithMarker(text2, path, marker16) {
144564
+ const raw = path;
144565
+ const file5 = `file://${raw}`;
144566
+ const escaped = raw.replace(/ /g, "\\ ");
144567
+ const candidates = [file5, raw, escaped];
144568
+ let out = text2;
144569
+ for (const c of candidates) {
144570
+ out = out.replace(new RegExp(`(["'])${escapeRegExp2(c)}\\1`, "g"), marker16);
144571
+ out = out.replace(new RegExp(escapeRegExp2(c), "g"), marker16);
144572
+ }
144573
+ return out;
144574
+ }
144124
144575
  function imagePathsInText(text2, cwd2 = process.cwd(), limit = 6) {
144125
144576
  const found = [];
144126
144577
  const add2 = (rawMatch) => {
@@ -144146,8 +144597,7 @@ function imagePathsInText(text2, cwd2 = process.cwd(), limit = 6) {
144146
144597
  }
144147
144598
  function loadImageAttachment(path) {
144148
144599
  const abs = isAbsolute2(path) ? path : resolve11(process.cwd(), path);
144149
- const ext = abs.toLowerCase().match(/\.[^.]+$/)?.[0] ?? "";
144150
- const mimeType = IMAGE_EXT[ext];
144600
+ const mimeType = imageMimeForPath(abs);
144151
144601
  if (!mimeType)
144152
144602
  throw new Error(`unsupported image type: ${path}`);
144153
144603
  const size2 = statSync4(abs).size;
@@ -144168,7 +144618,7 @@ function imageContent(text2, images) {
144168
144618
  init_capabilities();
144169
144619
 
144170
144620
  // src/init.ts
144171
- import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
144621
+ import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
144172
144622
  import { join as join13 } from "node:path";
144173
144623
 
144174
144624
  // src/verify.ts
@@ -144327,11 +144777,14 @@ function writeProjectGuide(cwd2 = process.cwd()) {
144327
144777
  const path = join13(cwd2, "GEARBOX.md");
144328
144778
  const before2 = existsSync10(path) ? readFileSync15(path, "utf8") : "";
144329
144779
  const after2 = buildProjectGuide(cwd2);
144330
- writeFileSync7(path, after2, "utf8");
144780
+ writeFileSync8(path, after2, "utf8");
144331
144781
  const diff2 = computeDiff(before2, after2);
144332
144782
  return { path, summary: `wrote GEARBOX.md (${diffStat(diff2)})`, diff: diff2 };
144333
144783
  }
144334
144784
 
144785
+ // src/ui/App.tsx
144786
+ init_mcp();
144787
+
144335
144788
  // src/ui/clipboard.ts
144336
144789
  init_proc();
144337
144790
  function osc52(text2) {
@@ -144534,7 +144987,7 @@ function gitBranch() {
144534
144987
  // src/ui/App.tsx
144535
144988
  init_proc();
144536
144989
  var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
144537
- import { basename as basename2, extname } from "node:path";
144990
+ import { basename as basename3, extname, resolve as resolve12 } from "node:path";
144538
144991
  import { existsSync as existsSync11, readFileSync as readFileSync16, statSync as statSync5 } from "node:fs";
144539
144992
  import { writeFile as fsWriteFile } from "node:fs/promises";
144540
144993
  import { spawnSync as nodeSpawnSync2 } from "node:child_process";
@@ -144598,6 +145051,7 @@ function firstPath(text2) {
144598
145051
  function uniq2(xs) {
144599
145052
  return [...new Set(xs)];
144600
145053
  }
145054
+ var CLAUDE_CLI_EFFORTS = [];
144601
145055
  var FALLBACK_CODEX_MODELS = [
144602
145056
  { id: "gpt-5.5", label: "gpt-5.5", provider: "codex", efforts: ["low", "medium", "high", "xhigh"] },
144603
145057
  { id: "gpt-5.4", label: "gpt-5.4", provider: "codex", efforts: ["low", "medium", "high", "xhigh"] },
@@ -144687,33 +145141,8 @@ function ActivityRail({ items, width }) {
144687
145141
  }, undefined, true, undefined, this);
144688
145142
  }
144689
145143
  function SetupSplash({ state, width, skin, splashSize }) {
144690
- const providers = featuredApiKeyProviders().slice(0, width >= 92 ? 12 : 8);
144691
- const left = providers.filter((_, i2) => i2 % 2 === 0);
144692
- const right = providers.filter((_, i2) => i2 % 2 === 1);
144693
145144
  const detected = state.importable.length + state.cloudImportable.length;
144694
- const panelWidth = Math.min(Math.max(width - 4, 12), 104);
144695
- const showTwoCols = panelWidth >= 82;
144696
- const firstColumn = showTwoCols ? left : providers;
144697
- const secondColumn = showTwoCols ? right : [];
144698
- const providerLine = (id, label) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144699
- color: color.dim,
144700
- children: [
144701
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144702
- color: color.accent,
144703
- children: [
144704
- "/account add ",
144705
- id
144706
- ]
144707
- }, undefined, true, undefined, this),
144708
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144709
- color: color.faint,
144710
- children: [
144711
- " ",
144712
- label
144713
- ]
144714
- }, undefined, true, undefined, this)
144715
- ]
144716
- }, id, true, undefined, this);
145145
+ const panelWidth = Math.min(Math.max(width - 4, 30), 58);
144717
145146
  return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144718
145147
  flexDirection: "column",
144719
145148
  alignItems: "center",
@@ -144730,188 +145159,130 @@ function SetupSplash({ state, width, skin, splashSize }) {
144730
145159
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144731
145160
  color: color.accent,
144732
145161
  bold: true,
144733
- children: "GEARBOX"
144734
- }, undefined, false, undefined, this),
144735
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144736
- color: color.text,
144737
- children: "one terminal, every model account you already pay for"
145162
+ children: "gearbox"
144738
145163
  }, undefined, false, undefined, this),
144739
145164
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144740
- color: color.faint,
144741
- children: "Set up one provider. Gearbox routes from there."
145165
+ color: color.dim,
145166
+ children: "one terminal · every model you already pay for"
144742
145167
  }, undefined, false, undefined, this)
144743
145168
  ]
144744
145169
  }, undefined, true, undefined, this),
144745
145170
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144746
- marginTop: 1,
145171
+ marginTop: 2,
144747
145172
  width: panelWidth,
144748
145173
  borderStyle: "round",
144749
- borderColor: color.accentDim,
145174
+ borderColor: color.faint,
144750
145175
  paddingX: 2,
144751
145176
  paddingY: 1,
144752
145177
  flexDirection: "column",
144753
145178
  children: [
144754
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144755
- justifyContent: "space-between",
144756
- children: [
144757
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144758
- color: color.text,
144759
- bold: true,
144760
- children: "start here"
144761
- }, undefined, false, undefined, this),
144762
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144763
- color: detected ? color.ok : color.faint,
144764
- children: detected ? `${detected} detected` : "no account yet"
144765
- }, undefined, false, undefined, this)
144766
- ]
144767
- }, undefined, true, undefined, this),
144768
- detected ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144769
- marginTop: 1,
144770
- children: [
144771
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144772
- color: color.ok,
144773
- children: [
144774
- glyph.on,
144775
- " "
144776
- ]
144777
- }, undefined, true, undefined, this),
144778
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144779
- color: color.text,
144780
- children: "Credentials found on this machine: "
144781
- }, undefined, false, undefined, this),
144782
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144783
- color: color.accent,
144784
- children: "/account import"
144785
- }, undefined, false, undefined, this)
144786
- ]
144787
- }, undefined, true, undefined, this) : null,
144788
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144789
- marginTop: 1,
144790
- flexDirection: showTwoCols ? "row" : "column",
145179
+ detected > 0 ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(jsx_dev_runtime12.Fragment, {
144791
145180
  children: [
144792
145181
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144793
- flexDirection: "column",
144794
- width: showTwoCols ? Math.floor((panelWidth - 6) / 2) : undefined,
144795
145182
  children: [
144796
145183
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144797
- color: color.faint,
144798
- children: "API key"
144799
- }, undefined, false, undefined, this),
144800
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144801
- color: color.text,
145184
+ color: color.ok,
144802
145185
  children: [
144803
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144804
- color: color.accent,
144805
- children: "/account add <api-key>"
144806
- }, undefined, false, undefined, this),
144807
- " auto-detect when possible"
145186
+ glyph.on,
145187
+ " "
144808
145188
  ]
144809
145189
  }, undefined, true, undefined, this),
144810
145190
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144811
145191
  color: color.text,
144812
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144813
- color: color.accent,
144814
- children: "/account add <provider> <api-key>"
144815
- }, undefined, false, undefined, this)
144816
- }, undefined, false, undefined, this)
145192
+ children: [
145193
+ detected,
145194
+ " credential",
145195
+ detected > 1 ? "s" : "",
145196
+ " found on this machine"
145197
+ ]
145198
+ }, undefined, true, undefined, this)
144817
145199
  ]
144818
145200
  }, undefined, true, undefined, this),
144819
145201
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144820
- flexDirection: "column",
144821
- marginLeft: showTwoCols ? 4 : 0,
144822
- marginTop: showTwoCols ? 0 : 1,
145202
+ marginTop: 1,
144823
145203
  children: [
144824
145204
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144825
- color: color.faint,
144826
- children: "Subscriptions and cloud"
145205
+ color: color.accent,
145206
+ children: "/account import"
144827
145207
  }, undefined, false, undefined, this),
144828
145208
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144829
- color: color.text,
144830
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144831
- color: color.accent,
144832
- children: "/account add azure <endpoint> <api-key>"
144833
- }, undefined, false, undefined, this)
145209
+ color: color.dim,
145210
+ children: " connect automatically"
145211
+ }, undefined, false, undefined, this)
145212
+ ]
145213
+ }, undefined, true, undefined, this),
145214
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
145215
+ marginTop: 1,
145216
+ children: [
145217
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145218
+ color: color.faint,
145219
+ children: "or add a different key: "
144834
145220
  }, undefined, false, undefined, this),
144835
145221
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144836
- color: color.text,
144837
- children: [
144838
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144839
- color: color.accent,
144840
- children: "/account add claude"
144841
- }, undefined, false, undefined, this),
144842
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144843
- color: color.faint,
144844
- children: " "
144845
- }, undefined, false, undefined, this),
144846
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144847
- color: color.accent,
144848
- children: "/account add codex"
144849
- }, undefined, false, undefined, this)
144850
- ]
144851
- }, undefined, true, undefined, this)
145222
+ color: color.accent,
145223
+ children: "/account add <api-key>"
145224
+ }, undefined, false, undefined, this)
144852
145225
  ]
144853
145226
  }, undefined, true, undefined, this)
144854
145227
  ]
144855
- }, undefined, true, undefined, this),
144856
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144857
- marginTop: 1,
144858
- children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144859
- color: color.faint,
144860
- children: glyph.rule.repeat(Math.max(panelWidth - 6, 20))
144861
- }, undefined, false, undefined, this)
144862
- }, undefined, false, undefined, this),
144863
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144864
- marginTop: 1,
144865
- flexDirection: showTwoCols ? "row" : "column",
145228
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(jsx_dev_runtime12.Fragment, {
144866
145229
  children: [
145230
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145231
+ color: color.dim,
145232
+ children: "paste or type a key to get started"
145233
+ }, undefined, false, undefined, this),
144867
145234
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144868
- flexDirection: "column",
144869
- width: showTwoCols ? Math.floor((panelWidth - 6) / 2) : undefined,
145235
+ marginTop: 1,
145236
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145237
+ color: color.accent,
145238
+ children: "/account add <api-key>"
145239
+ }, undefined, false, undefined, this)
145240
+ }, undefined, false, undefined, this)
145241
+ ]
145242
+ }, undefined, true, undefined, this),
145243
+ (state.hasClaudeCli || state.hasCodexCli) && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
145244
+ marginTop: 2,
145245
+ flexDirection: "column",
145246
+ children: [
145247
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145248
+ color: color.faint,
145249
+ children: "subscriptions detected"
145250
+ }, undefined, false, undefined, this),
145251
+ state.hasClaudeCli && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144870
145252
  children: [
144871
145253
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144872
- color: color.faint,
144873
- children: "Common providers"
145254
+ color: color.accent,
145255
+ children: "/account add claude"
144874
145256
  }, undefined, false, undefined, this),
144875
- firstColumn.map((p) => providerLine(p.id, p.label))
145257
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145258
+ color: color.faint,
145259
+ children: " Claude Pro / Max"
145260
+ }, undefined, false, undefined, this)
144876
145261
  ]
144877
145262
  }, undefined, true, undefined, this),
144878
- secondColumn.length ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144879
- flexDirection: "column",
144880
- marginLeft: 4,
145263
+ state.hasCodexCli && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144881
145264
  children: [
144882
145265
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144883
- color: color.faint,
144884
- children: " "
145266
+ color: color.accent,
145267
+ children: "/account add codex"
144885
145268
  }, undefined, false, undefined, this),
144886
- secondColumn.map((p) => providerLine(p.id, p.label))
145269
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145270
+ color: color.faint,
145271
+ children: " ChatGPT Plus"
145272
+ }, undefined, false, undefined, this)
144887
145273
  ]
144888
- }, undefined, true, undefined, this) : null
144889
- ]
144890
- }, undefined, true, undefined, this),
144891
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
144892
- marginTop: 1,
144893
- justifyContent: "space-between",
144894
- children: [
144895
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144896
- color: color.faint,
144897
- children: "Full catalog: "
144898
- }, undefined, false, undefined, this),
144899
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144900
- color: color.accent,
144901
- children: "/onboard providers"
144902
- }, undefined, false, undefined, this),
144903
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144904
- color: color.faint,
144905
- children: " Help: "
144906
- }, undefined, false, undefined, this),
144907
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
144908
- color: color.accent,
144909
- children: "/account"
144910
- }, undefined, false, undefined, this)
145274
+ }, undefined, true, undefined, this)
144911
145275
  ]
144912
145276
  }, undefined, true, undefined, this)
144913
145277
  ]
144914
- }, undefined, true, undefined, this)
145278
+ }, undefined, true, undefined, this),
145279
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
145280
+ marginTop: 1,
145281
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
145282
+ color: color.faint,
145283
+ children: "/onboard · /account · /help"
145284
+ }, undefined, false, undefined, this)
145285
+ }, undefined, false, undefined, this)
144915
145286
  ]
144916
145287
  }, undefined, true, undefined, this);
144917
145288
  }
@@ -144999,7 +145370,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
144999
145370
  return () => clearInterval(t2);
145000
145371
  }, [busy]);
145001
145372
  import_react26.useEffect(() => {
145002
- const proj = basename2(process.cwd());
145373
+ const proj = basename3(process.cwd());
145003
145374
  setTitle(busy ? `✳ ${proj} · working` : `${proj} · gearbox`);
145004
145375
  }, [busy]);
145005
145376
  const editRef = import_react26.useRef(edit2);
@@ -145029,6 +145400,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145029
145400
  const activeCliModelRef = import_react26.useRef(undefined);
145030
145401
  const cliSessionRef = import_react26.useRef(undefined);
145031
145402
  const activeImagesRef = import_react26.useRef([]);
145403
+ const imageChipPathsRef = import_react26.useRef(new Map);
145032
145404
  const accountStatusCacheRef = import_react26.useRef({});
145033
145405
  const usedAccountRef = import_react26.useRef(null);
145034
145406
  const cliMetaRef = import_react26.useRef(null);
@@ -145072,12 +145444,12 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145072
145444
  }
145073
145445
  }, []);
145074
145446
  import_react26.useEffect(() => {
145075
- setPermissionHandler((req) => new Promise((resolve12) => {
145447
+ setPermissionHandler((req) => new Promise((resolve13) => {
145076
145448
  if (modeRef.current === "auto-accept" && (req.kind === "write" || req.kind === "edit")) {
145077
- resolve12("once");
145449
+ resolve13("once");
145078
145450
  return;
145079
145451
  }
145080
- permQueue.current.push({ req, resolve: resolve12 });
145452
+ permQueue.current.push({ req, resolve: resolve13 });
145081
145453
  pumpPerm();
145082
145454
  }));
145083
145455
  return () => setPermissionHandler(null);
@@ -145199,9 +145571,9 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145199
145571
  const y = Number(m2[3]);
145200
145572
  const up2 = m2[4] === "m";
145201
145573
  if (b === 64)
145202
- delta -= 3;
145574
+ delta -= 1;
145203
145575
  else if (b === 65)
145204
- delta += 3;
145576
+ delta += 1;
145205
145577
  else {
145206
145578
  const off = composerOffset(x2, y);
145207
145579
  const point = transcriptPoint(x2, y);
@@ -145210,9 +145582,9 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145210
145582
  if (isPrimary && isDrag && transcriptMouseAnchorRef.current && !point) {
145211
145583
  const bottom = viewportTop + transcriptHeightLiveRef.current - 1;
145212
145584
  if (y < viewportTop)
145213
- scrollBy(-3);
145585
+ scrollBy(-2);
145214
145586
  else if (y > bottom)
145215
- scrollBy(3);
145587
+ scrollBy(2);
145216
145588
  const edgeLine = y < viewportTop ? scrollTopLiveRef.current : scrollTopLiveRef.current + transcriptHeightLiveRef.current - 1;
145217
145589
  const edgeText = lineText(linesRef.current[edgeLine] ?? []);
145218
145590
  setTranscriptSel({
@@ -145372,6 +145744,28 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145372
145744
  transcriptSelectionRef.current = sel;
145373
145745
  setTranscriptSelectionState(sel);
145374
145746
  };
145747
+ const imageMarkerFor = (path) => {
145748
+ for (const [marker50, existing] of imageChipPathsRef.current) {
145749
+ if (existing === path)
145750
+ return marker50;
145751
+ }
145752
+ let idx = 1;
145753
+ let marker16 = imageChipLabel(path);
145754
+ while (imageChipPathsRef.current.has(marker16)) {
145755
+ idx++;
145756
+ marker16 = imageChipLabel(path, idx);
145757
+ }
145758
+ imageChipPathsRef.current.set(marker16, path);
145759
+ return marker16;
145760
+ };
145761
+ const chipImagePathsIn = (text2) => {
145762
+ const paths = [];
145763
+ for (const [marker16, path] of imageChipPathsRef.current) {
145764
+ if (text2.includes(marker16))
145765
+ paths.push(path);
145766
+ }
145767
+ return paths;
145768
+ };
145375
145769
  const cliCatalogId = (binary) => binary.includes("codex") ? "codex-cli" : binary.includes("claude") ? "claude-cli" : "";
145376
145770
  const cliModelChoices = (binary) => {
145377
145771
  if (binary.includes("codex"))
@@ -145379,7 +145773,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145379
145773
  const provider = binary.includes("claude") ? "anthropic" : binary;
145380
145774
  return (catalogProvider(cliCatalogId(binary))?.defaultModels ?? []).map((id) => {
145381
145775
  const m2 = findModel(id);
145382
- return { id, label: m2?.label ?? id, provider, efforts: m2 ? effortLevels(m2) : undefined };
145776
+ return { id, label: m2?.label ?? id, provider, efforts: binary.includes("claude") ? CLAUDE_CLI_EFFORTS : m2 ? effortLevels(m2) : undefined };
145383
145777
  });
145384
145778
  };
145385
145779
  const cliSupportsModel = (binary, modelId) => {
@@ -145417,7 +145811,8 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145417
145811
  rows2.push(" no named subscription models exposed yet");
145418
145812
  for (const m2 of models)
145419
145813
  rows2.push(` ${m2.id === currentId ? glyph.on : glyph.off} ${m2.label}`);
145420
- rows2.push("", "API models are hidden while a subscription is active — /account off returns to API routing");
145814
+ rows2.push("", `API models are hidden while a subscription is active — /account off returns to API routing
145815
+ tip: /model haiku (or any API model name) switches directly and leaves the subscription`);
145421
145816
  return rows2.join(`
145422
145817
  `);
145423
145818
  };
@@ -145443,7 +145838,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145443
145838
  const take2 = (rows2) => rows2.filter((r2) => !q || `${r2.label} ${r2.detail ?? ""} ${r2.value}`.toLowerCase().includes(q)).slice(0, 7);
145444
145839
  if (head2 === "/model") {
145445
145840
  const cli = activeCliRef.current;
145446
- const models = cli ? cliModelChoices(cli.binary) : MODELS;
145841
+ const models = cli ? cliModelChoices(cli.binary) : modelRegistry();
145447
145842
  return take2([
145448
145843
  { value: "/model auto", label: "auto", detail: cli ? "use subscription default" : "route per task" },
145449
145844
  ...models.map((m2) => ({ value: `/model ${m2.label}`, label: m2.label, detail: cli ? `${cli.binary} subscription` : `${m2.provider} · ${m2.id}` }))
@@ -145504,6 +145899,16 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145504
145899
  const subscription = activeCli ? activeCli.label : null;
145505
145900
  const routing = setupRequired || activeCli ? null : lastPick?.reason ?? choice?.reason ?? null;
145506
145901
  const ctxPct = !activeCli && model && lastInput > 0 ? Math.round(lastInput / model.contextWindow * 100) : null;
145902
+ const activeModelEfforts = (() => {
145903
+ const cli = activeCliRef.current;
145904
+ if (cli) {
145905
+ const choices = cliModelChoices(cli.binary);
145906
+ const m2 = choices.find((c) => c.id === activeCliModel) ?? choices[0];
145907
+ return m2?.efforts ?? [];
145908
+ }
145909
+ return model ? effortLevels(model) : [];
145910
+ })();
145911
+ const displayEffort = activeModelEfforts.length > 0 ? effort : undefined;
145507
145912
  const push = (it) => setItems((prev) => [...prev, it]);
145508
145913
  const pushPhase = (label, detail) => {
145509
145914
  const id = idRef.current++;
@@ -145588,7 +145993,14 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145588
145993
  onEvent({ type: "phase", label: "using subscription", detail: `${cli.binary}${modelLabel2 ? ` · ${modelLabel2}` : ""} owns tools and permissions`, state: "running" });
145589
145994
  const cliChoices = cliModelChoices(cli.binary);
145590
145995
  const cliChoice = cliChoices.find((m2) => m2.id === activeCliModelRef.current) ?? cliChoices[0];
145591
- const cliEffort = cliChoice ? normalizeEffort(effortRef.current, cliChoice.efforts ?? []) ?? undefined : undefined;
145996
+ const _cliEffortRaw = cliChoice ? normalizeEffort(effortRef.current, cliChoice.efforts ?? []) : null;
145997
+ if (_cliEffortRaw === null && effortRef.current !== "medium") {
145998
+ const supported = cliChoice?.efforts ?? [];
145999
+ const { level: nearest } = clampEffort(effortRef.current, supported);
146000
+ const hint = supported.length ? ` — try /effort ${nearest}` : "";
146001
+ throw new Error(`effort "${effortRef.current}" is not supported by ${cliChoice?.label ?? cli.binary} (supports: ${supported.join(", ") || "none"}${hint})`);
146002
+ }
146003
+ const cliEffort = _cliEffortRaw ?? undefined;
145592
146004
  const activeAccount = getAccount(cli.id);
145593
146005
  const activeName = activeAccount ? accountName(activeAccount).match(/\((.*)\)/)?.[1] : undefined;
145594
146006
  const reloginCommand = cli.binary.includes("codex") ? `/account add codex${activeName ? ` ${activeName}` : ""}` : `/account add claude${activeName ? ` ${activeName}` : ""}`;
@@ -145629,12 +146041,19 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145629
146041
  cliMetaRef.current = null;
145630
146042
  if (account)
145631
146043
  markUsed(account.id);
145632
- const modelEffort = normalizeEffort(effortRef.current, effortLevels(choice2.model)) ?? undefined;
146044
+ const _effortRaw = normalizeEffort(effortRef.current, effortLevels(choice2.model));
146045
+ if (_effortRaw === null && effortRef.current !== "medium") {
146046
+ const supported = effortLevels(choice2.model);
146047
+ const { level: nearest } = clampEffort(effortRef.current, supported);
146048
+ const hint = supported.length ? ` — try /effort ${nearest}` : "";
146049
+ throw new Error(`effort "${effortRef.current}" is not supported by ${choice2.model.label} (supports: ${supported.join(", ") || "none"}${hint})`);
146050
+ }
146051
+ const modelEffort = _effortRaw ?? undefined;
145633
146052
  const r2 = await runTask({ model: choice2.model, messages: ctx, onEvent, signal, plan, system, creds, effort: modelEffort });
145634
146053
  const produced = r2.messages.slice(ctx.length);
145635
146054
  const imageNote = activeImagesRef.current.length ? `
145636
146055
 
145637
- [Attached images: ${activeImagesRef.current.map((img) => basename2(img.path)).join(", ")}]` : "";
146056
+ [Attached images: ${activeImagesRef.current.map((img) => basename3(img.path)).join(", ")}]` : "";
145638
146057
  const ledger = sanitizeToolPairs([...messages, { role: "user", content: prompt + imageNote }, ...produced]);
145639
146058
  return { messages: ledger, usage: r2.usage };
145640
146059
  }, []);
@@ -145680,6 +146099,17 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145680
146099
  updatePrefs({ activeAccount: null });
145681
146100
  return " (left the subscription)";
145682
146101
  };
146102
+ const applyEffortClamp = (allowed) => {
146103
+ const { level, clamped } = clampEffort(effortRef.current, allowed);
146104
+ if (!clamped)
146105
+ return "";
146106
+ const prev = effortRef.current;
146107
+ effortRef.current = level;
146108
+ setEffortState(level);
146109
+ if (!allowed.length)
146110
+ return ` — effort reset to ${level} (no reasoning support)`;
146111
+ return ` — effort clamped: ${prev} → ${level} (${prev} not supported)`;
146112
+ };
145683
146113
  const setEffort = (raw) => {
145684
146114
  const target = effortTarget();
145685
146115
  if (!target?.efforts.length) {
@@ -145721,28 +146151,34 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145721
146151
  }
145722
146152
  };
145723
146153
  const runTurn = import_react26.useCallback(async (prompt) => {
145724
- echo(prompt);
145725
- lastPromptRef.current = prompt;
145726
146154
  setVerb(nextVerb());
145727
146155
  activeImagesRef.current = [];
145728
146156
  let { text: modelPrompt, attached } = expandMentions(prompt);
145729
146157
  if (attached.length)
145730
146158
  notice(`attached ${attached.length} file${attached.length > 1 ? "s" : ""}: ${attached.join(", ")}`);
145731
- const imagePaths = imagePathsInText(modelPrompt);
146159
+ const imagePaths = uniq2([...chipImagePathsIn(modelPrompt), ...imagePathsInText(modelPrompt)]);
146160
+ let displayPrompt = prompt;
146161
+ for (const path of imagePaths) {
146162
+ const marker16 = imageMarkerFor(path);
146163
+ modelPrompt = replaceImagePathWithMarker(modelPrompt, path, marker16);
146164
+ displayPrompt = replaceImagePathWithMarker(displayPrompt, path, marker16);
146165
+ }
145732
146166
  const images = [];
145733
146167
  for (const path of imagePaths) {
145734
146168
  try {
145735
146169
  images.push(loadImageAttachment(path));
145736
146170
  } catch (e2) {
145737
- notice(`couldn't attach ${path}: ${(e2?.message ?? String(e2)).split(`
146171
+ notice(`couldn't attach ${basename3(path)}: ${(e2?.message ?? String(e2)).split(`
145738
146172
  `)[0]}`);
145739
146173
  }
145740
146174
  }
145741
146175
  activeImagesRef.current = images;
145742
146176
  if (images.length) {
145743
- const names = images.map((img) => basename2(img.path)).join(", ");
146177
+ const names = images.map((img) => imageMarkerFor(img.path)).join(", ");
145744
146178
  notice(`attached ${images.length} image${images.length === 1 ? "" : "s"}: ${names}`);
145745
146179
  }
146180
+ echo(displayPrompt);
146181
+ lastPromptRef.current = displayPrompt;
145746
146182
  const urls = urlsInText(modelPrompt);
145747
146183
  if (urls.length) {
145748
146184
  const fetched = [];
@@ -146267,11 +146703,27 @@ ${fetched.join(`
146267
146703
  if (cli) {
146268
146704
  const cr = resolveCliModel(cli.binary, arg);
146269
146705
  if (!cr.ok) {
146706
+ const r3 = resolveModelSwitch(arg);
146707
+ if (r3.ok && r3.modelId) {
146708
+ const left = leaveSubscription();
146709
+ setSelector(new FixedSelector(r3.modelId));
146710
+ setLastPick(null);
146711
+ routedRef.current = null;
146712
+ updatePrefs({ pinnedModel: r3.modelId });
146713
+ const newSpec2 = findModel(r3.modelId);
146714
+ const effortSuffix2 = applyEffortClamp(newSpec2 ? effortLevels(newSpec2) : []);
146715
+ notice(`${r3.message} — pinned (left subscription).${left}${effortSuffix2}`);
146716
+ const kind = classify(lastPromptRef.current ?? "").replace("code", "code");
146717
+ push({ kind: "preference", id: idRef.current++, text: `Remember ${r3.modelId} for ${kind} tasks?`, acceptCommand: `/prefer ${kind} ${r3.modelId}` });
146718
+ return;
146719
+ }
146270
146720
  notice(cr.message);
146271
146721
  return;
146272
146722
  }
146273
146723
  setActiveCliModelId(cr.modelId);
146274
- notice(`subscription model ${cr.label} — using ${cli.binary}; tools and permissions still owned by the subscription`);
146724
+ const newCliModel = cliModelChoices(cli.binary).find((m2) => m2.id === cr.modelId);
146725
+ const effortSuffix = applyEffortClamp(newCliModel?.efforts ?? []);
146726
+ notice(`subscription model → ${cr.label} — using ${cli.binary}; tools and permissions still owned by the subscription${effortSuffix}`);
146275
146727
  return;
146276
146728
  }
146277
146729
  const r2 = resolveModelSwitch(arg);
@@ -146281,7 +146733,9 @@ ${fetched.join(`
146281
146733
  setLastPick(null);
146282
146734
  routedRef.current = null;
146283
146735
  updatePrefs({ pinnedModel: r2.modelId });
146284
- notice(`${r2.message} pinned (persists across sessions). /model auto to route per task again.` + left);
146736
+ const newSpec = findModel(r2.modelId);
146737
+ const effortSuffix = applyEffortClamp(newSpec ? effortLevels(newSpec) : []);
146738
+ notice(`${r2.message} — pinned (persists across sessions). /model auto to route per task again.${left}${effortSuffix}`);
146285
146739
  const kind = classify(lastPromptRef.current ?? "").replace("code", "code");
146286
146740
  push({ kind: "preference", id: idRef.current++, text: `Remember ${r2.modelId} for ${kind} tasks?`, acceptCommand: `/prefer ${kind} ${r2.modelId}` });
146287
146741
  } else {
@@ -146348,6 +146802,54 @@ ${fetched.join(`
146348
146802
  }
146349
146803
  return;
146350
146804
  }
146805
+ case "mcp": {
146806
+ echo(text2);
146807
+ const parts = shellSplit(arg);
146808
+ const sub = (parts[0] ?? "list").toLowerCase();
146809
+ if (sub === "list" || sub === "servers") {
146810
+ notice(formatMcpConfigList());
146811
+ return;
146812
+ }
146813
+ if (sub === "tools") {
146814
+ notice("checking MCP servers…");
146815
+ mcpToolSummary().then(notice).catch((e2) => notice(`couldn't list MCP tools: ${e2?.message ?? String(e2)}`));
146816
+ return;
146817
+ }
146818
+ if (sub === "paths") {
146819
+ notice(mcpConfigPaths().join(`
146820
+ `));
146821
+ return;
146822
+ }
146823
+ if (sub === "add") {
146824
+ const global2 = parts[1] === "--global";
146825
+ const offset = global2 ? 2 : 1;
146826
+ const serverName = parts[offset] ?? "";
146827
+ const command = parts[offset + 1] ?? "";
146828
+ const commandArgs = parts.slice(offset + 2);
146829
+ try {
146830
+ notice(addMcpServer(serverName, command, commandArgs, { scope: global2 ? "global" : "project" }) + `
146831
+ Restarting is not required; new turns can use the tools.`);
146832
+ } catch (e2) {
146833
+ notice(`${e2?.message ?? String(e2)}
146834
+ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
146835
+ }
146836
+ return;
146837
+ }
146838
+ if (sub === "remove" || sub === "rm") {
146839
+ const global2 = parts[1] === "--global";
146840
+ const name50 = parts[global2 ? 2 : 1] ?? "";
146841
+ notice(removeMcpServer(name50, { scope: global2 ? "global" : undefined }));
146842
+ return;
146843
+ }
146844
+ notice(`MCP commands:
146845
+ ` + ` /mcp list
146846
+ ` + ` /mcp tools
146847
+ ` + ` /mcp add <name> <command> [args...]
146848
+ ` + ` /mcp add --global <name> <command> [args...]
146849
+ ` + ` /mcp remove <name>
146850
+ ` + " /mcp paths");
146851
+ return;
146852
+ }
146351
146853
  case "accounts":
146352
146854
  case "account": {
146353
146855
  echo(text2);
@@ -146498,6 +147000,7 @@ ${fetched.join(`
146498
147000
  ` + ` /account add codex <name> a 2nd ChatGPT account, e.g. /account add codex work
146499
147001
  ` + ` /account add azure <foundry-endpoint> <api-key>
146500
147002
  ` + ` /account add azure <resource-name> <api-key> [api-version]
147003
+ ` + ` /account add openai-compat <name> <base-url> <api-key> <model> [model...]
146501
147004
  ` + ` /account add <api-key> paste any provider key (auto-detected)
146502
147005
  ` + " /account add <provider> <api-key> e.g. anthropic, openai, openrouter");
146503
147006
  return;
@@ -146509,6 +147012,10 @@ ${fetched.join(`
146509
147012
  const azureKey = parts[3] ?? "";
146510
147013
  const apiVersion = parts[4];
146511
147014
  res = /^https?:\/\//i.test(resource) ? await addAzureFoundryAccount(resource, azureKey) : await addAzureAccount(resource, azureKey, { apiVersion });
147015
+ } else if (["openai-compat", "openai-compatible", "custom", "proxy"].includes(first)) {
147016
+ res = await addOpenAICompatAccount(parts[2] ?? "", parts[3] ?? "", parts[4] ?? "", parts.slice(5));
147017
+ } else if (catalogProvider(first)?.authKind === "openai-compat" && !catalogProvider(first)?.baseUrl && /^https?:\/\//i.test(parts[2] ?? "")) {
147018
+ res = await addOpenAICompatAccount(first, parts[2] ?? "", parts[3] ?? "", parts.slice(4));
146512
147019
  } else if (provGiven)
146513
147020
  res = await addApiKeyAccount(provGiven, keyVal);
146514
147021
  else if (detectProviderByKey(key))
@@ -146577,7 +147084,7 @@ ${fetched.join(`
146577
147084
  case "usage": {
146578
147085
  echo(text2);
146579
147086
  const accounts = listAccounts();
146580
- const resolve12 = (id) => {
147087
+ const resolve13 = (id) => {
146581
147088
  const a = getAccount(id);
146582
147089
  if (a) {
146583
147090
  const bin = a.auth.kind === "cli" ? a.auth.binary : undefined;
@@ -146595,7 +147102,7 @@ ${fetched.join(`
146595
147102
  const session = estimateCost(sessionRef.current.turns);
146596
147103
  const withBalance = accounts.filter((a) => a.exec !== "cli" && balanceExposed(a.provider));
146597
147104
  if (!withBalance.length) {
146598
- pushUsage(buildUsageView(session, resolve12, Date.now(), accounts.map((a) => a.id)));
147105
+ pushUsage(buildUsageView(session, resolve13, Date.now(), accounts.map((a) => a.id)));
146599
147106
  return;
146600
147107
  }
146601
147108
  notice("checking balances…");
@@ -146605,7 +147112,7 @@ ${fetched.join(`
146605
147112
  if (bal?.remainingUSD != null)
146606
147113
  recordBalance(a.id, bal);
146607
147114
  }
146608
- pushUsage(buildUsageView(session, resolve12, Date.now(), accounts.map((a) => a.id)));
147115
+ pushUsage(buildUsageView(session, resolve13, Date.now(), accounts.map((a) => a.id)));
146609
147116
  })();
146610
147117
  return;
146611
147118
  }
@@ -146941,8 +147448,15 @@ ${fetched.join(`
146941
147448
  if (!busyRef.current && input.length > 3 && !input.includes(`
146942
147449
  `)) {
146943
147450
  const p = sanitizeInputText(input).trim().replace(/^'|'$/g, "").replace(/\\ /g, " ");
146944
- if (/[/\\.]/.test(p) && p.length < 1024 && existsSync11(p)) {
147451
+ const abs = p.startsWith("~") ? p.replace(/^~/, process.env.HOME ?? "~") : resolve12(process.cwd(), p);
147452
+ if (/[/\\.]/.test(p) && p.length < 1024 && existsSync11(abs)) {
146945
147453
  const e2 = editRef.current;
147454
+ if (isImageFilePath(abs)) {
147455
+ const marker16 = imageMarkerFor(abs);
147456
+ setEdit({ value: e2.value.slice(0, e2.cursor) + marker16 + " " + e2.value.slice(e2.cursor), cursor: e2.cursor + marker16.length + 1 });
147457
+ flashStatus(`attached ${basename3(abs)}`);
147458
+ return;
147459
+ }
146946
147460
  const ins = `@${p} `;
146947
147461
  setEdit({ value: e2.value.slice(0, e2.cursor) + ins + e2.value.slice(e2.cursor), cursor: e2.cursor + ins.length });
146948
147462
  return;
@@ -147272,7 +147786,7 @@ ${fetched.join(`
147272
147786
  cost: estimateCost(sessionRef.current.turns),
147273
147787
  width,
147274
147788
  mode: mode2,
147275
- effort,
147789
+ effort: displayEffort,
147276
147790
  online
147277
147791
  }, undefined, false, undefined, this),
147278
147792
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
@@ -147297,7 +147811,7 @@ ${fetched.join(`
147297
147811
  children: [
147298
147812
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Banner, {
147299
147813
  model: modelLabel,
147300
- cwd: basename2(process.cwd()),
147814
+ cwd: basename3(process.cwd()),
147301
147815
  width
147302
147816
  }, undefined, false, undefined, this),
147303
147817
  welcome ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
@@ -147321,7 +147835,7 @@ ${fetched.join(`
147321
147835
  }
147322
147836
  const banner = /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Banner, {
147323
147837
  model: modelLabel,
147324
- cwd: basename2(process.cwd()),
147838
+ cwd: basename3(process.cwd()),
147325
147839
  width
147326
147840
  }, undefined, false, undefined, this);
147327
147841
  return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
@@ -147353,7 +147867,7 @@ init_permission();
147353
147867
  var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
147354
147868
  process.env.LANG = process.env.LANG || "en_US.UTF-8";
147355
147869
  process.env.LC_ALL = process.env.LC_ALL || "en_US.UTF-8";
147356
- var VERSION16 = "0.1.15";
147870
+ var VERSION16 = "0.1.18";
147357
147871
  var args = process.argv.slice(2);
147358
147872
  var supportsAnsi = process.env.FORCE_COLOR === "1" || process.env.TERM !== "dumb" && process.env.NO_COLOR !== "1" && process.stdout.isTTY;
147359
147873
  var ansi = (code) => supportsAnsi ? `\x1B[${code}m` : "";
@@ -147582,8 +148096,8 @@ async function readStdin() {
147582
148096
  return input;
147583
148097
  }
147584
148098
  if (args[0] === "upgrade" || args[0] === "update") {
147585
- const root2 = resolve12(import.meta.dir, "..");
147586
- if (!existsSync12(resolve12(root2, ".git"))) {
148099
+ const root2 = resolve13(import.meta.dir, "..");
148100
+ if (!existsSync12(resolve13(root2, ".git"))) {
147587
148101
  console.log("This build can't self-update (not a git checkout).");
147588
148102
  console.log("Update by pulling the repo and reinstalling: git pull && bun install");
147589
148103
  process.exit(0);
@@ -147608,7 +148122,8 @@ Usage:
147608
148122
  gearbox onboard set up a provider before opening the app
147609
148123
  gearbox --model <name> start with a specific model
147610
148124
  gearbox --continue resume the most recent session in this directory
147611
- gearbox mcp list show configured MCP tools
148125
+ gearbox mcp list show configured MCP servers
148126
+ gearbox mcp add <name> <command> [args...]
147612
148127
  gearbox doctor models show provider/model capability matrix
147613
148128
  gearbox upgrade pull the latest version + reinstall deps
147614
148129
 
@@ -147625,11 +148140,12 @@ Set up at least one provider first:
147625
148140
  gearbox onboard
147626
148141
  gearbox auth add <api-key>
147627
148142
  gearbox auth add <provider> <api-key>
148143
+ gearbox auth add openai-compat <name> <base-url> <api-key> <model>
147628
148144
  gearbox auth add codex [name]
147629
148145
  gearbox auth add claude [name]
147630
148146
  gearbox auth import
147631
148147
 
147632
- Models: ${MODELS.map((m2) => m2.label).join(", ")}
148148
+ Models: ${modelRegistry().map((m2) => m2.label).join(", ")}
147633
148149
  In-app: / for commands, @ for files, !cmd for shell, shift+tab for plan mode.`);
147634
148150
  process.exit(0);
147635
148151
  }
@@ -147642,15 +148158,31 @@ if (args[0] === "onboard" || args[0] === "setup") {
147642
148158
  process.exit(0);
147643
148159
  }
147644
148160
  if (args[0] === "mcp") {
147645
- const { mcpToolSummary: mcpToolSummary2, mcpConfigPaths: mcpConfigPaths2 } = await Promise.resolve().then(() => (init_mcp(), exports_mcp));
148161
+ const { addMcpServer: addMcpServer2, formatMcpConfigList: formatMcpConfigList2, mcpToolSummary: mcpToolSummary2, mcpConfigPaths: mcpConfigPaths2, removeMcpServer: removeMcpServer2 } = await Promise.resolve().then(() => (init_mcp(), exports_mcp));
147646
148162
  const sub = args[1] ?? "list";
147647
148163
  if (sub === "list" || sub === "tools") {
147648
- console.log(await mcpToolSummary2());
148164
+ if (sub === "tools")
148165
+ console.log(await mcpToolSummary2());
148166
+ else
148167
+ console.log(formatMcpConfigList2());
147649
148168
  } else if (sub === "paths") {
147650
148169
  console.log(mcpConfigPaths2().join(`
147651
148170
  `));
148171
+ } else if (sub === "add") {
148172
+ const global2 = args[2] === "--global";
148173
+ const offset = global2 ? 3 : 2;
148174
+ try {
148175
+ console.log(addMcpServer2(args[offset] ?? "", args[offset + 1] ?? "", args.slice(offset + 2), { scope: global2 ? "global" : "project" }));
148176
+ } catch (e2) {
148177
+ console.log(e2?.message ?? String(e2));
148178
+ console.log("example: gearbox mcp add github npx -y @modelcontextprotocol/server-github");
148179
+ process.exit(1);
148180
+ }
148181
+ } else if (sub === "remove" || sub === "rm") {
148182
+ const global2 = args[2] === "--global";
148183
+ console.log(removeMcpServer2(args[global2 ? 3 : 2] ?? "", { scope: global2 ? "global" : undefined }));
147652
148184
  } else {
147653
- console.log("gearbox mcp [list|paths]");
148185
+ console.log("gearbox mcp [list|tools|paths|add <name> <command> [args...]|remove <name>]");
147654
148186
  }
147655
148187
  process.exit(0);
147656
148188
  }
@@ -147667,7 +148199,7 @@ if (args[0] === "doctor") {
147667
148199
  if (args[0] === "auth") {
147668
148200
  const { listAccounts: listAccounts2, loadAccounts: loadAccounts3, removeAccount: removeAccount2 } = await Promise.resolve().then(() => (init_store(), exports_store));
147669
148201
  const { importableEnvCreds: importableEnvCreds2, importEnvCred: importEnvCred2, importableCloudCreds: importableCloudCreds2, importCloudCred: importCloudCred2 } = await Promise.resolve().then(() => (init_detect(), exports_detect));
147670
- const { addApiKeyAccount: addApiKeyAccount2, addByPastedKey: addByPastedKey2, testAccount: testAccount2, addableProviders: addableProviders2, addCliAccount: addCliAccount2, cliAuthStatus: cliAuthStatus2, cliLoginArgs: cliLoginArgs2 } = await Promise.resolve().then(() => (init_onboard(), exports_onboard));
148202
+ const { addApiKeyAccount: addApiKeyAccount2, addByPastedKey: addByPastedKey2, addOpenAICompatAccount: addOpenAICompatAccount2, testAccount: testAccount2, addableProviders: addableProviders2, addCliAccount: addCliAccount2, cliAuthStatus: cliAuthStatus2, cliLoginArgs: cliLoginArgs2 } = await Promise.resolve().then(() => (init_onboard(), exports_onboard));
147671
148203
  const { subscriptionEnv: subscriptionEnv2 } = await Promise.resolve().then(() => (init_cli_backend(), exports_cli_backend));
147672
148204
  const { detectProviderByKey: detectProviderByKey2 } = await Promise.resolve().then(() => (init_catalog(), exports_catalog));
147673
148205
  const sub = args[1];
@@ -147694,7 +148226,7 @@ Importable from your env (gearbox auth import): ${imp.map((c) => c.envVar).join(
147694
148226
  } else if (sub === "add") {
147695
148227
  const head2 = (rest2[0] ?? "").toLowerCase();
147696
148228
  const cliProvider = head2 === "codex" || head2 === "chatgpt" ? "codex-cli" : head2 === "claude" ? "claude-cli" : "";
147697
- const res = cliProvider ? addCliAccount2(cliProvider, rest2.slice(1).join(" ").trim() || undefined) : rest2[0] && !rest2[1] && detectProviderByKey2(rest2[0]) ? await addByPastedKey2(rest2[0]) : rest2[0] && rest2[1] ? await addApiKeyAccount2(rest2[0], rest2[1]) : { ok: false, message: "usage: gearbox auth add <key> | gearbox auth add <provider> <key> | gearbox auth add codex [name]" };
148229
+ const res = cliProvider ? addCliAccount2(cliProvider, rest2.slice(1).join(" ").trim() || undefined) : ["openai-compat", "openai-compatible", "custom", "proxy"].includes(head2) ? await addOpenAICompatAccount2(rest2[1] ?? "", rest2[2] ?? "", rest2[3] ?? "", rest2.slice(4)) : rest2[0] && !rest2[1] && detectProviderByKey2(rest2[0]) ? await addByPastedKey2(rest2[0]) : rest2[0] && rest2[1] ? await addApiKeyAccount2(rest2[0], rest2[1]) : { ok: false, message: "usage: gearbox auth add <key> | gearbox auth add <provider> <key> | gearbox auth add openai-compat <name> <base-url> <key> <model> | gearbox auth add codex [name]" };
147698
148230
  console.log(res.message);
147699
148231
  if (res.ok && res.account) {
147700
148232
  if (res.account.exec === "cli" && res.account.auth.kind === "cli") {