gearbox-code 0.1.15 → 0.1.16

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 +408 -59
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -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,7 +106316,7 @@ 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"] },
@@ -106497,12 +106532,16 @@ __export(exports_capabilities, {
106497
106532
  capabilitiesFor: () => capabilitiesFor
106498
106533
  });
106499
106534
  function providerSource(spec6) {
106535
+ if (spec6.capabilities?.source)
106536
+ return spec6.capabilities.source;
106500
106537
  const group = catalogProvider(spec6.provider)?.group;
106501
106538
  if (group === "gateway" || group === "openai-compat" || group === "local")
106502
106539
  return "seeded";
106503
106540
  return "seeded";
106504
106541
  }
106505
106542
  function exactUsage(spec6) {
106543
+ if (spec6.capabilities?.usage)
106544
+ return spec6.capabilities.usage;
106506
106545
  if (spec6.provider === "anthropic" || spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "deepseek")
106507
106546
  return "exact";
106508
106547
  const group = catalogProvider(spec6.provider)?.group;
@@ -106511,6 +106550,8 @@ function exactUsage(spec6) {
106511
106550
  return "none";
106512
106551
  }
106513
106552
  function toolSupport(spec6) {
106553
+ if (spec6.capabilities?.tools != null)
106554
+ return spec6.capabilities.tools;
106514
106555
  const group = catalogProvider(spec6.provider)?.group;
106515
106556
  if (spec6.provider === "anthropic" || spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "deepseek")
106516
106557
  return true;
@@ -106521,6 +106562,8 @@ function toolSupport(spec6) {
106521
106562
  return "unknown";
106522
106563
  }
106523
106564
  function imageSupport(spec6) {
106565
+ if (spec6.capabilities?.images != null)
106566
+ return spec6.capabilities.images;
106524
106567
  if (spec6.provider === "anthropic" || spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "vertex")
106525
106568
  return true;
106526
106569
  if (spec6.provider === "deepseek")
@@ -106528,6 +106571,8 @@ function imageSupport(spec6) {
106528
106571
  return "unknown";
106529
106572
  }
106530
106573
  function schemaSupport(spec6) {
106574
+ if (spec6.capabilities?.jsonSchema != null)
106575
+ return spec6.capabilities.jsonSchema;
106531
106576
  if (spec6.provider === "openai" || spec6.provider === "google" || spec6.provider === "anthropic")
106532
106577
  return true;
106533
106578
  if (spec6.provider === "deepseek")
@@ -106551,7 +106596,7 @@ function capabilitiesFor(spec6) {
106551
106596
  images: imageSupport(spec6),
106552
106597
  jsonSchema: schemaSupport(spec6),
106553
106598
  reasoningEffort: efforts.length ? efforts : false,
106554
- systemPrompt: true,
106599
+ systemPrompt: spec6.capabilities?.systemPrompt ?? true,
106555
106600
  usage: exactUsage(spec6),
106556
106601
  contextWindow: profile?.contextWindow ?? spec6.contextWindow,
106557
106602
  maxOutputTokens: profile?.maxOutput,
@@ -106583,7 +106628,7 @@ function cell(v) {
106583
106628
  return "?";
106584
106629
  return String(v ?? "");
106585
106630
  }
106586
- function formatCapabilityMatrix(models = MODELS) {
106631
+ function formatCapabilityMatrix(models = modelRegistry()) {
106587
106632
  const rows = models.map((m2) => {
106588
106633
  const c = capabilitiesFor(m2);
106589
106634
  return {
@@ -128749,12 +128794,19 @@ var init_stdio2 = __esm(() => {
128749
128794
  // src/mcp.ts
128750
128795
  var exports_mcp = {};
128751
128796
  __export(exports_mcp, {
128797
+ shellSplit: () => shellSplit,
128798
+ removeMcpServer: () => removeMcpServer,
128799
+ reloadMcpConnections: () => reloadMcpConnections,
128752
128800
  mcpTools: () => mcpTools,
128753
128801
  mcpToolSummary: () => mcpToolSummary,
128754
- mcpConfigPaths: () => mcpConfigPaths
128802
+ mcpConfigPaths: () => mcpConfigPaths,
128803
+ mcpConfigPath: () => mcpConfigPath,
128804
+ formatMcpConfigList: () => formatMcpConfigList,
128805
+ configuredMcpServers: () => configuredMcpServers,
128806
+ addMcpServer: () => addMcpServer
128755
128807
  });
128756
- import { existsSync as existsSync3, readFileSync as readFileSync7 } from "node:fs";
128757
- import { join as join7 } from "node:path";
128808
+ import { existsSync as existsSync3, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
128809
+ import { dirname as dirname3, join as join7 } from "node:path";
128758
128810
  import { homedir as homedir6 } from "node:os";
128759
128811
  function readConfigFile(path) {
128760
128812
  if (!existsSync3(path))
@@ -128765,6 +128817,11 @@ function readConfigFile(path) {
128765
128817
  return {};
128766
128818
  }
128767
128819
  }
128820
+ function writeConfigFile(path, config3) {
128821
+ mkdirSync6(dirname3(path), { recursive: true });
128822
+ writeFileSync6(path, JSON.stringify(config3, null, 2) + `
128823
+ `, { mode: 384 });
128824
+ }
128768
128825
  function expandEnv(value) {
128769
128826
  return value.replace(/\$\{([A-Z0-9_]+)\}/gi, (_, name31) => process.env[name31] ?? "");
128770
128827
  }
@@ -128813,6 +128870,9 @@ async function connected() {
128813
128870
  connectedPromise ??= connectAll();
128814
128871
  return connectedPromise;
128815
128872
  }
128873
+ function reloadMcpConnections() {
128874
+ connectedPromise = null;
128875
+ }
128816
128876
  function formatMcpResult(result2) {
128817
128877
  const content = result2?.content ?? [];
128818
128878
  if (!Array.isArray(content) || !content.length)
@@ -128839,6 +128899,122 @@ async function mcpToolSummary() {
128839
128899
  return rows.flatMap((s2) => s2.tools.map((t2) => `${safeToolName(s2.name, t2.name).padEnd(34)} ${t2.description ?? ""}`)).join(`
128840
128900
  `);
128841
128901
  }
128902
+ function mcpConfigPath(scope = "project", cwd2 = process.cwd()) {
128903
+ return scope === "global" ? join7(HOME(), "mcp.json") : join7(cwd2, ".gearbox", "mcp.json");
128904
+ }
128905
+ function configuredMcpServers(cwd2 = process.cwd()) {
128906
+ const paths = [
128907
+ { scope: "global", path: join7(HOME(), "mcp.json") },
128908
+ { scope: "compat", path: join7(cwd2, ".mcp.json") },
128909
+ { scope: "project", path: join7(cwd2, ".gearbox", "mcp.json") }
128910
+ ];
128911
+ const rows = [];
128912
+ for (const p of paths) {
128913
+ const file5 = readConfigFile(p.path);
128914
+ for (const [name31, config3] of Object.entries(file5.mcpServers ?? file5.servers ?? {})) {
128915
+ if (!config3?.command)
128916
+ continue;
128917
+ rows.push({ name: name31, scope: p.scope, config: config3 });
128918
+ }
128919
+ }
128920
+ return rows;
128921
+ }
128922
+ function formatMcpConfigList(cwd2 = process.cwd()) {
128923
+ const rows = configuredMcpServers(cwd2);
128924
+ if (!rows.length) {
128925
+ return [
128926
+ "MCP servers",
128927
+ " none configured",
128928
+ "",
128929
+ "Add one:",
128930
+ " /mcp add github npx -y @modelcontextprotocol/server-github",
128931
+ " /mcp add --global linear npx -y @modelcontextprotocol/server-linear"
128932
+ ].join(`
128933
+ `);
128934
+ }
128935
+ return [
128936
+ "MCP servers",
128937
+ ...rows.map((r2) => {
128938
+ const args = r2.config.args?.length ? " " + r2.config.args.join(" ") : "";
128939
+ const off = r2.config.disabled ? " · disabled" : "";
128940
+ return ` ${r2.name.padEnd(18)} ${r2.scope.padEnd(7)} ${r2.config.command}${args}${off}`;
128941
+ }),
128942
+ "",
128943
+ "Tools: /mcp tools",
128944
+ "Remove: /mcp remove <name>"
128945
+ ].join(`
128946
+ `);
128947
+ }
128948
+ function shellSplit(input) {
128949
+ const out = [];
128950
+ let cur = "";
128951
+ let quote = null;
128952
+ let esc2 = false;
128953
+ for (const ch of input) {
128954
+ if (esc2) {
128955
+ cur += ch;
128956
+ esc2 = false;
128957
+ continue;
128958
+ }
128959
+ if (ch === "\\") {
128960
+ esc2 = true;
128961
+ continue;
128962
+ }
128963
+ if (quote) {
128964
+ if (ch === quote)
128965
+ quote = null;
128966
+ else
128967
+ cur += ch;
128968
+ continue;
128969
+ }
128970
+ if (ch === "'" || ch === '"') {
128971
+ quote = ch;
128972
+ continue;
128973
+ }
128974
+ if (/\s/.test(ch)) {
128975
+ if (cur) {
128976
+ out.push(cur);
128977
+ cur = "";
128978
+ }
128979
+ continue;
128980
+ }
128981
+ cur += ch;
128982
+ }
128983
+ if (cur)
128984
+ out.push(cur);
128985
+ return out;
128986
+ }
128987
+ function cleanServerName(name31) {
128988
+ return name31.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
128989
+ }
128990
+ function addMcpServer(name31, command, args = [], opts = {}) {
128991
+ const serverName = cleanServerName(name31);
128992
+ if (!serverName || !command)
128993
+ throw new Error("usage: /mcp add <name> <command> [args...]");
128994
+ const path = mcpConfigPath(opts.scope ?? "project", opts.cwd ?? process.cwd());
128995
+ const file5 = readConfigFile(path);
128996
+ const servers = { ...file5.mcpServers ?? file5.servers ?? {} };
128997
+ servers[serverName] = { command, args };
128998
+ writeConfigFile(path, { mcpServers: servers });
128999
+ reloadMcpConnections();
129000
+ return `connected ${serverName} (${opts.scope ?? "project"})`;
129001
+ }
129002
+ function removeMcpServer(name31, opts = {}) {
129003
+ const serverName = cleanServerName(name31);
129004
+ const scopes = opts.scope ? [opts.scope] : ["project", "global"];
129005
+ for (const scope of scopes) {
129006
+ const path = mcpConfigPath(scope, opts.cwd ?? process.cwd());
129007
+ const file5 = readConfigFile(path);
129008
+ const servers = { ...file5.mcpServers ?? file5.servers ?? {} };
129009
+ if (!(serverName in servers))
129010
+ continue;
129011
+ delete servers[serverName];
129012
+ writeConfigFile(path, { mcpServers: servers });
129013
+ reloadMcpConnections();
129014
+ return `removed ${serverName} (${scope})`;
129015
+ }
129016
+ return `no MCP server named ${serverName}`;
129017
+ }
128842
129018
  async function mcpTools(onEvent, readOnly = false) {
128843
129019
  const rows = await connected();
128844
129020
  const out = {};
@@ -129415,15 +129591,19 @@ __export(exports_onboard, {
129415
129591
  cliLoginArgs: () => cliLoginArgs,
129416
129592
  cliAuthStatus: () => cliAuthStatus,
129417
129593
  addableProviders: () => addableProviders,
129594
+ addOpenAICompatAccount: () => addOpenAICompatAccount,
129418
129595
  addCliAccount: () => addCliAccount,
129419
129596
  addByPastedKey: () => addByPastedKey,
129420
129597
  addAzureFoundryAccount: () => addAzureFoundryAccount,
129421
129598
  addAzureAccount: () => addAzureAccount,
129422
129599
  addApiKeyAccount: () => addApiKeyAccount
129423
129600
  });
129424
- import { mkdirSync as mkdirSync7 } from "node:fs";
129601
+ import { mkdirSync as mkdirSync8 } from "node:fs";
129425
129602
  import { join as join11 } from "node:path";
129426
129603
  import { homedir as homedir9 } from "node:os";
129604
+ function slugify(input) {
129605
+ return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || shortId();
129606
+ }
129427
129607
  async function addApiKeyAccount(provider, key, opts = {}) {
129428
129608
  provider = normalizeProviderId(provider);
129429
129609
  const cat = catalogProvider(provider);
@@ -129450,6 +129630,29 @@ async function addApiKeyAccount(provider, key, opts = {}) {
129450
129630
  putAccount(account);
129451
129631
  return { ok: true, account, message: `added ${account.label} (${id})` };
129452
129632
  }
129633
+ async function addOpenAICompatAccount(name31, baseUrl, key, models, opts = {}) {
129634
+ if (!/^https?:\/\//i.test(baseUrl) || !models.length) {
129635
+ return { ok: false, message: "usage: /account add openai-compat <name> <base-url> <api-key> <model> [model...]" };
129636
+ }
129637
+ const known = catalogProvider(normalizeProviderId(name31));
129638
+ const provider = known?.authKind === "openai-compat" ? known.id : `custom-${slugify(name31)}`;
129639
+ const id = opts.id ?? `${provider}-${shortId()}`;
129640
+ const ref = `${id}:api-key`;
129641
+ await setSecret(ref, key.trim());
129642
+ const account = {
129643
+ id,
129644
+ label: opts.label ?? (known?.label ?? (name31.trim() || "OpenAI-compatible")),
129645
+ provider,
129646
+ exec: "in-loop",
129647
+ auth: { kind: "openai-compat", ref },
129648
+ baseUrl: baseUrl.replace(/\/+$/, ""),
129649
+ models,
129650
+ enabled: true,
129651
+ addedAt: Date.now()
129652
+ };
129653
+ putAccount(account);
129654
+ return { ok: true, account, message: `added ${account.label} (${models.length} model${models.length === 1 ? "" : "s"})` };
129655
+ }
129453
129656
  function azureResourceName(input) {
129454
129657
  const s2 = input.trim();
129455
129658
  try {
@@ -129492,7 +129695,7 @@ async function addAzureAccount(resourceOrEndpoint, key, opts = {}) {
129492
129695
  const resourceName = azureResourceName(resourceOrEndpoint);
129493
129696
  if (!resourceName || !key.trim())
129494
129697
  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()}`;
129698
+ const id = opts.id ?? `azure-${slugify(resourceName)}`;
129496
129699
  const ref = `${id}:api-key`;
129497
129700
  await setSecret(ref, key.trim());
129498
129701
  const account = {
@@ -129521,7 +129724,7 @@ function addCliAccount(provider, name31) {
129521
129724
  const id = slug3 ? `${provider}-${slug3}` : provider;
129522
129725
  const profile = slug3 ? cliProfileDir(id) : undefined;
129523
129726
  if (profile)
129524
- mkdirSync7(profile, { recursive: true });
129727
+ mkdirSync8(profile, { recursive: true });
129525
129728
  const account = {
129526
129729
  id,
129527
129730
  label: slug3 ? `${cat.label.replace(/ \(.*\)$/, "")} (${name31.trim()})` : cat.label,
@@ -135173,7 +135376,7 @@ var import_react21 = __toESM(require_react(), 1);
135173
135376
  // src/cli.tsx
135174
135377
  import { createInterface } from "node:readline/promises";
135175
135378
  import { execFileSync as execFileSync4, spawnSync } from "node:child_process";
135176
- import { resolve as resolve12 } from "node:path";
135379
+ import { resolve as resolve13 } from "node:path";
135177
135380
  import { existsSync as existsSync12 } from "node:fs";
135178
135381
 
135179
135382
  // src/ui/App.tsx
@@ -139383,6 +139586,7 @@ var COMMANDS = [
139383
139586
  { name: "/memory", usage: "/memory [note]", desc: "show or add facts to remember (or start a line with #)", group: "chat" },
139384
139587
  { name: "/account", usage: "/account", desc: "list accounts; /account <number> to switch, /account add to add one", group: "accounts" },
139385
139588
  { name: "/onboard", usage: "/onboard", desc: "first-run setup; provider list and import/add commands", group: "accounts" },
139589
+ { name: "/mcp", usage: "/mcp", desc: "list or connect MCP servers: /mcp add <name> <command> [args]", group: "accounts" },
139386
139590
  { name: "/cost", usage: "/cost", desc: "see what you've spent per account", group: "accounts" },
139387
139591
  { name: "/copy", usage: "/copy", desc: "copy the last reply to the clipboard", group: "output" },
139388
139592
  { name: "/export", usage: "/export [file]", desc: "save the conversation to a file", group: "output" },
@@ -139503,9 +139707,10 @@ var ENV_LABEL = {
139503
139707
  deepseek: "DEEPSEEK_API_KEY"
139504
139708
  };
139505
139709
  function formatModelList(currentId, showAll = false) {
139710
+ const MODELS2 = modelRegistry();
139506
139711
  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));
139712
+ const usable = MODELS2.filter((m2) => providerAvailable(m2.provider));
139713
+ const rest2 = MODELS2.filter((m2) => !providerAvailable(m2.provider));
139509
139714
  const rows = ["models · /model <name> pins one · /model auto routes per task"];
139510
139715
  if (usable.length) {
139511
139716
  rows.push("", "ready to use");
@@ -139528,7 +139733,8 @@ function resolveModelSwitch(query) {
139528
139733
  const q = query.trim().toLowerCase();
139529
139734
  if (!q)
139530
139735
  return { ok: false, message: "usage: /model <name>" };
139531
- const matches2 = MODELS.filter((m3) => m3.label.toLowerCase().includes(q) || m3.id.toLowerCase().includes(q));
139736
+ const MODELS2 = modelRegistry();
139737
+ const matches2 = MODELS2.filter((m3) => m3.label.toLowerCase().includes(q) || m3.id.toLowerCase().includes(q));
139532
139738
  if (matches2.length === 0)
139533
139739
  return { ok: false, message: `no model matching “${query}” — /model to list` };
139534
139740
  const exact = matches2.find((m3) => m3.label.toLowerCase() === q || m3.id.toLowerCase() === q);
@@ -142120,10 +142326,10 @@ function pickDefaultModel(preferredId) {
142120
142326
  const wanted = findModel(pref);
142121
142327
  if (wanted && providerAvailable(wanted.provider))
142122
142328
  return wanted;
142123
- return MODELS.find((m2) => providerAvailable(m2.provider));
142329
+ return modelRegistry().find((m2) => providerAvailable(m2.provider));
142124
142330
  }
142125
142331
  function anyProviderAvailable() {
142126
- return MODELS.some((m2) => providerAvailable(m2.provider));
142332
+ return modelRegistry().some((m2) => providerAvailable(m2.provider));
142127
142333
  }
142128
142334
 
142129
142335
  // src/model/selector.ts
@@ -142239,7 +142445,7 @@ class RoutingSelector {
142239
142445
  const kind = task.kind ?? classify(task.prompt);
142240
142446
  const bar = BAR[kind];
142241
142447
  const required2 = task.requires ?? [];
142242
- const available = MODELS.filter((m2) => providerAvailable(m2.provider) && profileFor(m2.id));
142448
+ const available = modelRegistry().filter((m2) => providerAvailable(m2.provider));
142243
142449
  const capable = required2.length ? available.filter((m2) => supportsRequirements(m2, required2)) : available;
142244
142450
  if (available.length > 0 && capable.length === 0) {
142245
142451
  const missing = available.slice(0, 4).map((m2) => `${m2.label}: ${missingRequirements(m2, required2).join(", ")}`).join("; ");
@@ -143204,7 +143410,7 @@ function countTokens(text2, modelId) {
143204
143410
  }
143205
143411
 
143206
143412
  // src/context/memory.ts
143207
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync5 } from "node:fs";
143413
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync5 } from "node:fs";
143208
143414
  import { join as join8 } from "node:path";
143209
143415
  import { homedir as homedir7 } from "node:os";
143210
143416
  var DOC_CANDIDATES = ["GEARBOX.md", "CLAUDE.md", "AGENTS.md"];
@@ -143240,9 +143446,9 @@ function appendFact(text2, cwd2 = process.cwd()) {
143240
143446
  if (!fact)
143241
143447
  return false;
143242
143448
  try {
143243
- mkdirSync6(memDir(cwd2), { recursive: true });
143449
+ mkdirSync7(memDir(cwd2), { recursive: true });
143244
143450
  const stamp = new Date().toISOString().slice(0, 10);
143245
- writeFileSync6(factsFile(cwd2), `- [${stamp}] ${fact}
143451
+ writeFileSync7(factsFile(cwd2), `- [${stamp}] ${fact}
143246
143452
  `, { flag: "a" });
143247
143453
  return true;
143248
143454
  } catch {
@@ -144109,7 +144315,7 @@ ${summary}` },
144109
144315
 
144110
144316
  // src/image.ts
144111
144317
  import { existsSync as existsSync8, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
144112
- import { isAbsolute as isAbsolute2, resolve as resolve11 } from "node:path";
144318
+ import { basename as basename2, isAbsolute as isAbsolute2, resolve as resolve11 } from "node:path";
144113
144319
  var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
144114
144320
  var IMAGE_EXT = {
144115
144321
  ".png": "image/png",
@@ -144121,6 +144327,39 @@ var IMAGE_EXT = {
144121
144327
  function unquotePath(raw) {
144122
144328
  return raw.trim().replace(/^file:\/\//, "").replace(/^['"]|['"]$/g, "").replace(/\\ /g, " ");
144123
144329
  }
144330
+ function imageMimeForPath(path) {
144331
+ const ext = path.toLowerCase().match(/\.[^.]+$/)?.[0] ?? "";
144332
+ return IMAGE_EXT[ext];
144333
+ }
144334
+ function isImageFilePath(path) {
144335
+ return Boolean(imageMimeForPath(unquotePath(path)));
144336
+ }
144337
+ function shortName(path, max2 = 42) {
144338
+ const name31 = basename2(path);
144339
+ if (name31.length <= max2)
144340
+ return name31;
144341
+ const ext = name31.match(/\.[^.]+$/)?.[0] ?? "";
144342
+ const stem = ext ? name31.slice(0, -ext.length) : name31;
144343
+ return `${stem.slice(0, Math.max(8, max2 - ext.length - 1))}…${ext}`;
144344
+ }
144345
+ function imageChipLabel(path, index2) {
144346
+ return `[image${index2 && index2 > 1 ? ` ${index2}` : ""}: ${shortName(path)}]`;
144347
+ }
144348
+ function escapeRegExp2(s2) {
144349
+ return s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
144350
+ }
144351
+ function replaceImagePathWithMarker(text2, path, marker16) {
144352
+ const raw = path;
144353
+ const file5 = `file://${raw}`;
144354
+ const escaped = raw.replace(/ /g, "\\ ");
144355
+ const candidates = [file5, raw, escaped];
144356
+ let out = text2;
144357
+ for (const c of candidates) {
144358
+ out = out.replace(new RegExp(`(["'])${escapeRegExp2(c)}\\1`, "g"), marker16);
144359
+ out = out.replace(new RegExp(escapeRegExp2(c), "g"), marker16);
144360
+ }
144361
+ return out;
144362
+ }
144124
144363
  function imagePathsInText(text2, cwd2 = process.cwd(), limit = 6) {
144125
144364
  const found = [];
144126
144365
  const add2 = (rawMatch) => {
@@ -144146,8 +144385,7 @@ function imagePathsInText(text2, cwd2 = process.cwd(), limit = 6) {
144146
144385
  }
144147
144386
  function loadImageAttachment(path) {
144148
144387
  const abs = isAbsolute2(path) ? path : resolve11(process.cwd(), path);
144149
- const ext = abs.toLowerCase().match(/\.[^.]+$/)?.[0] ?? "";
144150
- const mimeType = IMAGE_EXT[ext];
144388
+ const mimeType = imageMimeForPath(abs);
144151
144389
  if (!mimeType)
144152
144390
  throw new Error(`unsupported image type: ${path}`);
144153
144391
  const size2 = statSync4(abs).size;
@@ -144168,7 +144406,7 @@ function imageContent(text2, images) {
144168
144406
  init_capabilities();
144169
144407
 
144170
144408
  // src/init.ts
144171
- import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
144409
+ import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
144172
144410
  import { join as join13 } from "node:path";
144173
144411
 
144174
144412
  // src/verify.ts
@@ -144327,11 +144565,14 @@ function writeProjectGuide(cwd2 = process.cwd()) {
144327
144565
  const path = join13(cwd2, "GEARBOX.md");
144328
144566
  const before2 = existsSync10(path) ? readFileSync15(path, "utf8") : "";
144329
144567
  const after2 = buildProjectGuide(cwd2);
144330
- writeFileSync7(path, after2, "utf8");
144568
+ writeFileSync8(path, after2, "utf8");
144331
144569
  const diff2 = computeDiff(before2, after2);
144332
144570
  return { path, summary: `wrote GEARBOX.md (${diffStat(diff2)})`, diff: diff2 };
144333
144571
  }
144334
144572
 
144573
+ // src/ui/App.tsx
144574
+ init_mcp();
144575
+
144335
144576
  // src/ui/clipboard.ts
144336
144577
  init_proc();
144337
144578
  function osc52(text2) {
@@ -144534,7 +144775,7 @@ function gitBranch() {
144534
144775
  // src/ui/App.tsx
144535
144776
  init_proc();
144536
144777
  var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
144537
- import { basename as basename2, extname } from "node:path";
144778
+ import { basename as basename3, extname, resolve as resolve12 } from "node:path";
144538
144779
  import { existsSync as existsSync11, readFileSync as readFileSync16, statSync as statSync5 } from "node:fs";
144539
144780
  import { writeFile as fsWriteFile } from "node:fs/promises";
144540
144781
  import { spawnSync as nodeSpawnSync2 } from "node:child_process";
@@ -144598,6 +144839,7 @@ function firstPath(text2) {
144598
144839
  function uniq2(xs) {
144599
144840
  return [...new Set(xs)];
144600
144841
  }
144842
+ var CLAUDE_CLI_EFFORTS = ["low", "medium", "high", "max"];
144601
144843
  var FALLBACK_CODEX_MODELS = [
144602
144844
  { id: "gpt-5.5", label: "gpt-5.5", provider: "codex", efforts: ["low", "medium", "high", "xhigh"] },
144603
144845
  { id: "gpt-5.4", label: "gpt-5.4", provider: "codex", efforts: ["low", "medium", "high", "xhigh"] },
@@ -144999,7 +145241,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
144999
145241
  return () => clearInterval(t2);
145000
145242
  }, [busy]);
145001
145243
  import_react26.useEffect(() => {
145002
- const proj = basename2(process.cwd());
145244
+ const proj = basename3(process.cwd());
145003
145245
  setTitle(busy ? `✳ ${proj} · working` : `${proj} · gearbox`);
145004
145246
  }, [busy]);
145005
145247
  const editRef = import_react26.useRef(edit2);
@@ -145029,6 +145271,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145029
145271
  const activeCliModelRef = import_react26.useRef(undefined);
145030
145272
  const cliSessionRef = import_react26.useRef(undefined);
145031
145273
  const activeImagesRef = import_react26.useRef([]);
145274
+ const imageChipPathsRef = import_react26.useRef(new Map);
145032
145275
  const accountStatusCacheRef = import_react26.useRef({});
145033
145276
  const usedAccountRef = import_react26.useRef(null);
145034
145277
  const cliMetaRef = import_react26.useRef(null);
@@ -145072,12 +145315,12 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145072
145315
  }
145073
145316
  }, []);
145074
145317
  import_react26.useEffect(() => {
145075
- setPermissionHandler((req) => new Promise((resolve12) => {
145318
+ setPermissionHandler((req) => new Promise((resolve13) => {
145076
145319
  if (modeRef.current === "auto-accept" && (req.kind === "write" || req.kind === "edit")) {
145077
- resolve12("once");
145320
+ resolve13("once");
145078
145321
  return;
145079
145322
  }
145080
- permQueue.current.push({ req, resolve: resolve12 });
145323
+ permQueue.current.push({ req, resolve: resolve13 });
145081
145324
  pumpPerm();
145082
145325
  }));
145083
145326
  return () => setPermissionHandler(null);
@@ -145199,9 +145442,9 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145199
145442
  const y = Number(m2[3]);
145200
145443
  const up2 = m2[4] === "m";
145201
145444
  if (b === 64)
145202
- delta -= 3;
145445
+ delta -= 1;
145203
145446
  else if (b === 65)
145204
- delta += 3;
145447
+ delta += 1;
145205
145448
  else {
145206
145449
  const off = composerOffset(x2, y);
145207
145450
  const point = transcriptPoint(x2, y);
@@ -145210,9 +145453,9 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145210
145453
  if (isPrimary && isDrag && transcriptMouseAnchorRef.current && !point) {
145211
145454
  const bottom = viewportTop + transcriptHeightLiveRef.current - 1;
145212
145455
  if (y < viewportTop)
145213
- scrollBy(-3);
145456
+ scrollBy(-2);
145214
145457
  else if (y > bottom)
145215
- scrollBy(3);
145458
+ scrollBy(2);
145216
145459
  const edgeLine = y < viewportTop ? scrollTopLiveRef.current : scrollTopLiveRef.current + transcriptHeightLiveRef.current - 1;
145217
145460
  const edgeText = lineText(linesRef.current[edgeLine] ?? []);
145218
145461
  setTranscriptSel({
@@ -145372,6 +145615,28 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145372
145615
  transcriptSelectionRef.current = sel;
145373
145616
  setTranscriptSelectionState(sel);
145374
145617
  };
145618
+ const imageMarkerFor = (path) => {
145619
+ for (const [marker50, existing] of imageChipPathsRef.current) {
145620
+ if (existing === path)
145621
+ return marker50;
145622
+ }
145623
+ let idx = 1;
145624
+ let marker16 = imageChipLabel(path);
145625
+ while (imageChipPathsRef.current.has(marker16)) {
145626
+ idx++;
145627
+ marker16 = imageChipLabel(path, idx);
145628
+ }
145629
+ imageChipPathsRef.current.set(marker16, path);
145630
+ return marker16;
145631
+ };
145632
+ const chipImagePathsIn = (text2) => {
145633
+ const paths = [];
145634
+ for (const [marker16, path] of imageChipPathsRef.current) {
145635
+ if (text2.includes(marker16))
145636
+ paths.push(path);
145637
+ }
145638
+ return paths;
145639
+ };
145375
145640
  const cliCatalogId = (binary) => binary.includes("codex") ? "codex-cli" : binary.includes("claude") ? "claude-cli" : "";
145376
145641
  const cliModelChoices = (binary) => {
145377
145642
  if (binary.includes("codex"))
@@ -145379,7 +145644,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145379
145644
  const provider = binary.includes("claude") ? "anthropic" : binary;
145380
145645
  return (catalogProvider(cliCatalogId(binary))?.defaultModels ?? []).map((id) => {
145381
145646
  const m2 = findModel(id);
145382
- return { id, label: m2?.label ?? id, provider, efforts: m2 ? effortLevels(m2) : undefined };
145647
+ return { id, label: m2?.label ?? id, provider, efforts: binary.includes("claude") ? CLAUDE_CLI_EFFORTS : m2 ? effortLevels(m2) : undefined };
145383
145648
  });
145384
145649
  };
145385
145650
  const cliSupportsModel = (binary, modelId) => {
@@ -145443,7 +145708,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145443
145708
  const take2 = (rows2) => rows2.filter((r2) => !q || `${r2.label} ${r2.detail ?? ""} ${r2.value}`.toLowerCase().includes(q)).slice(0, 7);
145444
145709
  if (head2 === "/model") {
145445
145710
  const cli = activeCliRef.current;
145446
- const models = cli ? cliModelChoices(cli.binary) : MODELS;
145711
+ const models = cli ? cliModelChoices(cli.binary) : modelRegistry();
145447
145712
  return take2([
145448
145713
  { value: "/model auto", label: "auto", detail: cli ? "use subscription default" : "route per task" },
145449
145714
  ...models.map((m2) => ({ value: `/model ${m2.label}`, label: m2.label, detail: cli ? `${cli.binary} subscription` : `${m2.provider} · ${m2.id}` }))
@@ -145634,7 +145899,7 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145634
145899
  const produced = r2.messages.slice(ctx.length);
145635
145900
  const imageNote = activeImagesRef.current.length ? `
145636
145901
 
145637
- [Attached images: ${activeImagesRef.current.map((img) => basename2(img.path)).join(", ")}]` : "";
145902
+ [Attached images: ${activeImagesRef.current.map((img) => basename3(img.path)).join(", ")}]` : "";
145638
145903
  const ledger = sanitizeToolPairs([...messages, { role: "user", content: prompt + imageNote }, ...produced]);
145639
145904
  return { messages: ledger, usage: r2.usage };
145640
145905
  }, []);
@@ -145721,28 +145986,34 @@ function App2({ selector: initialSelector, runner, fullscreen = false, resumeId
145721
145986
  }
145722
145987
  };
145723
145988
  const runTurn = import_react26.useCallback(async (prompt) => {
145724
- echo(prompt);
145725
- lastPromptRef.current = prompt;
145726
145989
  setVerb(nextVerb());
145727
145990
  activeImagesRef.current = [];
145728
145991
  let { text: modelPrompt, attached } = expandMentions(prompt);
145729
145992
  if (attached.length)
145730
145993
  notice(`attached ${attached.length} file${attached.length > 1 ? "s" : ""}: ${attached.join(", ")}`);
145731
- const imagePaths = imagePathsInText(modelPrompt);
145994
+ const imagePaths = uniq2([...chipImagePathsIn(modelPrompt), ...imagePathsInText(modelPrompt)]);
145995
+ let displayPrompt = prompt;
145996
+ for (const path of imagePaths) {
145997
+ const marker16 = imageMarkerFor(path);
145998
+ modelPrompt = replaceImagePathWithMarker(modelPrompt, path, marker16);
145999
+ displayPrompt = replaceImagePathWithMarker(displayPrompt, path, marker16);
146000
+ }
145732
146001
  const images = [];
145733
146002
  for (const path of imagePaths) {
145734
146003
  try {
145735
146004
  images.push(loadImageAttachment(path));
145736
146005
  } catch (e2) {
145737
- notice(`couldn't attach ${path}: ${(e2?.message ?? String(e2)).split(`
146006
+ notice(`couldn't attach ${basename3(path)}: ${(e2?.message ?? String(e2)).split(`
145738
146007
  `)[0]}`);
145739
146008
  }
145740
146009
  }
145741
146010
  activeImagesRef.current = images;
145742
146011
  if (images.length) {
145743
- const names = images.map((img) => basename2(img.path)).join(", ");
146012
+ const names = images.map((img) => imageMarkerFor(img.path)).join(", ");
145744
146013
  notice(`attached ${images.length} image${images.length === 1 ? "" : "s"}: ${names}`);
145745
146014
  }
146015
+ echo(displayPrompt);
146016
+ lastPromptRef.current = displayPrompt;
145746
146017
  const urls = urlsInText(modelPrompt);
145747
146018
  if (urls.length) {
145748
146019
  const fetched = [];
@@ -146348,6 +146619,54 @@ ${fetched.join(`
146348
146619
  }
146349
146620
  return;
146350
146621
  }
146622
+ case "mcp": {
146623
+ echo(text2);
146624
+ const parts = shellSplit(arg);
146625
+ const sub = (parts[0] ?? "list").toLowerCase();
146626
+ if (sub === "list" || sub === "servers") {
146627
+ notice(formatMcpConfigList());
146628
+ return;
146629
+ }
146630
+ if (sub === "tools") {
146631
+ notice("checking MCP servers…");
146632
+ mcpToolSummary().then(notice).catch((e2) => notice(`couldn't list MCP tools: ${e2?.message ?? String(e2)}`));
146633
+ return;
146634
+ }
146635
+ if (sub === "paths") {
146636
+ notice(mcpConfigPaths().join(`
146637
+ `));
146638
+ return;
146639
+ }
146640
+ if (sub === "add") {
146641
+ const global2 = parts[1] === "--global";
146642
+ const offset = global2 ? 2 : 1;
146643
+ const serverName = parts[offset] ?? "";
146644
+ const command = parts[offset + 1] ?? "";
146645
+ const commandArgs = parts.slice(offset + 2);
146646
+ try {
146647
+ notice(addMcpServer(serverName, command, commandArgs, { scope: global2 ? "global" : "project" }) + `
146648
+ Restarting is not required; new turns can use the tools.`);
146649
+ } catch (e2) {
146650
+ notice(`${e2?.message ?? String(e2)}
146651
+ Example: /mcp add github npx -y @modelcontextprotocol/server-github`);
146652
+ }
146653
+ return;
146654
+ }
146655
+ if (sub === "remove" || sub === "rm") {
146656
+ const global2 = parts[1] === "--global";
146657
+ const name50 = parts[global2 ? 2 : 1] ?? "";
146658
+ notice(removeMcpServer(name50, { scope: global2 ? "global" : undefined }));
146659
+ return;
146660
+ }
146661
+ notice(`MCP commands:
146662
+ ` + ` /mcp list
146663
+ ` + ` /mcp tools
146664
+ ` + ` /mcp add <name> <command> [args...]
146665
+ ` + ` /mcp add --global <name> <command> [args...]
146666
+ ` + ` /mcp remove <name>
146667
+ ` + " /mcp paths");
146668
+ return;
146669
+ }
146351
146670
  case "accounts":
146352
146671
  case "account": {
146353
146672
  echo(text2);
@@ -146498,6 +146817,7 @@ ${fetched.join(`
146498
146817
  ` + ` /account add codex <name> a 2nd ChatGPT account, e.g. /account add codex work
146499
146818
  ` + ` /account add azure <foundry-endpoint> <api-key>
146500
146819
  ` + ` /account add azure <resource-name> <api-key> [api-version]
146820
+ ` + ` /account add openai-compat <name> <base-url> <api-key> <model> [model...]
146501
146821
  ` + ` /account add <api-key> paste any provider key (auto-detected)
146502
146822
  ` + " /account add <provider> <api-key> e.g. anthropic, openai, openrouter");
146503
146823
  return;
@@ -146509,6 +146829,10 @@ ${fetched.join(`
146509
146829
  const azureKey = parts[3] ?? "";
146510
146830
  const apiVersion = parts[4];
146511
146831
  res = /^https?:\/\//i.test(resource) ? await addAzureFoundryAccount(resource, azureKey) : await addAzureAccount(resource, azureKey, { apiVersion });
146832
+ } else if (["openai-compat", "openai-compatible", "custom", "proxy"].includes(first)) {
146833
+ res = await addOpenAICompatAccount(parts[2] ?? "", parts[3] ?? "", parts[4] ?? "", parts.slice(5));
146834
+ } else if (catalogProvider(first)?.authKind === "openai-compat" && !catalogProvider(first)?.baseUrl && /^https?:\/\//i.test(parts[2] ?? "")) {
146835
+ res = await addOpenAICompatAccount(first, parts[2] ?? "", parts[3] ?? "", parts.slice(4));
146512
146836
  } else if (provGiven)
146513
146837
  res = await addApiKeyAccount(provGiven, keyVal);
146514
146838
  else if (detectProviderByKey(key))
@@ -146577,7 +146901,7 @@ ${fetched.join(`
146577
146901
  case "usage": {
146578
146902
  echo(text2);
146579
146903
  const accounts = listAccounts();
146580
- const resolve12 = (id) => {
146904
+ const resolve13 = (id) => {
146581
146905
  const a = getAccount(id);
146582
146906
  if (a) {
146583
146907
  const bin = a.auth.kind === "cli" ? a.auth.binary : undefined;
@@ -146595,7 +146919,7 @@ ${fetched.join(`
146595
146919
  const session = estimateCost(sessionRef.current.turns);
146596
146920
  const withBalance = accounts.filter((a) => a.exec !== "cli" && balanceExposed(a.provider));
146597
146921
  if (!withBalance.length) {
146598
- pushUsage(buildUsageView(session, resolve12, Date.now(), accounts.map((a) => a.id)));
146922
+ pushUsage(buildUsageView(session, resolve13, Date.now(), accounts.map((a) => a.id)));
146599
146923
  return;
146600
146924
  }
146601
146925
  notice("checking balances…");
@@ -146605,7 +146929,7 @@ ${fetched.join(`
146605
146929
  if (bal?.remainingUSD != null)
146606
146930
  recordBalance(a.id, bal);
146607
146931
  }
146608
- pushUsage(buildUsageView(session, resolve12, Date.now(), accounts.map((a) => a.id)));
146932
+ pushUsage(buildUsageView(session, resolve13, Date.now(), accounts.map((a) => a.id)));
146609
146933
  })();
146610
146934
  return;
146611
146935
  }
@@ -146941,8 +147265,15 @@ ${fetched.join(`
146941
147265
  if (!busyRef.current && input.length > 3 && !input.includes(`
146942
147266
  `)) {
146943
147267
  const p = sanitizeInputText(input).trim().replace(/^'|'$/g, "").replace(/\\ /g, " ");
146944
- if (/[/\\.]/.test(p) && p.length < 1024 && existsSync11(p)) {
147268
+ const abs = p.startsWith("~") ? p.replace(/^~/, process.env.HOME ?? "~") : resolve12(process.cwd(), p);
147269
+ if (/[/\\.]/.test(p) && p.length < 1024 && existsSync11(abs)) {
146945
147270
  const e2 = editRef.current;
147271
+ if (isImageFilePath(abs)) {
147272
+ const marker16 = imageMarkerFor(abs);
147273
+ setEdit({ value: e2.value.slice(0, e2.cursor) + marker16 + " " + e2.value.slice(e2.cursor), cursor: e2.cursor + marker16.length + 1 });
147274
+ flashStatus(`attached ${basename3(abs)}`);
147275
+ return;
147276
+ }
146946
147277
  const ins = `@${p} `;
146947
147278
  setEdit({ value: e2.value.slice(0, e2.cursor) + ins + e2.value.slice(e2.cursor), cursor: e2.cursor + ins.length });
146948
147279
  return;
@@ -147297,7 +147628,7 @@ ${fetched.join(`
147297
147628
  children: [
147298
147629
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Banner, {
147299
147630
  model: modelLabel,
147300
- cwd: basename2(process.cwd()),
147631
+ cwd: basename3(process.cwd()),
147301
147632
  width
147302
147633
  }, undefined, false, undefined, this),
147303
147634
  welcome ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
@@ -147321,7 +147652,7 @@ ${fetched.join(`
147321
147652
  }
147322
147653
  const banner = /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Banner, {
147323
147654
  model: modelLabel,
147324
- cwd: basename2(process.cwd()),
147655
+ cwd: basename3(process.cwd()),
147325
147656
  width
147326
147657
  }, undefined, false, undefined, this);
147327
147658
  return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
@@ -147353,7 +147684,7 @@ init_permission();
147353
147684
  var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
147354
147685
  process.env.LANG = process.env.LANG || "en_US.UTF-8";
147355
147686
  process.env.LC_ALL = process.env.LC_ALL || "en_US.UTF-8";
147356
- var VERSION16 = "0.1.15";
147687
+ var VERSION16 = "0.1.16";
147357
147688
  var args = process.argv.slice(2);
147358
147689
  var supportsAnsi = process.env.FORCE_COLOR === "1" || process.env.TERM !== "dumb" && process.env.NO_COLOR !== "1" && process.stdout.isTTY;
147359
147690
  var ansi = (code) => supportsAnsi ? `\x1B[${code}m` : "";
@@ -147582,8 +147913,8 @@ async function readStdin() {
147582
147913
  return input;
147583
147914
  }
147584
147915
  if (args[0] === "upgrade" || args[0] === "update") {
147585
- const root2 = resolve12(import.meta.dir, "..");
147586
- if (!existsSync12(resolve12(root2, ".git"))) {
147916
+ const root2 = resolve13(import.meta.dir, "..");
147917
+ if (!existsSync12(resolve13(root2, ".git"))) {
147587
147918
  console.log("This build can't self-update (not a git checkout).");
147588
147919
  console.log("Update by pulling the repo and reinstalling: git pull && bun install");
147589
147920
  process.exit(0);
@@ -147608,7 +147939,8 @@ Usage:
147608
147939
  gearbox onboard set up a provider before opening the app
147609
147940
  gearbox --model <name> start with a specific model
147610
147941
  gearbox --continue resume the most recent session in this directory
147611
- gearbox mcp list show configured MCP tools
147942
+ gearbox mcp list show configured MCP servers
147943
+ gearbox mcp add <name> <command> [args...]
147612
147944
  gearbox doctor models show provider/model capability matrix
147613
147945
  gearbox upgrade pull the latest version + reinstall deps
147614
147946
 
@@ -147625,11 +147957,12 @@ Set up at least one provider first:
147625
147957
  gearbox onboard
147626
147958
  gearbox auth add <api-key>
147627
147959
  gearbox auth add <provider> <api-key>
147960
+ gearbox auth add openai-compat <name> <base-url> <api-key> <model>
147628
147961
  gearbox auth add codex [name]
147629
147962
  gearbox auth add claude [name]
147630
147963
  gearbox auth import
147631
147964
 
147632
- Models: ${MODELS.map((m2) => m2.label).join(", ")}
147965
+ Models: ${modelRegistry().map((m2) => m2.label).join(", ")}
147633
147966
  In-app: / for commands, @ for files, !cmd for shell, shift+tab for plan mode.`);
147634
147967
  process.exit(0);
147635
147968
  }
@@ -147642,15 +147975,31 @@ if (args[0] === "onboard" || args[0] === "setup") {
147642
147975
  process.exit(0);
147643
147976
  }
147644
147977
  if (args[0] === "mcp") {
147645
- const { mcpToolSummary: mcpToolSummary2, mcpConfigPaths: mcpConfigPaths2 } = await Promise.resolve().then(() => (init_mcp(), exports_mcp));
147978
+ const { addMcpServer: addMcpServer2, formatMcpConfigList: formatMcpConfigList2, mcpToolSummary: mcpToolSummary2, mcpConfigPaths: mcpConfigPaths2, removeMcpServer: removeMcpServer2 } = await Promise.resolve().then(() => (init_mcp(), exports_mcp));
147646
147979
  const sub = args[1] ?? "list";
147647
147980
  if (sub === "list" || sub === "tools") {
147648
- console.log(await mcpToolSummary2());
147981
+ if (sub === "tools")
147982
+ console.log(await mcpToolSummary2());
147983
+ else
147984
+ console.log(formatMcpConfigList2());
147649
147985
  } else if (sub === "paths") {
147650
147986
  console.log(mcpConfigPaths2().join(`
147651
147987
  `));
147988
+ } else if (sub === "add") {
147989
+ const global2 = args[2] === "--global";
147990
+ const offset = global2 ? 3 : 2;
147991
+ try {
147992
+ console.log(addMcpServer2(args[offset] ?? "", args[offset + 1] ?? "", args.slice(offset + 2), { scope: global2 ? "global" : "project" }));
147993
+ } catch (e2) {
147994
+ console.log(e2?.message ?? String(e2));
147995
+ console.log("example: gearbox mcp add github npx -y @modelcontextprotocol/server-github");
147996
+ process.exit(1);
147997
+ }
147998
+ } else if (sub === "remove" || sub === "rm") {
147999
+ const global2 = args[2] === "--global";
148000
+ console.log(removeMcpServer2(args[global2 ? 3 : 2] ?? "", { scope: global2 ? "global" : undefined }));
147652
148001
  } else {
147653
- console.log("gearbox mcp [list|paths]");
148002
+ console.log("gearbox mcp [list|tools|paths|add <name> <command> [args...]|remove <name>]");
147654
148003
  }
147655
148004
  process.exit(0);
147656
148005
  }
@@ -147667,7 +148016,7 @@ if (args[0] === "doctor") {
147667
148016
  if (args[0] === "auth") {
147668
148017
  const { listAccounts: listAccounts2, loadAccounts: loadAccounts3, removeAccount: removeAccount2 } = await Promise.resolve().then(() => (init_store(), exports_store));
147669
148018
  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));
148019
+ 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
148020
  const { subscriptionEnv: subscriptionEnv2 } = await Promise.resolve().then(() => (init_cli_backend(), exports_cli_backend));
147672
148021
  const { detectProviderByKey: detectProviderByKey2 } = await Promise.resolve().then(() => (init_catalog(), exports_catalog));
147673
148022
  const sub = args[1];
@@ -147694,7 +148043,7 @@ Importable from your env (gearbox auth import): ${imp.map((c) => c.envVar).join(
147694
148043
  } else if (sub === "add") {
147695
148044
  const head2 = (rest2[0] ?? "").toLowerCase();
147696
148045
  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]" };
148046
+ 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
148047
  console.log(res.message);
147699
148048
  if (res.ok && res.account) {
147700
148049
  if (res.account.exec === "cli" && res.account.auth.kind === "cli") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gearbox-code",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "A beautiful multi-provider coding harness for the terminal. (Intelligent model routing lands on top of this soon.)",
5
5
  "type": "module",
6
6
  "license": "MIT",