claudish 7.46.0 → 7.47.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 (2) hide show
  1. package/dist/index.js +3184 -209
  2. 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.46.0";
732
+ var VERSION = "7.47.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -29217,6 +29217,11 @@ function removeUriFormat(schema) {
29217
29217
  }
29218
29218
  return result;
29219
29219
  }
29220
+ function stripBillingHeader(text) {
29221
+ if (!text || !text.includes("x-anthropic-billing-header"))
29222
+ return text;
29223
+ return text.replace(BILLING_HEADER_RE, "");
29224
+ }
29220
29225
  function transformOpenAIToClaude(claudeRequestInput) {
29221
29226
  const req = JSON.parse(JSON.stringify(claudeRequestInput));
29222
29227
  const isO3Model = typeof req.model === "string" && (req.model.includes("o3") || req.model.includes("o1"));
@@ -29240,9 +29245,11 @@ function transformOpenAIToClaude(claudeRequestInput) {
29240
29245
  }
29241
29246
  }
29242
29247
  return JSON.stringify(item);
29243
- }).filter((text) => text && text.trim() !== "").join(`
29248
+ }).map((text) => stripBillingHeader(text)).filter((text) => text && text.trim() !== "").join(`
29244
29249
 
29245
29250
  `);
29251
+ } else if (typeof req.system === "string") {
29252
+ req.system = stripBillingHeader(req.system);
29246
29253
  }
29247
29254
  if (!Array.isArray(req.messages)) {
29248
29255
  if (req.messages == null)
@@ -29264,7 +29271,10 @@ function transformOpenAIToClaude(claudeRequestInput) {
29264
29271
  isO3Model
29265
29272
  };
29266
29273
  }
29267
- var init_transform = () => {};
29274
+ var BILLING_HEADER_RE;
29275
+ var init_transform = __esm(() => {
29276
+ BILLING_HEADER_RE = /x-anthropic-billing-header:[^\n]*\n?/gi;
29277
+ });
29268
29278
 
29269
29279
  // src/handlers/shared/format/openai-tools.ts
29270
29280
  function emptyParamsSchema() {
@@ -33137,6 +33147,13 @@ function createStreamingResponseHandler(c, response, adapter, target, middleware
33137
33147
  async start(controller) {
33138
33148
  const send = (e, d) => {
33139
33149
  if (!isClosed) {
33150
+ if (e === "content_block_start" && d?.content_block?.type === "tool_use") {
33151
+ try {
33152
+ behavior?.onToolCallObserved?.(String(d.content_block.name ?? ""));
33153
+ } catch (err) {
33154
+ log(`[Streaming] onToolCallObserved threw: ${err}`);
33155
+ }
33156
+ }
33140
33157
  controller.enqueue(encoder.encode(`event: ${e}
33141
33158
  data: ${JSON.stringify(d)}
33142
33159
 
@@ -33164,54 +33181,98 @@ data: ${JSON.stringify(d)}
33164
33181
  send("ping", { type: "ping" });
33165
33182
  }
33166
33183
  }, 1000);
33184
+ const teardown = () => {
33185
+ if (!isClosed) {
33186
+ try {
33187
+ controller.enqueue(encoder.encode(`data: [DONE]
33188
+
33189
+
33190
+ `));
33191
+ } catch {}
33192
+ try {
33193
+ controller.close();
33194
+ } catch {}
33195
+ isClosed = true;
33196
+ }
33197
+ if (ping) {
33198
+ clearInterval(ping);
33199
+ ping = null;
33200
+ }
33201
+ };
33167
33202
  const finalize = async (reason, err) => {
33168
- if (state.finalized)
33203
+ if (state.finalized) {
33204
+ teardown();
33169
33205
  return;
33206
+ }
33170
33207
  state.finalized = true;
33171
- if (state.accumulatedText.length > 0) {
33172
- const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
33173
- log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
33174
- }
33175
- const textToolCalls = extractToolCallsFromText(state.accumulatedText);
33176
- log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
33177
- if (textToolCalls.length > 0) {
33178
- log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
33208
+ try {
33209
+ if (state.accumulatedText.length > 0) {
33210
+ const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
33211
+ log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
33212
+ }
33213
+ const textToolCalls = extractToolCallsFromText(state.accumulatedText);
33214
+ log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
33215
+ if (textToolCalls.length > 0) {
33216
+ log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
33217
+ if (state.textStarted) {
33218
+ send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
33219
+ state.textStarted = false;
33220
+ }
33221
+ for (const tc of textToolCalls) {
33222
+ const toolIdx = state.curIdx++;
33223
+ const toolId = `tool_${Date.now()}_${toolIdx}`;
33224
+ send("content_block_start", {
33225
+ type: "content_block_start",
33226
+ index: toolIdx,
33227
+ content_block: { type: "tool_use", id: toolId, name: tc.name }
33228
+ });
33229
+ send("content_block_delta", {
33230
+ type: "content_block_delta",
33231
+ index: toolIdx,
33232
+ delta: {
33233
+ type: "input_json_delta",
33234
+ partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
33235
+ }
33236
+ });
33237
+ send("content_block_stop", { type: "content_block_stop", index: toolIdx });
33238
+ }
33239
+ }
33240
+ if (state.reasoningStarted) {
33241
+ send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
33242
+ }
33179
33243
  if (state.textStarted) {
33180
33244
  send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
33181
- state.textStarted = false;
33182
33245
  }
33183
- for (const tc of textToolCalls) {
33184
- const toolIdx = state.curIdx++;
33185
- const toolId = `tool_${Date.now()}_${toolIdx}`;
33186
- send("content_block_start", {
33187
- type: "content_block_start",
33188
- index: toolIdx,
33189
- content_block: { type: "tool_use", id: toolId, name: tc.name }
33190
- });
33191
- send("content_block_delta", {
33192
- type: "content_block_delta",
33193
- index: toolIdx,
33194
- delta: {
33195
- type: "input_json_delta",
33196
- partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
33197
- }
33198
- });
33199
- send("content_block_stop", { type: "content_block_stop", index: toolIdx });
33200
- }
33201
- }
33202
- if (state.reasoningStarted) {
33203
- send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
33204
- }
33205
- if (state.textStarted) {
33206
- send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
33207
- }
33208
- for (const t of Array.from(state.tools.values())) {
33209
- if (!t.closed && t.buffered && !t.started) {
33210
- if (toolSchemas && toolSchemas.length > 0) {
33211
- const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
33212
- if (validation.valid || validation.repaired && validation.repairedArgs) {
33213
- const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
33214
- log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
33246
+ for (const t of Array.from(state.tools.values())) {
33247
+ if (!t.closed && t.buffered && !t.started) {
33248
+ if (toolSchemas && toolSchemas.length > 0) {
33249
+ const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
33250
+ if (validation.valid || validation.repaired && validation.repairedArgs) {
33251
+ const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
33252
+ log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
33253
+ send("content_block_start", {
33254
+ type: "content_block_start",
33255
+ index: t.blockIndex,
33256
+ content_block: { type: "tool_use", id: t.id, name: t.name }
33257
+ });
33258
+ send("content_block_delta", {
33259
+ type: "content_block_delta",
33260
+ index: t.blockIndex,
33261
+ delta: { type: "input_json_delta", partial_json: argsJson }
33262
+ });
33263
+ send("content_block_stop", {
33264
+ type: "content_block_stop",
33265
+ index: t.blockIndex
33266
+ });
33267
+ t.started = true;
33268
+ t.closed = true;
33269
+ } else {
33270
+ log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
33271
+ t.closed = true;
33272
+ }
33273
+ } else {
33274
+ const argsJson = repairArgs(t.name, t.arguments || "{}");
33275
+ log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
33215
33276
  send("content_block_start", {
33216
33277
  type: "content_block_start",
33217
33278
  index: t.blockIndex,
@@ -33228,83 +33289,51 @@ data: ${JSON.stringify(d)}
33228
33289
  });
33229
33290
  t.started = true;
33230
33291
  t.closed = true;
33231
- } else {
33232
- log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
33233
- t.closed = true;
33234
33292
  }
33235
- } else {
33236
- const argsJson = repairArgs(t.name, t.arguments || "{}");
33237
- log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
33238
- send("content_block_start", {
33239
- type: "content_block_start",
33240
- index: t.blockIndex,
33241
- content_block: { type: "tool_use", id: t.id, name: t.name }
33242
- });
33243
- send("content_block_delta", {
33244
- type: "content_block_delta",
33245
- index: t.blockIndex,
33246
- delta: { type: "input_json_delta", partial_json: argsJson }
33247
- });
33248
- send("content_block_stop", {
33249
- type: "content_block_stop",
33250
- index: t.blockIndex
33251
- });
33252
- t.started = true;
33293
+ }
33294
+ }
33295
+ for (const t of Array.from(state.tools.values())) {
33296
+ if (t.started && !t.closed) {
33297
+ send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
33253
33298
  t.closed = true;
33254
33299
  }
33255
33300
  }
33256
- }
33257
- for (const t of Array.from(state.tools.values())) {
33258
- if (t.started && !t.closed) {
33259
- send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
33260
- t.closed = true;
33301
+ if (middlewareManager) {
33302
+ await middlewareManager.afterStreamComplete(target, streamMetadata);
33261
33303
  }
33262
- }
33263
- if (middlewareManager) {
33264
- await middlewareManager.afterStreamComplete(target, streamMetadata);
33265
- }
33266
- if (reason === "error") {
33267
- send("error", { type: "error", error: { type: "api_error", message: err } });
33268
- } else {
33269
- const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
33270
- const truncated = state.finishReason === "length";
33271
- const refused = state.finishReason === "content_filter";
33272
- const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
33273
- if (truncated || refused) {
33274
- log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
33304
+ if (reason === "error") {
33305
+ send("error", { type: "error", error: { type: "api_error", message: err } });
33306
+ } else {
33307
+ const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
33308
+ const truncated = state.finishReason === "length";
33309
+ const refused = state.finishReason === "content_filter";
33310
+ const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
33311
+ if (truncated || refused) {
33312
+ log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
33313
+ }
33314
+ send("message_delta", {
33315
+ type: "message_delta",
33316
+ delta: { stop_reason: stopReason, stop_sequence: null },
33317
+ usage: {
33318
+ ...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
33319
+ output_tokens: state.usage?.completion_tokens || 0
33320
+ }
33321
+ });
33322
+ behavior?.onTurnEnd?.();
33323
+ send("message_stop", { type: "message_stop" });
33275
33324
  }
33276
- send("message_delta", {
33277
- type: "message_delta",
33278
- delta: { stop_reason: stopReason, stop_sequence: null },
33279
- usage: {
33280
- ...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
33281
- output_tokens: state.usage?.completion_tokens || 0
33325
+ if (onTokenUpdate) {
33326
+ if (state.usage) {
33327
+ log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
33328
+ onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
33329
+ } else {
33330
+ const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
33331
+ log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
33332
+ onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
33282
33333
  }
33283
- });
33284
- behavior?.onTurnEnd?.();
33285
- send("message_stop", { type: "message_stop" });
33286
- }
33287
- if (onTokenUpdate) {
33288
- if (state.usage) {
33289
- log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
33290
- onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
33291
- } else {
33292
- const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
33293
- log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
33294
- onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
33295
33334
  }
33296
- }
33297
- if (!isClosed) {
33298
- try {
33299
- controller.enqueue(encoder.encode(`data: [DONE]
33300
-
33301
-
33302
- `));
33303
- } catch (e) {}
33304
- controller.close();
33305
- isClosed = true;
33306
- if (ping)
33307
- clearInterval(ping);
33335
+ } finally {
33336
+ teardown();
33308
33337
  }
33309
33338
  };
33310
33339
  try {
@@ -39184,62 +39213,70 @@ data: ${JSON.stringify(data)}
39184
39213
  send("ping", { type: "ping" });
39185
39214
  }
39186
39215
  }, 1000);
39216
+ const teardown = () => {
39217
+ if (!isClosed) {
39218
+ isClosed = true;
39219
+ try {
39220
+ controller.close();
39221
+ } catch {}
39222
+ }
39223
+ if (pingInterval) {
39224
+ clearInterval(pingInterval);
39225
+ pingInterval = null;
39226
+ }
39227
+ };
39187
39228
  const finalize = async (reason, err) => {
39188
- if (finalized2)
39229
+ if (finalized2) {
39230
+ teardown();
39189
39231
  return;
39190
- finalized2 = true;
39191
- if (thinkingStarted) {
39192
- send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
39193
39232
  }
39194
- if (textStarted) {
39195
- send("content_block_stop", { type: "content_block_stop", index: textIdx });
39196
- }
39197
- for (const t of toolCalls.values()) {
39198
- if (t.started && !t.closed) {
39199
- send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
39200
- t.closed = true;
39233
+ finalized2 = true;
39234
+ try {
39235
+ if (thinkingStarted) {
39236
+ send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
39201
39237
  }
39202
- }
39203
- if (opts.middlewareManager) {
39204
- await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
39205
- }
39206
- const inputTokens = usage?.promptTokenCount || 0;
39207
- const outputTokens = usage?.candidatesTokenCount || 0;
39208
- if (usage) {
39209
- log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
39210
- }
39211
- if (opts.onTokenUpdate) {
39212
- opts.onTokenUpdate(inputTokens, outputTokens);
39213
- }
39214
- if (reason === "error") {
39215
- log(`[GeminiSSE] Stream error: ${err}`);
39216
- send("error", { type: "error", error: { type: "api_error", message: err } });
39217
- } else {
39218
- const hasToolCalls = toolCalls.size > 0;
39219
- const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
39220
- if (truncated) {
39221
- log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
39238
+ if (textStarted) {
39239
+ send("content_block_stop", { type: "content_block_stop", index: textIdx });
39222
39240
  }
39223
- send("message_delta", {
39224
- type: "message_delta",
39225
- delta: { stop_reason: stopReason, stop_sequence: null },
39226
- usage: {
39227
- ...inputTokens > 0 ? { input_tokens: inputTokens } : {},
39228
- output_tokens: outputTokens
39241
+ for (const t of toolCalls.values()) {
39242
+ if (t.started && !t.closed) {
39243
+ send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
39244
+ t.closed = true;
39229
39245
  }
39230
- });
39231
- opts.onTurnEnd?.();
39232
- send("message_stop", { type: "message_stop" });
39233
- }
39234
- if (!isClosed) {
39235
- isClosed = true;
39236
- if (pingInterval) {
39237
- clearInterval(pingInterval);
39238
- pingInterval = null;
39239
39246
  }
39240
- try {
39241
- controller.close();
39242
- } catch {}
39247
+ if (opts.middlewareManager) {
39248
+ await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
39249
+ }
39250
+ const inputTokens = usage?.promptTokenCount || 0;
39251
+ const outputTokens = usage?.candidatesTokenCount || 0;
39252
+ if (usage) {
39253
+ log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
39254
+ }
39255
+ if (opts.onTokenUpdate) {
39256
+ opts.onTokenUpdate(inputTokens, outputTokens);
39257
+ }
39258
+ if (reason === "error") {
39259
+ log(`[GeminiSSE] Stream error: ${err}`);
39260
+ send("error", { type: "error", error: { type: "api_error", message: err } });
39261
+ } else {
39262
+ const hasToolCalls = toolCalls.size > 0;
39263
+ const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
39264
+ if (truncated) {
39265
+ log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
39266
+ }
39267
+ send("message_delta", {
39268
+ type: "message_delta",
39269
+ delta: { stop_reason: stopReason, stop_sequence: null },
39270
+ usage: {
39271
+ ...inputTokens > 0 ? { input_tokens: inputTokens } : {},
39272
+ output_tokens: outputTokens
39273
+ }
39274
+ });
39275
+ opts.onTurnEnd?.();
39276
+ send("message_stop", { type: "message_stop" });
39277
+ }
39278
+ } finally {
39279
+ teardown();
39243
39280
  }
39244
39281
  };
39245
39282
  try {
@@ -39457,38 +39494,48 @@ data: ${JSON.stringify(data)}
39457
39494
  send("ping", { type: "ping" });
39458
39495
  }
39459
39496
  }, 1000);
39460
- const finalize = (reason, err) => {
39461
- if (isClosed)
39462
- return;
39463
- if (textStarted) {
39464
- send("content_block_stop", { type: "content_block_stop", index: 0 });
39465
- }
39466
- if (reason === "error") {
39467
- send("error", { type: "error", error: { type: "api_error", message: err } });
39468
- } else {
39469
- send("message_delta", {
39470
- type: "message_delta",
39471
- delta: { stop_reason: "end_turn", stop_sequence: null },
39472
- usage: {
39473
- ...promptTokens > 0 ? { input_tokens: promptTokens } : {},
39474
- output_tokens: completionTokens
39475
- }
39476
- });
39477
- send("message_stop", { type: "message_stop" });
39478
- }
39479
- if (opts.onTokenUpdate) {
39480
- opts.onTokenUpdate(promptTokens, completionTokens);
39481
- }
39497
+ let finalized2 = false;
39498
+ const teardown = () => {
39482
39499
  if (!isClosed) {
39483
39500
  isClosed = true;
39484
- if (pingInterval) {
39485
- clearInterval(pingInterval);
39486
- pingInterval = null;
39487
- }
39488
39501
  try {
39489
39502
  controller.close();
39490
39503
  } catch {}
39491
39504
  }
39505
+ if (pingInterval) {
39506
+ clearInterval(pingInterval);
39507
+ pingInterval = null;
39508
+ }
39509
+ };
39510
+ const finalize = (reason, err) => {
39511
+ if (finalized2) {
39512
+ teardown();
39513
+ return;
39514
+ }
39515
+ finalized2 = true;
39516
+ try {
39517
+ if (textStarted) {
39518
+ send("content_block_stop", { type: "content_block_stop", index: 0 });
39519
+ }
39520
+ if (reason === "error") {
39521
+ send("error", { type: "error", error: { type: "api_error", message: err } });
39522
+ } else {
39523
+ send("message_delta", {
39524
+ type: "message_delta",
39525
+ delta: { stop_reason: "end_turn", stop_sequence: null },
39526
+ usage: {
39527
+ ...promptTokens > 0 ? { input_tokens: promptTokens } : {},
39528
+ output_tokens: completionTokens
39529
+ }
39530
+ });
39531
+ send("message_stop", { type: "message_stop" });
39532
+ }
39533
+ if (opts.onTokenUpdate) {
39534
+ opts.onTokenUpdate(promptTokens, completionTokens);
39535
+ }
39536
+ } finally {
39537
+ teardown();
39538
+ }
39492
39539
  };
39493
39540
  try {
39494
39541
  const reader = response.body.getReader();
@@ -39975,10 +40022,26 @@ class TokenTracker {
39975
40022
  modelNameOverride;
39976
40023
  planUsage;
39977
40024
  lastPlanSerialized = "";
40025
+ toolCallsByName = new Map;
40026
+ startedAt = Date.now();
40027
+ sessionBilledInputTokens = 0;
39978
40028
  constructor(port, config2) {
39979
40029
  this.port = port;
39980
40030
  this.config = config2;
39981
40031
  }
40032
+ recordToolUse(name) {
40033
+ const key = name.trim() || "unknown";
40034
+ this.toolCallsByName.set(key, (this.toolCallsByName.get(key) ?? 0) + 1);
40035
+ }
40036
+ getToolCallCount() {
40037
+ let n = 0;
40038
+ for (const v of this.toolCallsByName.values())
40039
+ n += v;
40040
+ return n;
40041
+ }
40042
+ getToolCalls() {
40043
+ return [...this.toolCallsByName].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
40044
+ }
39982
40045
  setActiveModelName(name) {
39983
40046
  this.modelNameOverride = name;
39984
40047
  }
@@ -40000,6 +40063,7 @@ class TokenTracker {
40000
40063
  this.sessionInputTokens = inputTokens;
40001
40064
  this.lastInputTokens = inputTokens;
40002
40065
  this.sessionOutputTokens += outputTokens;
40066
+ this.sessionBilledInputTokens += inputTokens;
40003
40067
  const pricing = this.getPricing();
40004
40068
  const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
40005
40069
  this.sessionTotalCost += cost;
@@ -40012,6 +40076,7 @@ class TokenTracker {
40012
40076
  const pricing = this.getPricing();
40013
40077
  const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M;
40014
40078
  this.sessionTotalCost = cost;
40079
+ this.sessionBilledInputTokens = this.sessionInputTokens;
40015
40080
  this.writeFile(this.sessionInputTokens, this.sessionOutputTokens, pricing.isEstimate);
40016
40081
  }
40017
40082
  updateWithDelta(inputTokens, outputTokens) {
@@ -40030,6 +40095,7 @@ class TokenTracker {
40030
40095
  }
40031
40096
  this.sessionOutputTokens += outputTokens;
40032
40097
  const pricing = this.getPricing();
40098
+ this.sessionBilledInputTokens += incrementalInputTokens;
40033
40099
  const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
40034
40100
  this.sessionTotalCost += cost;
40035
40101
  this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
@@ -40038,6 +40104,7 @@ class TokenTracker {
40038
40104
  this.sessionInputTokens = inputTokens;
40039
40105
  this.lastInputTokens = inputTokens;
40040
40106
  this.sessionOutputTokens += outputTokens;
40107
+ this.sessionBilledInputTokens += inputTokens;
40041
40108
  if (typeof actualCost === "number" && actualCost > 0) {
40042
40109
  this.sessionTotalCost += actualCost;
40043
40110
  log(`[TokenTracker] Actual cost from API: $${actualCost.toFixed(6)}`);
@@ -40055,6 +40122,7 @@ class TokenTracker {
40055
40122
  this.lastInputTokens = inputTokens;
40056
40123
  }
40057
40124
  this.sessionOutputTokens += outputTokens;
40125
+ this.sessionBilledInputTokens += inputTokens;
40058
40126
  this.writeFile(this.sessionInputTokens, this.sessionOutputTokens);
40059
40127
  }
40060
40128
  setContextWindow(contextWindow) {
@@ -40107,7 +40175,12 @@ class TokenTracker {
40107
40175
  provider_name: this.getDisplayName(),
40108
40176
  updated_at: Date.now(),
40109
40177
  is_free: isFreeModel,
40110
- is_estimated: isEstimate || false
40178
+ is_estimated: isEstimate || false,
40179
+ started_at: this.startedAt,
40180
+ tool_calls: this.getToolCalls(),
40181
+ billed_input_tokens: this.sessionBilledInputTokens,
40182
+ input_per_m: pricing.inputCostPer1M,
40183
+ output_per_m: pricing.outputCostPer1M
40111
40184
  };
40112
40185
  const displayModel = stripProviderPrefix(this.modelNameOverride || this.config.modelName || "");
40113
40186
  if (displayModel) {
@@ -40813,14 +40886,18 @@ class ComposedHandler {
40813
40886
  };
40814
40887
  const streamFormat = this.resolveStreamFormat();
40815
40888
  const priorInputTokens = this.tokenTracker.getLastInputTokens();
40889
+ const observeToolCall = (name) => {
40890
+ this.tokenTracker.recordToolUse(name);
40891
+ behaviorSession?.observeToolCall(name);
40892
+ };
40816
40893
  switch (streamFormat) {
40817
40894
  case "openai-sse":
40818
- return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, behaviorSession && {
40819
- shouldBufferTool: (name) => behaviorSession.interceptsTool(name),
40820
- onToolCall: (name, argsJson) => behaviorSession.repairToolCall(name, argsJson),
40821
- onAssistantText: (text, kind) => behaviorSession.observeText(text, kind),
40822
- onToolCallObserved: (name) => behaviorSession.observeToolCall(name),
40823
- onTurnEnd: () => behaviorSession.finishTurn()
40895
+ return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, {
40896
+ shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40897
+ onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40898
+ onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40899
+ onToolCallObserved: observeToolCall,
40900
+ onTurnEnd: () => behaviorSession?.finishTurn()
40824
40901
  });
40825
40902
  case "openai-responses-sse":
40826
40903
  return createResponsesStreamHandler(c, response, {
@@ -40834,7 +40911,7 @@ class ComposedHandler {
40834
40911
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40835
40912
  onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40836
40913
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40837
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40914
+ onToolCallObserved: observeToolCall,
40838
40915
  onTurnEnd: () => behaviorSession?.finishTurn()
40839
40916
  });
40840
40917
  case "anthropic-sse":
@@ -40845,7 +40922,7 @@ class ComposedHandler {
40845
40922
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40846
40923
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40847
40924
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40848
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40925
+ onToolCallObserved: observeToolCall,
40849
40926
  onTurnEnd: () => behaviorSession?.finishTurn()
40850
40927
  });
40851
40928
  case "gemini-sse": {
@@ -40862,7 +40939,7 @@ class ComposedHandler {
40862
40939
  onToolCall,
40863
40940
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40864
40941
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40865
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40942
+ onToolCallObserved: observeToolCall,
40866
40943
  onTurnEnd: () => behaviorSession?.finishTurn(),
40867
40944
  unwrapResponse: this.options.unwrapGeminiResponse,
40868
40945
  priorInputTokens
@@ -40882,7 +40959,7 @@ class ComposedHandler {
40882
40959
  repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
40883
40960
  shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
40884
40961
  onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
40885
- onToolCallObserved: (name) => behaviorSession?.observeToolCall(name),
40962
+ onToolCallObserved: observeToolCall,
40886
40963
  onTurnEnd: () => behaviorSession?.finishTurn()
40887
40964
  });
40888
40965
  case "ollama-jsonl":
@@ -42891,6 +42968,9 @@ async function routeBare(model, nativeProvider, rules, defaultProvider, cachePat
42891
42968
  const [primary, ...fallbacks] = credentialed;
42892
42969
  return { kind: "ok", primary, fallbacks };
42893
42970
  }
42971
+ function normalizeGlmSlug(model) {
42972
+ return model.replace(/^glm-(\d+)-(\d+)(-.*)?$/i, (_m, major, minor, suffix) => `glm-${major}.${minor}${suffix ?? ""}`);
42973
+ }
42894
42974
  async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
42895
42975
  const parsed = parseModelSpec(modelSpec);
42896
42976
  if (parsed.isExplicitProvider) {
@@ -42898,7 +42978,7 @@ async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePat
42898
42978
  }
42899
42979
  const rules = rulesOverride ?? loadRoutingRules();
42900
42980
  const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
42901
- return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
42981
+ return routeBare(normalizeGlmSlug(parsed.model), parsed.provider, rules, defaultProvider, cachePath);
42902
42982
  }
42903
42983
  var init_routing_rules = __esm(() => {
42904
42984
  init_model_catalog();
@@ -68509,6 +68589,8 @@ Usage: claudish --models --provider <slug>`);
68509
68589
  if (rest.length > 0)
68510
68590
  config3._hasPositionalPrompt = true;
68511
68591
  break;
68592
+ } else if (arg === "--resume" && (i + 1 >= args.length || args[i + 1].startsWith("-"))) {
68593
+ config3._resumePicker = true;
68512
68594
  } else if (arg.startsWith("-")) {
68513
68595
  config3.claudeArgs.push(arg);
68514
68596
  if (arg === "-p" || arg === "--print") {
@@ -78411,12 +78493,2864 @@ var init_team_grid = __esm(() => {
78411
78493
  ];
78412
78494
  });
78413
78495
 
78496
+ // src/tui/viz/text.ts
78497
+ function columns(n, fn, arg = "width") {
78498
+ if (!Number.isFinite(n))
78499
+ throw new RangeError(`${fn}: ${arg} must be a finite number, got ${n}`);
78500
+ return Math.max(0, Math.floor(n));
78501
+ }
78502
+ function isWide2(cp) {
78503
+ let lo = 0;
78504
+ let hi = WIDE.length - 1;
78505
+ while (lo <= hi) {
78506
+ const mid = lo + hi >> 1;
78507
+ const [a, b] = WIDE[mid];
78508
+ if (cp < a)
78509
+ hi = mid - 1;
78510
+ else if (cp > b)
78511
+ lo = mid + 1;
78512
+ else
78513
+ return true;
78514
+ }
78515
+ return false;
78516
+ }
78517
+ function fallbackClusterWidth(cluster) {
78518
+ const cp = cluster.codePointAt(0) ?? 0;
78519
+ if (cp < 32 || cp >= 127 && cp <= 159 || LEADING_FORMAT.test(cluster))
78520
+ return 0;
78521
+ return isWide2(cp) || LEADING_EMOJI_PRESENTATION.test(cluster) || cluster.includes(VS16) ? 2 : 1;
78522
+ }
78523
+ function displayWidth(s) {
78524
+ if (NATIVE_WIDTH)
78525
+ return NATIVE_WIDTH(s);
78526
+ let w = 0;
78527
+ for (const { segment } of seg.segment(s))
78528
+ w += fallbackClusterWidth(segment);
78529
+ return w;
78530
+ }
78531
+ function truncate3(s, width) {
78532
+ const n = columns(width, "truncate");
78533
+ if (n === 0)
78534
+ return "";
78535
+ const clean = sanitize2(s);
78536
+ if (displayWidth(clean) <= n)
78537
+ return clean;
78538
+ let out = "";
78539
+ let w = 0;
78540
+ for (const { segment } of seg.segment(clean)) {
78541
+ const cw = displayWidth(segment);
78542
+ if (w + cw > n - 1)
78543
+ break;
78544
+ out += segment;
78545
+ w += cw;
78546
+ }
78547
+ return `${out}\u2026`;
78548
+ }
78549
+ function padTo(s, width) {
78550
+ const n = columns(width, "padTo");
78551
+ if (n === 0)
78552
+ return "";
78553
+ const clean = sanitize2(s);
78554
+ const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
78555
+ return clipped + " ".repeat(Math.max(0, n - displayWidth(clipped)));
78556
+ }
78557
+ function padStartTo(s, width) {
78558
+ const n = columns(width, "padStartTo");
78559
+ if (n === 0)
78560
+ return "";
78561
+ const clean = sanitize2(s);
78562
+ const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
78563
+ return " ".repeat(Math.max(0, n - displayWidth(clipped))) + clipped;
78564
+ }
78565
+ function splitCells(parts, cells) {
78566
+ const n = columns(cells, "splitCells", "cells");
78567
+ const finite = parts.map((p) => Number.isFinite(p) && p > 0 ? p : 0);
78568
+ const out = finite.map(() => 0);
78569
+ let safe = finite;
78570
+ let sum = finite.reduce((a, b) => a + b, 0);
78571
+ if (!Number.isFinite(sum)) {
78572
+ const max = finite.reduce((a, b) => b > a ? b : a, 0);
78573
+ safe = finite.map((p) => p / max);
78574
+ sum = safe.reduce((a, b) => a + b, 0);
78575
+ }
78576
+ if (n <= 0 || sum <= 0)
78577
+ return out;
78578
+ const byShare = safe.map((p, i) => ({ p, i })).sort((a, b) => b.p - a.p);
78579
+ if (n < byShare.filter(({ p }) => p > 0).length) {
78580
+ for (const { i } of byShare.slice(0, n))
78581
+ out[i] = 1;
78582
+ return out;
78583
+ }
78584
+ const exact = safe.map((p) => n * (p / sum));
78585
+ exact.forEach((e, i) => {
78586
+ out[i] = Math.floor(e);
78587
+ });
78588
+ const rank = exact.map((e, i) => ({ rem: e % 1, i })).sort((a, b) => b.rem - a.rem);
78589
+ for (let k = 0, left = n - out.reduce((a, b) => a + b, 0);left > 0; k++, left--)
78590
+ out[rank[k % rank.length].i] += 1;
78591
+ for (const { p, i } of byShare)
78592
+ if (p > 0 && out[i] === 0) {
78593
+ const d = byShare.reduce((m, c) => out[c.i] > out[m] ? c.i : m, 0);
78594
+ if (out[d] > 1) {
78595
+ out[d] -= 1;
78596
+ out[i] = 1;
78597
+ }
78598
+ }
78599
+ return out;
78600
+ }
78601
+ var NATIVE_WIDTH, WIDE, seg, VS16 = "\uFE0F", LEADING_FORMAT, LEADING_EMOJI_PRESENTATION, CONTROL, sanitize2 = (s) => s.replace(CONTROL, " ");
78602
+ var init_text = __esm(() => {
78603
+ NATIVE_WIDTH = (() => {
78604
+ const b = globalThis.Bun;
78605
+ return typeof b?.stringWidth === "function" ? b.stringWidth.bind(b) : null;
78606
+ })();
78607
+ WIDE = [
78608
+ [4352, 4447],
78609
+ [9001, 9002],
78610
+ [11904, 12350],
78611
+ [12353, 12771],
78612
+ [12774, 12871],
78613
+ [12880, 13311],
78614
+ [13312, 19903],
78615
+ [19968, 40959],
78616
+ [40960, 42191],
78617
+ [43360, 43391],
78618
+ [44032, 55203],
78619
+ [63744, 64255],
78620
+ [65040, 65049],
78621
+ [65072, 65135],
78622
+ [65280, 65376],
78623
+ [65504, 65510],
78624
+ [94176, 94180],
78625
+ [94192, 94193],
78626
+ [94208, 100343],
78627
+ [100352, 101589],
78628
+ [101632, 101640],
78629
+ [110576, 110579],
78630
+ [110581, 110587],
78631
+ [110589, 110590],
78632
+ [110592, 110882],
78633
+ [110898, 110898],
78634
+ [110928, 110930],
78635
+ [110933, 110933],
78636
+ [110948, 110951],
78637
+ [110960, 111355],
78638
+ [127488, 127490],
78639
+ [127504, 127547],
78640
+ [127552, 127560],
78641
+ [127568, 127569],
78642
+ [127584, 127589],
78643
+ [131072, 196605],
78644
+ [196608, 262141]
78645
+ ];
78646
+ seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
78647
+ LEADING_FORMAT = /^\p{Cf}/u;
78648
+ LEADING_EMOJI_PRESENTATION = /^\p{Emoji_Presentation}/u;
78649
+ CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
78650
+ });
78651
+
78652
+ // src/tui/viz/tokens.ts
78653
+ var tokens, ramps;
78654
+ var init_tokens = __esm(() => {
78655
+ init_theme2();
78656
+ tokens = {
78657
+ fatal: C.red,
78658
+ error: C.red,
78659
+ warn: C.orange,
78660
+ info: C.cyan,
78661
+ debug: C.fgMuted,
78662
+ trace: C.dim,
78663
+ success: C.green,
78664
+ running: C.blue,
78665
+ idle: C.fgMuted,
78666
+ dead: C.dim,
78667
+ border: C.border,
78668
+ subtle: C.dim,
78669
+ text: C.fg,
78670
+ accent: C.focusBorder,
78671
+ bgPanel: C.bgAlt,
78672
+ ink: C.black
78673
+ };
78674
+ ramps = {
78675
+ load: [tokens.success, C.yellow, tokens.error],
78676
+ temperature: [tokens.running, tokens.success, C.orange, tokens.error],
78677
+ network: [tokens.success, C.yellow, tokens.error],
78678
+ savings: [tokens.error, C.yellow, tokens.success],
78679
+ volume: [C.border, C.blue, C.cyan]
78680
+ };
78681
+ });
78682
+
78683
+ // src/tui/viz/color.ts
78684
+ import { RGBA, parseColor, rgbToHex } from "@opentui/core";
78685
+ function mix(a, b, t) {
78686
+ const k = clamp01(t);
78687
+ return hex3(RGBA.fromValues(a.r + (b.r - a.r) * k, a.g + (b.g - a.g) * k, a.b + (b.b - a.b) * k, a.a + (b.a - a.a) * k));
78688
+ }
78689
+ function blend1D(steps, from, to) {
78690
+ if (!Number.isInteger(steps) || steps <= 0)
78691
+ return [];
78692
+ const a = rgba(from);
78693
+ if (steps === 1)
78694
+ return [hex3(a)];
78695
+ const b = rgba(to);
78696
+ return Array.from({ length: steps }, (_, i) => mix(a, b, i / (steps - 1)));
78697
+ }
78698
+ function blendStops(steps, ...stops) {
78699
+ if (!Number.isInteger(steps) || steps <= 0 || stops.length === 0)
78700
+ return [];
78701
+ if (stops.length === 1 || steps === 1)
78702
+ return blend1D(steps, stops[0], stops.at(-1));
78703
+ const pts = stops.map(rgba);
78704
+ const segs = pts.length - 1;
78705
+ return Array.from({ length: steps }, (_, i) => {
78706
+ const p = i / (steps - 1) * segs;
78707
+ const s = Math.min(segs - 1, Math.floor(p));
78708
+ return mix(pts[s], pts[s + 1], p - s);
78709
+ });
78710
+ }
78711
+ function luminance(c) {
78712
+ const f = (v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
78713
+ return 0.2126 * f(clamp01(c.r)) + 0.7152 * f(clamp01(c.g)) + 0.0722 * f(clamp01(c.b));
78714
+ }
78715
+ function contrastRatio(a, b) {
78716
+ const [x, y] = [luminance(a), luminance(b)];
78717
+ return x >= y ? (x + 0.05) / (y + 0.05) : (y + 0.05) / (x + 0.05);
78718
+ }
78719
+ function over(fg, bg) {
78720
+ const a = clamp01(fg.a);
78721
+ if (a >= 1)
78722
+ return fg;
78723
+ return RGBA.fromValues(fg.r * a + bg.r * (1 - a), fg.g * a + bg.g * (1 - a), fg.b * a + bg.b * (1 - a), 1);
78724
+ }
78725
+ function pickInk(bg, dark = tokens.ink, light = tokens.text) {
78726
+ const surface = over(rgba(bg), rgba(tokens.bgPanel));
78727
+ const inkDark = over(rgba(dark), surface);
78728
+ const inkLight = over(rgba(light), surface);
78729
+ return hex3(contrastRatio(inkDark, surface) >= contrastRatio(inkLight, surface) ? inkDark : inkLight);
78730
+ }
78731
+ var rgba = (c) => parseColor(c), hex3 = (c) => rgbToHex(c), clamp01 = (n) => Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
78732
+ var init_color = __esm(() => {
78733
+ init_tokens();
78734
+ });
78735
+
78736
+ // src/tui/viz/widgets.tsx
78737
+ import { createTextAttributes as createTextAttributes2 } from "@opentui/core";
78738
+ import { jsxDEV as jsxDEV18, Fragment as Fragment11 } from "@opentui/react/jsx-dev-runtime";
78739
+ function rampFor(width, stops) {
78740
+ const use = stops.length > 0 ? stops : ramps.load;
78741
+ const key = `${width}|${use.join(":")}`;
78742
+ const hit = RAMP_CACHE.get(key);
78743
+ if (hit)
78744
+ return hit;
78745
+ const built = blendStops(width, ...use);
78746
+ if (RAMP_CACHE.size >= RAMP_CACHE_MAX)
78747
+ RAMP_CACHE.delete(RAMP_CACHE.keys().next().value);
78748
+ RAMP_CACHE.set(key, built);
78749
+ return built;
78750
+ }
78751
+ function fillCells(pct, width) {
78752
+ const cells = Math.floor(width);
78753
+ if (!Number.isFinite(cells) || cells <= 0 || Number.isNaN(pct))
78754
+ return 0;
78755
+ return Math.round(Math.min(100, Math.max(0, pct)) / 100 * cells);
78756
+ }
78757
+ function MeterSpan({
78758
+ pct,
78759
+ width,
78760
+ ramp = ramps.load
78761
+ }) {
78762
+ const cells = Math.floor(width);
78763
+ if (!Number.isFinite(cells) || cells <= 0)
78764
+ return null;
78765
+ if (Number.isNaN(pct))
78766
+ return /* @__PURE__ */ jsxDEV18("span", {
78767
+ fg: tokens.dead,
78768
+ children: NODATA.repeat(cells)
78769
+ }, undefined, false, undefined, this);
78770
+ const cols = rampFor(cells, ramp);
78771
+ const filled = fillCells(pct, cells);
78772
+ return /* @__PURE__ */ jsxDEV18(Fragment11, {
78773
+ children: Array.from({ length: cells }, (_, i) => /* @__PURE__ */ jsxDEV18("span", {
78774
+ fg: i < filled ? cols[i] : tokens.border,
78775
+ children: i < filled ? FILL : TRACK
78776
+ }, i, false, undefined, this))
78777
+ }, undefined, false, undefined, this);
78778
+ }
78779
+ function Sparkline({
78780
+ values,
78781
+ fg = tokens.info,
78782
+ style,
78783
+ ...layout
78784
+ }) {
78785
+ const row = sparkGlyphs(values);
78786
+ if (row === null)
78787
+ return null;
78788
+ return /* @__PURE__ */ jsxDEV18("text", {
78789
+ fg,
78790
+ flexShrink: 0,
78791
+ ...layout,
78792
+ style,
78793
+ children: row
78794
+ }, undefined, false, undefined, this);
78795
+ }
78796
+ function sparkGlyphs(values) {
78797
+ if (values.length === 0)
78798
+ return null;
78799
+ let max = Number.NEGATIVE_INFINITY;
78800
+ let min = Number.POSITIVE_INFINITY;
78801
+ for (const v of values)
78802
+ if (Number.isFinite(v)) {
78803
+ max = Math.max(max, v);
78804
+ min = Math.min(min, v);
78805
+ }
78806
+ if (max === Number.NEGATIVE_INFINITY)
78807
+ return GAP.repeat(values.length);
78808
+ const half = max / 2 - min / 2;
78809
+ const mid = SPARK[Math.floor((SPARK.length - 1) / 2)];
78810
+ const top = SPARK.length - 1;
78811
+ const glyph = (v) => SPARK[Math.min(top, Math.max(0, Math.round((v / 2 - min / 2) / half * top)))];
78812
+ return values.map((v) => !Number.isFinite(v) ? GAP : half > 0 ? glyph(v) : mid).join("");
78813
+ }
78814
+ function SparklineSpan({
78815
+ values,
78816
+ fg = tokens.info
78817
+ }) {
78818
+ const row = sparkGlyphs(values);
78819
+ return row === null ? null : /* @__PURE__ */ jsxDEV18("span", {
78820
+ fg,
78821
+ children: row
78822
+ }, undefined, false, undefined, this);
78823
+ }
78824
+ function badgePad(label, width) {
78825
+ const pad2 = Math.max(0, (width ?? 0) - displayWidth(label) - 2);
78826
+ return pad2 > 0 ? /* @__PURE__ */ jsxDEV18("span", {
78827
+ children: " ".repeat(pad2)
78828
+ }, undefined, false, undefined, this) : null;
78829
+ }
78830
+ function BadgeSpan({ label, bg, width }) {
78831
+ return /* @__PURE__ */ jsxDEV18(Fragment11, {
78832
+ children: [
78833
+ /* @__PURE__ */ jsxDEV18("span", {
78834
+ fg: pickInk(bg),
78835
+ bg,
78836
+ attributes: BOLD4,
78837
+ children: ` ${label} `
78838
+ }, undefined, false, undefined, this),
78839
+ badgePad(label, width)
78840
+ ]
78841
+ }, undefined, true, undefined, this);
78842
+ }
78843
+ function Panel({
78844
+ title,
78845
+ focused = false,
78846
+ flush = false,
78847
+ children,
78848
+ style,
78849
+ ...layout
78850
+ }) {
78851
+ return /* @__PURE__ */ jsxDEV18("box", {
78852
+ border: true,
78853
+ borderStyle: "rounded",
78854
+ borderColor: focused ? tokens.accent : tokens.border,
78855
+ backgroundColor: tokens.bgPanel,
78856
+ title,
78857
+ titleAlignment: "left",
78858
+ flexDirection: "column",
78859
+ overflow: "hidden",
78860
+ paddingLeft: flush ? 0 : 1,
78861
+ paddingRight: flush ? 0 : 1,
78862
+ ...layout,
78863
+ style,
78864
+ children
78865
+ }, undefined, false, undefined, this);
78866
+ }
78867
+ var BOLD4, FILL = "\u2588", TRACK = "\u2591", SPARK, GAP = " ", NODATA = "\u254C", RAMP_CACHE, RAMP_CACHE_MAX = 64;
78868
+ var init_widgets = __esm(() => {
78869
+ init_color();
78870
+ init_text();
78871
+ init_tokens();
78872
+ BOLD4 = createTextAttributes2({ bold: true });
78873
+ SPARK = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
78874
+ RAMP_CACHE = new Map;
78875
+ });
78876
+
78877
+ // src/session/session-discovery.ts
78878
+ var exports_session_discovery = {};
78879
+ __export(exports_session_discovery, {
78880
+ slugForPath: () => slugForPath,
78881
+ sessionLabel: () => sessionLabel,
78882
+ mainConversationTurn: () => mainConversationTurn,
78883
+ isHarnessNoise: () => isHarnessNoise,
78884
+ isAgentSession: () => isAgentSession,
78885
+ isActive: () => isActive,
78886
+ hydrateSession: () => hydrateSession,
78887
+ hydrateConversation: () => hydrateConversation,
78888
+ getRepoContext: () => getRepoContext,
78889
+ findLatestSessionId: () => findLatestSessionId,
78890
+ enrichWorktreeGit: () => enrichWorktreeGit,
78891
+ discoverWorktreeGroups: () => discoverWorktreeGroups,
78892
+ PROJECTS_DIR: () => PROJECTS_DIR,
78893
+ ACTIVE_WINDOW_MS: () => ACTIVE_WINDOW_MS
78894
+ });
78895
+ import { execFile, execFileSync as execFileSync2 } from "child_process";
78896
+ import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
78897
+ import { homedir as homedir34 } from "os";
78898
+ import { basename, join as join37 } from "path";
78899
+ function slugForPath(absPath) {
78900
+ return absPath.replace(/[/.]/g, "-");
78901
+ }
78902
+ function isAgentSession(row) {
78903
+ return row.entrypoint !== undefined && row.entrypoint !== "cli";
78904
+ }
78905
+ function getRepoContext(cwd = process.cwd()) {
78906
+ const git = (args) => {
78907
+ try {
78908
+ return execFileSync2("git", args, {
78909
+ cwd,
78910
+ encoding: "utf-8",
78911
+ stdio: ["ignore", "pipe", "ignore"]
78912
+ }).trim();
78913
+ } catch {
78914
+ return null;
78915
+ }
78916
+ };
78917
+ const current = git(["rev-parse", "--show-toplevel"]);
78918
+ const commonDir = git(["rev-parse", "--git-common-dir"]);
78919
+ if (!current || !commonDir)
78920
+ return null;
78921
+ const root = commonDir.endsWith("/.git") ? commonDir.slice(0, -"/.git".length) : current;
78922
+ const liveWorktrees = [];
78923
+ const branchByPath = new Map;
78924
+ const porcelain = git(["worktree", "list", "--porcelain"]);
78925
+ if (porcelain) {
78926
+ let currentPath = null;
78927
+ for (const line of porcelain.split(`
78928
+ `)) {
78929
+ if (line.startsWith("worktree ")) {
78930
+ currentPath = line.slice("worktree ".length);
78931
+ liveWorktrees.push(currentPath);
78932
+ } else if (line.startsWith("branch ") && currentPath) {
78933
+ branchByPath.set(currentPath, line.slice("branch ".length).replace(/^refs\/heads\//, ""));
78934
+ }
78935
+ }
78936
+ }
78937
+ return { root, current, liveWorktrees, branchByPath };
78938
+ }
78939
+ function projectDirs() {
78940
+ try {
78941
+ return readdirSync7(PROJECTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
78942
+ } catch {
78943
+ return [];
78944
+ }
78945
+ }
78946
+ function sessionsIn(dirName) {
78947
+ const dir = join37(PROJECTS_DIR, dirName);
78948
+ let names;
78949
+ try {
78950
+ names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
78951
+ } catch {
78952
+ return [];
78953
+ }
78954
+ const rows = [];
78955
+ for (const n of names) {
78956
+ const file2 = join37(dir, n);
78957
+ try {
78958
+ const st = statSync6(file2);
78959
+ if (st.size === 0)
78960
+ continue;
78961
+ const row = {
78962
+ id: basename(n, ".jsonl"),
78963
+ file: file2,
78964
+ mtimeMs: st.mtimeMs,
78965
+ sizeBytes: st.size
78966
+ };
78967
+ const head = readChunk(file2, 0, Math.min(ENTRYPOINT_BYTES, st.size));
78968
+ const m = /"entrypoint":"([a-z-]+)"/.exec(head);
78969
+ if (m)
78970
+ row.entrypoint = m[1];
78971
+ rows.push(row);
78972
+ } catch {}
78973
+ }
78974
+ return rows;
78975
+ }
78976
+ async function enrichWorktreeGit(groups, repoRoot) {
78977
+ const run = (cwd, args) => new Promise((resolve5) => {
78978
+ execFile("git", args, { cwd, encoding: "utf-8" }, (err, stdout) => resolve5(err ? "" : stdout));
78979
+ });
78980
+ const trackByBranch = new Map;
78981
+ const refs = await run(repoRoot, [
78982
+ "for-each-ref",
78983
+ "--format=%(refname:short)\t%(upstream:track)",
78984
+ "refs/heads/"
78985
+ ]);
78986
+ for (const line of refs.split(`
78987
+ `)) {
78988
+ const [name, track] = line.split("\t");
78989
+ if (!name || !track)
78990
+ continue;
78991
+ const ahead = /ahead (\d+)/.exec(track)?.[1];
78992
+ const behind = /behind (\d+)/.exec(track)?.[1];
78993
+ if (ahead || behind) {
78994
+ trackByBranch.set(name, {
78995
+ ...ahead ? { ahead: Number(ahead) } : {},
78996
+ ...behind ? { behind: Number(behind) } : {}
78997
+ });
78998
+ }
78999
+ }
79000
+ await Promise.all(groups.map(async (g) => {
79001
+ if (g.branch)
79002
+ Object.assign(g, trackByBranch.get(g.branch) ?? {});
79003
+ if (!g.path || !g.live)
79004
+ return;
79005
+ const out = await run(g.path, ["status", "--porcelain"]);
79006
+ g.dirty = out.split(`
79007
+ `).filter((l) => l.trim().length > 0).length;
79008
+ }));
79009
+ }
79010
+ function isUnder(p, root) {
79011
+ return p === root || p.startsWith(`${root}/`);
79012
+ }
79013
+ function readProjectCwd(dirName) {
79014
+ const rows = sessionsIn(dirName);
79015
+ if (rows.length === 0)
79016
+ return null;
79017
+ rows.sort((a, b) => b.mtimeMs - a.mtimeMs);
79018
+ for (const row of rows.slice(0, 2)) {
79019
+ for (const r of parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false)) {
79020
+ if (typeof r.cwd === "string" && r.cwd)
79021
+ return r.cwd;
79022
+ }
79023
+ }
79024
+ return null;
79025
+ }
79026
+ function isActive(row, now = Date.now()) {
79027
+ return now - row.mtimeMs < ACTIVE_WINDOW_MS;
79028
+ }
79029
+ function discoverWorktreeGroups(repo) {
79030
+ const rootSlug = slugForPath(repo.root);
79031
+ const mine = projectDirs();
79032
+ const known = [...repo.liveWorktrees].map((p) => ({ path: p, slug: slugForPath(p) })).sort((a, b) => b.slug.length - a.slug.length);
79033
+ const groups = new Map;
79034
+ const groupCwd = new Map;
79035
+ const upsert = (name, path2, live) => {
79036
+ let g = groups.get(name);
79037
+ if (!g) {
79038
+ g = {
79039
+ name,
79040
+ path: path2,
79041
+ live,
79042
+ current: path2 !== null && path2 === repo.current,
79043
+ sessions: [],
79044
+ lastActiveMs: 0,
79045
+ activeNow: false,
79046
+ ...path2 ? { branch: repo.branchByPath.get(path2) } : {}
79047
+ };
79048
+ groups.set(name, g);
79049
+ }
79050
+ return g;
79051
+ };
79052
+ const WORKTREE_MARK = "--claude-worktrees-";
79053
+ const worktreeMatches = known.filter((k) => k.path !== repo.root);
79054
+ for (const dir of mine) {
79055
+ const at = dir.indexOf(WORKTREE_MARK);
79056
+ let name;
79057
+ let path2;
79058
+ let live;
79059
+ const hit = worktreeMatches.find((k) => dir === k.slug || dir.startsWith(`${k.slug}-`));
79060
+ if (hit) {
79061
+ path2 = hit.path;
79062
+ live = true;
79063
+ name = hit.path === repo.root ? "(root)" : basename(hit.path);
79064
+ } else if (at !== -1 && dir.startsWith(rootSlug)) {
79065
+ path2 = null;
79066
+ live = false;
79067
+ name = dir.slice(at + WORKTREE_MARK.length);
79068
+ } else if (dir === rootSlug) {
79069
+ name = "(root)";
79070
+ path2 = repo.root;
79071
+ live = true;
79072
+ } else if (dir.startsWith(`${rootSlug}-`) && at === -1) {
79073
+ const cwd = readProjectCwd(dir);
79074
+ if (!cwd || !isUnder(cwd, repo.root))
79075
+ continue;
79076
+ name = "(root)";
79077
+ path2 = repo.root;
79078
+ live = true;
79079
+ } else {
79080
+ continue;
79081
+ }
79082
+ const g = upsert(name, path2, live);
79083
+ if (path2)
79084
+ groupCwd.set(name, path2);
79085
+ else if (!groupCwd.has(name)) {
79086
+ const cwd = readProjectCwd(dir);
79087
+ if (cwd)
79088
+ groupCwd.set(name, cwd);
79089
+ }
79090
+ for (const s of sessionsIn(dir)) {
79091
+ g.sessions.push(s);
79092
+ if (s.mtimeMs > g.lastActiveMs)
79093
+ g.lastActiveMs = s.mtimeMs;
79094
+ }
79095
+ }
79096
+ const names = [...groups.keys()].sort((a, b) => b.length - a.length);
79097
+ for (const name of [...groups.keys()]) {
79098
+ const g = groups.get(name);
79099
+ if (!g || g.live)
79100
+ continue;
79101
+ const parent = names.find((n) => n !== name && name.startsWith(`${n}-`) && groups.has(n));
79102
+ if (!parent)
79103
+ continue;
79104
+ const into = groups.get(parent);
79105
+ const childCwd = groupCwd.get(name);
79106
+ const parentCwd = groupCwd.get(parent);
79107
+ if (!childCwd || !parentCwd || !isUnder(childCwd, parentCwd))
79108
+ continue;
79109
+ into.sessions.push(...g.sessions);
79110
+ into.lastActiveMs = Math.max(into.lastActiveMs, g.lastActiveMs);
79111
+ groups.delete(name);
79112
+ }
79113
+ for (const g of groups.values()) {
79114
+ g.sessions.sort((a, b) => b.mtimeMs - a.mtimeMs);
79115
+ g.activeNow = g.sessions.some((s) => isActive(s));
79116
+ if (g.path) {
79117
+ try {
79118
+ g.createdMs = statSync6(g.path).birthtimeMs;
79119
+ } catch {}
79120
+ }
79121
+ if (!g.createdMs && g.sessions.length > 0) {
79122
+ g.createdMs = g.sessions.reduce((m, s) => Math.min(m, s.mtimeMs), Number.POSITIVE_INFINITY);
79123
+ }
79124
+ }
79125
+ return [...groups.values()].filter((g) => g.sessions.length > 0).sort((a, b) => {
79126
+ if (a.current !== b.current)
79127
+ return a.current ? -1 : 1;
79128
+ return b.lastActiveMs - a.lastActiveMs;
79129
+ });
79130
+ }
79131
+ function readChunk(file2, pos, len) {
79132
+ if (len <= 0)
79133
+ return "";
79134
+ let fd = null;
79135
+ try {
79136
+ fd = openSync5(file2, "r");
79137
+ const buf = Buffer.allocUnsafe(len);
79138
+ const n = readSync(fd, buf, 0, len, pos);
79139
+ return buf.subarray(0, n).toString("utf-8");
79140
+ } catch {
79141
+ return "";
79142
+ } finally {
79143
+ if (fd !== null) {
79144
+ try {
79145
+ closeSync5(fd);
79146
+ } catch {}
79147
+ }
79148
+ }
79149
+ }
79150
+ function parseRecords(chunk, dropFirstPartial) {
79151
+ const lines = chunk.split(`
79152
+ `);
79153
+ if (dropFirstPartial)
79154
+ lines.shift();
79155
+ else
79156
+ lines.pop();
79157
+ const out = [];
79158
+ for (const l of lines) {
79159
+ if (!l)
79160
+ continue;
79161
+ try {
79162
+ const o = JSON.parse(l);
79163
+ if (o && typeof o === "object")
79164
+ out.push(o);
79165
+ } catch {}
79166
+ }
79167
+ return out;
79168
+ }
79169
+ function contentText(content) {
79170
+ if (typeof content === "string")
79171
+ return content;
79172
+ if (!Array.isArray(content))
79173
+ return "";
79174
+ return content.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join(`
79175
+ `);
79176
+ }
79177
+ function isHarnessNoise(raw2) {
79178
+ const t = raw2.trimStart();
79179
+ if (t.startsWith("[Request interrupted by user"))
79180
+ return true;
79181
+ return HARNESS_ENVELOPES.some((e) => t.startsWith(e));
79182
+ }
79183
+ function mainConversationTurn(r) {
79184
+ if (r.type !== "user" && r.type !== "assistant")
79185
+ return null;
79186
+ if (r.isMeta || r.isSidechain)
79187
+ return null;
79188
+ const content = r.message?.content;
79189
+ if (Array.isArray(content) && content.some((b) => b?.type === "tool_result")) {
79190
+ return null;
79191
+ }
79192
+ const raw2 = contentText(content);
79193
+ if (!raw2.trim() || isHarnessNoise(raw2))
79194
+ return null;
79195
+ return { role: r.type === "user" ? "user" : "assistant", raw: raw2 };
79196
+ }
79197
+ function cleanPrompt(text) {
79198
+ return text.replace(/<command-[a-z-]+>[\s\S]*?<\/command-[a-z-]+>/g, " ").replace(/<local-command-[a-z-]+>[\s\S]*?<\/local-command-[a-z-]+>/g, " ").replace(/<[^>]{1,40}>/g, " ").replace(/\s+/g, " ").trim();
79199
+ }
79200
+ function hydrateSession(row) {
79201
+ if (row.hydrated)
79202
+ return row;
79203
+ row.hydrated = true;
79204
+ if (isActive(row)) {
79205
+ try {
79206
+ row.sizeBytes = statSync6(row.file).size;
79207
+ } catch {}
79208
+ }
79209
+ const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
79210
+ for (const r of head) {
79211
+ if (!row.gitBranch && typeof r.gitBranch === "string")
79212
+ row.gitBranch = r.gitBranch;
79213
+ if (!row.firstPrompt && r.type === "user" && !r.isMeta) {
79214
+ const raw2 = contentText(r.message?.content);
79215
+ const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
79216
+ if (t)
79217
+ row.firstPrompt = t;
79218
+ }
79219
+ if (row.gitBranch && row.firstPrompt)
79220
+ break;
79221
+ }
79222
+ const tailStart = Math.max(0, row.sizeBytes - TAIL_BYTES);
79223
+ const tail = parseRecords(readChunk(row.file, tailStart, row.sizeBytes - tailStart), tailStart > 0);
79224
+ for (let i = tail.length - 1;i >= 0; i--) {
79225
+ const r = tail[i];
79226
+ if (!row.title && r.type === "ai-title" && typeof r.aiTitle === "string" && r.aiTitle.trim()) {
79227
+ row.title = r.aiTitle.trim();
79228
+ }
79229
+ if (row.lastMessageChars === undefined && r.type === "user" && !r.isMeta) {
79230
+ const raw2 = contentText(r.message?.content);
79231
+ const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
79232
+ if (t)
79233
+ row.lastMessageChars = t.length;
79234
+ }
79235
+ }
79236
+ row.recentTurns = extractRecentTurns(tail);
79237
+ return row;
79238
+ }
79239
+ function extractRecentTurns(records) {
79240
+ const out = [];
79241
+ let ai = 0;
79242
+ let user = 0;
79243
+ for (let i = records.length - 1;i >= 0; i--) {
79244
+ if (ai >= RECENT_AI_TURNS && user >= RECENT_USER_TURNS)
79245
+ break;
79246
+ const r = records[i];
79247
+ if (r.type !== "user" && r.type !== "assistant")
79248
+ continue;
79249
+ const role = r.type === "user" ? "user" : "assistant";
79250
+ if (role === "assistant" ? ai >= RECENT_AI_TURNS : user >= RECENT_USER_TURNS)
79251
+ continue;
79252
+ const turn = mainConversationTurn(r);
79253
+ if (!turn)
79254
+ continue;
79255
+ const text = cleanPrompt(turn.raw);
79256
+ if (!text)
79257
+ continue;
79258
+ out.push({ role, text });
79259
+ if (role === "assistant")
79260
+ ai++;
79261
+ else
79262
+ user++;
79263
+ }
79264
+ return out.reverse();
79265
+ }
79266
+ function hydrateConversation(row) {
79267
+ if (row.conversationDeepened)
79268
+ return row;
79269
+ row.conversationDeepened = true;
79270
+ const turns = row.recentTurns;
79271
+ if (!turns || turns.some((t) => t.role === "user"))
79272
+ return row;
79273
+ const start = Math.max(0, row.sizeBytes - DEEP_TAIL_BYTES);
79274
+ const chunk = readChunk(row.file, start, row.sizeBytes - start);
79275
+ if (!chunk)
79276
+ return row;
79277
+ const lines = chunk.split(`
79278
+ `);
79279
+ if (start > 0)
79280
+ lines.shift();
79281
+ for (let i = lines.length - 1;i >= 0; i--) {
79282
+ const line = lines[i];
79283
+ if (!line.includes('"type":"user"'))
79284
+ continue;
79285
+ let r;
79286
+ try {
79287
+ r = JSON.parse(line);
79288
+ } catch {
79289
+ continue;
79290
+ }
79291
+ const turn = mainConversationTurn(r);
79292
+ if (!turn || turn.role !== "user")
79293
+ continue;
79294
+ const text = cleanPrompt(turn.raw);
79295
+ if (!text)
79296
+ continue;
79297
+ turns.unshift({ role: "user", text });
79298
+ if (row.lastMessageChars === undefined)
79299
+ row.lastMessageChars = text.length;
79300
+ return row;
79301
+ }
79302
+ return row;
79303
+ }
79304
+ function sessionLabel(row) {
79305
+ return row.title || row.firstPrompt || row.id;
79306
+ }
79307
+ function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
79308
+ const rows = sessionsIn(slugForPath(cwd)).filter((r) => r.mtimeMs >= sinceMs);
79309
+ if (rows.length === 0)
79310
+ return null;
79311
+ return rows.reduce((a, b) => b.mtimeMs > a.mtimeMs ? b : a).id;
79312
+ }
79313
+ 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;
79314
+ var init_session_discovery = __esm(() => {
79315
+ PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
79316
+ HEAD_BYTES = 64 * 1024;
79317
+ TAIL_BYTES = 128 * 1024;
79318
+ HARNESS_ENVELOPES = [
79319
+ "<task-notification>",
79320
+ "<system-reminder>",
79321
+ "<local-command-stdout>",
79322
+ "<user-prompt-submit-hook>"
79323
+ ];
79324
+ DEEP_TAIL_BYTES = 4 * 1024 * 1024;
79325
+ });
79326
+
79327
+ // src/session/conversation.ts
79328
+ import { closeSync as closeSync6, openSync as openSync6, readSync as readSync2, statSync as statSync7 } from "fs";
79329
+ import { StringDecoder } from "string_decoder";
79330
+ function looksLikeTurn(line) {
79331
+ const assistant = line.includes('"type":"assistant"');
79332
+ if (!assistant && !line.includes('"type":"user"'))
79333
+ return false;
79334
+ if (line.includes('"isSidechain":true'))
79335
+ return false;
79336
+ if (line.includes('"isMeta":true'))
79337
+ return false;
79338
+ if (line.includes('"type":"tool_result"'))
79339
+ return false;
79340
+ if (assistant && !line.includes('"type":"text"'))
79341
+ return false;
79342
+ return true;
79343
+ }
79344
+ function cleanTurnText(raw2) {
79345
+ let t = raw2;
79346
+ for (const re of INLINE_ENVELOPES)
79347
+ t = t.replace(re, "");
79348
+ t = t.replace(/<command-name>([\s\S]*?)<\/command-name>/g, "/$1");
79349
+ return t.replace(/\t/g, " ").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").replace(/\r\n?/g, `
79350
+ `).split(`
79351
+ `).map((l) => l.replace(/\s+$/, "")).join(`
79352
+ `).replace(/\n{3,}/g, `
79353
+
79354
+ `).trim();
79355
+ }
79356
+ function readConversation(file2, opts = {}) {
79357
+ const maxTurn = opts.maxTurnChars ?? MAX_TURN_CHARS;
79358
+ const maxTotal = opts.maxTotalChars ?? MAX_TOTAL_CHARS;
79359
+ const chunkBytes = opts.chunkBytes ?? CHUNK_BYTES;
79360
+ const started = Date.now();
79361
+ const turns = [];
79362
+ let chars = 0;
79363
+ let dropped = 0;
79364
+ let anyElided = false;
79365
+ let bytes2 = 0;
79366
+ const take = (turn) => {
79367
+ turns.push(turn);
79368
+ chars += turn.text.length;
79369
+ if (chars <= maxTotal)
79370
+ return;
79371
+ let freed = 0;
79372
+ let n = 0;
79373
+ while (n < turns.length - 1 && chars - freed > maxTotal) {
79374
+ freed += turns[n].text.length;
79375
+ n++;
79376
+ }
79377
+ turns.splice(0, n);
79378
+ chars -= freed;
79379
+ dropped += n;
79380
+ };
79381
+ let fd = null;
79382
+ try {
79383
+ const size = statSync7(file2).size;
79384
+ fd = openSync6(file2, "r");
79385
+ const buf = Buffer.allocUnsafe(chunkBytes);
79386
+ const decoder = new StringDecoder("utf-8");
79387
+ let pending = "";
79388
+ let pos = 0;
79389
+ const consume = (line) => {
79390
+ if (!line || !looksLikeTurn(line))
79391
+ return;
79392
+ let record4;
79393
+ try {
79394
+ record4 = JSON.parse(line);
79395
+ } catch {
79396
+ return;
79397
+ }
79398
+ if (!record4 || typeof record4 !== "object")
79399
+ return;
79400
+ const raw2 = mainConversationTurn(record4);
79401
+ if (!raw2)
79402
+ return;
79403
+ let text = cleanTurnText(raw2.raw);
79404
+ if (!text)
79405
+ return;
79406
+ const elided = text.length > maxTurn;
79407
+ if (elided) {
79408
+ anyElided = true;
79409
+ text = `${text.slice(0, maxTurn)}
79410
+
79411
+ \u2026 turn truncated at ${maxTurn.toLocaleString()} characters`;
79412
+ }
79413
+ take({ role: raw2.role, text, elided });
79414
+ };
79415
+ while (pos < size) {
79416
+ const n = readSync2(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
79417
+ if (n <= 0)
79418
+ break;
79419
+ pos += n;
79420
+ bytes2 += n;
79421
+ pending += decoder.write(buf.subarray(0, n));
79422
+ let from = 0;
79423
+ let nl = pending.indexOf(`
79424
+ `, from);
79425
+ while (nl !== -1) {
79426
+ consume(pending.slice(from, nl));
79427
+ from = nl + 1;
79428
+ nl = pending.indexOf(`
79429
+ `, from);
79430
+ }
79431
+ if (from > 0)
79432
+ pending = pending.slice(from);
79433
+ }
79434
+ pending += decoder.end();
79435
+ if (pending)
79436
+ consume(pending);
79437
+ } catch {} finally {
79438
+ if (fd !== null) {
79439
+ try {
79440
+ closeSync6(fd);
79441
+ } catch {}
79442
+ }
79443
+ }
79444
+ return { turns, bytes: bytes2, elapsedMs: Date.now() - started, chars, dropped, anyElided };
79445
+ }
79446
+ function cpWidth(cp) {
79447
+ if (cp >= 32 && cp < 127)
79448
+ return 1;
79449
+ return displayWidth(String.fromCodePoint(cp));
79450
+ }
79451
+ function wrapOffsets(text, width) {
79452
+ const out = [];
79453
+ const w = Math.max(1, Math.floor(width));
79454
+ const n = text.length;
79455
+ let paraStart = 0;
79456
+ for (;; ) {
79457
+ let nl = text.indexOf(`
79458
+ `, paraStart);
79459
+ if (nl === -1)
79460
+ nl = n;
79461
+ let s = paraStart;
79462
+ for (;; ) {
79463
+ if (s >= nl) {
79464
+ if (s === paraStart || paraStart === nl)
79465
+ out.push({ start: s, end: nl });
79466
+ break;
79467
+ }
79468
+ let cols = 0;
79469
+ let j = s;
79470
+ let lastSpace = -1;
79471
+ while (j < nl) {
79472
+ const cp = text.codePointAt(j);
79473
+ const size = cp > 65535 ? 2 : 1;
79474
+ const cw = cpWidth(cp);
79475
+ if (cols + cw > w)
79476
+ break;
79477
+ if (cp === 32 && j > s && text.charCodeAt(j - 1) !== 32)
79478
+ lastSpace = j;
79479
+ cols += cw;
79480
+ j += size;
79481
+ }
79482
+ if (j >= nl) {
79483
+ out.push({ start: s, end: nl });
79484
+ break;
79485
+ }
79486
+ const brk = text.charCodeAt(j) === 32 ? j : lastSpace > s ? lastSpace : j;
79487
+ out.push({ start: s, end: brk });
79488
+ s = brk === j && text.charCodeAt(j) !== 32 ? j : brk + 1;
79489
+ while (s < nl && text.charCodeAt(s) === 32)
79490
+ s++;
79491
+ }
79492
+ if (nl >= n)
79493
+ break;
79494
+ paraStart = nl + 1;
79495
+ if (paraStart > n)
79496
+ break;
79497
+ if (paraStart === n) {
79498
+ out.push({ start: n, end: n });
79499
+ break;
79500
+ }
79501
+ }
79502
+ return out;
79503
+ }
79504
+ var CHUNK_BYTES, MAX_TURN_CHARS = 32000, MAX_TOTAL_CHARS = 8000000, INLINE_ENVELOPES;
79505
+ var init_conversation = __esm(() => {
79506
+ init_text();
79507
+ init_session_discovery();
79508
+ CHUNK_BYTES = 1024 * 1024;
79509
+ INLINE_ENVELOPES = [
79510
+ /<system-reminder>[\s\S]*?<\/system-reminder>/g,
79511
+ /<command-message>[\s\S]*?<\/command-message>/g,
79512
+ /<command-args>[\s\S]*?<\/command-args>/g,
79513
+ /<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g,
79514
+ /<user-prompt-submit-hook>[\s\S]*?<\/user-prompt-submit-hook>/g
79515
+ ];
79516
+ });
79517
+
79518
+ // src/session/conversation-reader.tsx
79519
+ import { useKeyboard as useKeyboard3 } from "@opentui/react";
79520
+ import { useEffect as useEffect5, useMemo as useMemo3, useState as useState6 } from "react";
79521
+ import { jsxDEV as jsxDEV19, Fragment as Fragment12 } from "@opentui/react/jsx-dev-runtime";
79522
+ function layoutRows(turns, textWidth) {
79523
+ const rows = [];
79524
+ const turnStart = [];
79525
+ const turnEnd = [];
79526
+ for (let t = 0;t < turns.length; t++) {
79527
+ turnStart.push(rows.length);
79528
+ const slices = wrapOffsets(turns[t].text, textWidth);
79529
+ for (let i = 0;i < slices.length; i++) {
79530
+ rows.push({ turn: t, first: i === 0, start: slices[i].start, end: slices[i].end });
79531
+ }
79532
+ turnEnd.push(rows.length);
79533
+ if (t < turns.length - 1)
79534
+ rows.push({ turn: -1, first: false, start: 0, end: 0 });
79535
+ }
79536
+ return { rows, turnStart, turnEnd };
79537
+ }
79538
+ function buildSearch(turns, rows, turnStart, turnEnd, query) {
79539
+ if (!query)
79540
+ return EMPTY_SEARCH;
79541
+ const q = query.toLowerCase();
79542
+ const hits = [];
79543
+ const ranges = new Map;
79544
+ let capped = false;
79545
+ for (let t = 0;t < turns.length && !capped; t++) {
79546
+ const hay = turns[t].text.toLowerCase();
79547
+ let at = hay.indexOf(q);
79548
+ if (at === -1)
79549
+ continue;
79550
+ const lo = turnStart[t];
79551
+ const hi = turnEnd[t];
79552
+ let r = lo;
79553
+ while (at !== -1) {
79554
+ if (hits.length >= MAX_MATCHES) {
79555
+ capped = true;
79556
+ break;
79557
+ }
79558
+ const end = at + q.length;
79559
+ while (r < hi - 1 && rows[r].end <= at)
79560
+ r++;
79561
+ const hit = hits.length;
79562
+ hits.push(r);
79563
+ for (let k = r;k < hi; k++) {
79564
+ const row = rows[k];
79565
+ if (row.start >= end)
79566
+ break;
79567
+ const s = Math.max(at, row.start);
79568
+ const e = Math.min(end, row.end);
79569
+ if (e > s) {
79570
+ const list = ranges.get(k);
79571
+ if (list)
79572
+ list.push({ s: s - row.start, e: e - row.start, hit });
79573
+ else
79574
+ ranges.set(k, [{ s: s - row.start, e: e - row.start, hit }]);
79575
+ }
79576
+ }
79577
+ at = hay.indexOf(q, at + 1);
79578
+ }
79579
+ }
79580
+ return { hits, ranges, capped };
79581
+ }
79582
+ function ConversationReader({
79583
+ row,
79584
+ conv,
79585
+ width,
79586
+ height: height2,
79587
+ onClose,
79588
+ onResume,
79589
+ onCancel
79590
+ }) {
79591
+ const [top, setTop] = useState6(0);
79592
+ const [query, setQuery] = useState6("");
79593
+ const [typing, setTyping] = useState6(false);
79594
+ const [matchIdx, setMatchIdx] = useState6(0);
79595
+ const CHROME_ROWS = 5;
79596
+ const viewport = Math.max(1, height2 - CHROME_ROWS);
79597
+ const inner = Math.max(20, width - 2);
79598
+ const contentW = inner - BAR_W;
79599
+ const textW = Math.max(8, contentW - GUTTER - 1);
79600
+ const turns = useMemo3(() => conv?.turns ?? NO_TURNS, [conv]);
79601
+ const { rows, turnStart, turnEnd } = useMemo3(() => layoutRows(turns, textW), [turns, textW]);
79602
+ const search = useMemo3(() => buildSearch(turns, rows, turnStart, turnEnd, query), [turns, rows, turnStart, turnEnd, query]);
79603
+ const maxTop = Math.max(0, rows.length - viewport);
79604
+ const clampTop = (v) => Math.max(0, Math.min(v, maxTop));
79605
+ const reveal = (r) => setTop(clampTop(r - Math.floor(viewport / 3)));
79606
+ useEffect5(() => {
79607
+ setTop(Math.max(0, rows.length - viewport));
79608
+ }, [rows.length, viewport]);
79609
+ useEffect5(() => {
79610
+ if (search.hits.length === 0)
79611
+ return;
79612
+ let i = search.hits.findIndex((r) => r >= top);
79613
+ if (i === -1)
79614
+ i = 0;
79615
+ setMatchIdx(i);
79616
+ reveal(search.hits[i]);
79617
+ }, [search]);
79618
+ const step = (d) => {
79619
+ if (search.hits.length === 0)
79620
+ return;
79621
+ const i = (matchIdx + d + search.hits.length) % search.hits.length;
79622
+ setMatchIdx(i);
79623
+ reveal(search.hits[i]);
79624
+ };
79625
+ useKeyboard3((key) => {
79626
+ const name = key.name;
79627
+ if (key.ctrl && name === "c") {
79628
+ onCancel();
79629
+ return;
79630
+ }
79631
+ if (name === "escape") {
79632
+ if (typing || query) {
79633
+ setTyping(false);
79634
+ setQuery("");
79635
+ return;
79636
+ }
79637
+ onClose();
79638
+ return;
79639
+ }
79640
+ if (name === "return" || name === "enter") {
79641
+ if (typing) {
79642
+ setTyping(false);
79643
+ return;
79644
+ }
79645
+ onResume();
79646
+ return;
79647
+ }
79648
+ if (name === "up")
79649
+ return setTop((t) => clampTop(t - 1));
79650
+ if (name === "down")
79651
+ return setTop((t) => clampTop(t + 1));
79652
+ if (name === "pageup")
79653
+ return setTop((t) => clampTop(t - viewport));
79654
+ if (name === "pagedown")
79655
+ return setTop((t) => clampTop(t + viewport));
79656
+ if (name === "home")
79657
+ return setTop(0);
79658
+ if (name === "end")
79659
+ return setTop(maxTop);
79660
+ if (name === "backspace") {
79661
+ if (typing)
79662
+ setQuery((q) => q.slice(0, -1));
79663
+ return;
79664
+ }
79665
+ const ch = key.raw;
79666
+ const printable = ch && ch.length === 1 && ch >= " " && ch !== "\x7F";
79667
+ if (typing) {
79668
+ if (printable)
79669
+ setQuery((q) => q + ch);
79670
+ return;
79671
+ }
79672
+ if (name === "slash" || ch === "/") {
79673
+ setTyping(true);
79674
+ return;
79675
+ }
79676
+ if (name === "n")
79677
+ return step(key.shift ? -1 : 1);
79678
+ if (ch === "g")
79679
+ return setTop(0);
79680
+ if (ch === "G")
79681
+ return setTop(maxTop);
79682
+ if (name === "space")
79683
+ return setTop((t) => clampTop(t + viewport));
79684
+ });
79685
+ const label = sessionLabel(row);
79686
+ const mb = row.sizeBytes / 1048576;
79687
+ const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
79688
+ const stats = conv ? `${turns.length} turns \xB7 ${rows.length} lines \xB7 ${size} transcript` : `reading ${size}\u2026`;
79689
+ const visible = rows.slice(top, top + viewport);
79690
+ const barCells = useMemo3(() => scrollbarCells(viewport, rows.length, top, search.hits), [viewport, rows.length, top, search.hits]);
79691
+ const pct = rows.length <= viewport ? 100 : top / maxTop * 100;
79692
+ const POSITION_COLS = 26;
79693
+ const matchLabel = search.hits.length ? `${matchIdx + 1} of ${search.hits.length}${search.capped ? "+" : ""} matches` : query ? "no matches" : "";
79694
+ return /* @__PURE__ */ jsxDEV19("box", {
79695
+ flexDirection: "column",
79696
+ height: height2,
79697
+ backgroundColor: C.bg,
79698
+ children: [
79699
+ /* @__PURE__ */ jsxDEV19("box", {
79700
+ flexDirection: "row",
79701
+ justifyContent: "space-between",
79702
+ height: 1,
79703
+ paddingX: 1,
79704
+ children: [
79705
+ /* @__PURE__ */ jsxDEV19("text", {
79706
+ children: [
79707
+ /* @__PURE__ */ jsxDEV19("span", {
79708
+ fg: tokens.accent,
79709
+ attributes: A.bold,
79710
+ children: "claudish"
79711
+ }, undefined, false, undefined, this),
79712
+ /* @__PURE__ */ jsxDEV19("span", {
79713
+ fg: tokens.subtle,
79714
+ children: " reader"
79715
+ }, undefined, false, undefined, this),
79716
+ conv && conv.dropped > 0 ? /* @__PURE__ */ jsxDEV19("span", {
79717
+ fg: tokens.warn,
79718
+ children: ` ${conv.dropped} older turns dropped (cap)`
79719
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this),
79720
+ conv?.anyElided ? /* @__PURE__ */ jsxDEV19("span", {
79721
+ fg: tokens.warn,
79722
+ children: " long turns truncated"
79723
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this)
79724
+ ]
79725
+ }, undefined, true, undefined, this),
79726
+ /* @__PURE__ */ jsxDEV19("text", {
79727
+ children: /* @__PURE__ */ jsxDEV19("span", {
79728
+ fg: tokens.subtle,
79729
+ children: stats
79730
+ }, undefined, false, undefined, this)
79731
+ }, undefined, false, undefined, this)
79732
+ ]
79733
+ }, undefined, true, undefined, this),
79734
+ /* @__PURE__ */ jsxDEV19(Panel, {
79735
+ title: `conversation \xB7 ${truncate3(label, Math.max(10, width - 20))}`,
79736
+ flush: true,
79737
+ flexGrow: 1,
79738
+ flexBasis: 0,
79739
+ children: !conv ? /* @__PURE__ */ jsxDEV19("text", {
79740
+ fg: tokens.subtle,
79741
+ children: " reading transcript\u2026"
79742
+ }, undefined, false, undefined, this) : rows.length === 0 ? /* @__PURE__ */ jsxDEV19("text", {
79743
+ fg: tokens.trace,
79744
+ children: " no conversation recorded \u2014 this transcript has no main-thread prose"
79745
+ }, undefined, false, undefined, this) : visible.map((r, i) => {
79746
+ const line = top + i;
79747
+ return /* @__PURE__ */ jsxDEV19(ReaderRow, {
79748
+ row: r,
79749
+ turn: r.turn >= 0 ? turns[r.turn] : undefined,
79750
+ hl: search.ranges.get(line),
79751
+ current: matchIdx,
79752
+ width: contentW,
79753
+ bar: barCells[i] ?? "track"
79754
+ }, line, false, undefined, this);
79755
+ })
79756
+ }, undefined, false, undefined, this),
79757
+ /* @__PURE__ */ jsxDEV19("box", {
79758
+ flexDirection: "row",
79759
+ justifyContent: "space-between",
79760
+ height: 1,
79761
+ paddingX: 1,
79762
+ children: [
79763
+ /* @__PURE__ */ jsxDEV19("text", {
79764
+ children: query || typing ? /* @__PURE__ */ jsxDEV19(Fragment12, {
79765
+ children: [
79766
+ /* @__PURE__ */ jsxDEV19("span", {
79767
+ fg: tokens.warn,
79768
+ attributes: A.bold,
79769
+ children: truncate3(`/${query}`, Math.max(4, width - POSITION_COLS - displayWidth(matchLabel) - 4))
79770
+ }, undefined, false, undefined, this),
79771
+ /* @__PURE__ */ jsxDEV19("span", {
79772
+ fg: typing ? tokens.warn : tokens.trace,
79773
+ children: typing ? "\u258C" : " "
79774
+ }, undefined, false, undefined, this),
79775
+ /* @__PURE__ */ jsxDEV19("span", {
79776
+ fg: search.hits.length ? tokens.subtle : tokens.trace,
79777
+ children: matchLabel ? ` ${matchLabel}` : ""
79778
+ }, undefined, false, undefined, this)
79779
+ ]
79780
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
79781
+ fg: tokens.trace,
79782
+ children: "/ to search"
79783
+ }, undefined, false, undefined, this)
79784
+ }, undefined, false, undefined, this),
79785
+ /* @__PURE__ */ jsxDEV19("text", {
79786
+ children: [
79787
+ /* @__PURE__ */ jsxDEV19("span", {
79788
+ fg: tokens.subtle,
79789
+ children: `${Math.min(rows.length, top + viewport)}/${rows.length} `
79790
+ }, undefined, false, undefined, this),
79791
+ /* @__PURE__ */ jsxDEV19(MeterSpan, {
79792
+ pct,
79793
+ width: 12,
79794
+ ramp: ramps.volume
79795
+ }, undefined, false, undefined, this)
79796
+ ]
79797
+ }, undefined, true, undefined, this)
79798
+ ]
79799
+ }, undefined, true, undefined, this),
79800
+ /* @__PURE__ */ jsxDEV19("box", {
79801
+ flexDirection: "row",
79802
+ height: 1,
79803
+ paddingX: 1,
79804
+ gap: 2,
79805
+ children: [
79806
+ /* @__PURE__ */ jsxDEV19("text", {
79807
+ fg: tokens.subtle,
79808
+ children: "\u2191\u2193 \u21DE\u21DF scroll"
79809
+ }, undefined, false, undefined, this),
79810
+ /* @__PURE__ */ jsxDEV19("text", {
79811
+ fg: tokens.subtle,
79812
+ children: "g/G ends"
79813
+ }, undefined, false, undefined, this),
79814
+ /* @__PURE__ */ jsxDEV19("text", {
79815
+ fg: typing ? tokens.warn : tokens.subtle,
79816
+ children: "/ search"
79817
+ }, undefined, false, undefined, this),
79818
+ /* @__PURE__ */ jsxDEV19("text", {
79819
+ fg: search.hits.length ? tokens.subtle : tokens.trace,
79820
+ children: "n/N match"
79821
+ }, undefined, false, undefined, this),
79822
+ /* @__PURE__ */ jsxDEV19("text", {
79823
+ fg: tokens.accent,
79824
+ children: "\u23CE resume"
79825
+ }, undefined, false, undefined, this),
79826
+ /* @__PURE__ */ jsxDEV19("text", {
79827
+ fg: tokens.subtle,
79828
+ children: "esc back"
79829
+ }, undefined, false, undefined, this)
79830
+ ]
79831
+ }, undefined, true, undefined, this)
79832
+ ]
79833
+ }, undefined, true, undefined, this);
79834
+ }
79835
+ function scrollbarCells(viewport, total, top, hits) {
79836
+ const cells = new Array(viewport).fill("track");
79837
+ if (total <= 0)
79838
+ return cells;
79839
+ if (total > viewport) {
79840
+ const size = Math.max(1, Math.round(viewport / total * viewport));
79841
+ const span = Math.max(1, total - viewport);
79842
+ const start = Math.min(viewport - size, Math.round(top / span * (viewport - size)));
79843
+ for (let i = 0;i < size; i++)
79844
+ cells[start + i] = "thumb";
79845
+ } else {
79846
+ cells.fill("thumb");
79847
+ }
79848
+ for (const h of hits) {
79849
+ const i = Math.min(viewport - 1, Math.floor(h / total * viewport));
79850
+ if (i >= 0)
79851
+ cells[i] = "hit";
79852
+ }
79853
+ return cells;
79854
+ }
79855
+ function ReaderRow({
79856
+ row,
79857
+ turn,
79858
+ hl,
79859
+ current,
79860
+ width,
79861
+ bar
79862
+ }) {
79863
+ if (!turn) {
79864
+ return /* @__PURE__ */ jsxDEV19("text", {
79865
+ children: [
79866
+ /* @__PURE__ */ jsxDEV19("span", {
79867
+ children: " ".repeat(Math.max(0, width))
79868
+ }, undefined, false, undefined, this),
79869
+ /* @__PURE__ */ jsxDEV19("span", {
79870
+ bg: BAR_COLOR[bar],
79871
+ children: " "
79872
+ }, undefined, false, undefined, this)
79873
+ ]
79874
+ }, undefined, true, undefined, this);
79875
+ }
79876
+ const text = turn.text.slice(row.start, row.end);
79877
+ const fg = turn.role === "user" ? tokens.text : C.fgMuted;
79878
+ const railFg = turn.role === "user" ? SPEAKER.you : SPEAKER.ai;
79879
+ const pad2 = Math.max(0, width - GUTTER - displayWidth(text));
79880
+ return /* @__PURE__ */ jsxDEV19("text", {
79881
+ children: [
79882
+ /* @__PURE__ */ jsxDEV19("span", {
79883
+ fg: railFg,
79884
+ children: RAIL
79885
+ }, undefined, false, undefined, this),
79886
+ row.first ? /* @__PURE__ */ jsxDEV19(BadgeSpan, {
79887
+ label: turn.role === "user" ? "you" : "ai",
79888
+ bg: turn.role === "user" ? SPEAKER.you : SPEAKER.ai,
79889
+ width: ROLE_W
79890
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
79891
+ children: " ".repeat(ROLE_W)
79892
+ }, undefined, false, undefined, this),
79893
+ hl && hl.length > 0 ? highlighted(text, hl, current, fg) : /* @__PURE__ */ jsxDEV19("span", {
79894
+ fg,
79895
+ children: text
79896
+ }, undefined, false, undefined, this),
79897
+ /* @__PURE__ */ jsxDEV19("span", {
79898
+ children: " ".repeat(pad2)
79899
+ }, undefined, false, undefined, this),
79900
+ /* @__PURE__ */ jsxDEV19("span", {
79901
+ bg: BAR_COLOR[bar],
79902
+ children: " "
79903
+ }, undefined, false, undefined, this)
79904
+ ]
79905
+ }, undefined, true, undefined, this);
79906
+ }
79907
+ function highlighted(text, hl, current, fg) {
79908
+ const out = [];
79909
+ let from = 0;
79910
+ for (let i = 0;i < hl.length; i++) {
79911
+ const { s, e, hit } = hl[i];
79912
+ if (e <= from)
79913
+ continue;
79914
+ const start = Math.max(from, s);
79915
+ if (start > from)
79916
+ out.push(/* @__PURE__ */ jsxDEV19("span", {
79917
+ fg,
79918
+ children: text.slice(from, start)
79919
+ }, `p${i}`, false, undefined, this));
79920
+ const bg = hit === current ? tokens.accent : tokens.warn;
79921
+ out.push(/* @__PURE__ */ jsxDEV19("span", {
79922
+ fg: tokens.ink,
79923
+ bg,
79924
+ attributes: A.bold,
79925
+ children: text.slice(start, e)
79926
+ }, `h${i}`, false, undefined, this));
79927
+ from = e;
79928
+ }
79929
+ if (from < text.length)
79930
+ out.push(/* @__PURE__ */ jsxDEV19("span", {
79931
+ fg,
79932
+ children: text.slice(from)
79933
+ }, "tail", false, undefined, this));
79934
+ return /* @__PURE__ */ jsxDEV19(Fragment12, {
79935
+ children: out
79936
+ }, undefined, false, undefined, this);
79937
+ }
79938
+ var RAIL = "\u258D", RAIL_W = 2, ROLE_W = 6, GUTTER, BAR_W = 1, SPEAKER, MAX_MATCHES = 5000, EMPTY_SEARCH, NO_TURNS, BAR_COLOR;
79939
+ var init_conversation_reader = __esm(() => {
79940
+ init_theme2();
79941
+ init_text();
79942
+ init_tokens();
79943
+ init_widgets();
79944
+ init_conversation();
79945
+ init_session_discovery();
79946
+ GUTTER = RAIL_W + ROLE_W;
79947
+ SPEAKER = {
79948
+ you: "#39d353",
79949
+ ai: "#39c5cf"
79950
+ };
79951
+ EMPTY_SEARCH = { hits: [], ranges: new Map, capped: false };
79952
+ NO_TURNS = [];
79953
+ BAR_COLOR = {
79954
+ track: tokens.border,
79955
+ thumb: tokens.accent,
79956
+ hit: tokens.warn
79957
+ };
79958
+ });
79959
+
79960
+ // src/session/resume-picker.tsx
79961
+ import { useKeyboard as useKeyboard4, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
79962
+ import { useEffect as useEffect6, useMemo as useMemo4, useState as useState7 } from "react";
79963
+ import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
79964
+ function detailTurns(height2) {
79965
+ return height2 >= 40 ? 6 : height2 >= 30 ? 4 : 2;
79966
+ }
79967
+ function fuzzy(needle, hay) {
79968
+ if (!needle)
79969
+ return true;
79970
+ const n = needle.toLowerCase();
79971
+ const h = hay.toLowerCase();
79972
+ let i = 0;
79973
+ for (let j = 0;j < h.length && i < n.length; j++)
79974
+ if (h[j] === n[i])
79975
+ i++;
79976
+ return i === n.length;
79977
+ }
79978
+ function age(ms) {
79979
+ const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
79980
+ if (s < 60)
79981
+ return `${s}s`;
79982
+ const m = Math.floor(s / 60);
79983
+ if (m < 60)
79984
+ return `${m}m`;
79985
+ const h = Math.floor(m / 60);
79986
+ if (h < 24)
79987
+ return `${h}h`;
79988
+ return `${Math.floor(h / 24)}d`;
79989
+ }
79990
+ function chipW(label) {
79991
+ return label + 2;
79992
+ }
79993
+ function blockWidth(c) {
79994
+ return LIVE_W + chipW(c.age) + chipW(c.count) + (c.dirty > 0 ? chipW(c.dirty + 1) : 0) + (c.sync > 0 ? 2 * chipW(c.sync + 1) : 0);
79995
+ }
79996
+ function sizePct(bytes2, max) {
79997
+ const lo = Math.log(SIZE_FLOOR_BYTES);
79998
+ const hi = Math.log(Math.max(max, SIZE_FLOOR_BYTES * 2));
79999
+ const v = Math.log(Math.max(bytes2, SIZE_FLOOR_BYTES));
80000
+ return Math.max(0, Math.min(100, (v - lo) / (hi - lo) * 100));
80001
+ }
80002
+ function Slot({
80003
+ label,
80004
+ bg,
80005
+ labelW
80006
+ }) {
80007
+ if (!label || !bg)
80008
+ return /* @__PURE__ */ jsxDEV20("span", {
80009
+ children: " ".repeat(chipW(labelW))
80010
+ }, undefined, false, undefined, this);
80011
+ return /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80012
+ label,
80013
+ bg
80014
+ }, undefined, false, undefined, this);
80015
+ }
80016
+ function WorktreeRow({
80017
+ g,
80018
+ cursor,
80019
+ width,
80020
+ count,
80021
+ cols
80022
+ }) {
80023
+ const nameColor = !g.live ? tokens.dead : g.current ? tokens.success : tokens.text;
80024
+ const stale = !g.lastActiveMs || Date.now() - g.lastActiveMs >= STALE_MS;
80025
+ const room = Math.max(0, width - blockWidth(cols));
80026
+ const series = room >= SPARK_MIN_DAYS ? activitySeries(g.sessions, room) : null;
80027
+ const spark = series && hasActivity(series) ? series : null;
80028
+ const chips = [];
80029
+ if (cols.sync > 0 && g.ahead) {
80030
+ chips.push({
80031
+ label: `\u2191${padStartTo(String(g.ahead), cols.sync)}`,
80032
+ bg: CHIP.ahead,
80033
+ labelW: cols.sync + 1
80034
+ });
80035
+ }
80036
+ if (cols.sync > 0 && g.behind) {
80037
+ chips.push({
80038
+ label: `\u2193${padStartTo(String(g.behind), cols.sync)}`,
80039
+ bg: CHIP.behind,
80040
+ labelW: cols.sync + 1
80041
+ });
80042
+ }
80043
+ if (cols.dirty > 0 && g.dirty) {
80044
+ chips.push({
80045
+ label: `${DIRTY_GLYPH}${padStartTo(String(g.dirty), cols.dirty)}`,
80046
+ bg: CHIP.dirty,
80047
+ labelW: cols.dirty + 1
80048
+ });
80049
+ }
80050
+ chips.push({ label: padStartTo(String(count), cols.count), bg: CHIP.count, labelW: cols.count });
80051
+ chips.push({
80052
+ label: padStartTo(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014", cols.age),
80053
+ bg: stale ? CHIP.stale : CHIP.fresh,
80054
+ labelW: cols.age
80055
+ });
80056
+ const used = chips.reduce((w, c) => w + c.labelW + 2, 0) + LIVE_W;
80057
+ const gap = Math.max(0, width - room - used);
80058
+ return /* @__PURE__ */ jsxDEV20("box", {
80059
+ flexDirection: "column",
80060
+ height: 2,
80061
+ backgroundColor: cursor ? C.bgHighlight : undefined,
80062
+ children: [
80063
+ /* @__PURE__ */ jsxDEV20("text", {
80064
+ children: /* @__PURE__ */ jsxDEV20("span", {
80065
+ fg: nameColor,
80066
+ attributes: cursor || g.current ? A.bold : undefined,
80067
+ children: truncate3(g.name, width)
80068
+ }, undefined, false, undefined, this)
80069
+ }, undefined, false, undefined, this),
80070
+ /* @__PURE__ */ jsxDEV20("box", {
80071
+ height: 1,
80072
+ children: /* @__PURE__ */ jsxDEV20("text", {
80073
+ children: [
80074
+ spark ? /* @__PURE__ */ jsxDEV20(SparklineSpan, {
80075
+ values: spark,
80076
+ fg: SPARK_FG
80077
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {
80078
+ children: " ".repeat(room)
80079
+ }, undefined, false, undefined, this),
80080
+ /* @__PURE__ */ jsxDEV20("span", {
80081
+ children: " ".repeat(gap)
80082
+ }, undefined, false, undefined, this),
80083
+ /* @__PURE__ */ jsxDEV20("span", {
80084
+ fg: tokens.success,
80085
+ children: g.activeNow ? "\u25CF " : " "
80086
+ }, undefined, false, undefined, this),
80087
+ chips.map((c) => /* @__PURE__ */ jsxDEV20(Slot, {
80088
+ label: c.label,
80089
+ bg: c.bg,
80090
+ labelW: c.labelW
80091
+ }, c.bg + c.label, false, undefined, this))
80092
+ ]
80093
+ }, undefined, true, undefined, this)
80094
+ }, undefined, false, undefined, this)
80095
+ ]
80096
+ }, undefined, true, undefined, this);
80097
+ }
80098
+ function dailyActivity(groups, days) {
80099
+ const day = 86400000;
80100
+ const today = Math.floor(Date.now() / day);
80101
+ const buckets = new Array(days).fill(0);
80102
+ for (const g of groups) {
80103
+ for (const s of g.sessions) {
80104
+ const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
80105
+ if (idx >= 0 && idx < days)
80106
+ buckets[idx] += 1;
80107
+ }
80108
+ }
80109
+ return buckets;
80110
+ }
80111
+ function activityLevels(days) {
80112
+ const nz = days.filter((v) => v > 0).sort((a, b) => a - b);
80113
+ if (nz.length === 0)
80114
+ return days.map(() => 0);
80115
+ const at = (p) => nz[Math.min(nz.length - 1, Math.floor(nz.length * p))];
80116
+ const q1 = at(0.25);
80117
+ const q2 = at(0.5);
80118
+ const q3 = at(0.75);
80119
+ return days.map((v) => v <= 0 ? 0 : v <= q1 ? 1 : v <= q2 ? 2 : v <= q3 ? 3 : 4);
80120
+ }
80121
+ function ActivityCalendar({
80122
+ days,
80123
+ width
80124
+ }) {
80125
+ const levels = activityLevels(days);
80126
+ const title = `activity \xB7 ${ACTIVITY_WEEKS}w `;
80127
+ const grid = Math.max(WEEK_DAYS, width - WEEK_LABEL_W);
80128
+ const base = Math.floor(grid / WEEK_DAYS);
80129
+ const extra = grid - base * WEEK_DAYS;
80130
+ return /* @__PURE__ */ jsxDEV20("box", {
80131
+ flexDirection: "column",
80132
+ flexShrink: 0,
80133
+ paddingTop: 1,
80134
+ children: [
80135
+ /* @__PURE__ */ jsxDEV20("text", {
80136
+ children: [
80137
+ /* @__PURE__ */ jsxDEV20("span", {
80138
+ fg: tokens.subtle,
80139
+ attributes: A.bold,
80140
+ children: title
80141
+ }, undefined, false, undefined, this),
80142
+ /* @__PURE__ */ jsxDEV20("span", {
80143
+ fg: tokens.border,
80144
+ children: "\u2500".repeat(Math.max(0, width - title.length))
80145
+ }, undefined, false, undefined, this)
80146
+ ]
80147
+ }, undefined, true, undefined, this),
80148
+ Array.from({ length: ACTIVITY_WEEKS }, (_, w) => {
80149
+ const ago = ACTIVITY_WEEKS - 1 - w;
80150
+ return /* @__PURE__ */ jsxDEV20("text", {
80151
+ children: [
80152
+ /* @__PURE__ */ jsxDEV20("span", {
80153
+ fg: tokens.trace,
80154
+ children: padTo(ago === 0 ? "now" : `-${ago}w`, WEEK_LABEL_W)
80155
+ }, undefined, false, undefined, this),
80156
+ Array.from({ length: WEEK_DAYS }, (_2, d) => /* @__PURE__ */ jsxDEV20("span", {
80157
+ bg: GH_LEVELS[levels[w * WEEK_DAYS + d] ?? 0],
80158
+ children: " ".repeat(base + (d < extra ? 1 : 0))
80159
+ }, d, false, undefined, this))
80160
+ ]
80161
+ }, w, true, undefined, this);
80162
+ })
80163
+ ]
80164
+ }, undefined, true, undefined, this);
80165
+ }
80166
+ function SectionHeader({ label, width }) {
80167
+ const text = `${label} `;
80168
+ return /* @__PURE__ */ jsxDEV20("box", {
80169
+ flexDirection: "column",
80170
+ height: 2,
80171
+ children: [
80172
+ /* @__PURE__ */ jsxDEV20("text", {
80173
+ children: " "
80174
+ }, undefined, false, undefined, this),
80175
+ /* @__PURE__ */ jsxDEV20("text", {
80176
+ children: [
80177
+ /* @__PURE__ */ jsxDEV20("span", {
80178
+ fg: tokens.subtle,
80179
+ attributes: A.bold,
80180
+ children: text
80181
+ }, undefined, false, undefined, this),
80182
+ /* @__PURE__ */ jsxDEV20("span", {
80183
+ fg: tokens.border,
80184
+ children: "\u2500".repeat(Math.max(0, width - text.length))
80185
+ }, undefined, false, undefined, this)
80186
+ ]
80187
+ }, undefined, true, undefined, this)
80188
+ ]
80189
+ }, undefined, true, undefined, this);
80190
+ }
80191
+ function hasActivity(series) {
80192
+ return series.some((v) => v > 0);
80193
+ }
80194
+ function activitySeries(sessions2, days = WEEK_DAYS * 2) {
80195
+ const day = 86400000;
80196
+ const today = Math.floor(Date.now() / day);
80197
+ const buckets = new Array(days).fill(0);
80198
+ for (const s of sessions2) {
80199
+ const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
80200
+ if (idx >= 0 && idx < days)
80201
+ buckets[idx] += 1;
80202
+ }
80203
+ return buckets;
80204
+ }
80205
+ function SessionRowView({
80206
+ row,
80207
+ cursor,
80208
+ width,
80209
+ even,
80210
+ indent = 0,
80211
+ maxSize,
80212
+ meterW
80213
+ }) {
80214
+ const live = isActive(row);
80215
+ const pad2 = " ".repeat(indent);
80216
+ const mb = row.sizeBytes / 1048576;
80217
+ const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
80218
+ const titleW = Math.max(10, width - indent - 2);
80219
+ const metaPad = `${pad2} `;
80220
+ const SIZE_COL = 8;
80221
+ return /* @__PURE__ */ jsxDEV20("box", {
80222
+ flexDirection: "column",
80223
+ height: 2,
80224
+ backgroundColor: cursor ? C.bgHighlight : even ? undefined : C.bgAlt,
80225
+ children: [
80226
+ /* @__PURE__ */ jsxDEV20("text", {
80227
+ children: [
80228
+ /* @__PURE__ */ jsxDEV20("span", {
80229
+ fg: live ? tokens.success : tokens.border,
80230
+ children: `${pad2}${live ? "\u25CF" : "\xB7"} `
80231
+ }, undefined, false, undefined, this),
80232
+ /* @__PURE__ */ jsxDEV20("span", {
80233
+ fg: tokens.text,
80234
+ attributes: cursor ? A.bold : undefined,
80235
+ children: truncate3(sessionLabel(row), titleW)
80236
+ }, undefined, false, undefined, this)
80237
+ ]
80238
+ }, undefined, true, undefined, this),
80239
+ /* @__PURE__ */ jsxDEV20("text", {
80240
+ children: [
80241
+ /* @__PURE__ */ jsxDEV20("span", {
80242
+ children: metaPad
80243
+ }, undefined, false, undefined, this),
80244
+ /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80245
+ label: padStartTo(age(row.mtimeMs), SESSION_AGE_W),
80246
+ bg: Date.now() - row.mtimeMs < STALE_MS ? CHIP.fresh : CHIP.stale,
80247
+ width: SESSION_AGE_COL
80248
+ }, undefined, false, undefined, this),
80249
+ /* @__PURE__ */ jsxDEV20(MeterSpan, {
80250
+ pct: sizePct(row.sizeBytes, maxSize),
80251
+ width: meterW,
80252
+ ramp: ramps.volume
80253
+ }, undefined, false, undefined, this),
80254
+ /* @__PURE__ */ jsxDEV20("span", {
80255
+ fg: MUTED,
80256
+ children: padStartTo(size, SIZE_COL)
80257
+ }, undefined, false, undefined, this),
80258
+ row.gitBranch ? /* @__PURE__ */ jsxDEV20("span", {
80259
+ fg: tokens.trace,
80260
+ children: ` ${BRANCH_ICON} ${truncate3(row.gitBranch, Math.max(6, width - indent - 2 - SESSION_AGE_COL - meterW - SIZE_COL - BRANCH_LEAD_DENSE))}`
80261
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
80262
+ ]
80263
+ }, undefined, true, undefined, this)
80264
+ ]
80265
+ }, undefined, true, undefined, this);
80266
+ }
80267
+ function AgentNode({
80268
+ count,
80269
+ open,
80270
+ cursor,
80271
+ width
80272
+ }) {
80273
+ const label = `${count} agent session${count === 1 ? "" : "s"}`;
80274
+ const hint = open ? "enter to collapse" : "enter to expand \xB7 showing 3";
80275
+ return /* @__PURE__ */ jsxDEV20("box", {
80276
+ height: 1,
80277
+ backgroundColor: cursor ? C.bgHighlight : undefined,
80278
+ children: /* @__PURE__ */ jsxDEV20("text", {
80279
+ children: [
80280
+ /* @__PURE__ */ jsxDEV20("span", {
80281
+ fg: cursor ? tokens.accent : tokens.trace,
80282
+ children: `${cursor ? "\u258D" : " "} `
80283
+ }, undefined, false, undefined, this),
80284
+ /* @__PURE__ */ jsxDEV20("span", {
80285
+ fg: tokens.warn,
80286
+ children: open ? "\u25BE " : "\u25B8 "
80287
+ }, undefined, false, undefined, this),
80288
+ /* @__PURE__ */ jsxDEV20("span", {
80289
+ fg: cursor ? tokens.text : tokens.subtle,
80290
+ children: label
80291
+ }, undefined, false, undefined, this),
80292
+ /* @__PURE__ */ jsxDEV20("span", {
80293
+ fg: tokens.trace,
80294
+ children: truncate3(` ${hint}`, Math.max(0, width - label.length - 6))
80295
+ }, undefined, false, undefined, this)
80296
+ ]
80297
+ }, undefined, true, undefined, this)
80298
+ }, undefined, false, undefined, this);
80299
+ }
80300
+ function ResumePicker({ groups, onDone }) {
80301
+ const { width, height: height2 } = useTerminalDimensions3();
80302
+ const [pane, setPane] = useState7("worktrees");
80303
+ const [wtCursor, setWtCursor] = useState7(0);
80304
+ const [sessCursor, setSessCursor] = useState7(0);
80305
+ const [filter, setFilter] = useState7("");
80306
+ const [agentsOpen, setAgentsOpen] = useState7(false);
80307
+ const [reader, setReader] = useState7(null);
80308
+ const [, setTick] = useState7(0);
80309
+ const listed = useMemo4(() => {
80310
+ const m = new Map;
80311
+ for (const g of groups)
80312
+ m.set(g.name, g.sessions.filter((s) => !isAgentSession(s)));
80313
+ return m;
80314
+ }, [groups]);
80315
+ const { fresh, stale, visibleGroups } = useMemo4(() => {
80316
+ const withSessions = groups.filter((g) => (listed.get(g.name)?.length ?? 0) > 0);
80317
+ const matching = filter ? withSessions.filter((g) => fuzzy(filter, g.name)) : withSessions;
80318
+ const now = Date.now();
80319
+ const byRecency = (a, b) => b.lastActiveMs - a.lastActiveMs;
80320
+ const f = matching.filter((g) => g.current || now - g.lastActiveMs < STALE_MS).sort((a, b) => a.current !== b.current ? a.current ? -1 : 1 : byRecency(a, b));
80321
+ const st = matching.filter((g) => !g.current && now - g.lastActiveMs >= STALE_MS).sort(byRecency);
80322
+ return { fresh: f, stale: st, visibleGroups: [...f, ...st] };
80323
+ }, [groups, filter, listed]);
80324
+ const group = visibleGroups[Math.min(wtCursor, visibleGroups.length - 1)];
80325
+ const sessions2 = useMemo4(() => {
80326
+ if (!group)
80327
+ return [];
80328
+ const base = listed.get(group.name) ?? [];
80329
+ if (!filter)
80330
+ return base;
80331
+ if (fuzzy(filter, group.name))
80332
+ return base;
80333
+ return base.filter((s) => fuzzy(filter, sessionLabel(s)));
80334
+ }, [group, filter, listed]);
80335
+ const agentRows = useMemo4(() => group ? group.sessions.filter(isAgentSession) : [], [group]);
80336
+ const items = useMemo4(() => {
80337
+ const out = sessions2.map((row) => ({ kind: "session", row }));
80338
+ if (agentRows.length > 0 && !filter) {
80339
+ out.push({ kind: "agents", count: agentRows.length });
80340
+ for (const row of agentsOpen ? agentRows : agentRows.slice(0, 3)) {
80341
+ out.push({ kind: "agent", row });
80342
+ }
80343
+ }
80344
+ return out;
80345
+ }, [sessions2, agentRows, agentsOpen, filter]);
80346
+ const cursorItem = items[Math.min(sessCursor, items.length - 1)];
80347
+ const selected = cursorItem && cursorItem.kind !== "agents" ? cursorItem.row : undefined;
80348
+ const turns = detailTurns(height2);
80349
+ const sessionDetailH = DETAIL_CHROME + turns;
80350
+ const listRows = Math.max(3, height2 - sessionDetailH - WORKTREE_DETAIL_H - 4);
80351
+ useEffect6(() => {
80352
+ const start = Math.max(0, Math.min(sessCursor - 2, items.length - listRows));
80353
+ for (const it of items.slice(start, start + listRows + 2)) {
80354
+ if (it.kind !== "agents")
80355
+ hydrateSession(it.row);
80356
+ }
80357
+ if (selected) {
80358
+ hydrateSession(selected);
80359
+ hydrateConversation(selected);
80360
+ }
80361
+ setTick((t) => t + 1);
80362
+ }, [items, sessCursor, listRows, selected]);
80363
+ useEffect6(() => {
80364
+ if (!reader || reader.conv)
80365
+ return;
80366
+ const file2 = reader.row.file;
80367
+ const timer = setTimeout(() => {
80368
+ const conv = readConversation(file2);
80369
+ setReader((r) => r && r.row.file === file2 && !r.conv ? { row: r.row, conv } : r);
80370
+ }, 0);
80371
+ return () => clearTimeout(timer);
80372
+ }, [reader]);
80373
+ const clamp = (v, len) => Math.max(0, Math.min(v, len - 1));
80374
+ useKeyboard4((key) => {
80375
+ if (reader)
80376
+ return;
80377
+ const name = key.name;
80378
+ if (name === "escape") {
80379
+ if (filter) {
80380
+ setFilter("");
80381
+ return;
80382
+ }
80383
+ onDone(null);
80384
+ return;
80385
+ }
80386
+ if (key.ctrl && name === "c") {
80387
+ onDone(null);
80388
+ return;
80389
+ }
80390
+ if (name === "return" || name === "enter") {
80391
+ if (pane === "worktrees") {
80392
+ setPane("sessions");
80393
+ setSessCursor(0);
80394
+ return;
80395
+ }
80396
+ if (cursorItem?.kind === "agents") {
80397
+ setAgentsOpen((v) => !v);
80398
+ return;
80399
+ }
80400
+ if (selected)
80401
+ onDone(selected.id);
80402
+ return;
80403
+ }
80404
+ if (name === "tab") {
80405
+ setPane((p) => p === "worktrees" ? "sessions" : "worktrees");
80406
+ return;
80407
+ }
80408
+ if (name === "left") {
80409
+ setPane("worktrees");
80410
+ return;
80411
+ }
80412
+ if (name === "right") {
80413
+ setPane("sessions");
80414
+ return;
80415
+ }
80416
+ if (name === "up" || name === "down") {
80417
+ const d = name === "up" ? -1 : 1;
80418
+ if (pane === "worktrees") {
80419
+ setWtCursor((c) => clamp(c + d, visibleGroups.length));
80420
+ setSessCursor(0);
80421
+ } else {
80422
+ setSessCursor((c) => clamp(c + d, items.length));
80423
+ }
80424
+ return;
80425
+ }
80426
+ if (name === "a" && !key.ctrl && !key.meta) {
80427
+ setAgentsOpen((v) => !v);
80428
+ return;
80429
+ }
80430
+ if (name === "v" && !key.ctrl && !key.meta) {
80431
+ if (selected)
80432
+ setReader({ row: selected, conv: null });
80433
+ return;
80434
+ }
80435
+ if (name === "backspace") {
80436
+ setFilter((f) => f.slice(0, -1));
80437
+ setWtCursor(0);
80438
+ setSessCursor(0);
80439
+ return;
80440
+ }
80441
+ const ch = key.raw;
80442
+ if (ch && ch.length === 1 && ch >= " " && ch !== "\x7F" && ch !== "a" && ch !== "v") {
80443
+ setFilter((f) => f + ch);
80444
+ setWtCursor(0);
80445
+ setSessCursor(0);
80446
+ }
80447
+ });
80448
+ const bodyW = width;
80449
+ const sidebarW = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, Math.round(width * 0.34)));
80450
+ const rightW = Math.max(30, bodyW - sidebarW);
80451
+ const sessInner = rightW - PANEL_BORDER - SCROLL_CHROME;
80452
+ const sideInner = sidebarW - PANEL_BORDER - SCROLL_CHROME - 1;
80453
+ const sessionDetailInner = rightW - PANEL_CHROME;
80454
+ const worktreeDetailInner = width - PANEL_CHROME;
80455
+ const chipCols = useMemo4(() => {
80456
+ let cols = { age: 1, count: 1, dirty: 0, sync: 0 };
80457
+ for (const g of visibleGroups) {
80458
+ cols.age = Math.max(cols.age, displayWidth(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014"));
80459
+ cols.count = Math.max(cols.count, String(listed.get(g.name)?.length ?? 0).length);
80460
+ if (g.dirty)
80461
+ cols.dirty = Math.max(cols.dirty, String(g.dirty).length);
80462
+ if (g.ahead)
80463
+ cols.sync = Math.max(cols.sync, String(g.ahead).length);
80464
+ if (g.behind)
80465
+ cols.sync = Math.max(cols.sync, String(g.behind).length);
80466
+ }
80467
+ if (blockWidth(cols) > sideInner)
80468
+ cols = { ...cols, sync: 0 };
80469
+ if (blockWidth(cols) > sideInner)
80470
+ cols = { ...cols, dirty: 0 };
80471
+ return cols;
80472
+ }, [visibleGroups, listed, sideInner]);
80473
+ const maxSize = Math.max(1, ...sessions2.slice(0, 400).map((s) => s.sizeBytes));
80474
+ const meterW = Math.max(6, Math.min(14, Math.round(sessInner * 0.16)));
80475
+ const sidebarInnerH = height2 - 1 - WORKTREE_DETAIL_H - 1 - PANEL_BORDER;
80476
+ const listContentH = visibleGroups.length * 2 + (stale.length > 0 ? 2 : 0);
80477
+ const showActivity = sidebarInnerH - listContentH >= ACTIVITY_H;
80478
+ const activityDays = useMemo4(() => dailyActivity(visibleGroups, ACTIVITY_WEEKS * WEEK_DAYS), [visibleGroups]);
80479
+ const shownSessions = [...listed.values()].reduce((a, v) => a + v.length, 0);
80480
+ if (reader) {
80481
+ return /* @__PURE__ */ jsxDEV20(ConversationReader, {
80482
+ row: reader.row,
80483
+ conv: reader.conv,
80484
+ width,
80485
+ height: height2,
80486
+ onClose: () => setReader(null),
80487
+ onResume: () => onDone(reader.row.id),
80488
+ onCancel: () => onDone(null)
80489
+ }, undefined, false, undefined, this);
80490
+ }
80491
+ return /* @__PURE__ */ jsxDEV20("box", {
80492
+ flexDirection: "column",
80493
+ height: height2,
80494
+ backgroundColor: C.bg,
80495
+ children: [
80496
+ /* @__PURE__ */ jsxDEV20("box", {
80497
+ flexDirection: "row",
80498
+ justifyContent: "space-between",
80499
+ height: 1,
80500
+ paddingX: 1,
80501
+ children: [
80502
+ /* @__PURE__ */ jsxDEV20("text", {
80503
+ children: [
80504
+ /* @__PURE__ */ jsxDEV20("span", {
80505
+ fg: tokens.accent,
80506
+ attributes: 1,
80507
+ children: "claudish"
80508
+ }, undefined, false, undefined, this),
80509
+ /* @__PURE__ */ jsxDEV20("span", {
80510
+ fg: tokens.subtle,
80511
+ children: " resume"
80512
+ }, undefined, false, undefined, this)
80513
+ ]
80514
+ }, undefined, true, undefined, this),
80515
+ /* @__PURE__ */ jsxDEV20("text", {
80516
+ children: [
80517
+ /* @__PURE__ */ jsxDEV20("span", {
80518
+ fg: tokens.subtle,
80519
+ children: `${visibleGroups.length} worktrees \xB7 ${shownSessions} sessions`
80520
+ }, undefined, false, undefined, this),
80521
+ filter ? /* @__PURE__ */ jsxDEV20("span", {
80522
+ fg: tokens.warn,
80523
+ children: ` /${filter}`
80524
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
80525
+ ]
80526
+ }, undefined, true, undefined, this)
80527
+ ]
80528
+ }, undefined, true, undefined, this),
80529
+ /* @__PURE__ */ jsxDEV20("box", {
80530
+ flexDirection: "row",
80531
+ flexGrow: 1,
80532
+ minHeight: 0,
80533
+ children: [
80534
+ /* @__PURE__ */ jsxDEV20("box", {
80535
+ width: sidebarW,
80536
+ flexDirection: "column",
80537
+ minHeight: 0,
80538
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
80539
+ title: "worktrees",
80540
+ focused: pane === "worktrees",
80541
+ flush: true,
80542
+ flexGrow: 1,
80543
+ flexBasis: 0,
80544
+ children: [
80545
+ /* @__PURE__ */ jsxDEV20("scrollbox", {
80546
+ focused: false,
80547
+ flexGrow: 1,
80548
+ scrollbarOptions: SCROLLBAR,
80549
+ children: [
80550
+ fresh.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
80551
+ g,
80552
+ cursor: i === wtCursor,
80553
+ width: sideInner,
80554
+ count: listed.get(g.name)?.length ?? 0,
80555
+ cols: chipCols
80556
+ }, g.name, false, undefined, this)),
80557
+ stale.length > 0 ? /* @__PURE__ */ jsxDEV20(SectionHeader, {
80558
+ label: `stale \xB7 ${stale.length} \xB7 idle 3d+`,
80559
+ width: sideInner
80560
+ }, undefined, false, undefined, this) : null,
80561
+ stale.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
80562
+ g,
80563
+ cursor: fresh.length + i === wtCursor,
80564
+ width: sideInner,
80565
+ count: listed.get(g.name)?.length ?? 0,
80566
+ cols: chipCols
80567
+ }, g.name, false, undefined, this))
80568
+ ]
80569
+ }, undefined, true, undefined, this),
80570
+ showActivity ? /* @__PURE__ */ jsxDEV20(ActivityCalendar, {
80571
+ days: activityDays,
80572
+ width: sideInner
80573
+ }, undefined, false, undefined, this) : null
80574
+ ]
80575
+ }, undefined, true, undefined, this)
80576
+ }, undefined, false, undefined, this),
80577
+ /* @__PURE__ */ jsxDEV20("box", {
80578
+ flexDirection: "column",
80579
+ flexGrow: 1,
80580
+ minWidth: 0,
80581
+ minHeight: 0,
80582
+ children: [
80583
+ /* @__PURE__ */ jsxDEV20(Panel, {
80584
+ title: group ? `sessions \xB7 ${truncate3(group.name, 28)}` : "sessions",
80585
+ focused: pane === "sessions",
80586
+ flush: true,
80587
+ flexGrow: 1,
80588
+ flexBasis: 0,
80589
+ children: /* @__PURE__ */ jsxDEV20("scrollbox", {
80590
+ focused: false,
80591
+ flexGrow: 1,
80592
+ scrollbarOptions: SCROLLBAR,
80593
+ children: items.length === 0 ? /* @__PURE__ */ jsxDEV20("text", {
80594
+ fg: tokens.subtle,
80595
+ children: " no sessions match"
80596
+ }, undefined, false, undefined, this) : items.map((it, i) => it.kind === "agents" ? /* @__PURE__ */ jsxDEV20(AgentNode, {
80597
+ count: it.count,
80598
+ open: agentsOpen,
80599
+ cursor: i === sessCursor && pane === "sessions",
80600
+ width: sessInner
80601
+ }, "agents", false, undefined, this) : /* @__PURE__ */ jsxDEV20(SessionRowView, {
80602
+ row: it.row,
80603
+ cursor: i === sessCursor && pane === "sessions",
80604
+ width: sessInner,
80605
+ even: i % 2 === 0,
80606
+ indent: it.kind === "agent" ? 2 : 0,
80607
+ maxSize,
80608
+ meterW
80609
+ }, it.row.id, false, undefined, this))
80610
+ }, undefined, false, undefined, this)
80611
+ }, undefined, false, undefined, this),
80612
+ /* @__PURE__ */ jsxDEV20("box", {
80613
+ height: sessionDetailH,
80614
+ flexShrink: 0,
80615
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
80616
+ title: "session",
80617
+ flexGrow: 1,
80618
+ children: selected ? /* @__PURE__ */ jsxDEV20(SessionDetail, {
80619
+ row: selected,
80620
+ width: sessionDetailInner,
80621
+ turns
80622
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("text", {
80623
+ fg: tokens.subtle,
80624
+ children: cursorItem?.kind === "agents" ? "agent sessions \u2014 enter to expand" : "nothing selected"
80625
+ }, undefined, false, undefined, this)
80626
+ }, undefined, false, undefined, this)
80627
+ }, undefined, false, undefined, this)
80628
+ ]
80629
+ }, undefined, true, undefined, this)
80630
+ ]
80631
+ }, undefined, true, undefined, this),
80632
+ /* @__PURE__ */ jsxDEV20("box", {
80633
+ height: WORKTREE_DETAIL_H,
80634
+ flexShrink: 0,
80635
+ children: /* @__PURE__ */ jsxDEV20(Panel, {
80636
+ title: "worktree",
80637
+ flexGrow: 1,
80638
+ children: /* @__PURE__ */ jsxDEV20(WorktreeDetail, {
80639
+ group,
80640
+ width: worktreeDetailInner,
80641
+ count: sessions2.length
80642
+ }, undefined, false, undefined, this)
80643
+ }, undefined, false, undefined, this)
80644
+ }, undefined, false, undefined, this),
80645
+ /* @__PURE__ */ jsxDEV20("box", {
80646
+ flexDirection: "row",
80647
+ height: 1,
80648
+ paddingX: 1,
80649
+ gap: 2,
80650
+ children: [
80651
+ /* @__PURE__ */ jsxDEV20("text", {
80652
+ fg: tokens.subtle,
80653
+ children: "\u2191\u2193 move"
80654
+ }, undefined, false, undefined, this),
80655
+ /* @__PURE__ */ jsxDEV20("text", {
80656
+ fg: tokens.subtle,
80657
+ children: "\u21E5 pane"
80658
+ }, undefined, false, undefined, this),
80659
+ /* @__PURE__ */ jsxDEV20("text", {
80660
+ fg: tokens.subtle,
80661
+ children: "type to filter"
80662
+ }, undefined, false, undefined, this),
80663
+ /* @__PURE__ */ jsxDEV20("text", {
80664
+ fg: tokens.accent,
80665
+ children: "\u23CE resume"
80666
+ }, undefined, false, undefined, this),
80667
+ /* @__PURE__ */ jsxDEV20("text", {
80668
+ fg: selected ? tokens.info : tokens.trace,
80669
+ children: "v read"
80670
+ }, undefined, false, undefined, this),
80671
+ /* @__PURE__ */ jsxDEV20("text", {
80672
+ fg: agentRows.length > 0 ? tokens.subtle : tokens.trace,
80673
+ children: agentRows.length > 0 ? `a ${agentsOpen ? "collapse" : "expand"} agents` : ""
80674
+ }, undefined, false, undefined, this),
80675
+ /* @__PURE__ */ jsxDEV20("text", {
80676
+ fg: tokens.subtle,
80677
+ children: "esc cancel"
80678
+ }, undefined, false, undefined, this)
80679
+ ]
80680
+ }, undefined, true, undefined, this)
80681
+ ]
80682
+ }, undefined, true, undefined, this);
80683
+ }
80684
+ function Field({
80685
+ label,
80686
+ width,
80687
+ children
80688
+ }) {
80689
+ return /* @__PURE__ */ jsxDEV20("box", {
80690
+ flexDirection: "row",
80691
+ height: 1,
80692
+ gap: 1,
80693
+ children: [
80694
+ /* @__PURE__ */ jsxDEV20("text", {
80695
+ fg: tokens.subtle,
80696
+ children: padTo(label, width)
80697
+ }, undefined, false, undefined, this),
80698
+ children
80699
+ ]
80700
+ }, undefined, true, undefined, this);
80701
+ }
80702
+ function WorktreeDetail({
80703
+ group,
80704
+ width,
80705
+ count
80706
+ }) {
80707
+ if (!group)
80708
+ return /* @__PURE__ */ jsxDEV20("text", {
80709
+ fg: tokens.subtle,
80710
+ children: "no worktree selected"
80711
+ }, undefined, false, undefined, this);
80712
+ const L = 9;
80713
+ const badges = [];
80714
+ if (group.dirty !== undefined) {
80715
+ badges.push({
80716
+ label: group.dirty > 0 ? `${DIRTY_GLYPH}${group.dirty} uncommitted` : "clean",
80717
+ bg: group.dirty > 0 ? CHIP.dirty : CHIP.clean
80718
+ });
80719
+ }
80720
+ if (group.ahead)
80721
+ badges.push({ label: `\u2191${group.ahead}`, bg: CHIP.ahead });
80722
+ if (group.behind)
80723
+ badges.push({ label: `\u2193${group.behind}`, bg: CHIP.behind });
80724
+ const badgeW = badges.reduce((w, b) => w + displayWidth(b.label) + 2, 0);
80725
+ const marker = group.current ? " \u25B6 you are here" : !group.live ? " worktree deleted" : "";
80726
+ const branch = group.branch ?? (group.live ? "detached" : "\u2014");
80727
+ const idAvail = Math.max(12, width - L - displayWidth(marker) - (BRANCH_LEAD + 1) - badgeW - 1);
80728
+ const nameW = Math.max(8, Math.min(displayWidth(group.name), Math.floor(idAvail / 2)));
80729
+ const branchW = Math.max(6, idAvail - nameW);
80730
+ const spark = activitySeries(group.sessions);
80731
+ const summary = ` ${count} session${count === 1 ? "" : "s"} \xB7 created ${group.createdMs ? age(group.createdMs) : "?"} ago \xB7 used ${group.lastActiveMs ? age(group.lastActiveMs) : "?"} ago`;
80732
+ const locAvail = Math.max(20, width - L - spark.length - 2);
80733
+ const pathW = Math.min(displayWidth(group.path ?? "\u2014"), Math.max(16, Math.floor(locAvail * 0.45)));
80734
+ return /* @__PURE__ */ jsxDEV20("box", {
80735
+ flexDirection: "column",
80736
+ children: [
80737
+ /* @__PURE__ */ jsxDEV20("text", {
80738
+ children: [
80739
+ /* @__PURE__ */ jsxDEV20("span", {
80740
+ fg: tokens.subtle,
80741
+ children: padTo("worktree", L)
80742
+ }, undefined, false, undefined, this),
80743
+ /* @__PURE__ */ jsxDEV20("span", {
80744
+ fg: tokens.text,
80745
+ attributes: A.bold,
80746
+ children: truncate3(group.name, nameW)
80747
+ }, undefined, false, undefined, this),
80748
+ /* @__PURE__ */ jsxDEV20("span", {
80749
+ fg: group.current ? HERE_FG : tokens.dead,
80750
+ children: marker
80751
+ }, undefined, false, undefined, this),
80752
+ /* @__PURE__ */ jsxDEV20("span", {
80753
+ fg: tokens.subtle,
80754
+ children: ` ${BRANCH_ICON} `
80755
+ }, undefined, false, undefined, this),
80756
+ /* @__PURE__ */ jsxDEV20("span", {
80757
+ fg: group.live ? tokens.info : tokens.dead,
80758
+ children: `${truncate3(branch, branchW)} `
80759
+ }, undefined, false, undefined, this),
80760
+ badges.map((b) => /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80761
+ label: b.label,
80762
+ bg: b.bg
80763
+ }, b.label, false, undefined, this))
80764
+ ]
80765
+ }, undefined, true, undefined, this),
80766
+ /* @__PURE__ */ jsxDEV20("box", {
80767
+ flexDirection: "row",
80768
+ height: 1,
80769
+ children: [
80770
+ /* @__PURE__ */ jsxDEV20("text", {
80771
+ fg: tokens.subtle,
80772
+ flexShrink: 0,
80773
+ children: padTo("path", L)
80774
+ }, undefined, false, undefined, this),
80775
+ /* @__PURE__ */ jsxDEV20("text", {
80776
+ fg: tokens.trace,
80777
+ flexShrink: 0,
80778
+ children: `${padTo(truncate3(group.path ?? "\u2014", pathW), pathW)} `
80779
+ }, undefined, false, undefined, this),
80780
+ /* @__PURE__ */ jsxDEV20(Sparkline, {
80781
+ values: hasActivity(spark) ? spark : [],
80782
+ fg: SPARK_FG
80783
+ }, undefined, false, undefined, this),
80784
+ /* @__PURE__ */ jsxDEV20("text", {
80785
+ fg: tokens.trace,
80786
+ flexShrink: 0,
80787
+ children: truncate3(summary, Math.max(0, locAvail - pathW))
80788
+ }, undefined, false, undefined, this)
80789
+ ]
80790
+ }, undefined, true, undefined, this)
80791
+ ]
80792
+ }, undefined, true, undefined, this);
80793
+ }
80794
+ function SessionDetail({
80795
+ row,
80796
+ width,
80797
+ turns
80798
+ }) {
80799
+ const mb = row.sizeBytes / 1048576;
80800
+ const L = 9;
80801
+ const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.round(row.sizeBytes / 1024)} KB`;
80802
+ const full = `\xB7 ${size}${row.lastMessageChars !== undefined ? ` \xB7 last msg ${row.lastMessageChars} ch` : ""}`;
80803
+ const suffixRoom = width - L - 1 - row.id.length - 1;
80804
+ const suffix = displayWidth(full) <= suffixRoom ? full : displayWidth(`\xB7 ${size}`) <= suffixRoom ? `\xB7 ${size}` : "";
80805
+ return /* @__PURE__ */ jsxDEV20("box", {
80806
+ flexDirection: "column",
80807
+ children: [
80808
+ /* @__PURE__ */ jsxDEV20(Field, {
80809
+ label: "title",
80810
+ width: L,
80811
+ children: /* @__PURE__ */ jsxDEV20("text", {
80812
+ fg: tokens.text,
80813
+ attributes: A.bold,
80814
+ children: truncate3(sessionLabel(row), width - L)
80815
+ }, undefined, false, undefined, this)
80816
+ }, undefined, false, undefined, this),
80817
+ /* @__PURE__ */ jsxDEV20(Field, {
80818
+ label: "id",
80819
+ width: L,
80820
+ children: [
80821
+ /* @__PURE__ */ jsxDEV20("text", {
80822
+ fg: tokens.info,
80823
+ flexShrink: 0,
80824
+ children: row.id
80825
+ }, undefined, false, undefined, this),
80826
+ suffix ? /* @__PURE__ */ jsxDEV20("text", {
80827
+ fg: tokens.trace,
80828
+ flexShrink: 0,
80829
+ children: suffix
80830
+ }, undefined, false, undefined, this) : null
80831
+ ]
80832
+ }, undefined, true, undefined, this),
80833
+ /* @__PURE__ */ jsxDEV20("box", {
80834
+ height: 1
80835
+ }, undefined, false, undefined, this),
80836
+ /* @__PURE__ */ jsxDEV20(Conversation, {
80837
+ turns: row.recentTurns ?? [],
80838
+ width,
80839
+ max: turns
80840
+ }, undefined, false, undefined, this)
80841
+ ]
80842
+ }, undefined, true, undefined, this);
80843
+ }
80844
+ function Conversation({
80845
+ turns,
80846
+ width,
80847
+ max
80848
+ }) {
80849
+ if (turns.length === 0) {
80850
+ return /* @__PURE__ */ jsxDEV20("box", {
80851
+ flexDirection: "column",
80852
+ width,
80853
+ children: /* @__PURE__ */ jsxDEV20("text", {
80854
+ fg: tokens.trace,
80855
+ children: "no conversation recorded"
80856
+ }, undefined, false, undefined, this)
80857
+ }, undefined, false, undefined, this);
80858
+ }
80859
+ const ROLE_W2 = 6;
80860
+ const textW = Math.max(10, width - ROLE_W2 - 2);
80861
+ const shown = turns.slice(-max);
80862
+ return /* @__PURE__ */ jsxDEV20("box", {
80863
+ flexDirection: "column",
80864
+ width,
80865
+ flexShrink: 0,
80866
+ children: shown.map((t, i) => /* @__PURE__ */ jsxDEV20("text", {
80867
+ children: [
80868
+ /* @__PURE__ */ jsxDEV20("span", {
80869
+ fg: tokens.trace,
80870
+ children: "\u258D"
80871
+ }, undefined, false, undefined, this),
80872
+ /* @__PURE__ */ jsxDEV20(BadgeSpan, {
80873
+ label: t.role === "user" ? "you" : "ai",
80874
+ bg: t.role === "user" ? SPEAKER.you : SPEAKER.ai,
80875
+ width: ROLE_W2
80876
+ }, undefined, false, undefined, this),
80877
+ /* @__PURE__ */ jsxDEV20("span", {
80878
+ fg: t.role === "user" ? tokens.text : MUTED,
80879
+ children: truncate3(t.text, textW)
80880
+ }, undefined, false, undefined, this)
80881
+ ]
80882
+ }, `${i}-${t.text.slice(0, 12)}`, true, undefined, this))
80883
+ }, undefined, false, undefined, this);
80884
+ }
80885
+ var PANEL_CHROME = 4, PANEL_BORDER = 2, SCROLL_CHROME = 1, SIDEBAR_MIN = 28, SIDEBAR_MAX = 50, DETAIL_CHROME = 5, WORKTREE_DETAIL_H = 4, STALE_MS, GH_LEVELS, CHIP, HERE_FG, LIVE_W = 2, DIRTY_GLYPH = "+", SESSION_AGE_W = 3, SESSION_AGE_COL, SIZE_FLOOR_BYTES, MUTED, SCROLLBAR, WEEK_DAYS = 7, ACTIVITY_WEEKS = 6, BRANCH_ICON = "\u2387", BRANCH_LEAD = 5, BRANCH_LEAD_DENSE = 6, WEEK_LABEL_W = 4, ACTIVITY_H, SPARK_MIN_DAYS = 7, SPARK_FG = "#3f6f9e";
80886
+ var init_resume_picker = __esm(() => {
80887
+ init_theme2();
80888
+ init_text();
80889
+ init_tokens();
80890
+ init_widgets();
80891
+ init_conversation_reader();
80892
+ init_conversation();
80893
+ init_session_discovery();
80894
+ STALE_MS = 3 * 86400000;
80895
+ GH_LEVELS = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
80896
+ CHIP = {
80897
+ fresh: GH_LEVELS[4],
80898
+ stale: "#a1a9b3",
80899
+ count: "#58a6ff",
80900
+ dirty: "#d29922",
80901
+ clean: GH_LEVELS[3],
80902
+ ahead: "#bc8cff",
80903
+ behind: "#d2a8ff"
80904
+ };
80905
+ HERE_FG = GH_LEVELS[4];
80906
+ SESSION_AGE_COL = SESSION_AGE_W + 3;
80907
+ SIZE_FLOOR_BYTES = 16 * 1024;
80908
+ MUTED = C.fgMuted;
80909
+ SCROLLBAR = {
80910
+ showArrows: false,
80911
+ trackOptions: { backgroundColor: tokens.bgPanel, foregroundColor: tokens.border }
80912
+ };
80913
+ ACTIVITY_H = 2 + ACTIVITY_WEEKS;
80914
+ });
80915
+
80916
+ // src/session/resume-picker-run.tsx
80917
+ var exports_resume_picker_run = {};
80918
+ __export(exports_resume_picker_run, {
80919
+ runResumePicker: () => runResumePicker
80920
+ });
80921
+ import { createCliRenderer as createCliRenderer3 } from "@opentui/core";
80922
+ import { createRoot as createRoot3 } from "@opentui/react";
80923
+ import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
80924
+ async function runResumePicker(cwd = process.cwd()) {
80925
+ const repo = getRepoContext(cwd);
80926
+ if (!repo)
80927
+ return { sessionId: null, hadSessions: false };
80928
+ const groups = discoverWorktreeGroups(repo);
80929
+ if (groups.length === 0)
80930
+ return { sessionId: null, hadSessions: false };
80931
+ await enrichWorktreeGit(groups, repo.root);
80932
+ setStderrQuiet(true);
80933
+ const renderer = await createCliRenderer3({
80934
+ useAlternateScreen: true,
80935
+ exitOnCtrlC: false
80936
+ });
80937
+ const root = createRoot3(renderer);
80938
+ let chosen = null;
80939
+ try {
80940
+ await new Promise((resolve5) => {
80941
+ let settled = false;
80942
+ const done = (id) => {
80943
+ if (settled)
80944
+ return;
80945
+ settled = true;
80946
+ chosen = id;
80947
+ resolve5();
80948
+ };
80949
+ root.render(/* @__PURE__ */ jsxDEV21(ResumePicker, {
80950
+ groups,
80951
+ onDone: done
80952
+ }, undefined, false, undefined, this));
80953
+ });
80954
+ } finally {
80955
+ try {
80956
+ root.unmount();
80957
+ } catch {}
80958
+ try {
80959
+ renderer.destroy();
80960
+ } catch {}
80961
+ setStderrQuiet(false);
80962
+ }
80963
+ return { sessionId: chosen, hadSessions: true };
80964
+ }
80965
+ var init_resume_picker_run = __esm(() => {
80966
+ init_logger();
80967
+ init_resume_picker();
80968
+ init_session_discovery();
80969
+ });
80970
+
80971
+ // src/session/baseline-pricing.ts
80972
+ function resolveBaseline(alias, label) {
80973
+ const entry = findEntryByAlias(alias);
80974
+ if (!entry)
80975
+ return null;
80976
+ const firstParty = entry.aggregators?.find((a) => a.provider === FIRST_PARTY && typeof a.pricing?.input === "number");
80977
+ const input = firstParty?.pricing?.input;
80978
+ const output = firstParty?.pricing?.output;
80979
+ if (typeof input !== "number" || typeof output !== "number")
80980
+ return null;
80981
+ if (input <= 0 || output <= 0)
80982
+ return null;
80983
+ return { modelId: entry.modelId, label, inputPerM: input, outputPerM: output };
80984
+ }
80985
+ function getBaselines() {
80986
+ return BASELINE_ALIASES.map(({ alias, label }) => resolveBaseline(alias, label)).filter((b) => b !== null);
80987
+ }
80988
+ function baselineCost(b, inputTokens, outputTokens) {
80989
+ return inputTokens / 1e6 * b.inputPerM + outputTokens / 1e6 * b.outputPerM;
80990
+ }
80991
+ var BASELINE_ALIASES, FIRST_PARTY = "anthropic";
80992
+ var init_baseline_pricing = __esm(() => {
80993
+ init_catalog_query();
80994
+ BASELINE_ALIASES = [
80995
+ { alias: "~anthropic/claude-sonnet-latest", label: "Sonnet" },
80996
+ { alias: "~anthropic/claude-opus-latest", label: "Opus" }
80997
+ ];
80998
+ });
80999
+
81000
+ // src/session/session-stats.ts
81001
+ var exports_session_stats = {};
81002
+ __export(exports_session_stats, {
81003
+ tokenFilePath: () => tokenFilePath,
81004
+ readSessionStats: () => readSessionStats,
81005
+ computeSavings: () => computeSavings
81006
+ });
81007
+ import { readFileSync as readFileSync28 } from "fs";
81008
+ import { homedir as homedir35 } from "os";
81009
+ import { join as join38 } from "path";
81010
+ function tokenFilePath(port) {
81011
+ return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
81012
+ }
81013
+ function readSessionStats(port) {
81014
+ let raw2;
81015
+ try {
81016
+ raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
81017
+ } catch {
81018
+ return null;
81019
+ }
81020
+ if (!raw2 || typeof raw2 !== "object")
81021
+ return null;
81022
+ const d = raw2;
81023
+ const inputTokens = num(d.input_tokens);
81024
+ const outputTokens = num(d.output_tokens);
81025
+ if (inputTokens <= 0 && outputTokens <= 0)
81026
+ return null;
81027
+ const costUsd = num(d.total_cost);
81028
+ const isFree = d.is_free === true;
81029
+ const contextWindow = typeof d.context_window === "number" ? d.context_window : null;
81030
+ const contextUsed = contextWindow && contextWindow > 0 ? Math.min(1, Math.max(0, inputTokens / contextWindow)) : null;
81031
+ const toolCalls = Array.isArray(d.tool_calls) ? d.tool_calls.map((t) => t).filter((t) => typeof t?.name === "string" && num(t.count) > 0).map((t) => ({ name: String(t.name), count: num(t.count) })) : [];
81032
+ const startedAt = num(d.started_at);
81033
+ const updatedAt = num(d.updated_at);
81034
+ const durationMs = startedAt > 0 && updatedAt > startedAt ? updatedAt - startedAt : 0;
81035
+ const billedInputTokens = num(d.billed_input_tokens) || inputTokens;
81036
+ const rawIn = billedInputTokens / 1e6 * num(d.input_per_m);
81037
+ const rawOut = outputTokens / 1e6 * num(d.output_per_m);
81038
+ const rawTotal = rawIn + rawOut;
81039
+ const scale = rawTotal > 0 && costUsd > 0 ? costUsd / rawTotal : 0;
81040
+ return {
81041
+ inputTokens,
81042
+ outputTokens,
81043
+ totalTokens: num(d.total_tokens) || inputTokens + outputTokens,
81044
+ costUsd,
81045
+ isFree,
81046
+ isEstimated: d.is_estimated === true,
81047
+ providerName: typeof d.provider_name === "string" ? d.provider_name : "",
81048
+ modelName: typeof d.model_name === "string" ? d.model_name : "",
81049
+ contextWindow,
81050
+ contextUsed,
81051
+ toolCalls,
81052
+ toolCallTotal: toolCalls.reduce((a, t) => a + t.count, 0),
81053
+ durationMs,
81054
+ savings: computeSavings(billedInputTokens, outputTokens, costUsd),
81055
+ inputCostUsd: rawIn * scale,
81056
+ outputCostUsd: rawOut * scale,
81057
+ billedInputTokens
81058
+ };
81059
+ }
81060
+ function computeSavings(inputTokens, outputTokens, actualUsd) {
81061
+ return getBaselines().map((b) => {
81062
+ const baselineUsd = baselineCost(b, inputTokens, outputTokens);
81063
+ return {
81064
+ label: b.label,
81065
+ modelId: b.modelId,
81066
+ baselineUsd,
81067
+ savedUsd: baselineUsd - actualUsd
81068
+ };
81069
+ });
81070
+ }
81071
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
81072
+ var init_session_stats = __esm(() => {
81073
+ init_baseline_pricing();
81074
+ });
81075
+
81076
+ // src/session/ansi-viz.ts
81077
+ function rgb(hex4) {
81078
+ return [
81079
+ Number.parseInt(hex4.slice(1, 3), 16),
81080
+ Number.parseInt(hex4.slice(3, 5), 16),
81081
+ Number.parseInt(hex4.slice(5, 7), 16)
81082
+ ];
81083
+ }
81084
+ function fg(hex4) {
81085
+ const [r, g, b] = rgb(hex4);
81086
+ return `\x1B[38;2;${r};${g};${b}m`;
81087
+ }
81088
+ function bg(hex4) {
81089
+ const [r, g, b] = rgb(hex4);
81090
+ return `\x1B[48;2;${r};${g};${b}m`;
81091
+ }
81092
+ function paint(text, hex4, bold4 = false) {
81093
+ return `${bold4 ? BOLD5 : ""}${fg(hex4)}${text}${RESET4}`;
81094
+ }
81095
+ function meter(pct, width, ramp = ramps.load) {
81096
+ const cells = Math.floor(width);
81097
+ if (!Number.isFinite(cells) || cells <= 0)
81098
+ return "";
81099
+ if (Number.isNaN(pct))
81100
+ return paint(NODATA2.repeat(cells), tokens.dead);
81101
+ const cols = rampFor(cells, ramp);
81102
+ const filled = fillCells(pct, cells);
81103
+ let out = "";
81104
+ let last = "";
81105
+ for (let i = 0;i < cells; i++) {
81106
+ const hex4 = i < filled ? cols[i] : tokens.border;
81107
+ if (hex4 !== last) {
81108
+ out += fg(hex4);
81109
+ last = hex4;
81110
+ }
81111
+ out += i < filled ? FILL2 : TRACK2;
81112
+ }
81113
+ return out + RESET4;
81114
+ }
81115
+ function stackedBar(segments, width) {
81116
+ const cells = Math.floor(width);
81117
+ if (!Number.isFinite(cells) || cells <= 0 || segments.length === 0)
81118
+ return "";
81119
+ const split = splitCells(segments.map((s) => s.value), cells);
81120
+ if (split.reduce((a, b) => a + b, 0) === 0)
81121
+ return paint(TRACK2.repeat(cells), tokens.border);
81122
+ let out = "";
81123
+ for (let i = 0;i < split.length; i++) {
81124
+ const n = split[i];
81125
+ if (n > 0)
81126
+ out += `${bg(segments[i].color)}${" ".repeat(n)}`;
81127
+ }
81128
+ return out + RESET4;
81129
+ }
81130
+ function badge(label, hex4) {
81131
+ return `${BOLD5}${fg(pickInk(hex4))}${bg(hex4)} ${label} ${RESET4}`;
81132
+ }
81133
+ function stripAnsi4(s) {
81134
+ return s.replace(ANSI_RE3, "");
81135
+ }
81136
+ function visibleWidth(s) {
81137
+ return displayWidth(stripAnsi4(s));
81138
+ }
81139
+ function clipStyled(s, width) {
81140
+ if (width <= 0)
81141
+ return "";
81142
+ if (visibleWidth(s) <= width)
81143
+ return s;
81144
+ let out = "";
81145
+ let w = 0;
81146
+ let i = 0;
81147
+ while (i < s.length) {
81148
+ if (s[i] === "\x1B") {
81149
+ const end = s.indexOf("m", i);
81150
+ if (end === -1)
81151
+ break;
81152
+ out += s.slice(i, end + 1);
81153
+ i = end + 1;
81154
+ continue;
81155
+ }
81156
+ const ch = [...s.slice(i)][0] ?? "";
81157
+ const cw = displayWidth(ch);
81158
+ if (w + cw > width)
81159
+ break;
81160
+ out += ch;
81161
+ w += cw;
81162
+ i += ch.length;
81163
+ }
81164
+ return out + RESET4;
81165
+ }
81166
+ function padVisible2(s, width, align = "left") {
81167
+ const clipped = clipStyled(s, width);
81168
+ const pad2 = Math.max(0, width - visibleWidth(clipped));
81169
+ return align === "left" ? clipped + " ".repeat(pad2) : " ".repeat(pad2) + clipped;
81170
+ }
81171
+ function compact(n) {
81172
+ if (!Number.isFinite(n))
81173
+ return "\u2014";
81174
+ const a = Math.abs(n);
81175
+ if (a >= 1e9)
81176
+ return `${(n / 1e9).toFixed(1)}G`;
81177
+ if (a >= 1e6)
81178
+ return `${(n / 1e6).toFixed(1)}M`;
81179
+ if (a >= 1000)
81180
+ return `${(n / 1000).toFixed(1)}K`;
81181
+ return String(Math.round(n));
81182
+ }
81183
+ function duration3(ms) {
81184
+ if (!Number.isFinite(ms) || ms <= 0)
81185
+ return "\u2014";
81186
+ const s = Math.round(ms / 1000);
81187
+ if (s < 60)
81188
+ return `${s}s`;
81189
+ const m = Math.floor(s / 60);
81190
+ if (m < 60)
81191
+ return `${m}m ${String(s % 60).padStart(2, "0")}s`;
81192
+ return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
81193
+ }
81194
+ function usd(n) {
81195
+ if (!Number.isFinite(n))
81196
+ return "\u2014";
81197
+ const a = Math.abs(n);
81198
+ if (a === 0)
81199
+ return "$0";
81200
+ if (a < 0.01)
81201
+ return `$${n.toFixed(4)}`;
81202
+ if (a < 1)
81203
+ return `$${n.toFixed(3)}`;
81204
+ return `$${n.toFixed(2)}`;
81205
+ }
81206
+ var RESET4 = "\x1B[0m", BOLD5 = "\x1B[1m", FILL2 = "\u2588", TRACK2 = "\u2591", NODATA2 = "\u254C", ANSI_RE3;
81207
+ var init_ansi_viz = __esm(() => {
81208
+ init_color();
81209
+ init_text();
81210
+ init_tokens();
81211
+ init_widgets();
81212
+ ANSI_RE3 = /\x1b\[[0-9;]*m/g;
81213
+ });
81214
+
81215
+ // src/session/session-summary.ts
81216
+ var exports_session_summary = {};
81217
+ __export(exports_session_summary, {
81218
+ renderSessionSummary: () => renderSessionSummary,
81219
+ printSessionSummary: () => printSessionSummary
81220
+ });
81221
+ function cardWidth() {
81222
+ const cols = process.stdout.columns || 80;
81223
+ return Math.max(MIN_W, Math.min(MAX_W, cols - 2));
81224
+ }
81225
+ function renderSessionSummary(input) {
81226
+ const { stats, modelSpec, resumeModelSpec, resumeId, exitCode } = input;
81227
+ const W2 = cardWidth();
81228
+ const inner = W2 - CHROME;
81229
+ const out = [];
81230
+ const dim3 = (s) => paint(s, tokens.subtle);
81231
+ const body = (s) => paint(s, tokens.text);
81232
+ const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
81233
+ const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
81234
+ const rule = "\u2500".repeat(Math.max(0, W2 - 2 - visibleWidth(titleText) - 1));
81235
+ out.push(paint("\u256D\u2500", tokens.border) + paint(titleText, titleHex) + paint(rule + "\u256E", tokens.border));
81236
+ const row = (s) => {
81237
+ out.push(`${paint("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint("\u2502", tokens.border)}`);
81238
+ };
81239
+ const blank = () => row("");
81240
+ const chips = [badge(truncate3(modelSpec, 34), tokens.accent)];
81241
+ if (stats.isFree)
81242
+ chips.push(badge("FREE", C.pillKeyBg));
81243
+ else if (stats.isEstimated)
81244
+ chips.push(badge("EST", "#8a7d1e"));
81245
+ if (exitCode !== 0)
81246
+ chips.push(badge(`EXIT ${exitCode}`, "#9e2b2b"));
81247
+ const right = body(duration3(stats.durationMs));
81248
+ const left = clipStyled(chips.join(" "), Math.max(0, inner - visibleWidth(right) - 1));
81249
+ const gap = Math.max(1, inner - visibleWidth(left) - visibleWidth(right));
81250
+ row(left + " ".repeat(gap) + right);
81251
+ if (stats.providerName)
81252
+ row(dim3(truncate3(stats.providerName, inner)));
81253
+ blank();
81254
+ const VALUE_W = 24;
81255
+ const barW = Math.max(12, inner - LABEL_W - VALUE_W);
81256
+ const dataRow = (label, bar, values) => {
81257
+ row(dim3(padTo(label, LABEL_W)) + bar + padVisible2(values, VALUE_W, "right"));
81258
+ };
81259
+ if (stats.contextUsed !== null && stats.contextWindow) {
81260
+ const pct = stats.contextUsed * 100;
81261
+ dataRow("context", meter(pct, barW, ramps.load), body(padStartTo(`${Math.round(pct)}%`, 4)) + dim3(` ${compact(stats.inputTokens)}/${compact(stats.contextWindow)}`));
81262
+ }
81263
+ dataRow("tokens", stackedBar([
81264
+ { value: stats.inputTokens, color: C.blue },
81265
+ { value: stats.outputTokens, color: C.cyan }
81266
+ ], barW), dim3("in ") + body(compact(stats.inputTokens)) + dim3(" out ") + body(compact(stats.outputTokens)));
81267
+ if (!stats.isFree && stats.inputCostUsd + stats.outputCostUsd > 0) {
81268
+ dataRow("spend", stackedBar([
81269
+ { value: stats.inputCostUsd, color: C.blue },
81270
+ { value: stats.outputCostUsd, color: C.cyan }
81271
+ ], barW), dim3("in ") + body(usd(stats.inputCostUsd)) + dim3(" out ") + body(usd(stats.outputCostUsd)));
81272
+ }
81273
+ if (stats.toolCallTotal > 0) {
81274
+ const shown = stats.toolCalls.slice(0, TOOL_COLORS.length);
81275
+ const rest = stats.toolCalls.slice(TOOL_COLORS.length).reduce((a, t) => a + t.count, 0);
81276
+ const segs = shown.map((t, i) => ({ value: t.count, color: TOOL_COLORS[i] }));
81277
+ if (rest > 0)
81278
+ segs.push({ value: rest, color: TOOL_OTHER });
81279
+ dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim3(" calls"));
81280
+ const legend = shown.map((t, i) => paint(`${t.name} ${t.count}`, TOOL_COLORS[i])).concat(rest > 0 ? [paint(`other ${rest}`, TOOL_OTHER)] : []);
81281
+ for (const line of wrapStyled(legend, dim3(" \xB7 "), inner - LABEL_W)) {
81282
+ row(" ".repeat(LABEL_W) + line);
81283
+ }
81284
+ }
81285
+ blank();
81286
+ row(dim3(padTo("cost", LABEL_W)) + paint(stats.isFree ? "free" : usd(stats.costUsd), stats.isFree ? tokens.success : tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim3(" estimated") : ""));
81287
+ for (const s of stats.savings) {
81288
+ const label = padTo(`vs ${s.label}`, LABEL_W);
81289
+ if (s.savedUsd >= 0) {
81290
+ const pct = s.baselineUsd > 0 ? s.savedUsd / s.baselineUsd * 100 : 0;
81291
+ dataRow(label, meter(pct, barW, ramps.savings), paint(padStartTo(`${Math.round(pct)}%`, 4), tokens.success) + dim3(" saved ") + paint(usd(s.savedUsd), tokens.success));
81292
+ } else {
81293
+ dataRow(label, meter(0, barW, ramps.savings), dim3("over by ") + paint(usd(-s.savedUsd), tokens.error));
81294
+ }
81295
+ }
81296
+ out.push(paint(`\u2570${"\u2500".repeat(W2 - 2)}\u256F`, tokens.border));
81297
+ if (resumeId) {
81298
+ out.push("");
81299
+ out.push(dim3("Resume this session with:"));
81300
+ const modelFlag = resumeModelSpec ? `--model ${resumeModelSpec} ` : "";
81301
+ out.push(`claudish ${modelFlag}--resume ${resumeId}`);
81302
+ }
81303
+ return out;
81304
+ }
81305
+ function wrapStyled(chips, sep, width) {
81306
+ const lines = [];
81307
+ let cur = "";
81308
+ let curW = 0;
81309
+ const sepW = visibleWidth(sep);
81310
+ for (const chip of chips) {
81311
+ const w = visibleWidth(chip);
81312
+ if (cur && curW + sepW + w > width) {
81313
+ lines.push(cur);
81314
+ cur = chip;
81315
+ curW = w;
81316
+ } else {
81317
+ cur = cur ? cur + sep + chip : chip;
81318
+ curW = cur === chip ? w : curW + sepW + w;
81319
+ }
81320
+ }
81321
+ if (cur)
81322
+ lines.push(cur);
81323
+ return lines;
81324
+ }
81325
+ function printSessionSummary(input, write) {
81326
+ for (const line of renderSessionSummary(input))
81327
+ write(line);
81328
+ write(RESET4);
81329
+ }
81330
+ var TOOL_COLORS, TOOL_OTHER, MIN_W = 62, MAX_W = 96, CHROME = 4, LABEL_W = 10;
81331
+ var init_session_summary = __esm(() => {
81332
+ init_theme2();
81333
+ init_text();
81334
+ init_tokens();
81335
+ init_ansi_viz();
81336
+ TOOL_COLORS = [
81337
+ C.blue,
81338
+ C.cyan,
81339
+ "#8a7d1e",
81340
+ "#1f6d75",
81341
+ C.magenta,
81342
+ "#2d6e3e",
81343
+ C.orange
81344
+ ];
81345
+ TOOL_OTHER = C.dim;
81346
+ });
81347
+
78414
81348
  // src/index.ts
78415
81349
  init_op_source();
78416
81350
  init_startup_trace();
78417
81351
  var import_dotenv3 = __toESM(require_main(), 1);
78418
- import { existsSync as existsSync29, readFileSync as readFileSync28 } from "fs";
78419
- import { join as join37, resolve as resolve5 } from "path";
81352
+ import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
81353
+ import { join as join39, resolve as resolve5 } from "path";
78420
81354
  import_dotenv3.config({ quiet: true });
78421
81355
  function classifyStartupKind() {
78422
81356
  const argv = process.argv.slice(2);
@@ -78659,14 +81593,14 @@ async function runCli() {
78659
81593
  if (cliConfig.team && cliConfig.team.length > 0) {
78660
81594
  let prompt = cliConfig.claudeArgs.join(" ");
78661
81595
  if (cliConfig.inputFile) {
78662
- prompt = readFileSync28(cliConfig.inputFile, "utf-8");
81596
+ prompt = readFileSync29(cliConfig.inputFile, "utf-8");
78663
81597
  }
78664
81598
  if (!prompt.trim()) {
78665
81599
  console.error("Error: --team requires a prompt (positional args or -f <file>)");
78666
81600
  process.exit(1);
78667
81601
  }
78668
81602
  const mode = cliConfig.teamMode ?? "default";
78669
- const sessionPath = join37(process.cwd(), `.claudish-team-${Date.now()}`);
81603
+ const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
78670
81604
  if (mode === "json") {
78671
81605
  const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
78672
81606
  setupSession2(sessionPath, cliConfig.team, prompt);
@@ -78676,9 +81610,9 @@ async function runCli() {
78676
81610
  });
78677
81611
  const result = { ...status2, responses: {} };
78678
81612
  for (const anonId of Object.keys(status2.models)) {
78679
- const responsePath = join37(sessionPath, `response-${anonId}.md`);
81613
+ const responsePath = join39(sessionPath, `response-${anonId}.md`);
78680
81614
  try {
78681
- const raw2 = readFileSync28(responsePath, "utf-8").trim();
81615
+ const raw2 = readFileSync29(responsePath, "utf-8").trim();
78682
81616
  try {
78683
81617
  result.responses[anonId] = JSON.parse(raw2);
78684
81618
  } catch {
@@ -78703,8 +81637,8 @@ async function runCli() {
78703
81637
  Team Status`);
78704
81638
  for (const id of modelIds) {
78705
81639
  const m = status.models[id];
78706
- const duration3 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
78707
- console.log(` ${id} ${m.state.padEnd(10)} ${duration3}`);
81640
+ const duration4 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
81641
+ console.log(` ${id} ${m.state.padEnd(10)} ${duration4}`);
78708
81642
  }
78709
81643
  process.exit(0);
78710
81644
  }
@@ -78855,6 +81789,28 @@ Team Status`);
78855
81789
  haiku: cliConfig.modelHaiku,
78856
81790
  subagent: cliConfig.modelSubagent
78857
81791
  };
81792
+ let resumedSessionId = (() => {
81793
+ const i = cliConfig.claudeArgs.indexOf("--resume");
81794
+ const v = i !== -1 ? cliConfig.claudeArgs[i + 1] : undefined;
81795
+ return v && !v.startsWith("-") ? v : null;
81796
+ })();
81797
+ if (cliConfig._resumePicker) {
81798
+ const canDrawTui = Boolean(process.stdin.isTTY && process.stdout.isTTY);
81799
+ if (!canDrawTui || cliConfig._hasPrintFlag || !cliConfig.interactive) {
81800
+ cliConfig.claudeArgs.push("--resume");
81801
+ } else {
81802
+ const { runResumePicker: runResumePicker2 } = await Promise.resolve().then(() => (init_resume_picker_run(), exports_resume_picker_run));
81803
+ const outcome = await runResumePicker2();
81804
+ if (!outcome.hadSessions) {
81805
+ cliConfig.claudeArgs.push("--resume");
81806
+ } else if (!outcome.sessionId) {
81807
+ process.exit(0);
81808
+ } else {
81809
+ cliConfig.claudeArgs.push("--resume", outcome.sessionId);
81810
+ resumedSessionId = outcome.sessionId;
81811
+ }
81812
+ }
81813
+ }
78858
81814
  const proxy = await traceSpan("startup:proxy-start", () => createProxyServer2(port, cliConfig.monitor ? undefined : cliConfig.openrouterApiKey, cliConfig.monitor ? undefined : explicitModel, cliConfig.monitor, cliConfig.anthropicApiKey, modelMap, {
78859
81815
  summarizeTools: cliConfig.summarizeTools,
78860
81816
  quiet: cliConfig.quiet,
@@ -78888,6 +81844,25 @@ Team Status`);
78888
81844
  const write = cliConfig.interactive ? console.log : console.error;
78889
81845
  write(`[claudish] Done
78890
81846
  `);
81847
+ try {
81848
+ const [{ readSessionStats: readSessionStats2 }, { printSessionSummary: printSessionSummary2 }, { findLatestSessionId: findLatestSessionId2 }] = await Promise.all([
81849
+ Promise.resolve().then(() => (init_session_stats(), exports_session_stats)),
81850
+ Promise.resolve().then(() => (init_session_summary(), exports_session_summary)),
81851
+ Promise.resolve().then(() => (init_session_discovery(), exports_session_discovery))
81852
+ ]);
81853
+ const stats = readSessionStats2(port);
81854
+ if (stats) {
81855
+ printSessionSummary2({
81856
+ stats,
81857
+ modelSpec: explicitModel || stats.modelName || "",
81858
+ resumeModelSpec: explicitModel ?? null,
81859
+ resumeId: resumedSessionId ?? findLatestSessionId2(process.cwd(), Date.now() - stats.durationMs),
81860
+ exitCode
81861
+ }, write);
81862
+ }
81863
+ } catch (e) {
81864
+ console.error(`[claudish] session summary unavailable: ${e}`);
81865
+ }
78891
81866
  }
78892
81867
  const sessionLogPath = getAlwaysOnLogPath2();
78893
81868
  if (exitCode !== 0 && sessionLogPath && !cliConfig.quiet) {