bertrand 0.22.1 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bertrand.js CHANGED
@@ -599,8 +599,6 @@ var init_router = __esm(() => {
599
599
  aliases = new Map;
600
600
  HOOK_COMMANDS = new Set([
601
601
  "update",
602
- "snapshot",
603
- "recap-thinking",
604
602
  "assistant-message",
605
603
  "notify",
606
604
  "badge"
@@ -610,7 +608,6 @@ var init_router = __esm(() => {
610
608
  // src/db/schema.ts
611
609
  var exports_schema = {};
612
610
  __export(exports_schema, {
613
- worktreeAssociations: () => worktreeAssociations,
614
611
  sessions: () => sessions,
615
612
  sessionStats: () => sessionStats,
616
613
  sessionLabels: () => sessionLabels,
@@ -621,7 +618,7 @@ __export(exports_schema, {
621
618
  });
622
619
  import { sqliteTable, text, integer, index, uniqueIndex } from "drizzle-orm/sqlite-core";
623
620
  import { sql } from "drizzle-orm";
624
- var categories, labels, sessions, sessionLabels, conversations, events, worktreeAssociations, sessionStats;
621
+ var categories, labels, sessions, sessionLabels, conversations, events, sessionStats;
625
622
  var init_schema = __esm(() => {
626
623
  categories = sqliteTable("categories", {
627
624
  id: text("id").primaryKey(),
@@ -677,7 +674,6 @@ var init_schema = __esm(() => {
677
674
  startedAt: text("started_at").notNull().default(sql`(datetime('now'))`),
678
675
  endedAt: text("ended_at"),
679
676
  discarded: integer("discarded", { mode: "boolean" }).notNull().default(false),
680
- lastQuestion: text("last_question"),
681
677
  eventCount: integer("event_count").notNull().default(0)
682
678
  }, (t) => [index("conv_session").on(t.sessionId)]);
683
679
  events = sqliteTable("events", {
@@ -694,24 +690,11 @@ var init_schema = __esm(() => {
694
690
  index("ev_event_created").on(t.event, t.createdAt),
695
691
  index("ev_conversation").on(t.conversationId)
696
692
  ]);
697
- worktreeAssociations = sqliteTable("worktree_associations", {
698
- id: text("id").primaryKey(),
699
- sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
700
- branch: text("branch").notNull(),
701
- worktreePath: text("worktree_path"),
702
- active: integer("active", { mode: "boolean" }).notNull().default(true),
703
- enteredAt: text("entered_at").notNull().default(sql`(datetime('now'))`),
704
- exitedAt: text("exited_at")
705
- }, (t) => [
706
- index("wt_session").on(t.sessionId),
707
- index("wt_active").on(t.active)
708
- ]);
709
693
  sessionStats = sqliteTable("session_stats", {
710
694
  sessionId: text("session_id").primaryKey().references(() => sessions.id, { onDelete: "cascade" }),
711
695
  eventCount: integer("event_count").notNull().default(0),
712
696
  conversationCount: integer("conversation_count").notNull().default(0),
713
697
  interactionCount: integer("interaction_count").notNull().default(0),
714
- prCount: integer("pr_count").notNull().default(0),
715
698
  claudeWorkS: integer("claude_work_s").notNull().default(0),
716
699
  userWaitS: integer("user_wait_s").notNull().default(0),
717
700
  activePct: integer("active_pct").notNull().default(0),
@@ -859,9 +842,6 @@ function getConversationsBySession(sessionId) {
859
842
  function endConversation(id) {
860
843
  return getDb().update(conversations).set({ endedAt: sql3`(datetime('now'))` }).where(eq2(conversations.id, id)).returning().get();
861
844
  }
862
- function updateLastQuestion(id, question) {
863
- return getDb().update(conversations).set({ lastQuestion: question }).where(eq2(conversations.id, id)).returning().get();
864
- }
865
845
  var init_conversations = __esm(() => {
866
846
  init_client();
867
847
  init_schema();
@@ -966,50 +946,6 @@ var init_events = __esm(() => {
966
946
  });
967
947
 
968
948
  // src/db/events/emit.ts
969
- function emitSessionStarted(args) {
970
- return insertEvent({
971
- sessionId: args.sessionId,
972
- conversationId: args.conversationId,
973
- event: "session.started",
974
- meta: {
975
- category_path: args.categoryPath,
976
- session_name: args.sessionName,
977
- session_slug: args.sessionSlug,
978
- labels: args.labels,
979
- summary: args.summary ?? null
980
- }
981
- });
982
- }
983
- function emitSessionResumed(args) {
984
- return insertEvent({
985
- sessionId: args.sessionId,
986
- conversationId: args.conversationId,
987
- event: "session.resumed",
988
- meta: { claude_id: args.conversationId }
989
- });
990
- }
991
- function emitSessionPaused(args) {
992
- return insertEvent({
993
- sessionId: args.sessionId,
994
- conversationId: args.conversationId,
995
- event: "session.paused",
996
- meta: { claude_id: args.conversationId }
997
- });
998
- }
999
- function emitSessionPausedByRecovery(args) {
1000
- return insertEvent({
1001
- sessionId: args.sessionId,
1002
- event: "session.paused",
1003
- summary: "Recovered from stale state (process not found)",
1004
- meta: { stale_pid: args.stalePid }
1005
- });
1006
- }
1007
- function emitSessionEnded(args) {
1008
- return insertEvent({
1009
- sessionId: args.sessionId,
1010
- event: "session.end"
1011
- });
1012
- }
1013
949
  function emitClaudeStarted(args) {
1014
950
  return insertEvent({
1015
951
  sessionId: args.sessionId,
@@ -1017,9 +953,6 @@ function emitClaudeStarted(args) {
1017
953
  event: "claude.started",
1018
954
  meta: {
1019
955
  claude_id: args.conversationId,
1020
- model: args.model,
1021
- claude_version: args.claudeVersion,
1022
- git: args.git,
1023
956
  cwd: args.cwd
1024
957
  }
1025
958
  });
@@ -1073,31 +1006,6 @@ function emitSessionRecap(args) {
1073
1006
  meta: { recap: args.recap, claude_id: args.conversationId }
1074
1007
  });
1075
1008
  }
1076
- function emitPermissionRequested(args) {
1077
- return insertEvent({
1078
- sessionId: args.sessionId,
1079
- conversationId: args.conversationId,
1080
- event: "permission.request",
1081
- meta: {
1082
- tool: args.tool,
1083
- detail: args.detail,
1084
- claude_id: args.conversationId
1085
- }
1086
- });
1087
- }
1088
- function emitPermissionResolved(args) {
1089
- return insertEvent({
1090
- sessionId: args.sessionId,
1091
- conversationId: args.conversationId,
1092
- event: "permission.resolve",
1093
- meta: {
1094
- tool: args.tool,
1095
- detail: args.detail,
1096
- outcome: args.outcome,
1097
- claude_id: args.conversationId
1098
- }
1099
- });
1100
- }
1101
1009
  function emitToolUsed(args) {
1102
1010
  const summary = formatToolSummary(args.tool, args.detail);
1103
1011
  return insertEvent({
@@ -1171,23 +1079,6 @@ function emitAssistantRecap(args) {
1171
1079
  meta: { recap: args.recap, claude_id: args.conversationId }
1172
1080
  });
1173
1081
  }
1174
- function emitContextSnapshot(args) {
1175
- return insertEvent({
1176
- sessionId: args.sessionId,
1177
- conversationId: args.conversationId,
1178
- event: "context.snapshot",
1179
- summary: `${args.remainingPct}% remaining`,
1180
- meta: {
1181
- model: args.model,
1182
- input_tokens: String(args.inputTokens),
1183
- cache_creation_tokens: String(args.cacheCreationTokens),
1184
- cache_read_tokens: String(args.cacheReadTokens),
1185
- context_window_tokens: String(args.totalContextTokens),
1186
- remaining_pct: String(args.remainingPct),
1187
- claude_id: args.conversationId
1188
- }
1189
- });
1190
- }
1191
1082
  var init_emit = __esm(() => {
1192
1083
  init_events();
1193
1084
  });
@@ -1237,26 +1128,6 @@ function dispatchHookEvent(event, ctx) {
1237
1128
  recap: String(meta.recap ?? "")
1238
1129
  });
1239
1130
  return true;
1240
- case "session.paused":
1241
- emitSessionPaused({ sessionId, conversationId });
1242
- return true;
1243
- case "permission.request":
1244
- emitPermissionRequested({
1245
- sessionId,
1246
- conversationId,
1247
- tool: String(meta.tool ?? ""),
1248
- detail: String(meta.detail ?? "")
1249
- });
1250
- return true;
1251
- case "permission.resolve":
1252
- emitPermissionResolved({
1253
- sessionId,
1254
- conversationId,
1255
- tool: String(meta.tool ?? ""),
1256
- detail: String(meta.detail ?? ""),
1257
- outcome: meta.outcome === "denied" ? "denied" : "approved"
1258
- });
1259
- return true;
1260
1131
  case "tool.applied":
1261
1132
  emitToolApplied({
1262
1133
  sessionId,
@@ -1283,15 +1154,11 @@ var init_update = __esm(() => {
1283
1154
  init_router();
1284
1155
  init_sessions();
1285
1156
  init_conversations();
1286
- init_trigger();
1287
1157
  init_emit();
1288
1158
  EVENT_STATUS_MAP = {
1289
1159
  "session.waiting": "waiting",
1290
1160
  "session.answered": "active",
1291
- "session.active": "active",
1292
- "session.paused": "paused",
1293
- "session.started": "active",
1294
- "session.end": "paused"
1161
+ "session.paused": "paused"
1295
1162
  };
1296
1163
  register("update", async (args) => {
1297
1164
  let sessionId = "";
@@ -1340,24 +1207,15 @@ var init_update = __esm(() => {
1340
1207
  }
1341
1208
  const rawConvoId = meta?.claude_id || process.env.BERTRAND_CLAUDE_ID || undefined;
1342
1209
  const conversationId = rawConvoId && getConversation(rawConvoId) ? rawConvoId : undefined;
1343
- const dispatched = dispatchHookEvent(event, {
1210
+ dispatchHookEvent(event, {
1344
1211
  sessionId,
1345
1212
  conversationId,
1346
1213
  meta,
1347
1214
  summary: summaryArg
1348
1215
  });
1349
- if (!dispatched) {
1350
- return;
1351
- }
1352
1216
  if (newStatus && !ignoreStatusFlip) {
1353
1217
  updateSessionStatus(sessionId, newStatus);
1354
1218
  }
1355
- if (event === "session.waiting" && conversationId && meta?.question) {
1356
- updateLastQuestion(conversationId, meta.question);
1357
- }
1358
- if (event === "session.end") {
1359
- triggerBackgroundPush();
1360
- }
1361
1219
  });
1362
1220
  });
1363
1221
 
@@ -1365,13 +1223,6 @@ var init_update = __esm(() => {
1365
1223
  import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1366
1224
  import { homedir as homedir2 } from "os";
1367
1225
  import { join as join6 } from "path";
1368
- function getContextWindowSize(model) {
1369
- for (const [prefix, size] of Object.entries(CONTEXT_WINDOW_SIZES)) {
1370
- if (model.startsWith(prefix))
1371
- return size;
1372
- }
1373
- return 200000;
1374
- }
1375
1226
  function claudeTranscriptPath(sessionId, cwd) {
1376
1227
  const dir = (cwd ?? process.cwd()).replace(/\//g, "-");
1377
1228
  return join6(homedir2(), ".claude", "projects", dir, `${sessionId}.jsonl`);
@@ -1439,108 +1290,7 @@ function getLatestAssistantTurn(filePath) {
1439
1290
  thinkingBytes
1440
1291
  };
1441
1292
  }
1442
- function getContextSnapshot(filePath) {
1443
- if (!existsSync5(filePath))
1444
- return null;
1445
- const text2 = readFileSync4(filePath, "utf-8");
1446
- const lines = text2.split(`
1447
- `);
1448
- for (let i = lines.length - 1;i >= 0; i--) {
1449
- const line = lines[i];
1450
- if (!line)
1451
- continue;
1452
- let entry;
1453
- try {
1454
- entry = JSON.parse(line);
1455
- } catch {
1456
- continue;
1457
- }
1458
- if (entry.type !== "assistant")
1459
- continue;
1460
- const message = entry.message;
1461
- const usage = message?.usage;
1462
- if (!usage)
1463
- continue;
1464
- const model = message?.model ?? "";
1465
- const inputTokens = usage.input_tokens ?? 0;
1466
- const outputTokens = usage.output_tokens ?? 0;
1467
- const cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
1468
- const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
1469
- const totalContextTokens = inputTokens + cacheCreationTokens + cacheReadTokens;
1470
- const windowSize = getContextWindowSize(model);
1471
- const remainingPct = Math.max(0, Math.min(100, Math.round(100 - totalContextTokens * 100 / windowSize)));
1472
- return {
1473
- model,
1474
- inputTokens,
1475
- outputTokens,
1476
- cacheCreationTokens,
1477
- cacheReadTokens,
1478
- totalContextTokens,
1479
- remainingPct
1480
- };
1481
- }
1482
- return null;
1483
- }
1484
- var CONTEXT_WINDOW_SIZES;
1485
- var init_transcript = __esm(() => {
1486
- CONTEXT_WINDOW_SIZES = {
1487
- "claude-opus-4": 1e6,
1488
- "claude-sonnet-4": 200000,
1489
- "claude-haiku-4": 200000
1490
- };
1491
- });
1492
-
1493
- // src/cli/commands/snapshot.ts
1494
- var exports_snapshot = {};
1495
- var init_snapshot = __esm(() => {
1496
- init_router();
1497
- init_sessions();
1498
- init_conversations();
1499
- init_emit();
1500
- init_transcript();
1501
- register("snapshot", async (args) => {
1502
- let sessionId = "";
1503
- let transcriptPath = "";
1504
- let conversationId = "";
1505
- for (let i = 0;i < args.length; i++) {
1506
- const arg = args[i];
1507
- const next = args[i + 1];
1508
- if (arg === "--session-id" && next) {
1509
- sessionId = next;
1510
- i++;
1511
- } else if (arg === "--transcript-path" && next) {
1512
- transcriptPath = next;
1513
- i++;
1514
- } else if (arg === "--conversation-id" && next) {
1515
- conversationId = next;
1516
- i++;
1517
- }
1518
- }
1519
- if (!sessionId || !transcriptPath) {
1520
- console.error("Usage: bertrand snapshot --session-id <id> --transcript-path <path> [--conversation-id <id>]");
1521
- process.exit(1);
1522
- }
1523
- const session = getSession(sessionId);
1524
- if (!session) {
1525
- console.error(`Session not found: ${sessionId}`);
1526
- process.exit(1);
1527
- }
1528
- const snapshot = getContextSnapshot(transcriptPath);
1529
- if (!snapshot)
1530
- return;
1531
- const convoId = conversationId && getConversation(conversationId) ? conversationId : undefined;
1532
- emitContextSnapshot({
1533
- sessionId,
1534
- conversationId: convoId,
1535
- model: snapshot.model,
1536
- inputTokens: snapshot.inputTokens,
1537
- cacheCreationTokens: snapshot.cacheCreationTokens,
1538
- cacheReadTokens: snapshot.cacheReadTokens,
1539
- totalContextTokens: snapshot.totalContextTokens,
1540
- remainingPct: snapshot.remainingPct
1541
- });
1542
- });
1543
- });
1293
+ var init_transcript = () => {};
1544
1294
 
1545
1295
  // src/cli/commands/assistant-message.ts
1546
1296
  var exports_assistant_message = {};
@@ -1624,56 +1374,6 @@ var init_assistant_message = __esm(() => {
1624
1374
  });
1625
1375
  });
1626
1376
 
1627
- // src/cli/commands/recap-thinking.ts
1628
- var exports_recap_thinking = {};
1629
- var RECAP_RE2;
1630
- var init_recap_thinking = __esm(() => {
1631
- init_router();
1632
- init_sessions();
1633
- init_conversations();
1634
- init_emit();
1635
- init_transcript();
1636
- RECAP_RE2 = /<recap>([\s\S]*?)<\/recap>/i;
1637
- register("recap-thinking", async (args) => {
1638
- let sessionId = "";
1639
- let transcriptPath = "";
1640
- let conversationId = "";
1641
- for (let i = 0;i < args.length; i++) {
1642
- const arg = args[i];
1643
- const next = args[i + 1];
1644
- if (arg === "--session-id" && next) {
1645
- sessionId = next;
1646
- i++;
1647
- } else if (arg === "--transcript-path" && next) {
1648
- transcriptPath = next;
1649
- i++;
1650
- } else if (arg === "--conversation-id" && next) {
1651
- conversationId = next;
1652
- i++;
1653
- }
1654
- }
1655
- if (!sessionId || !transcriptPath) {
1656
- console.error("Usage: bertrand recap-thinking --session-id <id> --transcript-path <path> [--conversation-id <id>]");
1657
- process.exit(1);
1658
- }
1659
- if (!getSession(sessionId))
1660
- return;
1661
- const turn = getLatestAssistantTurn(transcriptPath);
1662
- if (!turn?.text)
1663
- return;
1664
- const match = turn.text.match(RECAP_RE2);
1665
- const recap = match?.[1]?.trim();
1666
- if (!recap)
1667
- return;
1668
- const convoId = conversationId && getConversation(conversationId) ? conversationId : undefined;
1669
- emitAssistantRecap({
1670
- sessionId,
1671
- conversationId: convoId,
1672
- recap
1673
- });
1674
- });
1675
- });
1676
-
1677
1377
  // src/terminal/wave.ts
1678
1378
  import { execSync } from "child_process";
1679
1379
 
@@ -1935,7 +1635,6 @@ function computeTimings(events2) {
1935
1635
  function computeSessionStats(sessionId) {
1936
1636
  const events2 = getEventsBySession(sessionId);
1937
1637
  const summary = computeTimings(events2);
1938
- const prEvents = getEventsByType(sessionId, "gh.pr.created");
1939
1638
  const conversationIds = new Set(events2.filter((e) => e.conversationId).map((e) => e.conversationId));
1940
1639
  const interactionCount = events2.filter((e) => e.event === "session.waiting" || e.event === "session.answered").length;
1941
1640
  const diff = computeDiffStats(sessionId);
@@ -1943,7 +1642,6 @@ function computeSessionStats(sessionId) {
1943
1642
  eventCount: events2.length,
1944
1643
  conversationCount: conversationIds.size,
1945
1644
  interactionCount,
1946
- prCount: prEvents.length,
1947
1645
  claudeWorkS: Math.round(summary.totalClaudeWorkMs / 1000),
1948
1646
  userWaitS: Math.round(summary.totalUserWaitMs / 1000),
1949
1647
  activePct: summary.activePct,
@@ -1981,14 +1679,6 @@ function aggregateToolUsage(sessionId) {
1981
1679
  counts[p.tool] = (counts[p.tool] ?? 0) + (p.count ?? 1);
1982
1680
  }
1983
1681
  }
1984
- const resolves = getEventsByType(sessionId, "permission.resolve");
1985
- for (const ev of resolves) {
1986
- const meta = ev.meta;
1987
- const tool = meta?.tool;
1988
- if (!tool)
1989
- continue;
1990
- counts[tool] = (counts[tool] ?? 0) + 1;
1991
- }
1992
1682
  const used = getEventsByType(sessionId, "tool.used");
1993
1683
  for (const ev of used) {
1994
1684
  const meta = ev.meta;
@@ -1999,40 +1689,6 @@ function aggregateToolUsage(sessionId) {
1999
1689
  }
2000
1690
  return counts;
2001
1691
  }
2002
- function contextTokenStats(sessionId) {
2003
- const snapshots = getEventsByType(sessionId, "context.snapshot");
2004
- const samples = [];
2005
- for (const ev of snapshots) {
2006
- const meta = ev.meta;
2007
- const total = parseInt(meta?.context_window_tokens ?? "0", 10);
2008
- if (Number.isFinite(total) && total > 0)
2009
- samples.push(total);
2010
- }
2011
- if (samples.length === 0)
2012
- return { avg: 0, max: 0, latest: 0 };
2013
- let sum = 0;
2014
- let max = 0;
2015
- for (const n of samples) {
2016
- sum += n;
2017
- if (n > max)
2018
- max = n;
2019
- }
2020
- const avg = Math.round(sum / samples.length);
2021
- const latest = samples[samples.length - 1] ?? 0;
2022
- return { avg, max, latest };
2023
- }
2024
- function permissionDenialCount(sessionId) {
2025
- const all = getEventsBySession(sessionId);
2026
- let requests = 0;
2027
- let resolves = 0;
2028
- for (const ev of all) {
2029
- if (ev.event === "permission.request")
2030
- requests++;
2031
- else if (ev.event === "permission.resolve")
2032
- resolves++;
2033
- }
2034
- return Math.max(0, requests - resolves);
2035
- }
2036
1692
  function discardRate(sessionId) {
2037
1693
  const row = getDb().select({
2038
1694
  total: sql5`count(*)`,
@@ -2046,8 +1702,6 @@ function discardRate(sessionId) {
2046
1702
  function computeEngagementStats(sessionId) {
2047
1703
  return {
2048
1704
  toolUsage: aggregateToolUsage(sessionId),
2049
- contextTokens: contextTokenStats(sessionId),
2050
- permissionDenials: permissionDenialCount(sessionId),
2051
1705
  discardRate: discardRate(sessionId)
2052
1706
  };
2053
1707
  }
@@ -2364,7 +2018,7 @@ function cleanupSnapshot() {
2364
2018
  }
2365
2019
  }
2366
2020
  var SIDECAR_SUFFIXES;
2367
- var init_snapshot2 = __esm(() => {
2021
+ var init_snapshot = __esm(() => {
2368
2022
  init_resolve();
2369
2023
  SIDECAR_SUFFIXES = ["", "-wal", "-shm"];
2370
2024
  });
@@ -2563,7 +2217,7 @@ var init_engine = __esm(() => {
2563
2217
  init_resolve();
2564
2218
  init_lsof();
2565
2219
  init_config();
2566
- init_snapshot2();
2220
+ init_snapshot();
2567
2221
  init_crypto();
2568
2222
  });
2569
2223
 
@@ -3281,73 +2935,6 @@ var init_process = __esm(() => {
3281
2935
  init_resolve();
3282
2936
  });
3283
2937
 
3284
- // src/engine/spawn-context.ts
3285
- import { execFile as execFile2 } from "child_process";
3286
- import { promisify } from "util";
3287
- function captureModel() {
3288
- return process.env.BERTRAND_MODEL || process.env.CLAUDE_MODEL || undefined;
3289
- }
3290
- async function captureClaudeVersion() {
3291
- if (cachedClaudeVersion !== null)
3292
- return cachedClaudeVersion;
3293
- try {
3294
- const { stdout } = await execFileAsync("claude", ["--version"], {
3295
- timeout: 5000
3296
- });
3297
- const match2 = stdout.trim().match(/(\d+\.\d+\.\d+(?:\.\d+)?)/);
3298
- cachedClaudeVersion = match2 ? match2[1] : stdout.trim() || undefined;
3299
- return cachedClaudeVersion;
3300
- } catch {
3301
- cachedClaudeVersion = undefined;
3302
- return;
3303
- }
3304
- }
3305
- async function captureGit() {
3306
- try {
3307
- const [statusRes, shaRes] = await Promise.all([
3308
- execFileAsync("git", ["status", "--porcelain=v2", "--branch"], {
3309
- timeout: 5000
3310
- }),
3311
- execFileAsync("git", ["rev-parse", "HEAD"], { timeout: 5000 })
3312
- ]);
3313
- let branch;
3314
- let dirty = false;
3315
- for (const line of statusRes.stdout.split(`
3316
- `)) {
3317
- if (line.startsWith("# branch.head ")) {
3318
- branch = line.slice("# branch.head ".length).trim();
3319
- } else if (line && !line.startsWith("#")) {
3320
- dirty = true;
3321
- }
3322
- }
3323
- if (!branch)
3324
- return;
3325
- return {
3326
- branch,
3327
- sha: shaRes.stdout.trim(),
3328
- dirty
3329
- };
3330
- } catch {
3331
- return;
3332
- }
3333
- }
3334
- async function captureSpawnContext() {
3335
- const [claudeVersion, git] = await Promise.all([
3336
- captureClaudeVersion(),
3337
- captureGit()
3338
- ]);
3339
- return {
3340
- model: captureModel(),
3341
- claudeVersion,
3342
- git,
3343
- cwd: process.cwd()
3344
- };
3345
- }
3346
- var execFileAsync, cachedClaudeVersion = null;
3347
- var init_spawn_context = __esm(() => {
3348
- execFileAsync = promisify(execFile2);
3349
- });
3350
-
3351
2938
  // src/lib/server-lifecycle.ts
3352
2939
  import { spawn as spawn3 } from "child_process";
3353
2940
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "fs";
@@ -3496,24 +3083,11 @@ async function launch(opts) {
3496
3083
  liveSession = { sessionId: session.id, claudeId };
3497
3084
  installExitHandlers();
3498
3085
  await ensureServerStarted();
3499
- const spawnContext = await captureSpawnContext();
3500
3086
  const sessionName = `${opts.categoryPath}/${opts.slug}`;
3501
- emitSessionStarted({
3502
- sessionId: session.id,
3503
- conversationId: claudeId,
3504
- categoryPath: opts.categoryPath,
3505
- sessionName: opts.name ?? opts.slug,
3506
- sessionSlug: opts.slug,
3507
- labels: opts.labelNames ?? [],
3508
- summary: session.summary ?? null
3509
- });
3510
3087
  emitClaudeStarted({
3511
3088
  sessionId: session.id,
3512
3089
  conversationId: claudeId,
3513
- model: spawnContext.model,
3514
- claudeVersion: spawnContext.claudeVersion,
3515
- git: spawnContext.git,
3516
- cwd: spawnContext.cwd
3090
+ cwd: process.cwd()
3517
3091
  });
3518
3092
  const siblingContext = buildSiblingContext(categoryId, opts.categoryPath, session.id);
3519
3093
  const contract = buildContract(sessionName, siblingContext);
@@ -3538,19 +3112,11 @@ async function resume(opts) {
3538
3112
  liveSession = { sessionId: session.id, claudeId: opts.conversationId };
3539
3113
  installExitHandlers();
3540
3114
  await ensureServerStarted();
3541
- emitSessionResumed({
3542
- sessionId: session.id,
3543
- conversationId: opts.conversationId
3544
- });
3545
3115
  if (isFreshClaudeSession) {
3546
- const spawnContext = await captureSpawnContext();
3547
3116
  emitClaudeStarted({
3548
3117
  sessionId: session.id,
3549
3118
  conversationId: opts.conversationId,
3550
- model: spawnContext.model,
3551
- claudeVersion: spawnContext.claudeVersion,
3552
- git: spawnContext.git,
3553
- cwd: spawnContext.cwd
3119
+ cwd: process.cwd()
3554
3120
  });
3555
3121
  }
3556
3122
  const categoryPath = category?.path ?? "";
@@ -3587,7 +3153,6 @@ function finalizeSession(sessionId, conversationId, exitCode) {
3587
3153
  });
3588
3154
  if (liveSession?.sessionId === sessionId)
3589
3155
  liveSession = null;
3590
- emitSessionEnded({ sessionId });
3591
3156
  computeAndPersist(sessionId);
3592
3157
  stopServerIfIdle();
3593
3158
  triggerBackgroundPush();
@@ -3602,7 +3167,6 @@ var init_session = __esm(() => {
3602
3167
  init_template2();
3603
3168
  init_context();
3604
3169
  init_process();
3605
- init_spawn_context();
3606
3170
  init_timing();
3607
3171
  init_server_lifecycle();
3608
3172
  init_trigger();
@@ -3820,10 +3384,6 @@ function recoverStaleSessions() {
3820
3384
  status: "paused",
3821
3385
  pid: null
3822
3386
  });
3823
- emitSessionPausedByRecovery({
3824
- sessionId: session.id,
3825
- stalePid: session.pid
3826
- });
3827
3387
  recovered++;
3828
3388
  }
3829
3389
  }
@@ -3831,7 +3391,6 @@ function recoverStaleSessions() {
3831
3391
  }
3832
3392
  var init_recovery = __esm(() => {
3833
3393
  init_sessions();
3834
- init_emit();
3835
3394
  });
3836
3395
 
3837
3396
  // src/cli/commands/launch.ts
@@ -3900,14 +3459,11 @@ rm -f "${runtimeDir}/working-$sid"
3900
3459
 
3901
3460
  bq update --session-id "$sid" --event session.waiting --meta "$(jq -n --arg q "$question" --arg cid "$cid" '{question:$q, claude_id:$cid}')"
3902
3461
 
3903
- # Context snapshot \u2014 extract transcript path and capture token usage.
3904
- # assistant-message captures the latest turn's text + recap tag in one pass;
3905
- # replaces the older recap-thinking call which only grabbed the recap. Dedup
3906
- # inside the command makes it idempotent vs the matching Stop-time capture,
3907
- # so the same turn never lands twice.
3462
+ # Capture the latest assistant turn's text + recap tag. Dedup inside the
3463
+ # command makes it idempotent vs the matching Stop-time capture so the same
3464
+ # turn never lands twice.
3908
3465
  tpath="$(printf '%s' "$input" | grep -o '"transcript_path":"[^"]*"' | cut -d'"' -f4)"
3909
3466
  if [ -n "$tpath" ]; then
3910
- bq snapshot --session-id "$sid" --transcript-path "$tpath" --conversation-id "$cid" &
3911
3467
  bq assistant-message --session-id "$sid" --transcript-path "$tpath" --conversation-id "$cid" &
3912
3468
  fi
3913
3469
 
@@ -3971,35 +3527,9 @@ fi
3971
3527
  wait
3972
3528
  `;
3973
3529
  }
3974
- function activeScript(bin, runtimeDir) {
3975
- return `#!/usr/bin/env bash
3976
- # Hook: PreToolUse (catch-all) \u2192 flip waiting to active
3977
- ${quietHelper(bin)}
3978
- sid="\${BERTRAND_SESSION:-}"
3979
- [ -z "$sid" ] && exit 0
3980
-
3981
- # Debounce: skip if we already sent session.active within the last 5 seconds.
3982
- # This avoids spawning bertrand (~31ms) on every tool call during rapid sequences.
3983
- marker="${runtimeDir}/working-$sid"
3984
- if [ -f "$marker" ]; then
3985
- age=$(( $(date +%s) - $(stat -f%m "$marker" 2>/dev/null || echo 0) ))
3986
- [ "$age" -lt 5 ] && exit 0
3987
- fi
3988
-
3989
- input="$(cat)"
3990
- ${EXTRACT_TOOL}
3991
-
3992
- # AskUserQuestion has its own PreToolUse hook
3993
- [ "$tool" = "AskUserQuestion" ] && exit 0
3994
-
3995
- touch "$marker"
3996
- cid="\${BERTRAND_CLAUDE_ID:-}"
3997
- bq update --session-id "$sid" --event session.active --meta "$(jq -n --arg cid "$cid" '{claude_id:$cid}')"
3998
- `;
3999
- }
4000
3530
  function permissionWaitScript(bin, runtimeDir) {
4001
3531
  return `#!/usr/bin/env bash
4002
- # Hook: PermissionRequest \u2192 mark pending, emit permission.request
3532
+ # Hook: PermissionRequest \u2192 mark pending, badge + notify
4003
3533
  ${quietHelper(bin)}
4004
3534
  sid="\${BERTRAND_SESSION:-}"
4005
3535
  [ -z "$sid" ] && exit 0
@@ -4008,19 +3538,11 @@ input="$(cat)"
4008
3538
  ${EXTRACT_TOOL}
4009
3539
  [ "$tool" = "AskUserQuestion" ] && exit 0
4010
3540
 
4011
- # Write marker so PostToolUse knows this was a real permission prompt (not auto-approved)
3541
+ # Marker tells the PostToolUse hook to emit tool.used with outcome:approved
3542
+ # instead of outcome:auto. Without it, every prompted-then-approved tool call
3543
+ # would look identical to an auto-approved one.
4012
3544
  touch "${runtimeDir}/perm-pending-$sid"
4013
3545
 
4014
- # Extract detail from tool_input via grep (avoid jq for simple fields)
4015
- detail=""
4016
- case "$tool" in
4017
- Bash) detail="$(printf '%s' "$input" | grep -o '"command":"[^"]*"' | head -1 | cut -d'"' -f4 | cut -c1-1000)" ;;
4018
- Edit|Write|Read) detail="$(printf '%s' "$input" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4 | cut -c1-1000)" ;;
4019
- esac
4020
-
4021
- cid="\${BERTRAND_CLAUDE_ID:-}"
4022
- bq update --session-id "$sid" --event permission.request --meta "$(jq -n --arg t "$tool" --arg d "$detail" --arg cid "$cid" '{tool:$t, detail:$d, claude_id:$cid}')"
4023
-
4024
3546
  # Badge + notify in background
4025
3547
  bq badge bell-exclamation --color '#ff6b35' --priority 25 --beep &
4026
3548
  bq notify bertrand "Needs permission: $tool" &
@@ -4031,17 +3553,15 @@ function permissionDoneScript(bin, runtimeDir) {
4031
3553
  return `#!/usr/bin/env bash
4032
3554
  # Hook: PostToolUse (catch-all)
4033
3555
  #
4034
- # Captures every tool call Claude makes. Three event flows:
3556
+ # Captures every tool call Claude makes. Two event flows:
4035
3557
  # 1. Edit/Write/MultiEdit \u2192 tool.applied with diff payload. Keeps the
4036
3558
  # existing dashboard diff-renderer happy and is the only place we get
4037
3559
  # old_string/new_string on auto-approved edits.
4038
- # 2. Tools that went through a permission prompt \u2192 permission.resolve.
4039
- # The PermissionRequest hook set a marker; we clear it and log the
4040
- # approval. (Denials never reach PostToolUse, so absence-of-resolve
4041
- # means the user said no.)
4042
- # 3. Everything else (auto-approved Bash/Read/Grep/Glob/etc.) \u2192 tool.used
4043
- # with outcome:"auto". Previously this case dropped the call entirely;
4044
- # now Claude's read-only / shell activity shows up in the timeline.
3560
+ # 2. Everything else \u2192 tool.used. The PermissionRequest hook may have set
3561
+ # a marker; if so the call was prompted-then-approved (outcome:approved),
3562
+ # otherwise it was auto-approved (outcome:auto). Denials never reach
3563
+ # PostToolUse, so absence of a tool.used after a permission.request means
3564
+ # the user said no.
4045
3565
  ${quietHelper(bin)}
4046
3566
  sid="\${BERTRAND_SESSION:-}"
4047
3567
  [ -z "$sid" ] && exit 0
@@ -4100,14 +3620,9 @@ case "$tool" in
4100
3620
  WebSearch) detail="$(printf '%s' "$input" | grep -o '"query":"[^"]*"' | head -1 | cut -d'"' -f4 | cut -c1-200)" ;;
4101
3621
  esac
4102
3622
 
4103
- if [ "$had_marker" = "1" ]; then
4104
- # Prompted-then-approved path. Keep permission.resolve for back-compat
4105
- # rendering; downstream consumers can migrate to tool.used at their pace.
4106
- bq update --session-id "$sid" --event permission.resolve --meta "$(jq -n --arg t "$tool" --arg d "$detail" --arg cid "$cid" '{tool:$t, detail:$d, outcome:"approved", claude_id:$cid}')"
4107
- else
4108
- # Auto-approved path. Without tool.used, these calls were invisible.
4109
- bq update --session-id "$sid" --event tool.used --meta "$(jq -n --arg t "$tool" --arg d "$detail" --arg cid "$cid" '{tool:$t, detail:$d, outcome:"auto", claude_id:$cid}')"
4110
- fi
3623
+ outcome="auto"
3624
+ [ "$had_marker" = "1" ] && outcome="approved"
3625
+ bq update --session-id "$sid" --event tool.used --meta "$(jq -n --arg t "$tool" --arg d "$detail" --arg o "$outcome" --arg cid "$cid" '{tool:$t, detail:$d, outcome:$o, claude_id:$cid}')"
4111
3626
  wait
4112
3627
  `;
4113
3628
  }
@@ -4129,22 +3644,22 @@ bq update --session-id "$sid" --event user.prompt --meta "$meta"
4129
3644
  }
4130
3645
  function doneScript(bin, _runtimeDir) {
4131
3646
  return `#!/usr/bin/env bash
4132
- # Hook: Stop \u2192 mark session as paused
3647
+ # Hook: Stop \u2192 flip session status to paused; no event row written.
3648
+ # The status flip is driven by EVENT_STATUS_MAP[session.paused]; dispatchHookEvent
3649
+ # no longer has a session.paused case, so update.ts flips status without inserting.
4133
3650
  ${quietHelper(bin)}
4134
3651
  sid="\${BERTRAND_SESSION:-}"
4135
3652
  [ -z "$sid" ] && exit 0
4136
3653
 
4137
3654
  input="$(cat)"
4138
3655
  cid="\${BERTRAND_CLAUDE_ID:-}"
4139
- bq update --session-id "$sid" --event session.paused --meta "$(jq -n --arg cid "$cid" '{claude_id:$cid}')"
3656
+ bq update --session-id "$sid" --event session.paused
4140
3657
 
4141
- # Final transcript reads. assistant-message dedups against the most-recent
4142
- # AskUQ-time capture, so a Done-for-now exit (no work between AskUQ and Stop)
4143
- # lands zero new events here; intermediate Stops with fresh assistant output
4144
- # do land new ones.
3658
+ # Final assistant-message read. Dedups against the most-recent AskUQ-time
3659
+ # capture, so a Done-for-now exit (no work between AskUQ and Stop) lands zero
3660
+ # new events here; intermediate Stops with fresh assistant output do.
4145
3661
  tpath="$(printf '%s' "$input" | grep -o '"transcript_path":"[^"]*"' | cut -d'"' -f4)"
4146
3662
  if [ -n "$tpath" ]; then
4147
- bq snapshot --session-id "$sid" --transcript-path "$tpath" --conversation-id "$cid" &
4148
3663
  bq assistant-message --session-id "$sid" --transcript-path "$tpath" --conversation-id "$cid" &
4149
3664
  fi
4150
3665
 
@@ -4156,7 +3671,6 @@ var init_scripts = __esm(() => {
4156
3671
  HOOK_SCRIPTS = {
4157
3672
  "on-waiting.sh": waitingScript,
4158
3673
  "on-answered.sh": answeredScript,
4159
- "on-active.sh": activeScript,
4160
3674
  "on-permission-wait.sh": permissionWaitScript,
4161
3675
  "on-permission-done.sh": permissionDoneScript,
4162
3676
  "on-user-prompt.sh": userPromptScript,
@@ -4215,10 +3729,6 @@ var init_settings = __esm(() => {
4215
3729
  {
4216
3730
  matcher: "AskUserQuestion",
4217
3731
  hooks: [{ type: "command", command: `${paths.hooks}/on-waiting.sh` }]
4218
- },
4219
- {
4220
- matcher: "",
4221
- hooks: [{ type: "command", command: `${paths.hooks}/on-active.sh` }]
4222
3732
  }
4223
3733
  ],
4224
3734
  PostToolUse: [
@@ -4542,31 +4052,8 @@ function extractSummary(row) {
4542
4052
  const answers = meta.answers;
4543
4053
  return answers ? Object.values(answers).join(", ") : "";
4544
4054
  }
4545
- case "permission.request":
4546
- case "permission.resolve": {
4547
- const tool = meta.tool ?? "";
4548
- const detail = meta.detail ?? "";
4549
- return detail ? `${tool}: ${detail}` : tool;
4550
- }
4551
- case "gh.pr.created": {
4552
- const title = meta.pr_title ?? "";
4553
- const url = meta.pr_url ?? "";
4554
- return title || url;
4555
- }
4556
- case "gh.pr.merged":
4557
- return meta.branch ?? "";
4558
- case "worktree.entered":
4559
- return meta.branch ?? "";
4560
- case "linear.issue.read":
4561
- return meta.issue_title ?? "";
4562
- case "notion.page.read":
4563
- return meta.page_title ?? "";
4564
- case "vercel.deploy":
4565
- return meta.project_name ?? "";
4566
4055
  case "user.prompt":
4567
4056
  return meta.prompt ?? "";
4568
- case "context.snapshot":
4569
- return meta.remaining_pct ? `${meta.remaining_pct}% remaining` : "";
4570
4057
  case "session.recap":
4571
4058
  return meta.recap ?? "";
4572
4059
  default:
@@ -4596,26 +4083,13 @@ function enrichAll(rows) {
4596
4083
  var catalog, DEFAULT_INFO;
4597
4084
  var init_catalog = __esm(() => {
4598
4085
  catalog = {
4599
- "session.started": { label: "started", category: "lifecycle", color: 34, detailColor: 245, skip: false },
4600
- "session.resumed": { label: "resumed", category: "lifecycle", color: 34, detailColor: 245, skip: false },
4601
- "session.end": { label: "ended", category: "lifecycle", color: 245, detailColor: 245, skip: false },
4602
4086
  "claude.started": { label: "claude started", category: "lifecycle", color: 35, detailColor: 245, skip: false },
4603
4087
  "claude.ended": { label: "claude ended", category: "lifecycle", color: 35, detailColor: 245, skip: false },
4604
4088
  "claude.discarded": { label: "discarded", category: "lifecycle", color: 245, detailColor: 245, skip: false },
4605
4089
  "session.waiting": { label: "waiting", category: "interaction", color: 33, detailColor: 245, skip: false },
4606
4090
  "session.answered": { label: "answered", category: "interaction", color: 36, detailColor: 245, skip: false },
4607
- "permission.request": { label: "permission", category: "work", color: 214, detailColor: 245, skip: false },
4608
- "permission.resolve": { label: "allowed", category: "work", color: 214, detailColor: 245, skip: false },
4609
- "worktree.entered": { label: "worktree", category: "lifecycle", color: 35, detailColor: 245, skip: false },
4610
- "worktree.exited": { label: "worktree exited", category: "lifecycle", color: 35, detailColor: 245, skip: false },
4611
- "gh.pr.created": { label: "PR created", category: "integration", color: 32, detailColor: 32, skip: false },
4612
- "gh.pr.merged": { label: "PR merged", category: "integration", color: 35, detailColor: 35, skip: false },
4613
- "linear.issue.read": { label: "Linear issue", category: "integration", color: 33, detailColor: 33, skip: false },
4614
- "notion.page.read": { label: "Notion page", category: "integration", color: 245, detailColor: 245, skip: false },
4615
- "vercel.deploy": { label: "deployed", category: "integration", color: 245, detailColor: 245, skip: false },
4616
4091
  "user.prompt": { label: "prompt", category: "interaction", color: 36, detailColor: 245, skip: false },
4617
- "context.snapshot": { label: "context", category: "context", color: 245, detailColor: 245, skip: true },
4618
- "tool.used": { label: "tool", category: "work", color: 214, detailColor: 245, skip: true },
4092
+ "tool.used": { label: "tool", category: "work", color: 214, detailColor: 245, skip: false },
4619
4093
  "tool.work": { label: "tool work", category: "work", color: 214, detailColor: 245, skip: false },
4620
4094
  "session.recap": { label: "session recap", category: "lifecycle", color: 33, detailColor: 245, skip: false },
4621
4095
  "assistant.message": { label: "claude", category: "interaction", color: 39, detailColor: 245, skip: false },
@@ -4676,8 +4150,6 @@ function collapsePermissions(events2) {
4676
4150
  continue;
4677
4151
  const toolCounts = new Map;
4678
4152
  for (const pev of batch) {
4679
- if (pev.event !== "permission.request" && pev.event !== "tool.used")
4680
- continue;
4681
4153
  const meta = pev.meta;
4682
4154
  const tool = meta?.tool ?? "unknown";
4683
4155
  toolCounts.set(tool, (toolCounts.get(tool) ?? 0) + 1);
@@ -4739,8 +4211,6 @@ var ROLLUP_EVENTS;
4739
4211
  var init_compact = __esm(() => {
4740
4212
  init_catalog();
4741
4213
  ROLLUP_EVENTS = new Set([
4742
- "permission.request",
4743
- "permission.resolve",
4744
4214
  "tool.used"
4745
4215
  ]);
4746
4216
  });
@@ -4982,14 +4452,11 @@ function getMetrics(sessionId, name, status2) {
4982
4452
  const allEvents = getEventsBySession(sessionId);
4983
4453
  const conversations2 = new Set;
4984
4454
  let interactionCount = 0;
4985
- let prCount = 0;
4986
4455
  for (const ev of allEvents) {
4987
4456
  if (ev.conversationId)
4988
4457
  conversations2.add(ev.conversationId);
4989
4458
  if (ev.event === "session.waiting" || ev.event === "session.answered")
4990
4459
  interactionCount++;
4991
- if (ev.event === "gh.pr.created")
4992
- prCount++;
4993
4460
  }
4994
4461
  return {
4995
4462
  name,
@@ -4997,7 +4464,6 @@ function getMetrics(sessionId, name, status2) {
4997
4464
  eventCount: allEvents.length,
4998
4465
  conversationCount: conversations2.size,
4999
4466
  interactionCount,
5000
- prCount,
5001
4467
  claudeWorkS: Math.round(timing.totalClaudeWorkMs / 1000),
5002
4468
  userWaitS: Math.round(timing.totalUserWaitMs / 1000),
5003
4469
  activePct: timing.activePct,
@@ -5018,9 +4484,8 @@ function renderGlobal(metrics, isJson) {
5018
4484
  claudeWorkS: acc.claudeWorkS + m.claudeWorkS,
5019
4485
  userWaitS: acc.userWaitS + m.userWaitS,
5020
4486
  conversations: acc.conversations + m.conversationCount,
5021
- prs: acc.prs + m.prCount,
5022
4487
  interactions: acc.interactions + m.interactionCount
5023
- }), { sessions: 0, active: 0, durationS: 0, claudeWorkS: 0, userWaitS: 0, conversations: 0, prs: 0, interactions: 0 });
4488
+ }), { sessions: 0, active: 0, durationS: 0, claudeWorkS: 0, userWaitS: 0, conversations: 0, interactions: 0 });
5024
4489
  const totalTracked = totals.claudeWorkS + totals.userWaitS;
5025
4490
  const globalActivePct = totalTracked > 0 ? Math.round(totals.claudeWorkS / totalTracked * 100) : 0;
5026
4491
  if (isJson) {
@@ -5037,7 +4502,6 @@ function renderGlobal(metrics, isJson) {
5037
4502
  console.log(` Claude work: ${dur(totals.claudeWorkS)} ${dim}(${pct(globalActivePct)})${reset}`);
5038
4503
  console.log(` User wait: ${dur(totals.userWaitS)} ${dim}(${pct(100 - globalActivePct)})${reset}`);
5039
4504
  console.log(` Conversations: ${totals.conversations}`);
5040
- console.log(` PRs: ${totals.prs}`);
5041
4505
  console.log(` Interactions: ${totals.interactions}`);
5042
4506
  }
5043
4507
  function renderCategory(metrics, categoryPath, isJson) {
@@ -5052,21 +4516,20 @@ function renderCategory(metrics, categoryPath, isJson) {
5052
4516
  const maxName = Math.max(...sorted.map((m) => m.name.length), 4);
5053
4517
  console.log(`${bold}${categoryPath}${reset}
5054
4518
  `);
5055
- console.log(`${dim}${"NAME".padEnd(maxName)} ${"DURATION".padEnd(8)} ${"CLAUDE".padEnd(8)} ${"WAIT".padEnd(8)} ${"ACT%".padEnd(5)} ${"CONVOS".padEnd(6)} PRS${reset}`);
4519
+ console.log(`${dim}${"NAME".padEnd(maxName)} ${"DURATION".padEnd(8)} ${"CLAUDE".padEnd(8)} ${"WAIT".padEnd(8)} ${"ACT%".padEnd(5)} CONVOS${reset}`);
5056
4520
  for (const m of sorted) {
5057
- console.log(`${m.name.padEnd(maxName)} ${dur(m.durationS).padEnd(8)} ${dur(m.claudeWorkS).padEnd(8)} ${dur(m.userWaitS).padEnd(8)} ${pct(m.activePct).padEnd(5)} ${String(m.conversationCount).padEnd(6)} ${m.prCount}`);
4521
+ console.log(`${m.name.padEnd(maxName)} ${dur(m.durationS).padEnd(8)} ${dur(m.claudeWorkS).padEnd(8)} ${dur(m.userWaitS).padEnd(8)} ${pct(m.activePct).padEnd(5)} ${m.conversationCount}`);
5058
4522
  }
5059
4523
  const totals = sorted.reduce((acc, m) => ({
5060
4524
  durationS: acc.durationS + m.durationS,
5061
4525
  claudeWorkS: acc.claudeWorkS + m.claudeWorkS,
5062
4526
  userWaitS: acc.userWaitS + m.userWaitS,
5063
- conversations: acc.conversations + m.conversationCount,
5064
- prs: acc.prs + m.prCount
5065
- }), { durationS: 0, claudeWorkS: 0, userWaitS: 0, conversations: 0, prs: 0 });
4527
+ conversations: acc.conversations + m.conversationCount
4528
+ }), { durationS: 0, claudeWorkS: 0, userWaitS: 0, conversations: 0 });
5066
4529
  const totalTracked = totals.claudeWorkS + totals.userWaitS;
5067
4530
  const totalPct = totalTracked > 0 ? Math.round(totals.claudeWorkS / totalTracked * 100) : 0;
5068
4531
  console.log(`${dim}${"\u2500".repeat(maxName + 50)}${reset}`);
5069
- console.log(`${"TOTAL".padEnd(maxName)} ${dur(totals.durationS).padEnd(8)} ${dur(totals.claudeWorkS).padEnd(8)} ${dur(totals.userWaitS).padEnd(8)} ${pct(totalPct).padEnd(5)} ${String(totals.conversations).padEnd(6)} ${totals.prs}`);
4532
+ console.log(`${"TOTAL".padEnd(maxName)} ${dur(totals.durationS).padEnd(8)} ${dur(totals.claudeWorkS).padEnd(8)} ${dur(totals.userWaitS).padEnd(8)} ${pct(totalPct).padEnd(5)} ${totals.conversations}`);
5070
4533
  }
5071
4534
  function renderSession(m, isJson) {
5072
4535
  if (isJson) {
@@ -5084,7 +4547,6 @@ function renderSession(m, isJson) {
5084
4547
  console.log(` Events: ${m.eventCount}`);
5085
4548
  console.log(` Conversations: ${m.conversationCount}`);
5086
4549
  console.log(` Interactions: ${m.interactionCount}`);
5087
- console.log(` PRs: ${m.prCount}`);
5088
4550
  }
5089
4551
  var ACTIVE_STATUSES2;
5090
4552
  var init_stats2 = __esm(() => {
@@ -5534,9 +4996,7 @@ init_router();
5534
4996
  var command = process.argv[2];
5535
4997
  var hotPath = {
5536
4998
  update: () => Promise.resolve().then(() => (init_update(), exports_update)),
5537
- snapshot: () => Promise.resolve().then(() => (init_snapshot(), exports_snapshot)),
5538
4999
  "assistant-message": () => Promise.resolve().then(() => (init_assistant_message(), exports_assistant_message)),
5539
- "recap-thinking": () => Promise.resolve().then(() => (init_recap_thinking(), exports_recap_thinking)),
5540
5000
  badge: () => Promise.resolve().then(() => (init_badge(), exports_badge)),
5541
5001
  notify: () => Promise.resolve().then(() => (init_notify(), exports_notify)),
5542
5002
  serve: () => Promise.resolve().then(() => (init_serve(), exports_serve)),
@@ -5554,9 +5014,7 @@ if (command && command in hotPath) {
5554
5014
  Promise.resolve().then(() => (init_backfill_stats(), exports_backfill_stats)),
5555
5015
  Promise.resolve().then(() => (init_archive(), exports_archive)),
5556
5016
  Promise.resolve().then(() => (init_update(), exports_update)),
5557
- Promise.resolve().then(() => (init_snapshot(), exports_snapshot)),
5558
5017
  Promise.resolve().then(() => (init_assistant_message(), exports_assistant_message)),
5559
- Promise.resolve().then(() => (init_recap_thinking(), exports_recap_thinking)),
5560
5018
  Promise.resolve().then(() => (init_serve(), exports_serve)),
5561
5019
  Promise.resolve().then(() => (init_badge(), exports_badge)),
5562
5020
  Promise.resolve().then(() => (init_notify(), exports_notify)),