claudish 7.48.0 → 7.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/bin/claudish.cjs +69 -25
  2. package/dist/index.js +1066 -420
  3. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
729
729
  });
730
730
 
731
731
  // src/version.ts
732
- var VERSION = "7.48.0";
732
+ var VERSION = "7.49.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -28142,7 +28142,7 @@ var init_provider_definitions = __esm(() => {
28142
28142
  name: "minimax",
28143
28143
  displayName: "MiniMax",
28144
28144
  transport: "anthropic",
28145
- baseUrl: "https://api.minimax.io",
28145
+ baseUrl: "https://api.minimaxi.com",
28146
28146
  baseUrlEnvVars: ["MINIMAX_BASE_URL"],
28147
28147
  apiPath: "/anthropic/v1/messages",
28148
28148
  apiKeyEnvVar: "MINIMAX_API_KEY",
@@ -29640,6 +29640,48 @@ var init_base_api_format = __esm(() => {
29640
29640
  });
29641
29641
 
29642
29642
  // src/adapters/anthropic-api-format.ts
29643
+ function systemText(content) {
29644
+ if (typeof content === "string")
29645
+ return content;
29646
+ if (Array.isArray(content)) {
29647
+ return content.map((block) => typeof block?.text === "string" ? block.text : "").filter(Boolean).join(`
29648
+ `);
29649
+ }
29650
+ return "";
29651
+ }
29652
+ function hoistInlineSystem(messages) {
29653
+ if (!Array.isArray(messages) || !messages.some((m) => m?.role === "system")) {
29654
+ return { messages, hoisted: [] };
29655
+ }
29656
+ const kept = [];
29657
+ const hoisted = [];
29658
+ for (const msg of messages) {
29659
+ if (msg?.role === "system") {
29660
+ const text = systemText(msg.content);
29661
+ if (text)
29662
+ hoisted.push(text);
29663
+ continue;
29664
+ }
29665
+ kept.push(msg);
29666
+ }
29667
+ return { messages: kept, hoisted };
29668
+ }
29669
+ function mergeSystem(existing, hoisted) {
29670
+ if (hoisted.length === 0)
29671
+ return existing;
29672
+ const merged = hoisted.join(`
29673
+
29674
+ `);
29675
+ if (existing === undefined || existing === null || existing === "")
29676
+ return merged;
29677
+ if (Array.isArray(existing))
29678
+ return [...existing, { type: "text", text: merged }];
29679
+ if (typeof existing === "string")
29680
+ return `${existing}
29681
+
29682
+ ${merged}`;
29683
+ return existing;
29684
+ }
29643
29685
  var AnthropicAPIFormat;
29644
29686
  var init_anthropic_api_format = __esm(() => {
29645
29687
  init_base_api_format();
@@ -29684,14 +29726,16 @@ var init_anthropic_api_format = __esm(() => {
29684
29726
  return claudeRequest.tools || [];
29685
29727
  }
29686
29728
  buildPayload(claudeRequest, messages, tools) {
29729
+ const { messages: cleanMessages, hoisted } = hoistInlineSystem(messages);
29687
29730
  const payload = {
29688
29731
  model: this.modelId,
29689
- messages,
29732
+ messages: cleanMessages,
29690
29733
  max_tokens: claudeRequest.max_tokens || 4096,
29691
29734
  stream: true
29692
29735
  };
29693
- if (claudeRequest.system) {
29694
- payload.system = claudeRequest.system;
29736
+ const system = mergeSystem(claudeRequest.system, hoisted);
29737
+ if (system !== undefined) {
29738
+ payload.system = system;
29695
29739
  }
29696
29740
  if (tools.length > 0) {
29697
29741
  payload.tools = tools;
@@ -34884,14 +34928,14 @@ var init_config = __esm(() => {
34884
34928
  });
34885
34929
 
34886
34930
  // src/behavior/harness.ts
34887
- function extractAvailableSkills(systemText) {
34888
- if (!systemText)
34931
+ function extractAvailableSkills(systemText2) {
34932
+ if (!systemText2)
34889
34933
  return [];
34890
- const start = SKILL_SECTION.exec(systemText);
34934
+ const start = SKILL_SECTION.exec(systemText2);
34891
34935
  if (!start)
34892
34936
  return [];
34893
34937
  const out = [];
34894
- const body = systemText.slice(start.index + start[0].length);
34938
+ const body = systemText2.slice(start.index + start[0].length);
34895
34939
  for (const line of body.split(`
34896
34940
  `)) {
34897
34941
  const trimmed2 = line.trim();
@@ -38024,6 +38068,170 @@ var init_anthropic_error = __esm(() => {
38024
38068
  CONTROL_CHARS = /[\x00-\x1F\x7F]/g;
38025
38069
  });
38026
38070
 
38071
+ // src/handlers/shared/collect-sse-message.ts
38072
+ function finalizeBlock(block) {
38073
+ if (block.type === "text") {
38074
+ return { type: "text", text: block.text ?? "" };
38075
+ }
38076
+ if (block.type === "thinking") {
38077
+ return {
38078
+ type: "thinking",
38079
+ thinking: block.thinking ?? "",
38080
+ ...block.signature ? { signature: block.signature } : {}
38081
+ };
38082
+ }
38083
+ let input = block.input ?? {};
38084
+ if (block.partialJson !== undefined && block.partialJson !== "") {
38085
+ try {
38086
+ input = JSON.parse(block.partialJson);
38087
+ } catch {
38088
+ log(`[CollectSSE] tool_use ${block.name ?? "?"} had unparseable input, emitting empty object`);
38089
+ input = {};
38090
+ }
38091
+ }
38092
+ return {
38093
+ type: "tool_use",
38094
+ id: block.id ?? "",
38095
+ name: block.name ?? "",
38096
+ input
38097
+ };
38098
+ }
38099
+ async function collectSseMessage(sse, fallbackModel, opts = {}) {
38100
+ const message = {
38101
+ id: `msg_${Date.now()}`,
38102
+ type: "message",
38103
+ role: "assistant",
38104
+ model: fallbackModel,
38105
+ content: [],
38106
+ stop_reason: null,
38107
+ stop_sequence: null,
38108
+ usage: { input_tokens: 0, output_tokens: 0 }
38109
+ };
38110
+ const blocks = new Map;
38111
+ const blockAt = (index) => {
38112
+ let block = blocks.get(index);
38113
+ if (!block) {
38114
+ block = { type: "text", text: "" };
38115
+ blocks.set(index, block);
38116
+ }
38117
+ return block;
38118
+ };
38119
+ if (!sse.body)
38120
+ return message;
38121
+ const reader = sse.body.getReader();
38122
+ const decoder = new TextDecoder;
38123
+ const stallMs = opts.stallTimeoutMs ?? STALL_TIMEOUT_MS;
38124
+ let buffer = "";
38125
+ const handle = (data) => {
38126
+ switch (data?.type) {
38127
+ case "message_start": {
38128
+ const m = data.message ?? {};
38129
+ if (m.id)
38130
+ message.id = m.id;
38131
+ if (m.model)
38132
+ message.model = m.model;
38133
+ if (m.usage?.input_tokens != null)
38134
+ message.usage.input_tokens = m.usage.input_tokens;
38135
+ if (m.usage?.output_tokens != null)
38136
+ message.usage.output_tokens = m.usage.output_tokens;
38137
+ break;
38138
+ }
38139
+ case "content_block_start": {
38140
+ const cb = data.content_block ?? {};
38141
+ const kind = cb.type === "thinking" ? "thinking" : cb.type === "tool_use" ? "tool_use" : "text";
38142
+ blocks.set(data.index, {
38143
+ type: kind,
38144
+ text: kind === "text" ? cb.text ?? "" : undefined,
38145
+ thinking: kind === "thinking" ? cb.thinking ?? "" : undefined,
38146
+ id: cb.id,
38147
+ name: cb.name,
38148
+ partialJson: kind === "tool_use" ? "" : undefined
38149
+ });
38150
+ break;
38151
+ }
38152
+ case "content_block_delta": {
38153
+ const block = blockAt(data.index);
38154
+ const d = data.delta ?? {};
38155
+ if (d.type === "text_delta")
38156
+ block.text = (block.text ?? "") + (d.text ?? "");
38157
+ else if (d.type === "thinking_delta")
38158
+ block.thinking = (block.thinking ?? "") + (d.thinking ?? "");
38159
+ else if (d.type === "signature_delta")
38160
+ block.signature = d.signature;
38161
+ else if (d.type === "input_json_delta")
38162
+ block.partialJson = (block.partialJson ?? "") + (d.partial_json ?? "");
38163
+ break;
38164
+ }
38165
+ case "message_delta": {
38166
+ if (data.delta?.stop_reason !== undefined)
38167
+ message.stop_reason = data.delta.stop_reason;
38168
+ if (data.delta?.stop_sequence !== undefined)
38169
+ message.stop_sequence = data.delta.stop_sequence;
38170
+ if (data.usage?.input_tokens != null)
38171
+ message.usage.input_tokens = data.usage.input_tokens;
38172
+ if (data.usage?.output_tokens != null)
38173
+ message.usage.output_tokens = data.usage.output_tokens;
38174
+ break;
38175
+ }
38176
+ default:
38177
+ break;
38178
+ }
38179
+ };
38180
+ try {
38181
+ while (true) {
38182
+ let timer;
38183
+ const chunk = await Promise.race([
38184
+ reader.read(),
38185
+ new Promise((resolve2) => {
38186
+ timer = setTimeout(() => resolve2("stalled"), stallMs);
38187
+ })
38188
+ ]);
38189
+ clearTimeout(timer);
38190
+ if (chunk === "stalled") {
38191
+ log(`[CollectSSE] no data for ${stallMs}ms \u2014 returning ${blocks.size} block(s) collected so far`);
38192
+ await reader.cancel().catch(() => {});
38193
+ break;
38194
+ }
38195
+ if (chunk.done)
38196
+ break;
38197
+ buffer += decoder.decode(chunk.value, { stream: true });
38198
+ const lines = buffer.split(`
38199
+ `);
38200
+ buffer = lines.pop() ?? "";
38201
+ for (const line of lines) {
38202
+ if (!line.startsWith("data:"))
38203
+ continue;
38204
+ const payload = line.slice(5).trim();
38205
+ if (!payload || payload === "[DONE]")
38206
+ continue;
38207
+ try {
38208
+ handle(JSON.parse(payload));
38209
+ } catch {}
38210
+ }
38211
+ }
38212
+ } catch (e) {
38213
+ log(`[CollectSSE] read failed, keeping ${blocks.size} block(s): ${e}`);
38214
+ }
38215
+ for (const index of Array.from(blocks.keys()).sort((a, b) => a - b)) {
38216
+ message.content.push(finalizeBlock(blocks.get(index)));
38217
+ }
38218
+ if (message.stop_reason === null) {
38219
+ message.stop_reason = message.content.some((b) => b.type === "tool_use") ? "tool_use" : "end_turn";
38220
+ }
38221
+ return message;
38222
+ }
38223
+ async function sseResponseToJson(sse, fallbackModel, opts = {}) {
38224
+ const message = await collectSseMessage(sse, fallbackModel, opts);
38225
+ return new Response(JSON.stringify(message), {
38226
+ status: 200,
38227
+ headers: { "Content-Type": "application/json" }
38228
+ });
38229
+ }
38230
+ var STALL_TIMEOUT_MS = 120000;
38231
+ var init_collect_sse_message = __esm(() => {
38232
+ init_logger();
38233
+ });
38234
+
38027
38235
  // src/handlers/shared/connection-error.ts
38028
38236
  function findConnectionCode(error46) {
38029
38237
  let e = error46;
@@ -38584,6 +38792,31 @@ function createAnthropicPassthroughStream(c, response, opts) {
38584
38792
  const filterThinking = opts.adapter?.shouldFilterThinking() ?? false;
38585
38793
  const interceptToolFrame = createToolRepairInterceptor(opts);
38586
38794
  let pendingEventLine = null;
38795
+ let inputTokens = 0;
38796
+ let outputTokens = 0;
38797
+ let stopReason = null;
38798
+ let sawMessageStart = false;
38799
+ let sawMessageStop = false;
38800
+ let openBlockIndex = null;
38801
+ const noteLifecycle = (data, emittedIndex) => {
38802
+ switch (data?.type) {
38803
+ case "message_start":
38804
+ sawMessageStart = true;
38805
+ break;
38806
+ case "message_stop":
38807
+ sawMessageStop = true;
38808
+ break;
38809
+ case "content_block_start":
38810
+ if (emittedIndex !== null)
38811
+ openBlockIndex = emittedIndex;
38812
+ break;
38813
+ case "content_block_stop":
38814
+ openBlockIndex = null;
38815
+ break;
38816
+ default:
38817
+ break;
38818
+ }
38819
+ };
38587
38820
  const flushPendingEvent = (controller) => {
38588
38821
  if (pendingEventLine !== null && !isClosed) {
38589
38822
  controller.enqueue(encoder.encode(`${pendingEventLine}
@@ -38601,6 +38834,48 @@ function createAnthropicPassthroughStream(c, response, opts) {
38601
38834
  }
38602
38835
  flushPendingEvent(controller);
38603
38836
  controller.enqueue(encoder.encode(out));
38837
+ noteLifecycle(data, typeof data?.index === "number" ? data.index : null);
38838
+ };
38839
+ const finalizeAbandonedStream = (controller) => {
38840
+ if (isClosed || sawMessageStop)
38841
+ return;
38842
+ const send = (event, data) => {
38843
+ controller.enqueue(encoder.encode(`event: ${event}
38844
+ data: ${JSON.stringify(data)}
38845
+
38846
+ `));
38847
+ };
38848
+ try {
38849
+ if (!sawMessageStart) {
38850
+ send("message_start", {
38851
+ type: "message_start",
38852
+ message: {
38853
+ id: `msg_${Date.now()}`,
38854
+ type: "message",
38855
+ role: "assistant",
38856
+ content: [],
38857
+ model: opts.modelName,
38858
+ stop_reason: null,
38859
+ stop_sequence: null,
38860
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens }
38861
+ }
38862
+ });
38863
+ }
38864
+ if (openBlockIndex !== null) {
38865
+ send("content_block_stop", { type: "content_block_stop", index: openBlockIndex });
38866
+ openBlockIndex = null;
38867
+ }
38868
+ send("message_delta", {
38869
+ type: "message_delta",
38870
+ delta: { stop_reason: stopReason ?? "end_turn", stop_sequence: null },
38871
+ usage: {
38872
+ ...inputTokens > 0 ? { input_tokens: inputTokens } : {},
38873
+ output_tokens: outputTokens
38874
+ }
38875
+ });
38876
+ send("message_stop", { type: "message_stop" });
38877
+ sawMessageStop = true;
38878
+ } catch {}
38604
38879
  };
38605
38880
  return c.body(new ReadableStream({
38606
38881
  async start(controller) {
@@ -38621,12 +38896,9 @@ data: {"type":"ping"}
38621
38896
  try {
38622
38897
  const reader = response.body.getReader();
38623
38898
  let buffer = "";
38624
- let inputTokens = 0;
38625
- let outputTokens = 0;
38626
38899
  let totalLines = 0;
38627
38900
  let textChunks = 0;
38628
38901
  let toolUseBlocks = 0;
38629
- let stopReason = null;
38630
38902
  let insideThinkingBlock = false;
38631
38903
  let thinkingBlocksSuppressed = 0;
38632
38904
  let suppressedFrame = false;
@@ -38691,6 +38963,7 @@ data: ${JSON.stringify({
38691
38963
  flushPendingEvent(controller);
38692
38964
  controller.enqueue(encoder.encode(`${modifiedLine}
38693
38965
  `));
38966
+ noteLifecycle(data, reindexed);
38694
38967
  }
38695
38968
  } else {
38696
38969
  enqueueData(controller, data, line);
@@ -38813,6 +39086,13 @@ data: ${JSON.stringify({
38813
39086
  }
38814
39087
  } catch (e) {
38815
39088
  log(`[AnthropicSSE] Stream error: ${e}`);
39089
+ finalizeAbandonedStream(controller);
39090
+ try {
39091
+ opts.onTurnEnd?.();
39092
+ } catch {}
39093
+ try {
39094
+ opts.onTokenUpdate?.(inputTokens, outputTokens);
39095
+ } catch {}
38816
39096
  if (!isClosed) {
38817
39097
  isClosed = true;
38818
39098
  if (pingInterval) {
@@ -40773,9 +41053,12 @@ class ComposedHandler {
40773
41053
  behaviorSession?.noteTurnComplete(this.tokenTracker.getInputTokens());
40774
41054
  } catch {}
40775
41055
  };
40776
- return this.handleStream(c, response, adapter, claudeRequest, toolNameMap, onStreamComplete, (code, message) => {
41056
+ const streamed = this.handleStream(c, response, adapter, claudeRequest, toolNameMap, onStreamComplete, (code, message) => {
40777
41057
  streamApiError = { code, message };
40778
41058
  }, behaviorSession);
41059
+ if (payload?.stream === true)
41060
+ return streamed;
41061
+ return sseResponseToJson(streamed, this.bareModelName);
40779
41062
  }
40780
41063
  async settleResponsesStreamHead(initial, reissue) {
40781
41064
  let response = initial;
@@ -41084,6 +41367,7 @@ var init_composed_handler = __esm(() => {
41084
41367
  init_telemetry();
41085
41368
  init_transform();
41086
41369
  init_anthropic_error();
41370
+ init_collect_sse_message();
41087
41371
  init_connection_error();
41088
41372
  init_devin_stream_head_sniffer();
41089
41373
  init_openai_compat();
@@ -44029,6 +44313,83 @@ var init_channel = __esm(() => {
44029
44313
  init_session_manager();
44030
44314
  });
44031
44315
 
44316
+ // src/mcp/progress-heartbeat.ts
44317
+ function isProgressToken(v) {
44318
+ return typeof v === "string" || typeof v === "number" && Number.isFinite(v);
44319
+ }
44320
+ function clampInterval(ms) {
44321
+ if (ms < MIN_PROGRESS_INTERVAL_MS)
44322
+ return MIN_PROGRESS_INTERVAL_MS;
44323
+ if (ms > MAX_PROGRESS_INTERVAL_MS)
44324
+ return MAX_PROGRESS_INTERVAL_MS;
44325
+ return ms;
44326
+ }
44327
+ function resolveProgressIntervalMs(env = process.env) {
44328
+ const raw = env[PROGRESS_INTERVAL_ENV_VAR];
44329
+ if (raw === undefined || raw === null || raw.trim() === "")
44330
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44331
+ const parsed = Number(raw);
44332
+ if (!Number.isFinite(parsed) || parsed <= 0)
44333
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44334
+ return clampInterval(parsed);
44335
+ }
44336
+ function resolveExplicitIntervalMs(ms) {
44337
+ if (ms === undefined)
44338
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44339
+ if (!Number.isFinite(ms) || ms <= 0)
44340
+ return DEFAULT_PROGRESS_INTERVAL_MS;
44341
+ return Math.min(ms, MAX_PROGRESS_INTERVAL_MS);
44342
+ }
44343
+ function startHeartbeat(opts) {
44344
+ const { token, send, label } = opts;
44345
+ if (!isProgressToken(token))
44346
+ return NOOP_HEARTBEAT;
44347
+ const intervalMs = resolveExplicitIntervalMs(opts.intervalMs);
44348
+ const startedAt = Date.now();
44349
+ let progress = 0;
44350
+ let stopped = false;
44351
+ const emit2 = (message) => {
44352
+ if (stopped)
44353
+ return;
44354
+ progress += 1;
44355
+ try {
44356
+ const elapsedSeconds = Math.round((Date.now() - startedAt) / 1000);
44357
+ const result = send({
44358
+ progressToken: token,
44359
+ progress,
44360
+ message: message ?? `${label}: working (${elapsedSeconds}s)`
44361
+ });
44362
+ if (result && typeof result.then === "function") {
44363
+ result.catch(() => {});
44364
+ }
44365
+ } catch {}
44366
+ };
44367
+ const timer = setInterval(() => emit2(), intervalMs);
44368
+ timer.unref?.();
44369
+ return {
44370
+ tick: (message) => emit2(message),
44371
+ stop: () => {
44372
+ stopped = true;
44373
+ clearInterval(timer);
44374
+ },
44375
+ get active() {
44376
+ return !stopped;
44377
+ },
44378
+ get emitted() {
44379
+ return progress;
44380
+ }
44381
+ };
44382
+ }
44383
+ var DEFAULT_PROGRESS_INTERVAL_MS = 1e4, MIN_PROGRESS_INTERVAL_MS = 1000, MAX_PROGRESS_INTERVAL_MS = 60000, PROGRESS_INTERVAL_ENV_VAR = "CLAUDISH_MCP_PROGRESS_INTERVAL_MS", NOOP_HEARTBEAT;
44384
+ var init_progress_heartbeat = __esm(() => {
44385
+ NOOP_HEARTBEAT = Object.freeze({
44386
+ tick(_message) {},
44387
+ stop() {},
44388
+ active: false,
44389
+ emitted: 0
44390
+ });
44391
+ });
44392
+
44032
44393
  // src/providers/cache-ttl.ts
44033
44394
  var FIREBASE_CACHE_TTL_HOURS = 24, FIREBASE_CACHE_TTL_MS;
44034
44395
  var init_cache_ttl = __esm(() => {
@@ -46892,6 +47253,34 @@ var init_native_handler_advisor = __esm(() => {
46892
47253
  advisorToolUseIds = new Set;
46893
47254
  });
46894
47255
 
47256
+ // src/handlers/shared/thinking-signature.ts
47257
+ function isUnsignedThinking(block) {
47258
+ if (!block || typeof block !== "object")
47259
+ return false;
47260
+ const b = block;
47261
+ if (b.type !== "thinking")
47262
+ return false;
47263
+ return typeof b.signature !== "string" || b.signature.length === 0;
47264
+ }
47265
+ function stripUnsignedThinkingBlocks(messages) {
47266
+ if (!Array.isArray(messages))
47267
+ return 0;
47268
+ let removed = 0;
47269
+ for (const message of messages) {
47270
+ if (!message || typeof message !== "object")
47271
+ continue;
47272
+ const content = message.content;
47273
+ if (!Array.isArray(content))
47274
+ continue;
47275
+ const kept = content.filter((block) => !isUnsignedThinking(block));
47276
+ if (kept.length !== content.length) {
47277
+ removed += content.length - kept.length;
47278
+ message.content = kept;
47279
+ }
47280
+ }
47281
+ return removed;
47282
+ }
47283
+
46895
47284
  // src/handlers/native-handler.ts
46896
47285
  async function resolveAdvisorKeys() {
46897
47286
  const keyFromAuthority = async (name) => {
@@ -46937,6 +47326,10 @@ class NativeHandler {
46937
47326
  async handle(c, payload) {
46938
47327
  const originalHeaders = c.req.header();
46939
47328
  const target = payload.model;
47329
+ const strippedThinking = stripUnsignedThinkingBlocks(payload.messages);
47330
+ if (strippedThinking > 0) {
47331
+ log(`[Native] stripped ${strippedThinking} unsigned thinking block(s) from history for ${target} (foreign-provider origin)`);
47332
+ }
46940
47333
  const advisorCfg = loadAdvisorSwapConfig(this.advisorModels, this.advisorCollector);
46941
47334
  let advisorSwapped = null;
46942
47335
  let advisorRewrittenIds = [];
@@ -47958,7 +48351,8 @@ var init_vertex_oauth = __esm(() => {
47958
48351
 
47959
48352
  // src/providers/provider-profiles.ts
47960
48353
  function requiresResponsesApi(modelName) {
47961
- return /^gpt-5\.6/.test(modelName.toLowerCase());
48354
+ const name = modelName.toLowerCase();
48355
+ return /^gpt-5\.6/.test(name) || name.includes("codex");
47962
48356
  }
47963
48357
  function createHandlerForProvider(ctx) {
47964
48358
  const profile = PROVIDER_PROFILES[ctx.provider.name] ?? getRuntimeProfiles().get(ctx.provider.name);
@@ -49720,7 +50114,7 @@ import {
49720
50114
  } from "fs";
49721
50115
  import { join as join28, resolve as resolve3 } from "path";
49722
50116
  function classifyRunOutput(opts) {
49723
- const { outputSize, stdoutTail, stderr, minOutputBytes } = opts;
50117
+ const { outputSize, stdoutTail, stderr, minOutputBytes, requirePattern, fullOutput } = opts;
49724
50118
  const apiError = API_ERROR_RE.exec(stdoutTail);
49725
50119
  if (apiError) {
49726
50120
  return {
@@ -49748,6 +50142,21 @@ function classifyRunOutput(opts) {
49748
50142
  detail: `Child exited 0 but produced only ${outputSize} B of stdout ` + `(caller required at least ${minOutputBytes} B).`
49749
50143
  };
49750
50144
  }
50145
+ if (requirePattern) {
50146
+ const haystack = fullOutput ?? stdoutTail;
50147
+ let re = null;
50148
+ try {
50149
+ re = new RegExp(requirePattern);
50150
+ } catch {
50151
+ re = null;
50152
+ }
50153
+ if (re && !re.test(haystack)) {
50154
+ return {
50155
+ reason: "shape_mismatch",
50156
+ detail: `Child exited 0 with ${outputSize} B, but the response does not match the ` + `required pattern /${requirePattern}/. This is the signature of a child that ` + "answered and then took one more turn: `claude -p` prints only the FINAL " + "assistant message, so a background task completing (or any late notification) " + "replaces the real answer with an epilogue about it. Check the child's " + "transcript \u2014 the answer was generated, it just was not the last thing said."
50157
+ };
50158
+ }
50159
+ }
49751
50160
  return null;
49752
50161
  }
49753
50162
  function persistErrorLog(errorLogPath, header, stderr, stdoutTail) {
@@ -49826,8 +50235,28 @@ function setupSession(sessionPath, models, input) {
49826
50235
  writeFileSync12(join28(sessionPath, "status.json"), JSON.stringify(status, null, 2), "utf-8");
49827
50236
  return manifest;
49828
50237
  }
50238
+ function assertValidRequirePattern(pattern) {
50239
+ if (pattern === undefined)
50240
+ return;
50241
+ try {
50242
+ new RegExp(pattern);
50243
+ } catch (err) {
50244
+ throw new Error(`Invalid requirePattern /${pattern}/: ${err instanceof Error ? err.message : String(err)}`);
50245
+ }
50246
+ }
50247
+ function readFullOutputIfNeeded(opts) {
50248
+ const { crashed, requirePattern, outputSize, outputPath } = opts;
50249
+ if (crashed || !requirePattern || outputSize <= STDOUT_TAIL_LIMIT)
50250
+ return;
50251
+ try {
50252
+ return readFileSync18(outputPath, "utf-8");
50253
+ } catch {
50254
+ return;
50255
+ }
50256
+ }
49829
50257
  async function runModels(sessionPath, opts = {}) {
49830
50258
  const timeoutMs = (opts.timeout ?? 300) * 1000;
50259
+ assertValidRequirePattern(opts.requirePattern);
49831
50260
  const manifest = JSON.parse(readFileSync18(join28(sessionPath, "manifest.json"), "utf-8"));
49832
50261
  const statusPath = join28(sessionPath, "status.json");
49833
50262
  const inputPath = join28(sessionPath, "input.md");
@@ -49839,6 +50268,7 @@ async function runModels(sessionPath, opts = {}) {
49839
50268
  writeFileSync12(statusPath, JSON.stringify(statusCache, null, 2), "utf-8");
49840
50269
  }
49841
50270
  const minOutputBytes = opts.minOutputBytes ?? DEFAULT_MIN_OUTPUT_BYTES;
50271
+ const requirePattern = opts.requirePattern;
49842
50272
  mkdirSync12(statsDir(sessionPath), { recursive: true });
49843
50273
  const processes = new Map;
49844
50274
  const runtimes = new Map;
@@ -49905,7 +50335,20 @@ async function runModels(sessionPath, opts = {}) {
49905
50335
  resolved = true;
49906
50336
  const outputSize = byteCount;
49907
50337
  const crashed = exitCode !== 0;
49908
- const degraded = crashed ? null : classifyRunOutput({ outputSize, stdoutTail, stderr, minOutputBytes });
50338
+ const fullOutput = readFullOutputIfNeeded({
50339
+ crashed,
50340
+ requirePattern,
50341
+ outputSize,
50342
+ outputPath
50343
+ });
50344
+ const degraded = crashed ? null : classifyRunOutput({
50345
+ outputSize,
50346
+ stdoutTail,
50347
+ stderr,
50348
+ minOutputBytes,
50349
+ requirePattern,
50350
+ fullOutput
50351
+ });
49909
50352
  const failed = crashed || degraded !== null;
49910
50353
  const state = crashed ? "FAILED" : degraded ? "EMPTY" : "COMPLETED";
49911
50354
  if (failed) {
@@ -50438,6 +50881,7 @@ ${block.join(`
50438
50881
  required: ["model", "prompt"]
50439
50882
  },
50440
50883
  group: "low-level",
50884
+ heartbeat: true,
50441
50885
  handler: async (args) => {
50442
50886
  try {
50443
50887
  const result = await runPromptViaProxy(args.model, args.prompt, args.system_prompt, args.max_tokens);
@@ -50658,7 +51102,8 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50658
51102
  required: ["models", "prompt"]
50659
51103
  },
50660
51104
  group: "low-level",
50661
- handler: async (args) => {
51105
+ heartbeat: true,
51106
+ handler: async (args, ctx) => {
50662
51107
  const modelIds = args.models;
50663
51108
  const prompt = args.prompt;
50664
51109
  const systemPrompt = args.system_prompt;
@@ -50675,6 +51120,7 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50675
51120
  error: error46 instanceof Error ? error46.message : String(error46)
50676
51121
  });
50677
51122
  }
51123
+ ctx.reportProgress(`compare_models: ${results.length}/${modelIds.length} models done`);
50678
51124
  }
50679
51125
  let output = `# Model Comparison
50680
51126
 
@@ -50740,12 +51186,21 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50740
51186
  type: "string",
50741
51187
  description: "Task prompt text (or place input.md in the session directory before calling)"
50742
51188
  },
50743
- timeout: { type: "number", description: "Per-model timeout in seconds (default: 300)" }
51189
+ timeout: { type: "number", description: "Per-model timeout in seconds (default: 300)" },
51190
+ require_pattern: {
51191
+ type: "string",
51192
+ description: "Regex the response MUST match, or the slot is reported FAILED (state EMPTY, " + "reason 'shape_mismatch') instead of succeeded. Strongly recommended whenever " + "your prompt mandates an output shape \u2014 e.g. '```vote' for a voting panel. " + "Exit code 0 is not a success oracle: a child that answers and then takes one " + "more turn (a background Task finishing, a late notification) has its real " + "answer replaced by a short epilogue, because print mode emits only the FINAL " + "assistant message. That lands as a few hundred bytes of plausible prose with " + "exit 0 and no error, and is otherwise indistinguishable from success."
51193
+ },
51194
+ min_output_bytes: {
51195
+ type: "number",
51196
+ description: "Report a slot FAILED if it produced fewer than this many bytes (default 0 = " + "off). A blunter instrument than require_pattern \u2014 short answers can be " + "legitimate \u2014 so prefer require_pattern when you know the expected shape."
51197
+ }
50744
51198
  },
50745
51199
  required: ["mode", "path"]
50746
51200
  },
50747
51201
  group: "agentic",
50748
- handler: async (args) => {
51202
+ heartbeat: true,
51203
+ handler: async (args, ctx) => {
50749
51204
  try {
50750
51205
  const mode = args.mode;
50751
51206
  const path = args.path;
@@ -50753,19 +51208,26 @@ Use with: run_prompt(model="${results[0].model.id}", prompt="your prompt")`;
50753
51208
  const judges = args.judges;
50754
51209
  const input = args.input;
50755
51210
  const timeout = args.timeout;
51211
+ const requirePattern = args.require_pattern;
51212
+ const minOutputBytes = args.min_output_bytes;
50756
51213
  const resolved = validateSessionPath(path);
50757
51214
  const teamSessionId = resolved.split("/").filter(Boolean).pop() ?? "team";
50758
51215
  const teamCreatedAt = new Date().toISOString();
50759
51216
  const runOpts = {
50760
51217
  timeout,
50761
- onProgress: (u) => notifyChannel({
50762
- content: u.rendered,
50763
- sessionId: teamSessionId,
50764
- event: u.phase === "settled" ? u.allFailed ? "failed" : "completed" : "running",
50765
- model: "team",
50766
- elapsedSeconds: (Date.now() - Date.parse(teamCreatedAt)) / 1000,
50767
- createdAt: teamCreatedAt
50768
- })
51218
+ requirePattern,
51219
+ minOutputBytes,
51220
+ onProgress: (u) => {
51221
+ ctx.reportProgress(`team: ${u.phase}`);
51222
+ notifyChannel({
51223
+ content: u.rendered,
51224
+ sessionId: teamSessionId,
51225
+ event: u.phase === "settled" ? u.allFailed ? "failed" : "completed" : "running",
51226
+ model: "team",
51227
+ elapsedSeconds: (Date.now() - Date.parse(teamCreatedAt)) / 1000,
51228
+ createdAt: teamCreatedAt
51229
+ });
51230
+ }
50769
51231
  };
50770
51232
  switch (mode) {
50771
51233
  case "run": {
@@ -51200,6 +51662,7 @@ To report this error, use the report_error tool with error_type: "provider_failu
51200
51662
  watchNotificationResult(result, { sessionId: p.sessionId, eventType: p.event });
51201
51663
  } catch {}
51202
51664
  };
51665
+ const progressIntervalMs = resolveProgressIntervalMs();
51203
51666
  const allTools = defineTools(sessionManager, notifyChannel);
51204
51667
  const enabledTools = allTools.filter((t) => enabledGroups.has(t.group));
51205
51668
  const toolMap = new Map(enabledTools.map((t) => [t.name, t]));
@@ -51211,7 +51674,7 @@ To report this error, use the report_error tool with error_type: "provider_failu
51211
51674
  inputSchema: t.inputSchema
51212
51675
  }))
51213
51676
  }));
51214
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
51677
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
51215
51678
  const { name, arguments: args } = request.params;
51216
51679
  const tool = toolMap.get(name);
51217
51680
  if (!tool) {
@@ -51220,8 +51683,15 @@ To report this error, use the report_error tool with error_type: "provider_failu
51220
51683
  isError: true
51221
51684
  };
51222
51685
  }
51686
+ const heartbeat = tool.heartbeat ? startHeartbeat({
51687
+ token: extra._meta?.progressToken,
51688
+ label: name,
51689
+ intervalMs: progressIntervalMs,
51690
+ send: (frame) => extra.sendNotification({ method: "notifications/progress", params: frame })
51691
+ }) : NOOP_HEARTBEAT;
51692
+ const ctx = { reportProgress: (message) => heartbeat.tick(message) };
51223
51693
  try {
51224
- return await tool.handler(args ?? {});
51694
+ return await tool.handler(args ?? {}, ctx);
51225
51695
  } catch (error46) {
51226
51696
  return {
51227
51697
  content: [
@@ -51232,6 +51702,8 @@ To report this error, use the report_error tool with error_type: "provider_failu
51232
51702
  ],
51233
51703
  isError: true
51234
51704
  };
51705
+ } finally {
51706
+ heartbeat.stop();
51235
51707
  }
51236
51708
  });
51237
51709
  const transport = new StdioServerTransport;
@@ -51279,6 +51751,7 @@ var init_mcp_server = __esm(() => {
51279
51751
  init_prehydrate();
51280
51752
  init_diagnostics();
51281
51753
  init_channel();
51754
+ init_progress_heartbeat();
51282
51755
  init_model_loader();
51283
51756
  init_port_manager();
51284
51757
  init_onepassword();
@@ -51297,7 +51770,8 @@ var init_mcp_server = __esm(() => {
51297
51770
  timeout: "raise `timeout`, or pick a faster model",
51298
51771
  api_error: "retry once, or route via a different provider (or@<model>)",
51299
51772
  background_task_ceiling: "set CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0 for children, or forbid background work in the prompt",
51300
- empty_output: "retry once; if it repeats, drop the model"
51773
+ empty_output: "retry once; if it repeats, drop the model",
51774
+ shape_mismatch: "the answer was generated but not captured \u2014 print mode keeps only the FINAL " + "assistant message, so a late background task or notification overwrote it. " + "Retry, and forbid background work in the prompt; do NOT count this slot as a vote"
51301
51775
  };
51302
51776
  sanitize = sanitizeForReport;
51303
51777
  EVENT_TO_TASK_STATUS = new Map([
@@ -51603,6 +52077,440 @@ var init_behavior_command = __esm(() => {
51603
52077
  init_profile_config();
51604
52078
  });
51605
52079
 
52080
+ // src/team-grid.ts
52081
+ var exports_team_grid = {};
52082
+ __export(exports_team_grid, {
52083
+ runWithGrid: () => runWithGrid
52084
+ });
52085
+ import { spawn as spawn3 } from "child_process";
52086
+ import { execSync } from "child_process";
52087
+ import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync15 } from "fs";
52088
+ import { connect as netConnect } from "net";
52089
+ import { dirname as dirname10, join as join30 } from "path";
52090
+ import { setTimeout as wait } from "timers/promises";
52091
+ import { fileURLToPath as fileURLToPath2 } from "url";
52092
+ function resolveRouteInfo(modelId) {
52093
+ const parsed = parseModelSpec(modelId);
52094
+ if (parsed.isExplicitProvider) {
52095
+ return { chain: [parsed.provider], source: "direct" };
52096
+ }
52097
+ const local = loadLocalConfig();
52098
+ if (local?.routing && Object.keys(local.routing).length > 0) {
52099
+ const matched2 = matchRoutingRule(parsed.model, local.routing);
52100
+ if (matched2) {
52101
+ const routes = buildRoutingChain(matched2, parsed.model);
52102
+ const pattern = Object.keys(local.routing).find((k) => {
52103
+ if (k === parsed.model)
52104
+ return true;
52105
+ if (k.includes("*")) {
52106
+ const star = k.indexOf("*");
52107
+ return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
52108
+ }
52109
+ return false;
52110
+ });
52111
+ return {
52112
+ chain: routes.map((r) => r.displayName),
52113
+ source: "project routing",
52114
+ sourceDetail: pattern
52115
+ };
52116
+ }
52117
+ }
52118
+ const global_ = loadConfig();
52119
+ if (global_.routing && Object.keys(global_.routing).length > 0) {
52120
+ const matched2 = matchRoutingRule(parsed.model, global_.routing);
52121
+ if (matched2) {
52122
+ const routes = buildRoutingChain(matched2, parsed.model);
52123
+ const pattern = Object.keys(global_.routing).find((k) => {
52124
+ if (k === parsed.model)
52125
+ return true;
52126
+ if (k.includes("*")) {
52127
+ const star = k.indexOf("*");
52128
+ return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
52129
+ }
52130
+ return false;
52131
+ });
52132
+ return {
52133
+ chain: routes.map((r) => r.displayName),
52134
+ source: "user routing",
52135
+ sourceDetail: pattern
52136
+ };
52137
+ }
52138
+ }
52139
+ const merged = loadRoutingRules();
52140
+ const matched = matchRoutingRule(parsed.model, merged);
52141
+ if (matched) {
52142
+ const routes = buildRoutingChain(matched, parsed.model);
52143
+ return {
52144
+ chain: routes.map((r) => r.displayName),
52145
+ source: "auto"
52146
+ };
52147
+ }
52148
+ return {
52149
+ chain: [],
52150
+ source: "auto"
52151
+ };
52152
+ }
52153
+ function pickBannerColor(model, used) {
52154
+ let hash2 = 0;
52155
+ for (let i = 0;i < model.length; i++)
52156
+ hash2 = (hash2 << 5) - hash2 + model.charCodeAt(i) | 0;
52157
+ const start = Math.abs(hash2) % BANNER_BG_COLORS.length;
52158
+ let idx = start;
52159
+ if (used.size < BANNER_BG_COLORS.length) {
52160
+ while (used.has(idx))
52161
+ idx = (idx + 1) % BANNER_BG_COLORS.length;
52162
+ }
52163
+ used.add(idx);
52164
+ return BANNER_BG_COLORS[idx];
52165
+ }
52166
+ function buildPaneHeader(model, prompt, bg) {
52167
+ const route2 = resolveRouteInfo(model);
52168
+ const esc2 = (s) => s.replace(/'/g, "'\\''");
52169
+ const chainStr = route2.chain.join(" \u2192 ");
52170
+ const sourceLabel = route2.sourceDetail ? `${route2.source}: ${route2.sourceDetail}` : route2.source;
52171
+ const lines = [];
52172
+ lines.push(`printf '\\033[1;97;${bg}m %s \\033[0m\\n' '${esc2(model)}';`);
52173
+ lines.push(`printf '\\033[2m route: ${esc2(chainStr)} (${esc2(sourceLabel)})\\033[0m\\n' ;`);
52174
+ lines.push(`printf '\\033[2m %s\\033[0m\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
52175
+ const promptForShell = esc2(prompt).replace(/\n/g, "\\n");
52176
+ lines.push(`printf '%b\\n' '${promptForShell}' | fold -s -w 78 | sed 's/^/ /';`);
52177
+ lines.push(`printf '\\033[2m %s\\033[0m\\n\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
52178
+ return lines.join(" ");
52179
+ }
52180
+ function findMagmuxBinary() {
52181
+ const thisFile = fileURLToPath2(import.meta.url);
52182
+ const thisDir = dirname10(thisFile);
52183
+ const pkgRoot = join30(thisDir, "..");
52184
+ const platform2 = process.platform;
52185
+ const arch = process.arch;
52186
+ const bundledMagmux = join30(pkgRoot, "native", `magmux-${platform2}-${arch}`);
52187
+ if (existsSync23(bundledMagmux))
52188
+ return bundledMagmux;
52189
+ try {
52190
+ const pkgName = `@claudish/magmux-${platform2}-${arch}`;
52191
+ let searchDir = pkgRoot;
52192
+ for (let i = 0;i < 5; i++) {
52193
+ const candidate = join30(searchDir, "node_modules", pkgName, "bin", "magmux");
52194
+ if (existsSync23(candidate))
52195
+ return candidate;
52196
+ const parent = dirname10(searchDir);
52197
+ if (parent === searchDir)
52198
+ break;
52199
+ searchDir = parent;
52200
+ }
52201
+ } catch {}
52202
+ try {
52203
+ const result = execSync("which magmux", { encoding: "utf-8" }).trim();
52204
+ if (result)
52205
+ return result;
52206
+ } catch {}
52207
+ throw new Error(`magmux not found. Install it:
52208
+ brew install MadAppGang/tap/magmux`);
52209
+ }
52210
+ async function subscribeToMagmux(sockPath, onEvent) {
52211
+ let client = null;
52212
+ for (let attempt = 0;attempt < 40; attempt++) {
52213
+ if (existsSync23(sockPath)) {
52214
+ try {
52215
+ client = await new Promise((resolve5, reject) => {
52216
+ const s = netConnect(sockPath);
52217
+ s.once("connect", () => resolve5(s));
52218
+ s.once("error", reject);
52219
+ });
52220
+ break;
52221
+ } catch {}
52222
+ }
52223
+ await wait(50);
52224
+ }
52225
+ if (!client) {
52226
+ return { results: null, client: null };
52227
+ }
52228
+ return await new Promise((resolve5) => {
52229
+ let buf = "";
52230
+ let finalResults = null;
52231
+ client.on("data", (chunk) => {
52232
+ buf += chunk.toString("utf-8");
52233
+ let nl = buf.indexOf(`
52234
+ `);
52235
+ while (nl >= 0) {
52236
+ const line = buf.slice(0, nl).trim();
52237
+ buf = buf.slice(nl + 1);
52238
+ nl = buf.indexOf(`
52239
+ `);
52240
+ if (!line)
52241
+ continue;
52242
+ try {
52243
+ const evt = JSON.parse(line);
52244
+ onEvent?.(evt);
52245
+ if (evt.type === "results") {
52246
+ finalResults = evt;
52247
+ }
52248
+ } catch {}
52249
+ }
52250
+ });
52251
+ const done = () => resolve5({ results: finalResults, client });
52252
+ client.once("end", done);
52253
+ client.once("close", done);
52254
+ client.once("error", done);
52255
+ });
52256
+ }
52257
+ function buildTeamStatus(manifest, startedAt, results) {
52258
+ const anonIds = Object.keys(manifest.models);
52259
+ const models = {};
52260
+ for (let i = 0;i < anonIds.length; i++) {
52261
+ const anonId = anonIds[i];
52262
+ const result = results?.find((r) => r.pane === i);
52263
+ if (!result) {
52264
+ models[anonId] = {
52265
+ state: "TIMEOUT",
52266
+ exitCode: null,
52267
+ startedAt,
52268
+ completedAt: null,
52269
+ outputSize: 0
52270
+ };
52271
+ continue;
52272
+ }
52273
+ let state;
52274
+ switch (result.state) {
52275
+ case "completed":
52276
+ case "awaiting_input":
52277
+ state = "COMPLETED";
52278
+ break;
52279
+ case "failed":
52280
+ state = "FAILED";
52281
+ break;
52282
+ default:
52283
+ state = "TIMEOUT";
52284
+ }
52285
+ models[anonId] = {
52286
+ state,
52287
+ exitCode: result.exitCode,
52288
+ startedAt: result.startedAt ?? startedAt,
52289
+ completedAt: result.completedAt ?? new Date().toISOString(),
52290
+ outputSize: result.response?.length ?? 0
52291
+ };
52292
+ }
52293
+ return { startedAt, models };
52294
+ }
52295
+ async function runWithGrid(sessionPath, models, input, opts) {
52296
+ const mode = opts?.mode ?? "default";
52297
+ const keep = opts?.keep ?? false;
52298
+ const manifest = setupSession(sessionPath, models, input);
52299
+ const startedAt = new Date().toISOString();
52300
+ const gridfilePath = join30(sessionPath, "gridfile.txt");
52301
+ const prompt = readFileSync22(join30(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
52302
+ const rawPrompt = readFileSync22(join30(sessionPath, "input.md"), "utf-8");
52303
+ const usedBannerColors = new Set;
52304
+ const gridLines = Object.entries(manifest.models).map(([anonId]) => {
52305
+ const model = manifest.models[anonId].model;
52306
+ if (mode === "interactive") {
52307
+ return `claudish --model ${model} -i --dangerously-skip-permissions '${prompt}'`;
52308
+ }
52309
+ const bg = pickBannerColor(model, usedBannerColors);
52310
+ const header = buildPaneHeader(model, rawPrompt, bg);
52311
+ return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
52312
+ });
52313
+ writeFileSync15(gridfilePath, `${gridLines.join(`
52314
+ `)}
52315
+ `, "utf-8");
52316
+ const magmuxPath = findMagmuxBinary();
52317
+ const spawnArgs = ["-g", gridfilePath];
52318
+ if (!keep && mode === "default") {
52319
+ spawnArgs.push("-w");
52320
+ }
52321
+ const proc = spawn3(magmuxPath, spawnArgs, {
52322
+ stdio: "inherit",
52323
+ env: { ...process.env }
52324
+ });
52325
+ const sockPath = `/tmp/magmux-${proc.pid}.sock`;
52326
+ const subscription = subscribeToMagmux(sockPath);
52327
+ const procExit = new Promise((resolve5) => {
52328
+ proc.on("exit", () => resolve5());
52329
+ proc.on("error", () => resolve5());
52330
+ });
52331
+ const [{ results }] = await Promise.all([subscription, procExit]);
52332
+ const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
52333
+ const statusPath = join30(sessionPath, "status.json");
52334
+ writeFileSync15(statusPath, JSON.stringify(status, null, 2), "utf-8");
52335
+ return status;
52336
+ }
52337
+ var BANNER_BG_COLORS;
52338
+ var init_team_grid = __esm(() => {
52339
+ init_profile_config();
52340
+ init_model_parser();
52341
+ init_routing_rules();
52342
+ init_team_orchestrator();
52343
+ BANNER_BG_COLORS = [
52344
+ "48;2;40;90;180",
52345
+ "48;2;140;60;160",
52346
+ "48;2;30;130;100",
52347
+ "48;2;160;80;40",
52348
+ "48;2;60;120;60",
52349
+ "48;2;160;50;70"
52350
+ ];
52351
+ });
52352
+
52353
+ // src/team-cli.ts
52354
+ var exports_team_cli = {};
52355
+ __export(exports_team_cli, {
52356
+ teamCommand: () => teamCommand
52357
+ });
52358
+ import { readFileSync as readFileSync23 } from "fs";
52359
+ import { join as join31 } from "path";
52360
+ function getFlag(args, flag) {
52361
+ const idx = args.indexOf(flag);
52362
+ if (idx === -1 || idx + 1 >= args.length)
52363
+ return;
52364
+ return args[idx + 1];
52365
+ }
52366
+ function hasFlag(args, flag) {
52367
+ return args.includes(flag);
52368
+ }
52369
+ function printStatus(status) {
52370
+ const modelIds = Object.keys(status.models).sort();
52371
+ console.log(`
52372
+ Team Status (started: ${status.startedAt})`);
52373
+ console.log("\u2500".repeat(60));
52374
+ for (const id of modelIds) {
52375
+ const m = status.models[id];
52376
+ const duration3 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : m.startedAt ? "running" : "pending";
52377
+ const size = m.outputSize > 0 ? ` (${m.outputSize} bytes)` : "";
52378
+ console.log(` ${id} ${m.state.padEnd(10)} ${duration3}${size}`);
52379
+ }
52380
+ console.log("");
52381
+ }
52382
+ function printHelp() {
52383
+ console.log(`
52384
+ Usage: claudish team <subcommand> [options]
52385
+
52386
+ Subcommands:
52387
+ run Run multiple models on a task in parallel
52388
+ judge Blind-judge existing model outputs
52389
+ run-and-judge Run models then judge their outputs
52390
+ status Show current session status
52391
+
52392
+ Options (run / run-and-judge):
52393
+ --path <dir> Session directory (default: .)
52394
+ --models <a,b,...> Comma-separated model IDs to run
52395
+ --input <text> Task prompt (or create input.md in --path beforehand)
52396
+ --timeout <secs> Timeout per model in seconds (default: 300)
52397
+ --grid Show all models in a magmux grid with live output + status bar
52398
+
52399
+ Options (judge / run-and-judge):
52400
+ --judges <a,b,...> Comma-separated judge model IDs (default: same as runners)
52401
+
52402
+ Options (status):
52403
+ --path <dir> Session directory (default: .)
52404
+
52405
+ Examples:
52406
+ claudish team run --path ./review --models minimax-m2.5,kimi-k2.5 --input "Review this code"
52407
+ claudish team run --grid --models kimi-k2.5,gpt-5.4,gemini-3.1-pro --input "Solve this"
52408
+ claudish team judge --path ./review
52409
+ claudish team run-and-judge --path ./review --models gpt-5.4,gemini-3.1-pro-preview --input "Evaluate this design"
52410
+ claudish team status --path ./review
52411
+ `);
52412
+ }
52413
+ async function teamCommand(args) {
52414
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
52415
+ printHelp();
52416
+ process.exit(0);
52417
+ }
52418
+ const firstArg = args[0] ?? "";
52419
+ const legacySubs = ["run", "judge", "run-and-judge", "status"];
52420
+ const subcommand = legacySubs.includes(firstArg) ? firstArg : "run";
52421
+ const rawSessionPath = getFlag(args, "--path") ?? ".";
52422
+ let sessionPath;
52423
+ try {
52424
+ sessionPath = validateSessionPath(rawSessionPath);
52425
+ } catch (err) {
52426
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
52427
+ process.exit(1);
52428
+ }
52429
+ const modelsRaw = getFlag(args, "--models");
52430
+ const judgesRaw = getFlag(args, "--judges");
52431
+ const mode = getFlag(args, "--mode") ?? "default";
52432
+ const timeoutStr = getFlag(args, "--timeout");
52433
+ const timeout = timeoutStr ? Number.parseInt(timeoutStr, 10) : 300;
52434
+ let input = getFlag(args, "--input");
52435
+ if (!input) {
52436
+ const flagsWithValues = ["--models", "--judges", "--mode", "--path", "--timeout", "--input"];
52437
+ const positionals = args.filter((a, i) => {
52438
+ if (legacySubs.includes(a) && i === 0)
52439
+ return false;
52440
+ if (a.startsWith("--"))
52441
+ return false;
52442
+ const prev = args[i - 1];
52443
+ if (prev && flagsWithValues.includes(prev))
52444
+ return false;
52445
+ return true;
52446
+ });
52447
+ if (positionals.length > 0)
52448
+ input = positionals.join(" ");
52449
+ }
52450
+ const models = modelsRaw ? modelsRaw.split(",").map((m) => m.trim()).filter(Boolean) : [];
52451
+ const judges = judgesRaw ? judgesRaw.split(",").map((m) => m.trim()).filter(Boolean) : undefined;
52452
+ const effectiveMode = hasFlag(args, "--interactive") ? "interactive" : hasFlag(args, "--grid") ? "default" : mode;
52453
+ switch (subcommand) {
52454
+ case "run": {
52455
+ if (models.length === 0) {
52456
+ console.error("Error: --models is required");
52457
+ printHelp();
52458
+ process.exit(1);
52459
+ }
52460
+ if (effectiveMode === "json") {
52461
+ setupSession(sessionPath, models, input);
52462
+ const runStatus = await runModels(sessionPath, {
52463
+ timeout,
52464
+ onStatusChange: (id, s) => {
52465
+ process.stderr.write(`[team] ${id}: ${s.state}
52466
+ `);
52467
+ }
52468
+ });
52469
+ printStatus(runStatus);
52470
+ } else {
52471
+ const { runWithGrid: runWithGrid2 } = await Promise.resolve().then(() => (init_team_grid(), exports_team_grid));
52472
+ const gridStatus = await runWithGrid2(sessionPath, models, input ?? "", {
52473
+ timeout,
52474
+ mode: effectiveMode === "interactive" ? "interactive" : "default"
52475
+ });
52476
+ printStatus(gridStatus);
52477
+ }
52478
+ break;
52479
+ }
52480
+ case "judge": {
52481
+ await judgeResponses(sessionPath, { judges });
52482
+ console.log(readFileSync23(join31(sessionPath, "verdict.md"), "utf-8"));
52483
+ break;
52484
+ }
52485
+ case "run-and-judge": {
52486
+ if (models.length === 0) {
52487
+ console.error("Error: --models is required");
52488
+ process.exit(1);
52489
+ }
52490
+ setupSession(sessionPath, models, input);
52491
+ const status = await runModels(sessionPath, {
52492
+ timeout,
52493
+ onStatusChange: (id, s) => {
52494
+ process.stderr.write(`[team] ${id}: ${s.state}
52495
+ `);
52496
+ }
52497
+ });
52498
+ printStatus(status);
52499
+ await judgeResponses(sessionPath, { judges });
52500
+ console.log(readFileSync23(join31(sessionPath, "verdict.md"), "utf-8"));
52501
+ break;
52502
+ }
52503
+ case "status": {
52504
+ const statusResult = getStatus(sessionPath);
52505
+ printStatus(statusResult);
52506
+ break;
52507
+ }
52508
+ }
52509
+ }
52510
+ var init_team_cli = __esm(() => {
52511
+ init_team_orchestrator();
52512
+ });
52513
+
51606
52514
  // src/auth/credentials/source.ts
51607
52515
  function describeSourceSync(p, config3) {
51608
52516
  if (p.isLocal)
@@ -63014,8 +63922,8 @@ var init_RemoveFileError = __esm(() => {
63014
63922
  });
63015
63923
 
63016
63924
  // ../../node_modules/.bun/@inquirer+external-editor@2.0.1+04f2146be16c61ef/node_modules/@inquirer/external-editor/dist/index.js
63017
- import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
63018
- import { readFileSync as readFileSync22, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "fs";
63925
+ import { spawn as spawn4, spawnSync as spawnSync2 } from "child_process";
63926
+ import { readFileSync as readFileSync24, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "fs";
63019
63927
  import path from "path";
63020
63928
  import os from "os";
63021
63929
  import { randomUUID as randomUUID5 } from "crypto";
@@ -63124,14 +64032,14 @@ class ExternalEditor {
63124
64032
  if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
63125
64033
  opt.mode = this.fileOptions.mode;
63126
64034
  }
63127
- writeFileSync15(this.tempFile, this.text, opt);
64035
+ writeFileSync16(this.tempFile, this.text, opt);
63128
64036
  } catch (createFileError) {
63129
64037
  throw new CreateFileError(createFileError);
63130
64038
  }
63131
64039
  }
63132
64040
  readTemporaryFile() {
63133
64041
  try {
63134
- const tempFileBuffer = readFileSync22(this.tempFile);
64042
+ const tempFileBuffer = readFileSync24(this.tempFile);
63135
64043
  if (tempFileBuffer.length === 0) {
63136
64044
  this.text = "";
63137
64045
  } else {
@@ -63162,7 +64070,7 @@ class ExternalEditor {
63162
64070
  }
63163
64071
  launchEditorAsync(callback) {
63164
64072
  try {
63165
- const editorProcess = spawn3(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" });
64073
+ const editorProcess = spawn4(this.editor.bin, this.editor.args.concat([this.tempFile]), { stdio: "inherit" });
63166
64074
  editorProcess.on("exit", (code) => {
63167
64075
  this.lastExitStatus = code;
63168
64076
  setImmediate(callback);
@@ -64112,9 +65020,9 @@ var init_dist16 = __esm(() => {
64112
65020
 
64113
65021
  // src/auth/antigravity-oauth.ts
64114
65022
  import { spawnSync as spawnSync3 } from "child_process";
64115
- import { existsSync as existsSync23, unlinkSync as unlinkSync6 } from "fs";
65023
+ import { existsSync as existsSync24, unlinkSync as unlinkSync6 } from "fs";
64116
65024
  import { homedir as homedir28 } from "os";
64117
- import { join as join30 } from "path";
65025
+ import { join as join32 } from "path";
64118
65026
  async function defaultSuggestModel() {
64119
65027
  try {
64120
65028
  const tok = readSharedAntigravityToken();
@@ -64235,8 +65143,8 @@ No session detected yet. Starting the Antigravity CLI interactively \u2014
64235
65143
  async logout(deps) {
64236
65144
  deleteSharedAntigravityToken(deps);
64237
65145
  try {
64238
- const tokenFile = join30(homedir28(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
64239
- if (existsSync23(tokenFile))
65146
+ const tokenFile = join32(homedir28(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
65147
+ if (existsSync24(tokenFile))
64240
65148
  unlinkSync6(tokenFile);
64241
65149
  } catch {}
64242
65150
  log("[AntigravityOAuth] Antigravity session cleared (keychain + agy token file)");
@@ -68322,22 +69230,22 @@ __export(exports_cli, {
68322
69230
  });
68323
69231
  import {
68324
69232
  copyFileSync as copyFileSync2,
68325
- existsSync as existsSync24,
69233
+ existsSync as existsSync25,
68326
69234
  mkdirSync as mkdirSync14,
68327
- readFileSync as readFileSync23,
69235
+ readFileSync as readFileSync25,
68328
69236
  readdirSync as readdirSync5,
68329
69237
  unlinkSync as unlinkSync7,
68330
- writeFileSync as writeFileSync16
69238
+ writeFileSync as writeFileSync17
68331
69239
  } from "fs";
68332
69240
  import { homedir as homedir29 } from "os";
68333
- import { dirname as dirname10, join as join31 } from "path";
68334
- import { fileURLToPath as fileURLToPath2 } from "url";
69241
+ import { dirname as dirname11, join as join33 } from "path";
69242
+ import { fileURLToPath as fileURLToPath3 } from "url";
68335
69243
  function getVersion3() {
68336
69244
  return VERSION;
68337
69245
  }
68338
69246
  function clearAllModelCaches() {
68339
- const cacheDir = join31(homedir29(), ".claudish");
68340
- if (!existsSync24(cacheDir))
69247
+ const cacheDir = join33(homedir29(), ".claudish");
69248
+ if (!existsSync25(cacheDir))
68341
69249
  return;
68342
69250
  const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
68343
69251
  let cleared = 0;
@@ -68345,7 +69253,7 @@ function clearAllModelCaches() {
68345
69253
  const files = readdirSync5(cacheDir);
68346
69254
  for (const file2 of files) {
68347
69255
  if (cachePatterns.includes(file2)) {
68348
- unlinkSync7(join31(cacheDir, file2));
69256
+ unlinkSync7(join33(cacheDir, file2));
68349
69257
  cleared++;
68350
69258
  }
68351
69259
  }
@@ -68578,7 +69486,7 @@ async function parseArgs(args) {
68578
69486
  printVersion();
68579
69487
  process.exit(0);
68580
69488
  } else if (arg === "--help" || arg === "-h") {
68581
- printHelp();
69489
+ printHelp2();
68582
69490
  process.exit(0);
68583
69491
  } else if (arg === "--help-ai") {
68584
69492
  printAIAgentGuide();
@@ -68760,15 +69668,15 @@ Usage: claudish --models --provider <slug>`);
68760
69668
  });
68761
69669
  config3.resolvedDefaultProvider = resolved;
68762
69670
  if (resolved.legacyAutoPromoted && !config3.quiet) {
68763
- const markerFile = join31(homedir29(), ".claudish", ".legacy-litellm-hint-shown");
68764
- if (!existsSync24(markerFile)) {
69671
+ const markerFile = join33(homedir29(), ".claudish", ".legacy-litellm-hint-shown");
69672
+ if (!existsSync25(markerFile)) {
68765
69673
  const hint = buildLegacyHint(resolved);
68766
69674
  if (hint) {
68767
69675
  console.error(hint);
68768
69676
  }
68769
69677
  try {
68770
- mkdirSync14(dirname10(markerFile), { recursive: true });
68771
- writeFileSync16(markerFile, new Date().toISOString(), "utf-8");
69678
+ mkdirSync14(dirname11(markerFile), { recursive: true });
69679
+ writeFileSync17(markerFile, new Date().toISOString(), "utf-8");
68772
69680
  } catch {}
68773
69681
  }
68774
69682
  }
@@ -69535,7 +70443,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
69535
70443
  await tui.shutdown();
69536
70444
  }
69537
70445
  }
69538
- function printHelp() {
70446
+ function printHelp2() {
69539
70447
  const useColor = !!process.stdout.isTTY && !process.env.NO_COLOR;
69540
70448
  const c = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
69541
70449
  const bold4 = c("1");
@@ -69838,8 +70746,8 @@ ${h("MORE INFO")}
69838
70746
  }
69839
70747
  function printAIAgentGuide() {
69840
70748
  try {
69841
- const guidePath = join31(__dirname3, "../AI_AGENT_GUIDE.md");
69842
- const guideContent = readFileSync23(guidePath, "utf-8");
70749
+ const guidePath = join33(__dirname3, "../AI_AGENT_GUIDE.md");
70750
+ const guideContent = readFileSync25(guidePath, "utf-8");
69843
70751
  console.log(guideContent);
69844
70752
  } catch (error46) {
69845
70753
  console.error("Error reading AI Agent Guide:");
@@ -69855,19 +70763,19 @@ async function initializeClaudishSkill() {
69855
70763
  console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
69856
70764
  `);
69857
70765
  const cwd = process.cwd();
69858
- const claudeDir = join31(cwd, ".claude");
69859
- const skillsDir = join31(claudeDir, "skills");
69860
- const claudishSkillDir = join31(skillsDir, "claudish-usage");
69861
- const skillFile = join31(claudishSkillDir, "SKILL.md");
69862
- if (existsSync24(skillFile)) {
70766
+ const claudeDir = join33(cwd, ".claude");
70767
+ const skillsDir = join33(claudeDir, "skills");
70768
+ const claudishSkillDir = join33(skillsDir, "claudish-usage");
70769
+ const skillFile = join33(claudishSkillDir, "SKILL.md");
70770
+ if (existsSync25(skillFile)) {
69863
70771
  console.log("\u2705 Claudish skill already installed at:");
69864
70772
  console.log(` ${skillFile}
69865
70773
  `);
69866
70774
  console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
69867
70775
  return;
69868
70776
  }
69869
- const sourceSkillPath = join31(__dirname3, "../skills/claudish-usage/SKILL.md");
69870
- if (!existsSync24(sourceSkillPath)) {
70777
+ const sourceSkillPath = join33(__dirname3, "../skills/claudish-usage/SKILL.md");
70778
+ if (!existsSync25(sourceSkillPath)) {
69871
70779
  console.error("\u274C Error: Claudish skill file not found in installation.");
69872
70780
  console.error(` Expected at: ${sourceSkillPath}`);
69873
70781
  console.error(`
@@ -69876,15 +70784,15 @@ async function initializeClaudishSkill() {
69876
70784
  process.exit(1);
69877
70785
  }
69878
70786
  try {
69879
- if (!existsSync24(claudeDir)) {
70787
+ if (!existsSync25(claudeDir)) {
69880
70788
  mkdirSync14(claudeDir, { recursive: true });
69881
70789
  console.log("\uD83D\uDCC1 Created .claude/ directory");
69882
70790
  }
69883
- if (!existsSync24(skillsDir)) {
70791
+ if (!existsSync25(skillsDir)) {
69884
70792
  mkdirSync14(skillsDir, { recursive: true });
69885
70793
  console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
69886
70794
  }
69887
- if (!existsSync24(claudishSkillDir)) {
70795
+ if (!existsSync25(claudishSkillDir)) {
69888
70796
  mkdirSync14(claudishSkillDir, { recursive: true });
69889
70797
  console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
69890
70798
  }
@@ -69956,8 +70864,8 @@ var init_cli = __esm(() => {
69956
70864
  init_provider_definitions();
69957
70865
  init_routing_rules();
69958
70866
  init_provider_resolver();
69959
- __filename3 = fileURLToPath2(import.meta.url);
69960
- __dirname3 = dirname10(__filename3);
70867
+ __filename3 = fileURLToPath3(import.meta.url);
70868
+ __dirname3 = dirname11(__filename3);
69961
70869
  });
69962
70870
 
69963
70871
  // src/update-checker.ts
@@ -69969,33 +70877,33 @@ __export(exports_update_checker, {
69969
70877
  clearCache: () => clearCache,
69970
70878
  checkForUpdates: () => checkForUpdates
69971
70879
  });
69972
- import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync8, writeFileSync as writeFileSync17 } from "fs";
70880
+ import { existsSync as existsSync26, mkdirSync as mkdirSync15, readFileSync as readFileSync26, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18 } from "fs";
69973
70881
  import { homedir as homedir30, platform as platform2, tmpdir } from "os";
69974
- import { join as join32 } from "path";
70882
+ import { join as join34 } from "path";
69975
70883
  function getCacheFilePath() {
69976
70884
  let cacheDir;
69977
70885
  if (isWindows) {
69978
- const localAppData = process.env.LOCALAPPDATA || join32(homedir30(), "AppData", "Local");
69979
- cacheDir = join32(localAppData, "claudish");
70886
+ const localAppData = process.env.LOCALAPPDATA || join34(homedir30(), "AppData", "Local");
70887
+ cacheDir = join34(localAppData, "claudish");
69980
70888
  } else {
69981
- cacheDir = join32(homedir30(), ".cache", "claudish");
70889
+ cacheDir = join34(homedir30(), ".cache", "claudish");
69982
70890
  }
69983
70891
  try {
69984
- if (!existsSync25(cacheDir)) {
70892
+ if (!existsSync26(cacheDir)) {
69985
70893
  mkdirSync15(cacheDir, { recursive: true });
69986
70894
  }
69987
- return join32(cacheDir, "update-check.json");
70895
+ return join34(cacheDir, "update-check.json");
69988
70896
  } catch {
69989
- return join32(tmpdir(), "claudish-update-check.json");
70897
+ return join34(tmpdir(), "claudish-update-check.json");
69990
70898
  }
69991
70899
  }
69992
70900
  function readCache() {
69993
70901
  try {
69994
70902
  const cachePath = getCacheFilePath();
69995
- if (!existsSync25(cachePath)) {
70903
+ if (!existsSync26(cachePath)) {
69996
70904
  return null;
69997
70905
  }
69998
- const data = JSON.parse(readFileSync24(cachePath, "utf-8"));
70906
+ const data = JSON.parse(readFileSync26(cachePath, "utf-8"));
69999
70907
  return data;
70000
70908
  } catch {
70001
70909
  return null;
@@ -70008,7 +70916,7 @@ function writeCache(latestVersion) {
70008
70916
  lastCheck: Date.now(),
70009
70917
  latestVersion
70010
70918
  };
70011
- writeFileSync17(cachePath, JSON.stringify(data), "utf-8");
70919
+ writeFileSync18(cachePath, JSON.stringify(data), "utf-8");
70012
70920
  } catch {}
70013
70921
  }
70014
70922
  function isCacheValid(cache2) {
@@ -70018,7 +70926,7 @@ function isCacheValid(cache2) {
70018
70926
  function clearCache() {
70019
70927
  try {
70020
70928
  const cachePath = getCacheFilePath();
70021
- if (existsSync25(cachePath)) {
70929
+ if (existsSync26(cachePath)) {
70022
70930
  unlinkSync8(cachePath);
70023
70931
  }
70024
70932
  } catch {}
@@ -70106,7 +71014,7 @@ var exports_update_command = {};
70106
71014
  __export(exports_update_command, {
70107
71015
  updateCommand: () => updateCommand
70108
71016
  });
70109
- import { execSync } from "child_process";
71017
+ import { execSync as execSync2 } from "child_process";
70110
71018
  function detectInstallationMethod() {
70111
71019
  const scriptPath = process.argv[1] || "";
70112
71020
  if (scriptPath.includes("/opt/homebrew/") || scriptPath.includes("/usr/local/Cellar/")) {
@@ -70134,7 +71042,7 @@ function getUpdateCommand(method) {
70134
71042
  }
70135
71043
  async function executeUpdate(command) {
70136
71044
  try {
70137
- execSync(command, {
71045
+ execSync2(command, {
70138
71046
  stdio: "inherit",
70139
71047
  shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh"
70140
71048
  });
@@ -70271,7 +71179,7 @@ ${BOLD2}Unable to detect installation method.${RESET2}`);
70271
71179
  }
70272
71180
  function fetchLatestVersionViaNpm() {
70273
71181
  try {
70274
- const output = execSync("npm view claudish version", {
71182
+ const output = execSync2("npm view claudish version", {
70275
71183
  encoding: "utf-8",
70276
71184
  timeout: 20000,
70277
71185
  stdio: ["ignore", "pipe", "ignore"]
@@ -70903,15 +71811,15 @@ var init_local_liveness = __esm(() => {
70903
71811
  });
70904
71812
 
70905
71813
  // src/providers/probe-catalog.ts
70906
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync18 } from "fs";
71814
+ import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync27, writeFileSync as writeFileSync19 } from "fs";
70907
71815
  import { homedir as homedir31 } from "os";
70908
- import { dirname as dirname11, join as join33 } from "path";
71816
+ import { dirname as dirname12, join as join35 } from "path";
70909
71817
  function readProbeModelsCache(path2 = PROBE_MODELS_CACHE_PATH) {
70910
- if (!existsSync26(path2))
71818
+ if (!existsSync27(path2))
70911
71819
  return null;
70912
71820
  let raw2;
70913
71821
  try {
70914
- raw2 = JSON.parse(readFileSync25(path2, "utf-8"));
71822
+ raw2 = JSON.parse(readFileSync27(path2, "utf-8"));
70915
71823
  } catch {
70916
71824
  return null;
70917
71825
  }
@@ -70920,8 +71828,8 @@ function readProbeModelsCache(path2 = PROBE_MODELS_CACHE_PATH) {
70920
71828
  return raw2;
70921
71829
  }
70922
71830
  function writeProbeModelsCache(data, path2 = PROBE_MODELS_CACHE_PATH) {
70923
- mkdirSync16(dirname11(path2), { recursive: true });
70924
- writeFileSync18(path2, JSON.stringify(data), "utf-8");
71831
+ mkdirSync16(dirname12(path2), { recursive: true });
71832
+ writeFileSync19(path2, JSON.stringify(data), "utf-8");
70925
71833
  }
70926
71834
  function isCacheFresh(data, ttlMs = CACHE_TTL_MS4) {
70927
71835
  if (!data?.generatedAt)
@@ -71040,7 +71948,7 @@ function isValidResponse(raw2) {
71040
71948
  var PROBE_MODELS_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/probeModels", CACHE_TTL_MS4, FETCH_TIMEOUT_MS3 = 15000, PROBE_MODELS_CACHE_PATH, _inFlight = null;
71041
71949
  var init_probe_catalog = __esm(() => {
71042
71950
  CACHE_TTL_MS4 = 60 * 60 * 1000;
71043
- PROBE_MODELS_CACHE_PATH = join33(homedir31(), ".claudish", "probe-models.json");
71951
+ PROBE_MODELS_CACHE_PATH = join35(homedir31(), ".claudish", "probe-models.json");
71044
71952
  });
71045
71953
 
71046
71954
  // src/tui/constants.ts
@@ -77387,20 +78295,20 @@ __export(exports_claude_runner, {
77387
78295
  MIN_AUTO_COMPACT_WINDOW: () => MIN_AUTO_COMPACT_WINDOW,
77388
78296
  CLAUDE_CODE_DEFAULT_MAX_CONTEXT: () => CLAUDE_CODE_DEFAULT_MAX_CONTEXT
77389
78297
  });
77390
- import { spawn as spawn4 } from "child_process";
78298
+ import { spawn as spawn5 } from "child_process";
77391
78299
  import {
77392
78300
  closeSync as closeSync4,
77393
- existsSync as existsSync27,
78301
+ existsSync as existsSync28,
77394
78302
  mkdirSync as mkdirSync17,
77395
78303
  openSync as openSync4,
77396
- readFileSync as readFileSync26,
78304
+ readFileSync as readFileSync28,
77397
78305
  readdirSync as readdirSync6,
77398
78306
  statSync as statSync5,
77399
78307
  unlinkSync as unlinkSync9,
77400
- writeFileSync as writeFileSync19
78308
+ writeFileSync as writeFileSync20
77401
78309
  } from "fs";
77402
78310
  import { homedir as homedir32, tmpdir as tmpdir2 } from "os";
77403
- import { dirname as dirname12, join as join34 } from "path";
78311
+ import { dirname as dirname13, join as join36 } from "path";
77404
78312
  import { isatty } from "tty";
77405
78313
  function releaseTerminalIsolation() {
77406
78314
  if (!restoreTerminal)
@@ -77435,14 +78343,14 @@ function isProxyAuthMode(config3) {
77435
78343
  }
77436
78344
  function managedSettingsPath() {
77437
78345
  if (isWindows2()) {
77438
- return join34(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
78346
+ return join36(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
77439
78347
  }
77440
78348
  if (process.platform === "darwin") {
77441
78349
  return "/Library/Application Support/ClaudeCode/managed-settings.json";
77442
78350
  }
77443
78351
  return "/etc/claude-code/managed-settings.json";
77444
78352
  }
77445
- function managedSettingsForcesClaudeAi(readFile3 = readFileSync26) {
78353
+ function managedSettingsForcesClaudeAi(readFile3 = readFileSync28) {
77446
78354
  try {
77447
78355
  const raw2 = readFile3(managedSettingsPath(), "utf-8");
77448
78356
  const parsed = JSON.parse(raw2);
@@ -77456,9 +78364,9 @@ function isWindows2() {
77456
78364
  }
77457
78365
  function createStatusLineScript(tokenFilePath) {
77458
78366
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
77459
- const claudishDir = join34(homeDir, ".claudish");
78367
+ const claudishDir = join36(homeDir, ".claudish");
77460
78368
  const timestamp = Date.now();
77461
- const scriptPath = join34(claudishDir, `status-${timestamp}.js`);
78369
+ const scriptPath = join36(claudishDir, `status-${timestamp}.js`);
77462
78370
  const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
77463
78371
  const script = `
77464
78372
  const fs = require('fs');
@@ -77585,13 +78493,13 @@ process.stdin.on('end', () => {
77585
78493
  }
77586
78494
  });
77587
78495
  `;
77588
- writeFileSync19(scriptPath, script, "utf-8");
78496
+ writeFileSync20(scriptPath, script, "utf-8");
77589
78497
  return scriptPath;
77590
78498
  }
77591
78499
  function initializeTokenFile(tokenFilePath) {
77592
78500
  try {
77593
- mkdirSync17(dirname12(tokenFilePath), { recursive: true });
77594
- writeFileSync19(tokenFilePath, JSON.stringify({
78501
+ mkdirSync17(dirname13(tokenFilePath), { recursive: true });
78502
+ writeFileSync20(tokenFilePath, JSON.stringify({
77595
78503
  input_tokens: 0,
77596
78504
  output_tokens: 0,
77597
78505
  total_tokens: 0,
@@ -77622,7 +78530,7 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
77622
78530
  if (!name.startsWith("tokens-") || !name.endsWith(".json"))
77623
78531
  continue;
77624
78532
  scanned++;
77625
- const full = join34(dir, name);
78533
+ const full = join36(dir, name);
77626
78534
  try {
77627
78535
  if (statSync5(full).mtimeMs >= cutoff)
77628
78536
  continue;
@@ -77639,7 +78547,7 @@ function parseSettingsArg(value) {
77639
78547
  if (value.trimStart().startsWith("{")) {
77640
78548
  return JSON.parse(value);
77641
78549
  }
77642
- return JSON.parse(readFileSync26(value, "utf-8"));
78550
+ return JSON.parse(readFileSync28(value, "utf-8"));
77643
78551
  }
77644
78552
  function parseSettingsArgSafe(value) {
77645
78553
  try {
@@ -77651,13 +78559,13 @@ function parseSettingsArgSafe(value) {
77651
78559
  }
77652
78560
  function userSettingsFileCandidates(cwd) {
77653
78561
  return [
77654
- join34(homedir32(), ".claude", "settings.json"),
77655
- join34(cwd, ".claude", "settings.json"),
77656
- join34(cwd, ".claude", "settings.local.json")
78562
+ join36(homedir32(), ".claude", "settings.json"),
78563
+ join36(cwd, ".claude", "settings.json"),
78564
+ join36(cwd, ".claude", "settings.local.json")
77657
78565
  ];
77658
78566
  }
77659
78567
  function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
77660
- const sources = userSettingsFileCandidates(cwd).filter((file2) => existsSync27(file2));
78568
+ const sources = userSettingsFileCandidates(cwd).filter((file2) => existsSync28(file2));
77661
78569
  const idx = claudeArgs.indexOf("--settings");
77662
78570
  const settingsArg = idx === -1 ? undefined : claudeArgs[idx + 1];
77663
78571
  if (settingsArg)
@@ -77694,13 +78602,13 @@ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
77694
78602
  }
77695
78603
  function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
77696
78604
  const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
77697
- const claudishDir = join34(homeDir, ".claudish");
78605
+ const claudishDir = join36(homeDir, ".claudish");
77698
78606
  try {
77699
78607
  mkdirSync17(claudishDir, { recursive: true });
77700
78608
  } catch {}
77701
78609
  const timestamp = Date.now();
77702
- const tempPath = join34(claudishDir, `settings-${timestamp}.json`);
77703
- const tokenFilePath = join34(claudishDir, `tokens-${port}.json`);
78610
+ const tempPath = join36(claudishDir, `settings-${timestamp}.json`);
78611
+ const tokenFilePath = join36(claudishDir, `tokens-${port}.json`);
77704
78612
  cleanupStaleTokenFiles(claudishDir);
77705
78613
  initializeTokenFile(tokenFilePath);
77706
78614
  let statusCommand;
@@ -77733,7 +78641,7 @@ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLi
77733
78641
  padding: 0
77734
78642
  };
77735
78643
  const settings = buildClaudishSettingsOverlay(statusLine, proxyAuthMode);
77736
- writeFileSync19(tempPath, JSON.stringify(settings, null, 2), "utf-8");
78644
+ writeFileSync20(tempPath, JSON.stringify(settings, null, 2), "utf-8");
77737
78645
  return { path: tempPath, statusLine, tokenFilePath };
77738
78646
  }
77739
78647
  function buildClaudishSettingsOverlay(statusLine, proxyAuthMode) {
@@ -77758,7 +78666,7 @@ function mergeUserSettingsIfPresent(config3, tempSettingsPath, statusLine, proxy
77758
78666
  if (proxyAuthMode && !("forceLoginMethod" in userSettings)) {
77759
78667
  userSettings.forceLoginMethod = "console";
77760
78668
  }
77761
- writeFileSync19(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
78669
+ writeFileSync20(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
77762
78670
  } catch {
77763
78671
  if (!config3.quiet) {
77764
78672
  console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
@@ -77951,7 +78859,7 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
77951
78859
  console.error(`
77952
78860
  Or set CLAUDE_PATH to your custom installation:`);
77953
78861
  const home = homedir32();
77954
- const localPath = isWindows2() ? join34(home, ".claude", "local", "claude.exe") : join34(home, ".claude", "local", "claude");
78862
+ const localPath = isWindows2() ? join36(home, ".claude", "local", "claude.exe") : join36(home, ".claude", "local", "claude");
77955
78863
  console.error(` export CLAUDE_PATH=${localPath}`);
77956
78864
  process.exit(1);
77957
78865
  }
@@ -77975,7 +78883,7 @@ Or set CLAUDE_PATH to your custom installation:`);
77975
78883
  console.error("[claudish] An interactive session was requested but no terminal is attached (stdin and stdout are both non-TTY). Pass a prompt argument, or use --stdin / -p for non-interactive mode.");
77976
78884
  }
77977
78885
  const stdio = ttyFd !== undefined ? [0, ttyFd, ttyFd] : "inherit";
77978
- const proc = spawn4(spawnCommand, claudeArgs, {
78886
+ const proc = spawn5(spawnCommand, claudeArgs, {
77979
78887
  env,
77980
78888
  stdio,
77981
78889
  shell: needsShell
@@ -78031,23 +78939,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
78031
78939
  async function findClaudeBinary() {
78032
78940
  const isWindows3 = process.platform === "win32";
78033
78941
  if (process.env.CLAUDE_PATH) {
78034
- if (existsSync27(process.env.CLAUDE_PATH)) {
78942
+ if (existsSync28(process.env.CLAUDE_PATH)) {
78035
78943
  return process.env.CLAUDE_PATH;
78036
78944
  }
78037
78945
  }
78038
78946
  const home = homedir32();
78039
- const localPath = isWindows3 ? join34(home, ".claude", "local", "claude.exe") : join34(home, ".claude", "local", "claude");
78040
- if (existsSync27(localPath)) {
78947
+ const localPath = isWindows3 ? join36(home, ".claude", "local", "claude.exe") : join36(home, ".claude", "local", "claude");
78948
+ if (existsSync28(localPath)) {
78041
78949
  return localPath;
78042
78950
  }
78043
78951
  if (isWindows3) {
78044
78952
  const windowsPaths = [
78045
- join34(home, "AppData", "Roaming", "npm", "claude.cmd"),
78046
- join34(home, ".npm-global", "claude.cmd"),
78047
- join34(home, "node_modules", ".bin", "claude.cmd")
78953
+ join36(home, "AppData", "Roaming", "npm", "claude.cmd"),
78954
+ join36(home, ".npm-global", "claude.cmd"),
78955
+ join36(home, "node_modules", ".bin", "claude.cmd")
78048
78956
  ];
78049
78957
  for (const path2 of windowsPaths) {
78050
- if (existsSync27(path2)) {
78958
+ if (existsSync28(path2)) {
78051
78959
  return path2;
78052
78960
  }
78053
78961
  }
@@ -78055,21 +78963,21 @@ async function findClaudeBinary() {
78055
78963
  const commonPaths = [
78056
78964
  "/usr/local/bin/claude",
78057
78965
  "/opt/homebrew/bin/claude",
78058
- join34(home, ".npm-global/bin/claude"),
78059
- join34(home, ".local/bin/claude"),
78060
- join34(home, "node_modules/.bin/claude"),
78966
+ join36(home, ".npm-global/bin/claude"),
78967
+ join36(home, ".local/bin/claude"),
78968
+ join36(home, "node_modules/.bin/claude"),
78061
78969
  "/data/data/com.termux/files/usr/bin/claude",
78062
- join34(home, "../usr/bin/claude")
78970
+ join36(home, "../usr/bin/claude")
78063
78971
  ];
78064
78972
  for (const path2 of commonPaths) {
78065
- if (existsSync27(path2)) {
78973
+ if (existsSync28(path2)) {
78066
78974
  return path2;
78067
78975
  }
78068
78976
  }
78069
78977
  }
78070
78978
  try {
78071
78979
  const shellCommand = isWindows3 ? "where claude" : "command -v claude";
78072
- const proc = spawn4(shellCommand, [], {
78980
+ const proc = spawn5(shellCommand, [], {
78073
78981
  stdio: "pipe",
78074
78982
  shell: true
78075
78983
  });
@@ -78121,18 +79029,18 @@ __export(exports_diag_output, {
78121
79029
  NullDiagOutput: () => NullDiagOutput,
78122
79030
  LogFileDiagOutput: () => LogFileDiagOutput
78123
79031
  });
78124
- import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync18, unlinkSync as unlinkSync10, writeFileSync as writeFileSync20 } from "fs";
79032
+ import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync18, unlinkSync as unlinkSync10, writeFileSync as writeFileSync21 } from "fs";
78125
79033
  import { homedir as homedir33 } from "os";
78126
- import { join as join35 } from "path";
79034
+ import { join as join37 } from "path";
78127
79035
  function getClaudishDir() {
78128
- const dir = join35(homedir33(), ".claudish");
79036
+ const dir = join37(homedir33(), ".claudish");
78129
79037
  try {
78130
79038
  mkdirSync18(dir, { recursive: true });
78131
79039
  } catch {}
78132
79040
  return dir;
78133
79041
  }
78134
79042
  function getDiagLogPath() {
78135
- return join35(getClaudishDir(), `diag-${process.pid}.log`);
79043
+ return join37(getClaudishDir(), `diag-${process.pid}.log`);
78136
79044
  }
78137
79045
 
78138
79046
  class LogFileDiagOutput {
@@ -78141,7 +79049,7 @@ class LogFileDiagOutput {
78141
79049
  constructor() {
78142
79050
  this.logPath = getDiagLogPath();
78143
79051
  try {
78144
- writeFileSync20(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
79052
+ writeFileSync21(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
78145
79053
  `);
78146
79054
  } catch {}
78147
79055
  this.stream = createWriteStream3(this.logPath, { flags: "a" });
@@ -78328,279 +79236,6 @@ var init_catalog_warm = __esm(() => {
78328
79236
  ];
78329
79237
  });
78330
79238
 
78331
- // src/team-grid.ts
78332
- var exports_team_grid = {};
78333
- __export(exports_team_grid, {
78334
- runWithGrid: () => runWithGrid
78335
- });
78336
- import { spawn as spawn5 } from "child_process";
78337
- import { execSync as execSync2 } from "child_process";
78338
- import { existsSync as existsSync28, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
78339
- import { connect as netConnect } from "net";
78340
- import { dirname as dirname13, join as join36 } from "path";
78341
- import { setTimeout as wait } from "timers/promises";
78342
- import { fileURLToPath as fileURLToPath3 } from "url";
78343
- function resolveRouteInfo(modelId) {
78344
- const parsed = parseModelSpec(modelId);
78345
- if (parsed.isExplicitProvider) {
78346
- return { chain: [parsed.provider], source: "direct" };
78347
- }
78348
- const local = loadLocalConfig();
78349
- if (local?.routing && Object.keys(local.routing).length > 0) {
78350
- const matched2 = matchRoutingRule(parsed.model, local.routing);
78351
- if (matched2) {
78352
- const routes = buildRoutingChain(matched2, parsed.model);
78353
- const pattern = Object.keys(local.routing).find((k) => {
78354
- if (k === parsed.model)
78355
- return true;
78356
- if (k.includes("*")) {
78357
- const star = k.indexOf("*");
78358
- return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
78359
- }
78360
- return false;
78361
- });
78362
- return {
78363
- chain: routes.map((r) => r.displayName),
78364
- source: "project routing",
78365
- sourceDetail: pattern
78366
- };
78367
- }
78368
- }
78369
- const global_ = loadConfig();
78370
- if (global_.routing && Object.keys(global_.routing).length > 0) {
78371
- const matched2 = matchRoutingRule(parsed.model, global_.routing);
78372
- if (matched2) {
78373
- const routes = buildRoutingChain(matched2, parsed.model);
78374
- const pattern = Object.keys(global_.routing).find((k) => {
78375
- if (k === parsed.model)
78376
- return true;
78377
- if (k.includes("*")) {
78378
- const star = k.indexOf("*");
78379
- return parsed.model.startsWith(k.slice(0, star)) && parsed.model.endsWith(k.slice(star + 1));
78380
- }
78381
- return false;
78382
- });
78383
- return {
78384
- chain: routes.map((r) => r.displayName),
78385
- source: "user routing",
78386
- sourceDetail: pattern
78387
- };
78388
- }
78389
- }
78390
- const merged = loadRoutingRules();
78391
- const matched = matchRoutingRule(parsed.model, merged);
78392
- if (matched) {
78393
- const routes = buildRoutingChain(matched, parsed.model);
78394
- return {
78395
- chain: routes.map((r) => r.displayName),
78396
- source: "auto"
78397
- };
78398
- }
78399
- return {
78400
- chain: [],
78401
- source: "auto"
78402
- };
78403
- }
78404
- function pickBannerColor(model, used) {
78405
- let hash2 = 0;
78406
- for (let i = 0;i < model.length; i++)
78407
- hash2 = (hash2 << 5) - hash2 + model.charCodeAt(i) | 0;
78408
- const start = Math.abs(hash2) % BANNER_BG_COLORS.length;
78409
- let idx = start;
78410
- if (used.size < BANNER_BG_COLORS.length) {
78411
- while (used.has(idx))
78412
- idx = (idx + 1) % BANNER_BG_COLORS.length;
78413
- }
78414
- used.add(idx);
78415
- return BANNER_BG_COLORS[idx];
78416
- }
78417
- function buildPaneHeader(model, prompt, bg) {
78418
- const route2 = resolveRouteInfo(model);
78419
- const esc2 = (s) => s.replace(/'/g, "'\\''");
78420
- const chainStr2 = route2.chain.join(" \u2192 ");
78421
- const sourceLabel = route2.sourceDetail ? `${route2.source}: ${route2.sourceDetail}` : route2.source;
78422
- const lines = [];
78423
- lines.push(`printf '\\033[1;97;${bg}m %s \\033[0m\\n' '${esc2(model)}';`);
78424
- lines.push(`printf '\\033[2m route: ${esc2(chainStr2)} (${esc2(sourceLabel)})\\033[0m\\n' ;`);
78425
- lines.push(`printf '\\033[2m %s\\033[0m\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
78426
- const promptForShell = esc2(prompt).replace(/\n/g, "\\n");
78427
- lines.push(`printf '%b\\n' '${promptForShell}' | fold -s -w 78 | sed 's/^/ /';`);
78428
- lines.push(`printf '\\033[2m %s\\033[0m\\n\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
78429
- return lines.join(" ");
78430
- }
78431
- function findMagmuxBinary() {
78432
- const thisFile = fileURLToPath3(import.meta.url);
78433
- const thisDir = dirname13(thisFile);
78434
- const pkgRoot = join36(thisDir, "..");
78435
- const platform3 = process.platform;
78436
- const arch = process.arch;
78437
- const bundledMagmux = join36(pkgRoot, "native", `magmux-${platform3}-${arch}`);
78438
- if (existsSync28(bundledMagmux))
78439
- return bundledMagmux;
78440
- try {
78441
- const pkgName = `@claudish/magmux-${platform3}-${arch}`;
78442
- let searchDir = pkgRoot;
78443
- for (let i = 0;i < 5; i++) {
78444
- const candidate = join36(searchDir, "node_modules", pkgName, "bin", "magmux");
78445
- if (existsSync28(candidate))
78446
- return candidate;
78447
- const parent = dirname13(searchDir);
78448
- if (parent === searchDir)
78449
- break;
78450
- searchDir = parent;
78451
- }
78452
- } catch {}
78453
- try {
78454
- const result = execSync2("which magmux", { encoding: "utf-8" }).trim();
78455
- if (result)
78456
- return result;
78457
- } catch {}
78458
- throw new Error(`magmux not found. Install it:
78459
- brew install MadAppGang/tap/magmux`);
78460
- }
78461
- async function subscribeToMagmux(sockPath, onEvent) {
78462
- let client = null;
78463
- for (let attempt = 0;attempt < 40; attempt++) {
78464
- if (existsSync28(sockPath)) {
78465
- try {
78466
- client = await new Promise((resolve5, reject) => {
78467
- const s = netConnect(sockPath);
78468
- s.once("connect", () => resolve5(s));
78469
- s.once("error", reject);
78470
- });
78471
- break;
78472
- } catch {}
78473
- }
78474
- await wait(50);
78475
- }
78476
- if (!client) {
78477
- return { results: null, client: null };
78478
- }
78479
- return await new Promise((resolve5) => {
78480
- let buf = "";
78481
- let finalResults = null;
78482
- client.on("data", (chunk) => {
78483
- buf += chunk.toString("utf-8");
78484
- let nl = buf.indexOf(`
78485
- `);
78486
- while (nl >= 0) {
78487
- const line = buf.slice(0, nl).trim();
78488
- buf = buf.slice(nl + 1);
78489
- nl = buf.indexOf(`
78490
- `);
78491
- if (!line)
78492
- continue;
78493
- try {
78494
- const evt = JSON.parse(line);
78495
- onEvent?.(evt);
78496
- if (evt.type === "results") {
78497
- finalResults = evt;
78498
- }
78499
- } catch {}
78500
- }
78501
- });
78502
- const done = () => resolve5({ results: finalResults, client });
78503
- client.once("end", done);
78504
- client.once("close", done);
78505
- client.once("error", done);
78506
- });
78507
- }
78508
- function buildTeamStatus(manifest, startedAt, results) {
78509
- const anonIds = Object.keys(manifest.models);
78510
- const models = {};
78511
- for (let i = 0;i < anonIds.length; i++) {
78512
- const anonId = anonIds[i];
78513
- const result = results?.find((r) => r.pane === i);
78514
- if (!result) {
78515
- models[anonId] = {
78516
- state: "TIMEOUT",
78517
- exitCode: null,
78518
- startedAt,
78519
- completedAt: null,
78520
- outputSize: 0
78521
- };
78522
- continue;
78523
- }
78524
- let state;
78525
- switch (result.state) {
78526
- case "completed":
78527
- case "awaiting_input":
78528
- state = "COMPLETED";
78529
- break;
78530
- case "failed":
78531
- state = "FAILED";
78532
- break;
78533
- default:
78534
- state = "TIMEOUT";
78535
- }
78536
- models[anonId] = {
78537
- state,
78538
- exitCode: result.exitCode,
78539
- startedAt: result.startedAt ?? startedAt,
78540
- completedAt: result.completedAt ?? new Date().toISOString(),
78541
- outputSize: result.response?.length ?? 0
78542
- };
78543
- }
78544
- return { startedAt, models };
78545
- }
78546
- async function runWithGrid(sessionPath, models, input, opts) {
78547
- const mode = opts?.mode ?? "default";
78548
- const keep = opts?.keep ?? false;
78549
- const manifest = setupSession(sessionPath, models, input);
78550
- const startedAt = new Date().toISOString();
78551
- const gridfilePath = join36(sessionPath, "gridfile.txt");
78552
- const prompt = readFileSync27(join36(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
78553
- const rawPrompt = readFileSync27(join36(sessionPath, "input.md"), "utf-8");
78554
- const usedBannerColors = new Set;
78555
- const gridLines = Object.entries(manifest.models).map(([anonId]) => {
78556
- const model = manifest.models[anonId].model;
78557
- if (mode === "interactive") {
78558
- return `claudish --model ${model} -i --dangerously-skip-permissions '${prompt}'`;
78559
- }
78560
- const bg = pickBannerColor(model, usedBannerColors);
78561
- const header = buildPaneHeader(model, rawPrompt, bg);
78562
- return `${header} claudish --model ${model} -y --quiet '${prompt}'`;
78563
- });
78564
- writeFileSync21(gridfilePath, `${gridLines.join(`
78565
- `)}
78566
- `, "utf-8");
78567
- const magmuxPath = findMagmuxBinary();
78568
- const spawnArgs = ["-g", gridfilePath];
78569
- if (!keep && mode === "default") {
78570
- spawnArgs.push("-w");
78571
- }
78572
- const proc = spawn5(magmuxPath, spawnArgs, {
78573
- stdio: "inherit",
78574
- env: { ...process.env }
78575
- });
78576
- const sockPath = `/tmp/magmux-${proc.pid}.sock`;
78577
- const subscription = subscribeToMagmux(sockPath);
78578
- const procExit = new Promise((resolve5) => {
78579
- proc.on("exit", () => resolve5());
78580
- proc.on("error", () => resolve5());
78581
- });
78582
- const [{ results }] = await Promise.all([subscription, procExit]);
78583
- const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
78584
- const statusPath = join36(sessionPath, "status.json");
78585
- writeFileSync21(statusPath, JSON.stringify(status, null, 2), "utf-8");
78586
- return status;
78587
- }
78588
- var BANNER_BG_COLORS;
78589
- var init_team_grid = __esm(() => {
78590
- init_profile_config();
78591
- init_model_parser();
78592
- init_routing_rules();
78593
- init_team_orchestrator();
78594
- BANNER_BG_COLORS = [
78595
- "48;2;40;90;180",
78596
- "48;2;140;60;160",
78597
- "48;2;30;130;100",
78598
- "48;2;160;80;40",
78599
- "48;2;60;120;60",
78600
- "48;2;160;50;70"
78601
- ];
78602
- });
78603
-
78604
79239
  // src/tui/viz/text.ts
78605
79240
  function columns(n, fn, arg = "width") {
78606
79241
  if (!Number.isFinite(n))
@@ -79003,7 +79638,7 @@ __export(exports_session_discovery, {
79003
79638
  import { execFile, execFileSync as execFileSync2 } from "child_process";
79004
79639
  import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
79005
79640
  import { homedir as homedir34 } from "os";
79006
- import { basename, join as join37 } from "path";
79641
+ import { basename, join as join38 } from "path";
79007
79642
  function slugForPath(absPath) {
79008
79643
  return absPath.replace(/[/.]/g, "-");
79009
79644
  }
@@ -79052,7 +79687,7 @@ function projectDirs() {
79052
79687
  }
79053
79688
  }
79054
79689
  function sessionsIn(dirName) {
79055
- const dir = join37(PROJECTS_DIR, dirName);
79690
+ const dir = join38(PROJECTS_DIR, dirName);
79056
79691
  let names;
79057
79692
  try {
79058
79693
  names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
@@ -79061,7 +79696,7 @@ function sessionsIn(dirName) {
79061
79696
  }
79062
79697
  const rows = [];
79063
79698
  for (const n of names) {
79064
- const file2 = join37(dir, n);
79699
+ const file2 = join38(dir, n);
79065
79700
  try {
79066
79701
  const st = statSync6(file2);
79067
79702
  if (st.size === 0)
@@ -79420,7 +80055,7 @@ function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
79420
80055
  }
79421
80056
  var ENTRYPOINT_BYTES = 8192, PROJECTS_DIR, ACTIVE_WINDOW_MS = 120000, HEAD_BYTES, TAIL_BYTES, HARNESS_ENVELOPES, DEEP_TAIL_BYTES, RECENT_AI_TURNS = 5, RECENT_USER_TURNS = 1;
79422
80057
  var init_session_discovery = __esm(() => {
79423
- PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
80058
+ PROJECTS_DIR = join38(homedir34(), ".claude", "projects");
79424
80059
  HEAD_BYTES = 64 * 1024;
79425
80060
  TAIL_BYTES = 128 * 1024;
79426
80061
  HARNESS_ENVELOPES = [
@@ -81112,22 +81747,26 @@ __export(exports_session_stats, {
81112
81747
  readSessionStats: () => readSessionStats,
81113
81748
  computeSavings: () => computeSavings
81114
81749
  });
81115
- import { readFileSync as readFileSync28 } from "fs";
81750
+ import { readFileSync as readFileSync29 } from "fs";
81116
81751
  import { homedir as homedir35 } from "os";
81117
- import { join as join38 } from "path";
81752
+ import { join as join39 } from "path";
81118
81753
  function tokenFilePath(port) {
81119
- return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
81754
+ return process.env.CLAUDISH_TOKEN_FILE || join39(homedir35(), ".claudish", `tokens-${port}.json`);
81120
81755
  }
81121
- function readSessionStats(port) {
81756
+ function readSessionStats(port, opts) {
81122
81757
  let raw2;
81123
81758
  try {
81124
- raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
81759
+ raw2 = JSON.parse(readFileSync29(tokenFilePath(port), "utf-8"));
81125
81760
  } catch {
81126
81761
  return null;
81127
81762
  }
81128
81763
  if (!raw2 || typeof raw2 !== "object")
81129
81764
  return null;
81130
81765
  const d = raw2;
81766
+ const processStartMs = opts?.processStartMs ?? Date.now() - Math.round(process.uptime() * 1000);
81767
+ const trackerStartedAt = num(d.started_at);
81768
+ if (trackerStartedAt <= 0 || trackerStartedAt < processStartMs)
81769
+ return null;
81131
81770
  const inputTokens = num(d.input_tokens);
81132
81771
  const outputTokens = num(d.output_tokens);
81133
81772
  if (inputTokens <= 0 && outputTokens <= 0)
@@ -81457,8 +82096,8 @@ var init_session_summary = __esm(() => {
81457
82096
  init_op_source();
81458
82097
  init_startup_trace();
81459
82098
  var import_dotenv3 = __toESM(require_main(), 1);
81460
- import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
81461
- import { join as join39, resolve as resolve5 } from "path";
82099
+ import { existsSync as existsSync29, readFileSync as readFileSync30 } from "fs";
82100
+ import { join as join40, resolve as resolve5 } from "path";
81462
82101
  import_dotenv3.config({ quiet: true });
81463
82102
  function classifyStartupKind() {
81464
82103
  const argv = process.argv.slice(2);
@@ -81592,6 +82231,7 @@ var isConfigCommand = firstPositional === "config";
81592
82231
  var isServeCommand = firstPositional === "serve";
81593
82232
  var isProvidersCommand = firstPositional === "providers";
81594
82233
  var isBehaviorCommand = firstPositional === "behavior";
82234
+ var isTeamCommand = firstPositional === "team";
81595
82235
  var isLoginCommand = firstPositional === "login";
81596
82236
  var isLogoutCommand = firstPositional === "logout";
81597
82237
  var isQuotaCommand = firstPositional === "quota" || firstPositional === "usage";
@@ -81617,6 +82257,12 @@ if (isMcpMode) {
81617
82257
  console.error(`[claudish behavior] ${e instanceof Error ? e.message : String(e)}`);
81618
82258
  process.exit(1);
81619
82259
  }));
82260
+ } else if (isTeamCommand) {
82261
+ const teamArgIndex = args.indexOf("team");
82262
+ Promise.resolve().then(() => (init_team_cli(), exports_team_cli)).then((m) => m.teamCommand(args.slice(teamArgIndex + 1)).catch((e) => {
82263
+ console.error(`[claudish team] ${e instanceof Error ? e.message : String(e)}`);
82264
+ process.exit(1);
82265
+ }));
81620
82266
  } else if (isProvidersCommand) {
81621
82267
  const json2 = args.includes("--json");
81622
82268
  Promise.resolve().then(() => (init_providers_command(), exports_providers_command)).then((m) => m.providersCommand({ json: json2 }).catch((e) => {
@@ -81701,14 +82347,14 @@ async function runCli() {
81701
82347
  if (cliConfig.team && cliConfig.team.length > 0) {
81702
82348
  let prompt = cliConfig.claudeArgs.join(" ");
81703
82349
  if (cliConfig.inputFile) {
81704
- prompt = readFileSync29(cliConfig.inputFile, "utf-8");
82350
+ prompt = readFileSync30(cliConfig.inputFile, "utf-8");
81705
82351
  }
81706
82352
  if (!prompt.trim()) {
81707
82353
  console.error("Error: --team requires a prompt (positional args or -f <file>)");
81708
82354
  process.exit(1);
81709
82355
  }
81710
82356
  const mode = cliConfig.teamMode ?? "default";
81711
- const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
82357
+ const sessionPath = join40(process.cwd(), `.claudish-team-${Date.now()}`);
81712
82358
  if (mode === "json") {
81713
82359
  const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
81714
82360
  setupSession2(sessionPath, cliConfig.team, prompt);
@@ -81718,9 +82364,9 @@ async function runCli() {
81718
82364
  });
81719
82365
  const result = { ...status2, responses: {} };
81720
82366
  for (const anonId of Object.keys(status2.models)) {
81721
- const responsePath = join39(sessionPath, `response-${anonId}.md`);
82367
+ const responsePath = join40(sessionPath, `response-${anonId}.md`);
81722
82368
  try {
81723
- const raw2 = readFileSync29(responsePath, "utf-8").trim();
82369
+ const raw2 = readFileSync30(responsePath, "utf-8").trim();
81724
82370
  try {
81725
82371
  result.responses[anonId] = JSON.parse(raw2);
81726
82372
  } catch {