fleetlens 0.11.2 → 0.12.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 (179) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +3 -0
  3. package/app/apps/web/.next/build-manifest.json +3 -3
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/routes-manifest.json +22 -0
  6. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  16. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +1 -1
  17. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  18. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  20. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  21. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +1 -1
  22. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  23. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  24. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  26. package/app/apps/web/.next/server/app/api/pdf/[key]/route/app-paths-manifest.json +3 -0
  27. package/app/apps/web/.next/server/app/api/pdf/[key]/route/build-manifest.json +9 -0
  28. package/app/apps/web/.next/server/app/api/pdf/[key]/route/server-reference-manifest.json +4 -0
  29. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +10 -0
  30. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.map +5 -0
  31. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -0
  32. package/app/apps/web/.next/server/app/api/pdf/[key]/route_client-reference-manifest.js +3 -0
  33. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  34. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
  36. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  37. package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +1 -1
  38. package/app/apps/web/.next/server/app/api/workflow-agent/route/app-paths-manifest.json +3 -0
  39. package/app/apps/web/.next/server/app/api/workflow-agent/route/build-manifest.json +9 -0
  40. package/app/apps/web/.next/server/app/api/workflow-agent/route/server-reference-manifest.json +4 -0
  41. package/app/apps/web/.next/server/app/api/workflow-agent/route.js +7 -0
  42. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.map +5 -0
  43. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -0
  44. package/app/apps/web/.next/server/app/api/workflow-agent/route_client-reference-manifest.js +3 -0
  45. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  46. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  47. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  48. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  49. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  50. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  51. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  52. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  53. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  54. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  55. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  56. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  57. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  58. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  59. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  60. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  61. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  62. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  63. package/app/apps/web/.next/server/app/print/[key]/page/app-paths-manifest.json +3 -0
  64. package/app/apps/web/.next/server/app/print/[key]/page/build-manifest.json +17 -0
  65. package/app/apps/web/.next/server/app/print/[key]/page/next-font-manifest.json +6 -0
  66. package/app/apps/web/.next/server/app/print/[key]/page/react-loadable-manifest.json +1 -0
  67. package/app/apps/web/.next/server/app/print/[key]/page/server-reference-manifest.json +4 -0
  68. package/app/apps/web/.next/server/app/print/[key]/page.js +18 -0
  69. package/app/apps/web/.next/server/app/print/[key]/page.js.map +5 -0
  70. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -0
  71. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +3 -0
  72. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  73. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  74. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  75. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  76. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  77. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  78. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  79. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  80. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  81. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  82. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  83. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  84. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  85. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  86. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  87. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  88. package/app/apps/web/.next/server/app-paths-manifest.json +3 -0
  89. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0455vu3._.js +2 -2
  90. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0_8zh-i._.js → [root-of-the-server]__04j3tng._.js} +2 -2
  91. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0jprdks._.js → [root-of-the-server]__0cnh3k4._.js} +2 -2
  92. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0e.f-t.._.js +3 -0
  93. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0kc9e1n._.js +3 -0
  94. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0y5p73y._.js +3 -0
  95. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0ej_p3s._.js → [root-of-the-server]__132eosx._.js} +2 -2
  96. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_pdf_[key]_route_actions_0intcw9.js +3 -0
  97. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_workflow-agent_route_actions_0gxw9tv.js +3 -0
  98. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +1 -1
  99. package/app/apps/web/.next/server/chunks/packages_parser_dist_04rh1zd._.js +1 -1
  100. package/app/apps/web/.next/server/chunks/packages_parser_dist_0ndsxe6._.js +1 -1
  101. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0_xpuh7._.js → [root-of-the-server]__03y~03u._.js} +2 -2
  102. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04eq6z2._.js +1 -1
  103. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04il6m2._.js +11 -0
  104. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +1 -1
  105. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0o03ymy._.js +2 -2
  106. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0rvzdnx._.js +1 -1
  107. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0z7mg0b._.js +1 -1
  108. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +12 -0
  109. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__136-0me._.js +8 -0
  110. package/app/apps/web/.next/server/chunks/ssr/_0cgg5zi._.js +3 -0
  111. package/app/apps/web/.next/server/chunks/ssr/_0jw2sm7._.js +1 -1
  112. package/app/apps/web/.next/server/chunks/ssr/_0kajk31._.js +3 -0
  113. package/app/apps/web/.next/server/chunks/ssr/_0w.cv64._.js +8 -0
  114. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  115. package/app/apps/web/.next/server/chunks/ssr/apps_web_0.mu7c7._.js +1 -1
  116. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_print_[key]_page_actions_0_ygul-.js +3 -0
  117. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_01d-iwx._.js +6 -0
  118. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_week-digest_tsx_0c9ha31._.js +3 -0
  119. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0af94gl._.js +1 -1
  120. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07orc4m.js +4 -0
  121. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_12.endt._.js +1 -1
  122. package/app/apps/web/.next/server/edge/chunks/[root-of-the-server]__0.sx~id._.js +11 -0
  123. package/app/apps/web/.next/server/edge/chunks/node_modules_next_dist_esm_build_templates_edge-wrapper_0p1t1z8.js +3 -0
  124. package/app/apps/web/.next/server/edge/chunks/turbopack-node_modules_next_dist_esm_build_templates_edge-wrapper_0mw-sqe.js +3 -0
  125. package/app/apps/web/.next/server/functions-config-manifest.json +4 -1
  126. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  127. package/app/apps/web/.next/server/middleware-manifest.json +30 -2
  128. package/app/apps/web/.next/server/pages/500.html +1 -1
  129. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  130. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  131. package/app/apps/web/.next/static/WH20Ct_NdpZTKsqI_yquG/_clientMiddlewareManifest.js +6 -0
  132. package/app/apps/web/.next/static/chunks/00xkqnvk4-d1-.js +10 -0
  133. package/app/apps/web/.next/static/chunks/0716x58e1_2c5.js +1 -0
  134. package/app/apps/web/.next/static/chunks/{0s4eyofh28l~u.js → 0ck03i0l4zeun.js} +1 -1
  135. package/app/apps/web/.next/static/chunks/0nnjldasalwdu.js +4 -0
  136. package/app/apps/web/.next/static/chunks/0okg13phslr1..js +4 -0
  137. package/app/apps/web/.next/static/chunks/0vf-3rauafbek.js +6 -0
  138. package/app/apps/web/.next/static/chunks/0vl27-vjob2_9.js +6 -0
  139. package/app/apps/web/.next/static/chunks/0yo458-7577lr.js +1 -0
  140. package/app/apps/web/.next/static/chunks/14bjditmmrn7l.js +1 -0
  141. package/app/apps/web/app/api/events/route.ts +50 -2
  142. package/app/apps/web/app/api/pdf/[key]/route.ts +199 -0
  143. package/app/apps/web/app/api/workflow-agent/route.ts +27 -0
  144. package/app/apps/web/app/insights/[key]/page.tsx +4 -1
  145. package/app/apps/web/app/layout.tsx +23 -1
  146. package/app/apps/web/app/page.tsx +24 -1
  147. package/app/apps/web/app/print/[key]/page.tsx +91 -0
  148. package/app/apps/web/app/projects/[slug]/page.tsx +6 -7
  149. package/app/apps/web/app/projects/page.tsx +2 -3
  150. package/app/apps/web/app/sessions/[id]/session-view.tsx +1297 -19
  151. package/app/apps/web/app/sessions/sessions-grid.tsx +10 -1
  152. package/app/apps/web/components/dashboard-view.tsx +2 -1
  153. package/app/apps/web/components/export-pdf-button.tsx +76 -0
  154. package/app/apps/web/components/month-digest-view.tsx +10 -0
  155. package/app/apps/web/components/week-digest-view.tsx +10 -0
  156. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  157. package/app/apps/web/lib/entries-index.ts +5 -4
  158. package/app/apps/web/lib/find-chrome.ts +71 -0
  159. package/app/apps/web/middleware.ts +22 -0
  160. package/app/apps/web/package.json +1 -1
  161. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  162. package/dist/daemon-worker.js +1592 -252
  163. package/dist/index.js +1495 -168
  164. package/package.json +1 -1
  165. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0d~2~le._.js +0 -8
  166. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +0 -12
  167. package/app/apps/web/.next/server/chunks/ssr/_02s1n-x._.js +0 -8
  168. package/app/apps/web/.next/server/chunks/ssr/_0fjbf--._.js +0 -3
  169. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0ly1xes._.js +0 -6
  170. package/app/apps/web/.next/static/chunks/0.3mqbj24kwtc.js +0 -6
  171. package/app/apps/web/.next/static/chunks/0byz8fqw.exef.js +0 -1
  172. package/app/apps/web/.next/static/chunks/0l3ck.awv~4t7.js +0 -1
  173. package/app/apps/web/.next/static/chunks/0lxuac2v0_gdl.js +0 -6
  174. package/app/apps/web/.next/static/chunks/0m62vx92~0_0d.js +0 -1
  175. package/app/apps/web/.next/static/chunks/0rtba0saz74e1.js +0 -10
  176. package/app/apps/web/.next/static/chunks/0uev~g~qvbjv9.js +0 -4
  177. package/app/apps/web/.next/static/m6ibRSdAu8jD10VBA2OFH/_clientMiddlewareManifest.js +0 -1
  178. /package/app/apps/web/.next/static/{m6ibRSdAu8jD10VBA2OFH → WH20Ct_NdpZTKsqI_yquG}/_buildManifest.js +0 -0
  179. /package/app/apps/web/.next/static/{m6ibRSdAu8jD10VBA2OFH → WH20Ct_NdpZTKsqI_yquG}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -92,6 +92,16 @@ function findConductorWorkspace(projectName) {
92
92
  const canonicalEndIdx = m.index + m[0].length - workspaceName.length - 1 - trailingSlash;
93
93
  return { canonicalEndIdx, workspaceName };
94
94
  }
95
+ function findSupersetWorkspace(projectName) {
96
+ const idx = projectName.indexOf(SUPERSET_MARKER);
97
+ if (idx < 0)
98
+ return null;
99
+ const segs = projectName.slice(idx + SUPERSET_MARKER.length).split("/").filter(Boolean);
100
+ if (segs.length === 0)
101
+ return null;
102
+ const workspaceName = UUID_SEG_RE.test(segs[0]) ? segs[1] ?? segs[0] : segs[0];
103
+ return { canonical: projectName.slice(0, idx) + SUPERSET_MARKER + workspaceName, workspaceName };
104
+ }
95
105
  function canonicalProjectName(projectName) {
96
106
  const normalized = projectName.replace(/\/{2,}/g, "/");
97
107
  const wt = findWorktreeMarker(normalized);
@@ -100,6 +110,9 @@ function canonicalProjectName(projectName) {
100
110
  const cd = findConductorWorkspace(normalized);
101
111
  if (cd)
102
112
  return normalized.slice(0, cd.canonicalEndIdx);
113
+ const ss = findSupersetWorkspace(normalized);
114
+ if (ss)
115
+ return ss.canonical;
103
116
  return normalized;
104
117
  }
105
118
  function worktreeName(projectName) {
@@ -113,8 +126,16 @@ function worktreeName(projectName) {
113
126
  const cd = findConductorWorkspace(normalized);
114
127
  if (cd)
115
128
  return cd.workspaceName;
129
+ const ss = findSupersetWorkspace(normalized);
130
+ if (ss)
131
+ return ss.workspaceName;
116
132
  return null;
117
133
  }
134
+ function projectRepoName(projectName) {
135
+ const canonical = canonicalProjectName(projectName);
136
+ const segs = canonical.replace(/\/+$/, "").split("/").filter(Boolean);
137
+ return segs[segs.length - 1] || canonical;
138
+ }
118
139
  function makeDay(date) {
119
140
  return {
120
141
  date,
@@ -534,13 +555,13 @@ function summarizeBursts(bursts) {
534
555
  function groupByProject(sessions) {
535
556
  const map = /* @__PURE__ */ new Map();
536
557
  for (const s of sessions) {
537
- const canonical = canonicalProjectName(s.projectName);
538
- const key = canonical;
558
+ const repo = projectRepoName(s.projectName);
559
+ const key = repo;
539
560
  let cur = map.get(key);
540
561
  if (!cur) {
541
562
  cur = {
542
- projectDir: canonical,
543
- projectName: canonical,
563
+ projectDir: repo,
564
+ projectName: repo,
544
565
  sessions: [],
545
566
  rawProjectDirs: [],
546
567
  worktreeCount: 0,
@@ -582,7 +603,7 @@ function groupByProject(sessions) {
582
603
  }
583
604
  return Array.from(map.values()).sort((a, b) => (b.lastActiveMs ?? 0) - (a.lastActiveMs ?? 0));
584
605
  }
585
- var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
606
+ var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, SUPERSET_MARKER, UUID_SEG_RE, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
586
607
  var init_analytics = __esm({
587
608
  "../parser/dist/analytics.js"() {
588
609
  "use strict";
@@ -593,6 +614,8 @@ var init_analytics = __esm({
593
614
  "/claude/worktrees/"
594
615
  ];
595
616
  CONDUCTOR_RE = /\/conductor\/workspaces\/[^/]+\/([^/]+)(?:\/|$)/;
617
+ SUPERSET_MARKER = "/.superset/worktrees/";
618
+ UUID_SEG_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
596
619
  PR_CREATE_RE = /gh\s+pr\s+create\b/i;
597
620
  PR_NUMBER_RE = /#(\d+)/;
598
621
  TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
@@ -606,7 +629,7 @@ function getAgentMetadata(kind) {
606
629
  function isAgentKind(s) {
607
630
  return typeof s === "string" && KNOWN_AGENT_KINDS.has(s);
608
631
  }
609
- var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
632
+ var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, ANTIGRAVITY_METADATA, COWORK_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
610
633
  var init_agent_metadata = __esm({
611
634
  "../parser/dist/agent-metadata.js"() {
612
635
  "use strict";
@@ -631,10 +654,26 @@ var init_agent_metadata = __esm({
631
654
  accentColor: "rgb(124, 92, 246)",
632
655
  iconChar: "\u273A"
633
656
  };
657
+ ANTIGRAVITY_METADATA = {
658
+ kind: "antigravity",
659
+ displayName: "Antigravity",
660
+ shortLabel: "Agy",
661
+ accentColor: "rgb(236, 72, 153)",
662
+ iconChar: "\u23C3"
663
+ };
664
+ COWORK_METADATA = {
665
+ kind: "cowork",
666
+ displayName: "Cowork",
667
+ shortLabel: "Cowork",
668
+ accentColor: "rgb(217, 119, 87)",
669
+ iconChar: "\u2318"
670
+ };
634
671
  agentMetadata = [
635
672
  CLAUDE_CODE_METADATA,
636
673
  CODEX_METADATA,
637
- GEMINI_METADATA
674
+ GEMINI_METADATA,
675
+ ANTIGRAVITY_METADATA,
676
+ COWORK_METADATA
638
677
  ];
639
678
  KNOWN_AGENT_KINDS = new Set(agentMetadata.map((m) => m.kind));
640
679
  }
@@ -1660,6 +1699,52 @@ async function computeSessionUsageWithSubagents(parentLines, projectDirPath, ses
1660
1699
  }));
1661
1700
  return usage2;
1662
1701
  }
1702
+ async function scanWorkflowAggregate(projectDirPath, sessionId) {
1703
+ const dir = path.join(projectDirPath, sessionId, "workflows");
1704
+ let entries2;
1705
+ try {
1706
+ entries2 = await fs.readdir(dir);
1707
+ } catch {
1708
+ return { workflowCount: 0, spawnedAgentCount: 0, spans: [] };
1709
+ }
1710
+ const jsonFiles = entries2.filter((f) => f.startsWith("wf_") && f.endsWith(".json"));
1711
+ if (jsonFiles.length === 0)
1712
+ return { workflowCount: 0, spawnedAgentCount: 0, spans: [] };
1713
+ let spawnedAgentCount = 0;
1714
+ const spans = [];
1715
+ const parsed = await Promise.all(jsonFiles.map(async (f) => {
1716
+ try {
1717
+ const j = JSON.parse(await fs.readFile(path.join(dir, f), "utf8"));
1718
+ if (typeof j.agentCount === "number")
1719
+ spawnedAgentCount += j.agentCount;
1720
+ if (typeof j.startTime === "number" && typeof j.durationMs === "number" && j.durationMs > 0) {
1721
+ spans.push({ startMs: j.startTime, endMs: j.startTime + j.durationMs });
1722
+ }
1723
+ return true;
1724
+ } catch {
1725
+ return false;
1726
+ }
1727
+ }));
1728
+ return { workflowCount: parsed.filter(Boolean).length, spawnedAgentCount, spans };
1729
+ }
1730
+ function foldWorkflowAgentTime(activeSegments, spans) {
1731
+ if (spans.length === 0)
1732
+ return {};
1733
+ const all = [...activeSegments ?? [], ...spans].filter((s) => s.endMs > s.startMs).sort((a, b) => a.startMs - b.startMs);
1734
+ if (all.length === 0)
1735
+ return {};
1736
+ const merged = [{ ...all[0] }];
1737
+ for (let i = 1; i < all.length; i++) {
1738
+ const cur = all[i];
1739
+ const last = merged[merged.length - 1];
1740
+ if (cur.startMs <= last.endMs)
1741
+ last.endMs = Math.max(last.endMs, cur.endMs);
1742
+ else
1743
+ merged.push({ ...cur });
1744
+ }
1745
+ const airTimeMs = merged.reduce((n, s) => n + (s.endMs - s.startMs), 0);
1746
+ return { activeSegments: merged, airTimeMs };
1747
+ }
1663
1748
  async function getCachedMeta(f) {
1664
1749
  const cached = metaCache.get(f.fullPath);
1665
1750
  if (cached && cached.mtimeMs === f.mtimeMs && cached.sizeBytes === f.sizeBytes) {
@@ -1671,12 +1756,15 @@ async function getCachedMeta(f) {
1671
1756
  const sessionId = sessionIdFromFileName(f.fileName);
1672
1757
  const projectDirPath = path.dirname(f.fullPath);
1673
1758
  meta.totalUsage = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId);
1759
+ const wf = await scanWorkflowAggregate(projectDirPath, sessionId);
1674
1760
  const full = {
1675
1761
  ...meta,
1676
1762
  id: sessionId,
1677
1763
  filePath: f.fullPath,
1678
1764
  projectDir: f.projectDir,
1679
- projectName: meta.cwd ?? decodeProjectName(f.projectDir)
1765
+ projectName: meta.cwd ?? decodeProjectName(f.projectDir),
1766
+ ...wf.workflowCount > 0 ? { workflowCount: wf.workflowCount, spawnedAgentCount: wf.spawnedAgentCount } : {},
1767
+ ...foldWorkflowAgentTime(meta.activeSegments, wf.spans)
1680
1768
  };
1681
1769
  metaCache.set(f.fullPath, { meta: full, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
1682
1770
  return full;
@@ -1692,13 +1780,17 @@ async function getCachedDetail(f) {
1692
1780
  try {
1693
1781
  const rawLines = await readJsonlFile(f.fullPath);
1694
1782
  const { meta, events } = parseTranscript(rawLines);
1783
+ const sessionId = sessionIdFromFileName(f.fileName);
1784
+ const wf = await scanWorkflowAggregate(path.dirname(f.fullPath), sessionId);
1695
1785
  const detail = {
1696
1786
  ...meta,
1697
- id: sessionIdFromFileName(f.fileName),
1787
+ id: sessionId,
1698
1788
  filePath: f.fullPath,
1699
1789
  projectDir: f.projectDir,
1700
1790
  projectName: meta.cwd ?? decodeProjectName(f.projectDir),
1701
- events
1791
+ events,
1792
+ ...wf.workflowCount > 0 ? { workflowCount: wf.workflowCount, spawnedAgentCount: wf.spawnedAgentCount } : {},
1793
+ ...foldWorkflowAgentTime(meta.activeSegments, wf.spans)
1702
1794
  };
1703
1795
  detailCache.set(f.fullPath, { detail, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
1704
1796
  const metaOnly = { ...detail };
@@ -1730,8 +1822,18 @@ async function getSession(id, opts = {}) {
1730
1822
  return detail;
1731
1823
  const sessionStartMs = detail.firstTimestamp ? Date.parse(detail.firstTimestamp) : void 0;
1732
1824
  const subagentsDir = path.join(path.dirname(hit.fullPath), sessionIdFromFileName(hit.fileName), "subagents");
1733
- const subagents = await loadSubagents(subagentsDir, sessionStartMs, detail.events);
1734
- return { ...detail, subagents };
1825
+ const workflowsDir = path.join(path.dirname(hit.fullPath), sessionIdFromFileName(hit.fileName), "workflows");
1826
+ const [subagents, workflows] = await Promise.all([
1827
+ loadSubagents(subagentsDir, sessionStartMs, detail.events),
1828
+ loadWorkflows(workflowsDir, sessionStartMs, detail.events)
1829
+ ]);
1830
+ const spawnedAgentCount = workflows.reduce((n, w) => n + w.agentCount, 0);
1831
+ return {
1832
+ ...detail,
1833
+ subagents,
1834
+ workflows,
1835
+ ...workflows.length > 0 ? { workflowCount: workflows.length, spawnedAgentCount } : {}
1836
+ };
1735
1837
  }
1736
1838
  function summarizeSubagentLines(lines) {
1737
1839
  let startMs;
@@ -1921,6 +2023,248 @@ async function loadSubagents(dir, sessionStartMs, parentEvents) {
1921
2023
  }));
1922
2024
  return runs2.filter((r) => r !== null).sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
1923
2025
  }
2026
+ async function loadWorkflows(dir, sessionStartMs, parentEvents) {
2027
+ let entries2;
2028
+ try {
2029
+ entries2 = await fs.readdir(dir);
2030
+ } catch {
2031
+ return [];
2032
+ }
2033
+ const jsonFiles = entries2.filter((f) => f.startsWith("wf_") && f.endsWith(".json"));
2034
+ if (jsonFiles.length === 0)
2035
+ return [];
2036
+ const dispatches = [];
2037
+ for (const e of parentEvents) {
2038
+ if (e.role !== "tool-call" || e.toolName !== "Workflow")
2039
+ continue;
2040
+ const tsMs = e.timestamp ? Date.parse(e.timestamp) : NaN;
2041
+ if (Number.isNaN(tsMs))
2042
+ continue;
2043
+ for (const b of e.blocks) {
2044
+ if (b?.type !== "tool_use" || b.name !== "Workflow")
2045
+ continue;
2046
+ dispatches.push({ toolUseId: b.id, parentUuid: e.uuid ?? "", tsMs });
2047
+ }
2048
+ }
2049
+ dispatches.sort((a, b) => a.tsMs - b.tsMs);
2050
+ const matchDispatch = (startMs) => {
2051
+ if (startMs === void 0 || dispatches.length === 0)
2052
+ return void 0;
2053
+ const TOLERANCE_MS = 6e4;
2054
+ let before;
2055
+ let beforeDelta = Infinity;
2056
+ let nearest;
2057
+ let nearestDelta = Infinity;
2058
+ for (const d of dispatches) {
2059
+ const delta = Math.abs(startMs - d.tsMs);
2060
+ if (delta > TOLERANCE_MS)
2061
+ continue;
2062
+ if (delta < nearestDelta) {
2063
+ nearest = d;
2064
+ nearestDelta = delta;
2065
+ }
2066
+ if (d.tsMs <= startMs && delta < beforeDelta) {
2067
+ before = d;
2068
+ beforeDelta = delta;
2069
+ }
2070
+ }
2071
+ return before ?? nearest;
2072
+ };
2073
+ const num2 = (v) => typeof v === "number" ? v : void 0;
2074
+ const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
2075
+ const runs2 = await Promise.all(jsonFiles.map(async (f) => {
2076
+ let j;
2077
+ try {
2078
+ j = JSON.parse(await fs.readFile(path.join(dir, f), "utf8"));
2079
+ } catch {
2080
+ return null;
2081
+ }
2082
+ const runId = str(j.runId) ?? f.replace(/\.json$/, "");
2083
+ const startMs = num2(j.startTime);
2084
+ const durationMs = num2(j.durationMs);
2085
+ const endMs = startMs !== void 0 && durationMs !== void 0 ? startMs + durationMs : void 0;
2086
+ const startTOffsetMs = sessionStartMs !== void 0 && startMs !== void 0 ? Math.max(0, startMs - sessionStartMs) : void 0;
2087
+ const endTOffsetMs = sessionStartMs !== void 0 && endMs !== void 0 ? Math.max(0, endMs - sessionStartMs) : void 0;
2088
+ const phases = Array.isArray(j.phases) ? j.phases.filter((p) => !!p && typeof p === "object" && typeof p.title === "string").map((p) => ({ title: p.title, detail: str(p.detail) })) : [];
2089
+ const logs = Array.isArray(j.logs) ? j.logs.filter((l) => typeof l === "string") : [];
2090
+ const cap = (v, n) => {
2091
+ const s = str(v);
2092
+ return s === void 0 ? void 0 : s.length > n ? s.slice(0, n) + "\u2026" : s;
2093
+ };
2094
+ const progress = Array.isArray(j.workflowProgress) ? j.workflowProgress : [];
2095
+ const agents = progress.filter((e) => !!e && typeof e === "object" && e.type === "workflow_agent").map((e) => ({
2096
+ index: num2(e.index) ?? 0,
2097
+ label: str(e.label) ?? "(agent)",
2098
+ phaseIndex: num2(e.phaseIndex),
2099
+ phaseTitle: str(e.phaseTitle),
2100
+ agentId: str(e.agentId),
2101
+ model: str(e.model),
2102
+ state: str(e.state),
2103
+ startedAt: num2(e.startedAt),
2104
+ durationMs: num2(e.durationMs),
2105
+ tokens: num2(e.tokens),
2106
+ toolCalls: num2(e.toolCalls),
2107
+ lastToolSummary: cap(e.lastToolSummary, 200),
2108
+ promptPreview: cap(e.promptPreview, 600),
2109
+ resultPreview: cap(e.resultPreview, 600)
2110
+ })).sort((a, b) => a.index - b.index);
2111
+ const parent = matchDispatch(startMs);
2112
+ return {
2113
+ runId,
2114
+ name: str(j.workflowName) ?? runId,
2115
+ description: str(j.summary),
2116
+ status: str(j.status) ?? "unknown",
2117
+ agentCount: num2(j.agentCount) ?? 0,
2118
+ toolCallCount: num2(j.totalToolCalls) ?? 0,
2119
+ totalTokens: num2(j.totalTokens) ?? 0,
2120
+ durationMs,
2121
+ startMs,
2122
+ endMs,
2123
+ startTOffsetMs,
2124
+ endTOffsetMs,
2125
+ model: str(j.defaultModel),
2126
+ phases,
2127
+ logs,
2128
+ agents,
2129
+ parentToolUseId: parent?.toolUseId,
2130
+ parentUuid: parent?.parentUuid
2131
+ };
2132
+ }));
2133
+ return runs2.filter((r) => r !== null).sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
2134
+ }
2135
+ function salientToolInput(input) {
2136
+ if (!input || typeof input !== "object")
2137
+ return "";
2138
+ const o = input;
2139
+ const pick = (k) => typeof o[k] === "string" ? o[k] : void 0;
2140
+ return pick("command") ?? pick("file_path") ?? pick("path") ?? pick("pattern") ?? pick("url") ?? pick("description") ?? pick("query") ?? pick("prompt") ?? pick("old_string") ?? JSON.stringify(o);
2141
+ }
2142
+ async function loadWorkflowAgentDetail(sessionId, runId, agentId, opts = {}) {
2143
+ const { root = DEFAULT_ROOT } = opts;
2144
+ if (!/^[\w.-]+$/.test(runId) || !/^[\w.-]+$/.test(agentId))
2145
+ return null;
2146
+ const files = await walkJsonlFiles(root);
2147
+ const hit = files.find((f) => sessionIdFromFileName(f.fileName) === sessionId);
2148
+ if (!hit)
2149
+ return null;
2150
+ const file = path.join(path.dirname(hit.fullPath), sessionId, "subagents", "workflows", runId, `agent-${agentId}.jsonl`);
2151
+ let lines;
2152
+ try {
2153
+ lines = await readJsonlFile(file);
2154
+ } catch {
2155
+ return null;
2156
+ }
2157
+ const errored = /* @__PURE__ */ new Set();
2158
+ for (const raw of lines) {
2159
+ if (!raw || typeof raw !== "object")
2160
+ continue;
2161
+ const content = raw.message?.content;
2162
+ if (!Array.isArray(content))
2163
+ continue;
2164
+ for (const b of content) {
2165
+ if (b && typeof b === "object" && b.type === "tool_result" && b.is_error === true && typeof b.tool_use_id === "string") {
2166
+ errored.add(b.tool_use_id);
2167
+ }
2168
+ }
2169
+ }
2170
+ const steps = [];
2171
+ const toolCounts = /* @__PURE__ */ new Map();
2172
+ const totalUsage = { ...BLANK_USAGE3 };
2173
+ const seen = /* @__PURE__ */ new Set();
2174
+ let prompt;
2175
+ let finalText;
2176
+ let model;
2177
+ let assistantMessageCount = 0;
2178
+ let eventCount = 0;
2179
+ let startMs;
2180
+ let endMs;
2181
+ for (const raw of lines) {
2182
+ if (!raw || typeof raw !== "object")
2183
+ continue;
2184
+ const r = raw;
2185
+ eventCount++;
2186
+ const ts = typeof r.timestamp === "string" ? Date.parse(r.timestamp) : NaN;
2187
+ if (!Number.isNaN(ts)) {
2188
+ if (startMs === void 0 || ts < startMs)
2189
+ startMs = ts;
2190
+ if (endMs === void 0 || ts > endMs)
2191
+ endMs = ts;
2192
+ }
2193
+ if (prompt === void 0 && r.type === "user" && (r.parentUuid === null || r.parentUuid === void 0)) {
2194
+ const c = r.message?.content;
2195
+ if (typeof c === "string")
2196
+ prompt = c;
2197
+ else if (Array.isArray(c)) {
2198
+ const t = c.find((b) => b && typeof b === "object" && b.type === "text");
2199
+ if (t?.text)
2200
+ prompt = t.text;
2201
+ }
2202
+ }
2203
+ if (r.type !== "assistant")
2204
+ continue;
2205
+ const m = r.message;
2206
+ if (!m)
2207
+ continue;
2208
+ if (!model && typeof m.model === "string")
2209
+ model = m.model;
2210
+ const mid = typeof m.id === "string" ? m.id : void 0;
2211
+ const rid = typeof r.requestId === "string" ? r.requestId : void 0;
2212
+ const key = mid != null && rid != null ? `${mid}:${rid}` : void 0;
2213
+ const fresh = key ? !seen.has(key) : true;
2214
+ if (key && fresh)
2215
+ seen.add(key);
2216
+ if (fresh) {
2217
+ assistantMessageCount++;
2218
+ const u = m.usage;
2219
+ if (u) {
2220
+ const n = (v) => typeof v === "number" ? v : 0;
2221
+ totalUsage.input += n(u.input_tokens);
2222
+ totalUsage.output += n(u.output_tokens);
2223
+ totalUsage.cacheRead += n(u.cache_read_input_tokens);
2224
+ totalUsage.cacheWrite += n(u.cache_creation_input_tokens);
2225
+ }
2226
+ }
2227
+ const content = m.content;
2228
+ if (!Array.isArray(content))
2229
+ continue;
2230
+ for (const b of content) {
2231
+ if (!b || typeof b !== "object")
2232
+ continue;
2233
+ if (b.type === "text" && typeof b.text === "string") {
2234
+ finalText = b.text;
2235
+ } else if (b.type === "tool_use" && typeof b.name === "string") {
2236
+ toolCounts.set(b.name, (toolCounts.get(b.name) ?? 0) + 1);
2237
+ if (steps.length < 2e3) {
2238
+ const salient = salientToolInput(b.input);
2239
+ const preview = salient.replace(/\s+/g, " ").trim().slice(0, 160);
2240
+ const full = salient.length > 4e3 ? salient.slice(0, 4e3) + "\u2026" : salient;
2241
+ steps.push({
2242
+ index: steps.length + 1,
2243
+ tool: b.name,
2244
+ preview,
2245
+ // Only carry `full` when it adds something over the one-line preview.
2246
+ full: full.length > preview.length || full.includes("\n") ? full : void 0,
2247
+ isError: typeof b.id === "string" && errored.has(b.id)
2248
+ });
2249
+ }
2250
+ }
2251
+ }
2252
+ }
2253
+ const toolCalls = Array.from(toolCounts.entries()).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
2254
+ return {
2255
+ agentId,
2256
+ prompt,
2257
+ finalText,
2258
+ steps,
2259
+ toolCalls,
2260
+ toolCallCount: steps.length,
2261
+ assistantMessageCount,
2262
+ eventCount,
2263
+ totalUsage,
2264
+ model,
2265
+ durationMs: startMs !== void 0 && endMs !== void 0 ? endMs - startMs : void 0
2266
+ };
2267
+ }
1924
2268
  async function listProjects(root = DEFAULT_ROOT) {
1925
2269
  const files = await walkJsonlFiles(root);
1926
2270
  const byRawDir = /* @__PURE__ */ new Map();
@@ -1942,10 +2286,10 @@ async function listProjects(root = DEFAULT_ROOT) {
1942
2286
  const canonicalMap = /* @__PURE__ */ new Map();
1943
2287
  for (const [rawDir, { count, lastActiveMs }] of byRawDir) {
1944
2288
  const cwdOrDecoded = cwdForProject(rawDir) ?? decodeProjectName(rawDir);
1945
- const canonical = canonicalProjectName(cwdOrDecoded);
2289
+ const repo = projectRepoName(cwdOrDecoded);
1946
2290
  const wt = worktreeName(cwdOrDecoded);
1947
- const agg = canonicalMap.get(canonical) ?? {
1948
- canonicalName: canonical,
2291
+ const agg = canonicalMap.get(repo) ?? {
2292
+ canonicalName: repo,
1949
2293
  count: 0,
1950
2294
  lastActiveMs: 0,
1951
2295
  worktreeBranches: /* @__PURE__ */ new Set()
@@ -1955,7 +2299,7 @@ async function listProjects(root = DEFAULT_ROOT) {
1955
2299
  agg.lastActiveMs = lastActiveMs;
1956
2300
  if (wt)
1957
2301
  agg.worktreeBranches.add(wt);
1958
- canonicalMap.set(canonical, agg);
2302
+ canonicalMap.set(repo, agg);
1959
2303
  }
1960
2304
  return Array.from(canonicalMap.values()).map((a) => ({
1961
2305
  projectDir: a.canonicalName,
@@ -2913,17 +3257,410 @@ async function listGeminiSessions(opts = {}) {
2913
3257
  const files = await listSessionFiles(root);
2914
3258
  const out = [];
2915
3259
  for (const file of files) {
2916
- const cached = metaCache3.get(file.filePath);
2917
- if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes) {
3260
+ const cached = metaCache3.get(file.filePath);
3261
+ if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes) {
3262
+ out.push(cached.meta);
3263
+ continue;
3264
+ }
3265
+ try {
3266
+ const { meta } = await parseSession(file);
3267
+ metaCache3.set(file.filePath, {
3268
+ meta,
3269
+ mtimeMs: file.mtimeMs,
3270
+ sizeBytes: file.sizeBytes
3271
+ });
3272
+ out.push(meta);
3273
+ } catch {
3274
+ }
3275
+ }
3276
+ out.sort((a, b) => (b.firstTimestamp ?? "").localeCompare(a.firstTimestamp ?? ""));
3277
+ if (opts.limit !== void 0)
3278
+ return out.slice(0, opts.limit);
3279
+ return out;
3280
+ }
3281
+ async function getGeminiSession(id, opts = {}) {
3282
+ const root = opts.root ?? DEFAULT_GEMINI_ROOT;
3283
+ const files = await listSessionFiles(root);
3284
+ let chosen;
3285
+ for (const file of files) {
3286
+ if (file.filenameId === id || id.startsWith(file.filenameId)) {
3287
+ chosen = file;
3288
+ break;
3289
+ }
3290
+ }
3291
+ if (!chosen) {
3292
+ for (const file of files) {
3293
+ const { meta: meta2 } = await parseSession(file).catch(() => ({ meta: { id: "" } }));
3294
+ if (meta2.id === id) {
3295
+ chosen = file;
3296
+ break;
3297
+ }
3298
+ }
3299
+ }
3300
+ if (!chosen)
3301
+ return null;
3302
+ const cached = detailCache3.get(chosen.filePath);
3303
+ if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes) {
3304
+ return cached.detail;
3305
+ }
3306
+ const { meta, events } = await parseSession(chosen);
3307
+ const detail = { ...meta, events };
3308
+ detailCache3.set(chosen.filePath, {
3309
+ detail,
3310
+ mtimeMs: chosen.mtimeMs,
3311
+ sizeBytes: chosen.sizeBytes
3312
+ });
3313
+ return detail;
3314
+ }
3315
+ function geminiSessionLocalDay(meta) {
3316
+ if (!meta.firstTimestamp)
3317
+ return void 0;
3318
+ const ms = Date.parse(meta.firstTimestamp);
3319
+ return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
3320
+ }
3321
+ var DEFAULT_GEMINI_ROOT, GEMINI_PROJECTS_FILE, SESSION_FILE_RE, IDLE_GAP_MS2, projectsCache, metaCache3, detailCache3;
3322
+ var init_gemini = __esm({
3323
+ "../parser/dist/gemini.js"() {
3324
+ "use strict";
3325
+ init_analytics();
3326
+ init_user_input();
3327
+ DEFAULT_GEMINI_ROOT = path3.join(os3.homedir(), ".gemini", "tmp");
3328
+ GEMINI_PROJECTS_FILE = path3.join(os3.homedir(), ".gemini", "projects.json");
3329
+ SESSION_FILE_RE = /^session-.+\.(jsonl|json)$/;
3330
+ IDLE_GAP_MS2 = 3 * 60 * 1e3;
3331
+ projectsCache = null;
3332
+ metaCache3 = /* @__PURE__ */ new Map();
3333
+ detailCache3 = /* @__PURE__ */ new Map();
3334
+ }
3335
+ });
3336
+
3337
+ // ../parser/dist/antigravity.js
3338
+ import { promises as fs4 } from "node:fs";
3339
+ import os4 from "node:os";
3340
+ import path4 from "node:path";
3341
+ async function safeReaddir3(p) {
3342
+ return fs4.readdir(p).catch(() => []);
3343
+ }
3344
+ async function loadSlugToCwd2(root) {
3345
+ const historyFile = path4.join(root, "history.jsonl");
3346
+ const stat = await fs4.stat(historyFile).catch(() => null);
3347
+ if (!stat)
3348
+ return /* @__PURE__ */ new Map();
3349
+ if (historyCache && historyCache.mtimeMs === stat.mtimeMs) {
3350
+ return historyCache.slugToCwd;
3351
+ }
3352
+ const raw = await fs4.readFile(historyFile, "utf8").catch(() => "");
3353
+ const slugToCwd = /* @__PURE__ */ new Map();
3354
+ for (const line of raw.split("\n")) {
3355
+ const trimmed = line.trim();
3356
+ if (!trimmed)
3357
+ continue;
3358
+ try {
3359
+ const parsed = JSON.parse(trimmed);
3360
+ if (parsed.conversationId && parsed.workspace) {
3361
+ slugToCwd.set(parsed.conversationId, parsed.workspace);
3362
+ }
3363
+ } catch {
3364
+ }
3365
+ }
3366
+ historyCache = { mtimeMs: stat.mtimeMs, slugToCwd };
3367
+ return slugToCwd;
3368
+ }
3369
+ async function listSessionFiles2(root) {
3370
+ const slugToCwd = await loadSlugToCwd2(root);
3371
+ const out = [];
3372
+ const brainDir = path4.join(root, "brain");
3373
+ const entries2 = await safeReaddir3(brainDir);
3374
+ for (const entry of entries2) {
3375
+ const transcriptPath = path4.join(brainDir, entry, ".system_generated", "logs", "transcript.jsonl");
3376
+ const stat = await fs4.stat(transcriptPath).catch(() => null);
3377
+ if (stat && stat.isFile()) {
3378
+ out.push({
3379
+ filePath: transcriptPath,
3380
+ conversationId: entry,
3381
+ cwd: slugToCwd.get(entry),
3382
+ mtimeMs: stat.mtimeMs,
3383
+ sizeBytes: stat.size
3384
+ });
3385
+ }
3386
+ }
3387
+ return out;
3388
+ }
3389
+ function emptyUsage3() {
3390
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
3391
+ }
3392
+ function previewOf3(text) {
3393
+ return text.replace(/\s+/g, " ").trim().slice(0, 200);
3394
+ }
3395
+ function truncate4(s, n) {
3396
+ return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
3397
+ }
3398
+ async function parseSession2(file) {
3399
+ const raw = await fs4.readFile(file.filePath, "utf8");
3400
+ const events = [];
3401
+ const totalUsage = emptyUsage3();
3402
+ let firstTimestamp;
3403
+ let lastTimestamp;
3404
+ let model;
3405
+ let toolCallCount = 0;
3406
+ let turnCount = 0;
3407
+ let firstUserPreview;
3408
+ let lastUserPreview;
3409
+ let lastAgentPreview;
3410
+ const tsMs = [];
3411
+ let idx = 0;
3412
+ function pushEvent(e) {
3413
+ events.push(e);
3414
+ if (e.timestamp && e.role !== "meta") {
3415
+ if (!firstTimestamp)
3416
+ firstTimestamp = e.timestamp;
3417
+ lastTimestamp = e.timestamp;
3418
+ const ms = Date.parse(e.timestamp);
3419
+ if (Number.isFinite(ms))
3420
+ tsMs.push(ms);
3421
+ }
3422
+ }
3423
+ const pendingToolCalls = [];
3424
+ const parsedObjects = [];
3425
+ const indexMap = /* @__PURE__ */ new Map();
3426
+ for (const line of raw.split("\n")) {
3427
+ const trimmed = line.trim();
3428
+ if (!trimmed)
3429
+ continue;
3430
+ try {
3431
+ const obj = JSON.parse(trimmed);
3432
+ const stepIdx = obj.step_index;
3433
+ if (typeof stepIdx === "number") {
3434
+ if (indexMap.has(stepIdx)) {
3435
+ parsedObjects[indexMap.get(stepIdx)] = obj;
3436
+ } else {
3437
+ indexMap.set(stepIdx, parsedObjects.length);
3438
+ parsedObjects.push(obj);
3439
+ }
3440
+ } else {
3441
+ parsedObjects.push(obj);
3442
+ }
3443
+ } catch {
3444
+ }
3445
+ }
3446
+ for (const eventObj of parsedObjects) {
3447
+ const ts = eventObj.created_at || eventObj.timestamp;
3448
+ if (eventObj.type === "USER_INPUT") {
3449
+ if (typeof eventObj.content === "string") {
3450
+ const matchNoNeed = eventObj.content.match(/Model Selection` from .*? to (.*?)\.\s*No need to/);
3451
+ if (matchNoNeed) {
3452
+ let rawModel = matchNoNeed[1].trim();
3453
+ rawModel = rawModel.replace(/\s*\(.*?\)\s*$/, "").trim();
3454
+ model = rawModel;
3455
+ } else {
3456
+ const matchDot = eventObj.content.match(/Model Selection` from .*? to ([^\n]+)/);
3457
+ if (matchDot) {
3458
+ let rawModel = matchDot[1].trim();
3459
+ if (rawModel.endsWith(".")) {
3460
+ rawModel = rawModel.slice(0, -1).trim();
3461
+ }
3462
+ rawModel = rawModel.replace(/\s*\(.*?\)\s*$/, "").trim();
3463
+ model = rawModel;
3464
+ }
3465
+ }
3466
+ }
3467
+ let text2 = eventObj.content || "";
3468
+ const requestMatch = text2.match(/<USER_REQUEST>([\s\S]*?)<\/USER_REQUEST>/);
3469
+ if (requestMatch) {
3470
+ text2 = requestMatch[1].trim();
3471
+ }
3472
+ const preview = previewOf3(text2);
3473
+ const isHidden = isFrameworkInjectedUserInput(text2);
3474
+ if (text2 && !isHidden) {
3475
+ if (!firstUserPreview)
3476
+ firstUserPreview = preview;
3477
+ lastUserPreview = preview;
3478
+ turnCount += 1;
3479
+ }
3480
+ pushEvent({
3481
+ index: idx++,
3482
+ timestamp: ts,
3483
+ role: "user",
3484
+ rawType: "antigravity/user",
3485
+ preview,
3486
+ blocks: text2 ? [{ type: "text", text: text2 }] : [],
3487
+ messageId: eventObj.step_index?.toString(),
3488
+ raw: eventObj
3489
+ });
3490
+ continue;
3491
+ }
3492
+ if (eventObj.type === "PLANNER_RESPONSE") {
3493
+ const thinking = eventObj.thinking;
3494
+ const content = eventObj.content;
3495
+ if (thinking) {
3496
+ pushEvent({
3497
+ index: idx++,
3498
+ timestamp: ts,
3499
+ role: "agent-thinking",
3500
+ rawType: "antigravity/thought",
3501
+ preview: previewOf3(thinking),
3502
+ blocks: [{ type: "thinking", thinking }],
3503
+ model,
3504
+ raw: eventObj
3505
+ });
3506
+ }
3507
+ if (content) {
3508
+ const preview = previewOf3(content);
3509
+ lastAgentPreview = preview;
3510
+ pushEvent({
3511
+ index: idx++,
3512
+ timestamp: ts,
3513
+ role: "agent",
3514
+ rawType: "antigravity/agent",
3515
+ preview,
3516
+ blocks: [{ type: "text", text: content }],
3517
+ messageId: eventObj.step_index?.toString(),
3518
+ model,
3519
+ raw: eventObj
3520
+ });
3521
+ }
3522
+ const toolCalls = eventObj.tool_calls || [];
3523
+ for (let cIdx = 0; cIdx < toolCalls.length; cIdx++) {
3524
+ const tc = toolCalls[cIdx];
3525
+ toolCallCount += 1;
3526
+ const toolUseId = `tool-${eventObj.step_index}-${cIdx}`;
3527
+ const argsStr = tc.args ? JSON.stringify(tc.args) : "";
3528
+ pendingToolCalls.push({ id: toolUseId, name: tc.name });
3529
+ pushEvent({
3530
+ index: idx++,
3531
+ timestamp: ts,
3532
+ role: "tool-call",
3533
+ rawType: "antigravity/tool_call",
3534
+ preview: `${tc.name}(${truncate4(argsStr, 80)})`,
3535
+ blocks: [{ type: "tool_use", id: toolUseId, name: tc.name, input: tc.args || {} }],
3536
+ toolName: tc.name,
3537
+ toolUseId,
3538
+ model,
3539
+ raw: tc
3540
+ });
3541
+ }
3542
+ continue;
3543
+ }
3544
+ if (eventObj.source === "MODEL" && eventObj.type !== "PLANNER_RESPONSE" && eventObj.type !== "ERROR_MESSAGE" && pendingToolCalls.length > 0) {
3545
+ const matchedCall = pendingToolCalls.shift();
3546
+ const toolUseId = matchedCall ? matchedCall.id : `tool-${eventObj.step_index}-unknown`;
3547
+ const contentStr = typeof eventObj.content === "string" ? eventObj.content : JSON.stringify(eventObj.content || "");
3548
+ const isError = eventObj.status === "ERROR" || eventObj.status === "FAILED" || eventObj.status === "CANCELED" || !!eventObj.error;
3549
+ pushEvent({
3550
+ index: idx++,
3551
+ timestamp: ts,
3552
+ role: "tool-result",
3553
+ rawType: "antigravity/tool_result",
3554
+ preview: previewOf3(contentStr),
3555
+ blocks: [{
3556
+ type: "tool_result",
3557
+ tool_use_id: toolUseId,
3558
+ content: contentStr,
3559
+ is_error: isError
3560
+ }],
3561
+ toolUseId,
3562
+ toolResult: contentStr,
3563
+ raw: eventObj
3564
+ });
3565
+ continue;
3566
+ }
3567
+ const text = eventObj.content || eventObj.error || "";
3568
+ pushEvent({
3569
+ index: idx++,
3570
+ timestamp: ts,
3571
+ role: "meta",
3572
+ rawType: `antigravity/${eventObj.type?.toLowerCase() || "unknown"}`,
3573
+ preview: previewOf3(text),
3574
+ blocks: text ? [{ type: "text", text }] : [],
3575
+ raw: eventObj
3576
+ });
3577
+ }
3578
+ const startMs = firstTimestamp ? Date.parse(firstTimestamp) : void 0;
3579
+ if (startMs !== void 0 && Number.isFinite(startMs)) {
3580
+ let prevMs;
3581
+ for (const ev of events) {
3582
+ if (!ev.timestamp)
3583
+ continue;
3584
+ const ms = Date.parse(ev.timestamp);
3585
+ if (!Number.isFinite(ms))
3586
+ continue;
3587
+ ev.tOffsetMs = Math.max(0, ms - startMs);
3588
+ if (prevMs !== void 0)
3589
+ ev.gapMs = Math.max(0, ms - prevMs);
3590
+ prevMs = ms;
3591
+ }
3592
+ }
3593
+ const activeSegments = computeActiveSegments4(tsMs);
3594
+ const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
3595
+ const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
3596
+ const sessionId = file.conversationId;
3597
+ const cwd = file.cwd;
3598
+ const projectName = cwd ? canonicalProjectName(cwd) : file.conversationId;
3599
+ const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.conversationId;
3600
+ const meta = {
3601
+ agent: "antigravity",
3602
+ id: sessionId,
3603
+ filePath: file.filePath,
3604
+ projectName,
3605
+ projectDir,
3606
+ sessionId,
3607
+ firstTimestamp,
3608
+ lastTimestamp,
3609
+ durationMs: durationMs && Number.isFinite(durationMs) ? durationMs : void 0,
3610
+ eventCount: events.length,
3611
+ model,
3612
+ cwd,
3613
+ totalUsage,
3614
+ status: "idle",
3615
+ firstUserPreview,
3616
+ lastUserPreview,
3617
+ lastAgentPreview,
3618
+ toolCallCount,
3619
+ turnCount,
3620
+ airTimeMs,
3621
+ activeSegments
3622
+ };
3623
+ return { meta, events };
3624
+ }
3625
+ function computeActiveSegments4(tsMs) {
3626
+ if (tsMs.length === 0)
3627
+ return [];
3628
+ const sorted = [...tsMs].sort((a, b) => a - b);
3629
+ const out = [];
3630
+ let segStart = sorted[0];
3631
+ let segEnd = sorted[0];
3632
+ for (let i = 1; i < sorted.length; i++) {
3633
+ if (sorted[i] - segEnd > IDLE_GAP_MS3) {
3634
+ out.push({ startMs: segStart, endMs: segEnd });
3635
+ segStart = sorted[i];
3636
+ }
3637
+ segEnd = sorted[i];
3638
+ }
3639
+ out.push({ startMs: segStart, endMs: segEnd });
3640
+ return out;
3641
+ }
3642
+ function clearAntigravityCaches() {
3643
+ metaCache4.clear();
3644
+ detailCache4.clear();
3645
+ historyCache = null;
3646
+ }
3647
+ async function listAntigravitySessions(opts = {}) {
3648
+ const root = opts.root ?? DEFAULT_ANTIGRAVITY_ROOT;
3649
+ const files = await listSessionFiles2(root);
3650
+ const out = [];
3651
+ for (const file of files) {
3652
+ const cached = metaCache4.get(file.filePath);
3653
+ if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes && cached.cwd === file.cwd) {
2918
3654
  out.push(cached.meta);
2919
3655
  continue;
2920
3656
  }
2921
3657
  try {
2922
- const { meta } = await parseSession(file);
2923
- metaCache3.set(file.filePath, {
3658
+ const { meta } = await parseSession2(file);
3659
+ metaCache4.set(file.filePath, {
2924
3660
  meta,
2925
3661
  mtimeMs: file.mtimeMs,
2926
- sizeBytes: file.sizeBytes
3662
+ sizeBytes: file.sizeBytes,
3663
+ cwd: file.cwd
2927
3664
  });
2928
3665
  out.push(meta);
2929
3666
  } catch {
@@ -2934,59 +3671,274 @@ async function listGeminiSessions(opts = {}) {
2934
3671
  return out.slice(0, opts.limit);
2935
3672
  return out;
2936
3673
  }
2937
- async function getGeminiSession(id, opts = {}) {
2938
- const root = opts.root ?? DEFAULT_GEMINI_ROOT;
2939
- const files = await listSessionFiles(root);
3674
+ async function getAntigravitySession(id, opts = {}) {
3675
+ const root = opts.root ?? DEFAULT_ANTIGRAVITY_ROOT;
3676
+ const files = await listSessionFiles2(root);
2940
3677
  let chosen;
2941
3678
  for (const file of files) {
2942
- if (file.filenameId === id || id.startsWith(file.filenameId)) {
3679
+ if (file.conversationId === id) {
2943
3680
  chosen = file;
2944
3681
  break;
2945
3682
  }
2946
3683
  }
2947
- if (!chosen) {
2948
- for (const file of files) {
2949
- const { meta: meta2 } = await parseSession(file).catch(() => ({ meta: { id: "" } }));
2950
- if (meta2.id === id) {
2951
- chosen = file;
2952
- break;
2953
- }
2954
- }
2955
- }
2956
3684
  if (!chosen)
2957
3685
  return null;
2958
- const cached = detailCache3.get(chosen.filePath);
2959
- if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes) {
3686
+ const cached = detailCache4.get(chosen.filePath);
3687
+ if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes && cached.cwd === chosen.cwd) {
2960
3688
  return cached.detail;
2961
3689
  }
2962
- const { meta, events } = await parseSession(chosen);
3690
+ const { meta, events } = await parseSession2(chosen);
2963
3691
  const detail = { ...meta, events };
2964
- detailCache3.set(chosen.filePath, {
3692
+ detailCache4.set(chosen.filePath, {
2965
3693
  detail,
2966
3694
  mtimeMs: chosen.mtimeMs,
2967
- sizeBytes: chosen.sizeBytes
3695
+ sizeBytes: chosen.sizeBytes,
3696
+ cwd: chosen.cwd
2968
3697
  });
2969
3698
  return detail;
2970
3699
  }
2971
- function geminiSessionLocalDay(meta) {
3700
+ function antigravitySessionLocalDay(meta) {
2972
3701
  if (!meta.firstTimestamp)
2973
3702
  return void 0;
2974
3703
  const ms = Date.parse(meta.firstTimestamp);
2975
3704
  return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
2976
3705
  }
2977
- var DEFAULT_GEMINI_ROOT, GEMINI_PROJECTS_FILE, SESSION_FILE_RE, IDLE_GAP_MS2, projectsCache, metaCache3, detailCache3;
2978
- var init_gemini = __esm({
2979
- "../parser/dist/gemini.js"() {
3706
+ var DEFAULT_ANTIGRAVITY_ROOT, IDLE_GAP_MS3, historyCache, metaCache4, detailCache4;
3707
+ var init_antigravity = __esm({
3708
+ "../parser/dist/antigravity.js"() {
2980
3709
  "use strict";
2981
3710
  init_analytics();
2982
3711
  init_user_input();
2983
- DEFAULT_GEMINI_ROOT = path3.join(os3.homedir(), ".gemini", "tmp");
2984
- GEMINI_PROJECTS_FILE = path3.join(os3.homedir(), ".gemini", "projects.json");
2985
- SESSION_FILE_RE = /^session-.+\.(jsonl|json)$/;
2986
- IDLE_GAP_MS2 = 3 * 60 * 1e3;
2987
- projectsCache = null;
2988
- metaCache3 = /* @__PURE__ */ new Map();
2989
- detailCache3 = /* @__PURE__ */ new Map();
3712
+ DEFAULT_ANTIGRAVITY_ROOT = path4.join(os4.homedir(), ".gemini", "antigravity-cli");
3713
+ IDLE_GAP_MS3 = 3 * 60 * 1e3;
3714
+ historyCache = null;
3715
+ metaCache4 = /* @__PURE__ */ new Map();
3716
+ detailCache4 = /* @__PURE__ */ new Map();
3717
+ }
3718
+ });
3719
+
3720
+ // ../parser/dist/cowork.js
3721
+ import { promises as fs5 } from "node:fs";
3722
+ import os5 from "node:os";
3723
+ import path5 from "node:path";
3724
+ function defaultCoworkRoot() {
3725
+ const home = os5.homedir();
3726
+ if (process.platform === "darwin") {
3727
+ return path5.join(home, "Library", "Application Support", "Claude", "local-agent-mode-sessions");
3728
+ }
3729
+ if (process.platform === "win32") {
3730
+ const appData = process.env.APPDATA ?? path5.join(home, "AppData", "Roaming");
3731
+ return path5.join(appData, "Claude", "local-agent-mode-sessions");
3732
+ }
3733
+ return path5.join(home, ".config", "Claude", "local-agent-mode-sessions");
3734
+ }
3735
+ async function safeReaddir4(p) {
3736
+ return fs5.readdir(p).catch(() => []);
3737
+ }
3738
+ async function statMs(p) {
3739
+ const s = await fs5.stat(p).catch(() => null);
3740
+ return s ? s.mtimeMs : 0;
3741
+ }
3742
+ async function listSessionFiles3(root) {
3743
+ const out = [];
3744
+ const accounts = await safeReaddir4(root);
3745
+ for (const accountId of accounts) {
3746
+ const accountDir = path5.join(root, accountId);
3747
+ const workspaces = await safeReaddir4(accountDir);
3748
+ for (const workspaceId of workspaces) {
3749
+ const workspaceDir = path5.join(accountDir, workspaceId);
3750
+ const spacesMtimeMs = await statMs(path5.join(workspaceDir, "spaces.json"));
3751
+ const entries2 = await safeReaddir4(workspaceDir);
3752
+ for (const entry of entries2) {
3753
+ if (!SESSION_ID_RE.test(entry))
3754
+ continue;
3755
+ const auditPath = path5.join(workspaceDir, entry, "audit.jsonl");
3756
+ const auditStat = await fs5.stat(auditPath).catch(() => null);
3757
+ if (!auditStat || !auditStat.isFile() || auditStat.size === 0)
3758
+ continue;
3759
+ const metaPath = path5.join(workspaceDir, `${entry}.json`);
3760
+ const metaMtimeMs = await statMs(metaPath);
3761
+ out.push({
3762
+ sessionId: entry,
3763
+ auditPath,
3764
+ metaPath,
3765
+ workspaceDir,
3766
+ auditMtimeMs: auditStat.mtimeMs,
3767
+ auditSize: auditStat.size,
3768
+ metaMtimeMs,
3769
+ spacesMtimeMs
3770
+ });
3771
+ }
3772
+ }
3773
+ }
3774
+ return out;
3775
+ }
3776
+ async function loadSpacesForWorkspace(workspaceDir) {
3777
+ const spacesPath = path5.join(workspaceDir, "spaces.json");
3778
+ const stat = await fs5.stat(spacesPath).catch(() => null);
3779
+ if (!stat)
3780
+ return /* @__PURE__ */ new Map();
3781
+ const cached = spacesCache.get(workspaceDir);
3782
+ if (cached && cached.mtimeMs === stat.mtimeMs)
3783
+ return cached.spaceIdToPath;
3784
+ const raw = await fs5.readFile(spacesPath, "utf8").catch(() => "");
3785
+ const out = /* @__PURE__ */ new Map();
3786
+ try {
3787
+ const parsed = JSON.parse(raw);
3788
+ for (const space of parsed.spaces ?? []) {
3789
+ const id = typeof space.id === "string" ? space.id : void 0;
3790
+ const folders = Array.isArray(space.folders) ? space.folders : [];
3791
+ const first = folders[0];
3792
+ const folderPath = typeof first?.path === "string" ? first.path : void 0;
3793
+ if (id && folderPath)
3794
+ out.set(id, folderPath);
3795
+ }
3796
+ } catch {
3797
+ }
3798
+ spacesCache.set(workspaceDir, { mtimeMs: stat.mtimeMs, spaceIdToPath: out });
3799
+ return out;
3800
+ }
3801
+ async function readCoworkMeta(metaPath) {
3802
+ const raw = await fs5.readFile(metaPath, "utf8").catch(() => "");
3803
+ if (!raw)
3804
+ return {};
3805
+ try {
3806
+ const j = JSON.parse(raw);
3807
+ return {
3808
+ spaceId: typeof j.spaceId === "string" ? j.spaceId : void 0,
3809
+ userSelectedFolders: Array.isArray(j.userSelectedFolders) ? j.userSelectedFolders.filter((s) => typeof s === "string") : void 0,
3810
+ cwd: typeof j.cwd === "string" ? j.cwd : void 0,
3811
+ model: typeof j.model === "string" ? j.model : void 0,
3812
+ title: typeof j.title === "string" ? j.title : void 0,
3813
+ createdAt: typeof j.createdAt === "number" ? j.createdAt : void 0,
3814
+ lastActivityAt: typeof j.lastActivityAt === "number" ? j.lastActivityAt : void 0
3815
+ };
3816
+ } catch {
3817
+ return {};
3818
+ }
3819
+ }
3820
+ function resolveProject(meta, spaceIdToPath) {
3821
+ let projectPath;
3822
+ if (meta.spaceId)
3823
+ projectPath = spaceIdToPath.get(meta.spaceId);
3824
+ if (!projectPath && meta.userSelectedFolders && meta.userSelectedFolders.length > 0) {
3825
+ projectPath = meta.userSelectedFolders[0];
3826
+ }
3827
+ if (!projectPath) {
3828
+ return { projectName: "cowork:unspaced", projectDir: "cowork-unspaced" };
3829
+ }
3830
+ return {
3831
+ projectName: canonicalProjectName(projectPath),
3832
+ projectDir: projectPath.replace(/^\//, "-").replace(/\//g, "-"),
3833
+ cwd: projectPath
3834
+ };
3835
+ }
3836
+ async function readJsonl2(filePath) {
3837
+ const raw = await fs5.readFile(filePath, "utf8");
3838
+ const out = [];
3839
+ for (const line of raw.split("\n")) {
3840
+ const trimmed = line.trim();
3841
+ if (!trimmed)
3842
+ continue;
3843
+ try {
3844
+ out.push(JSON.parse(trimmed));
3845
+ } catch {
3846
+ }
3847
+ }
3848
+ return out;
3849
+ }
3850
+ async function parseCoworkSession(file) {
3851
+ const [lines, coworkMeta, spaceIdToPath] = await Promise.all([
3852
+ readJsonl2(file.auditPath),
3853
+ readCoworkMeta(file.metaPath),
3854
+ loadSpacesForWorkspace(file.workspaceDir)
3855
+ ]);
3856
+ const { meta: baseMeta, events } = parseTranscript(lines);
3857
+ const { projectName, projectDir, cwd } = resolveProject(coworkMeta, spaceIdToPath);
3858
+ const meta = {
3859
+ ...baseMeta,
3860
+ agent: "cowork",
3861
+ id: file.sessionId,
3862
+ sessionId: file.sessionId,
3863
+ filePath: file.auditPath,
3864
+ projectName,
3865
+ projectDir,
3866
+ cwd: cwd ?? baseMeta.cwd,
3867
+ model: baseMeta.model ?? coworkMeta.model
3868
+ };
3869
+ return { meta, events };
3870
+ }
3871
+ function cacheKeyFor(file) {
3872
+ return {
3873
+ auditMtimeMs: file.auditMtimeMs,
3874
+ auditSize: file.auditSize,
3875
+ metaMtimeMs: file.metaMtimeMs,
3876
+ spacesMtimeMs: file.spacesMtimeMs
3877
+ };
3878
+ }
3879
+ function cacheKeyMatches(a, b) {
3880
+ return a.auditMtimeMs === b.auditMtimeMs && a.auditSize === b.auditSize && a.metaMtimeMs === b.metaMtimeMs && a.spacesMtimeMs === b.spacesMtimeMs;
3881
+ }
3882
+ function clearCoworkCaches() {
3883
+ metaCache5.clear();
3884
+ detailCache5.clear();
3885
+ spacesCache.clear();
3886
+ }
3887
+ async function listCoworkSessions(opts = {}) {
3888
+ const root = opts.root ?? DEFAULT_COWORK_ROOT;
3889
+ const files = await listSessionFiles3(root);
3890
+ const out = [];
3891
+ for (const file of files) {
3892
+ const key = cacheKeyFor(file);
3893
+ const cached = metaCache5.get(file.auditPath);
3894
+ if (cached && cacheKeyMatches(cached.key, key)) {
3895
+ out.push(cached.meta);
3896
+ continue;
3897
+ }
3898
+ try {
3899
+ const { meta } = await parseCoworkSession(file);
3900
+ metaCache5.set(file.auditPath, { meta, key });
3901
+ out.push(meta);
3902
+ } catch {
3903
+ }
3904
+ }
3905
+ out.sort((a, b) => (b.firstTimestamp ?? "").localeCompare(a.firstTimestamp ?? ""));
3906
+ if (opts.limit !== void 0)
3907
+ return out.slice(0, opts.limit);
3908
+ return out;
3909
+ }
3910
+ async function getCoworkSession(id, opts = {}) {
3911
+ const root = opts.root ?? DEFAULT_COWORK_ROOT;
3912
+ const files = await listSessionFiles3(root);
3913
+ const file = files.find((f) => f.sessionId === id);
3914
+ if (!file)
3915
+ return null;
3916
+ const key = cacheKeyFor(file);
3917
+ const cached = detailCache5.get(file.auditPath);
3918
+ if (cached && cacheKeyMatches(cached.key, key))
3919
+ return cached.detail;
3920
+ const { meta, events } = await parseCoworkSession(file);
3921
+ const detail = { ...meta, events };
3922
+ detailCache5.set(file.auditPath, { detail, key });
3923
+ return detail;
3924
+ }
3925
+ function coworkSessionLocalDay(meta) {
3926
+ if (!meta.firstTimestamp)
3927
+ return void 0;
3928
+ const ms = Date.parse(meta.firstTimestamp);
3929
+ return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
3930
+ }
3931
+ var DEFAULT_COWORK_ROOT, SESSION_ID_RE, spacesCache, metaCache5, detailCache5;
3932
+ var init_cowork = __esm({
3933
+ "../parser/dist/cowork.js"() {
3934
+ "use strict";
3935
+ init_analytics();
3936
+ init_parser();
3937
+ DEFAULT_COWORK_ROOT = defaultCoworkRoot();
3938
+ SESSION_ID_RE = /^local_[0-9a-f-]+$/;
3939
+ spacesCache = /* @__PURE__ */ new Map();
3940
+ metaCache5 = /* @__PURE__ */ new Map();
3941
+ detailCache5 = /* @__PURE__ */ new Map();
2990
3942
  }
2991
3943
  });
2992
3944
 
@@ -3034,11 +3986,14 @@ var init_team_wire = __esm({
3034
3986
  // ../parser/dist/fs.js
3035
3987
  var fs_exports = {};
3036
3988
  __export(fs_exports, {
3989
+ DEFAULT_ANTIGRAVITY_ROOT: () => DEFAULT_ANTIGRAVITY_ROOT,
3037
3990
  DEFAULT_CODEX_ROOT: () => DEFAULT_CODEX_ROOT,
3991
+ DEFAULT_COWORK_ROOT: () => DEFAULT_COWORK_ROOT,
3038
3992
  DEFAULT_GEMINI_ROOT: () => DEFAULT_GEMINI_ROOT,
3039
3993
  DEFAULT_ROOT: () => DEFAULT_ROOT,
3040
3994
  RICH_ROLLUP_SCHEMA_VERSION: () => RICH_ROLLUP_SCHEMA_VERSION,
3041
3995
  agentSources: () => agentSources,
3996
+ antigravitySessionLocalDay: () => antigravitySessionLocalDay,
3042
3997
  buildCalibrationCurve: () => buildCalibrationCurve,
3043
3998
  cacheStats: () => cacheStats,
3044
3999
  cclensHome: () => cclensHome,
@@ -3046,19 +4001,24 @@ __export(fs_exports, {
3046
4001
  clearCaches: () => clearCaches,
3047
4002
  clearTeamConfig: () => clearTeamConfig,
3048
4003
  codexSessionLocalDay: () => codexSessionLocalDay,
4004
+ coworkSessionLocalDay: () => coworkSessionLocalDay,
3049
4005
  decodeProjectName: () => decodeProjectName,
3050
4006
  findTeamLead: () => findTeamLead,
3051
4007
  geminiSessionLocalDay: () => geminiSessionLocalDay,
3052
4008
  getAgentSource: () => getAgentSource,
4009
+ getAntigravitySession: () => getAntigravitySession,
3053
4010
  getAnySession: () => getAnySession,
3054
4011
  getCodexSession: () => getCodexSession,
4012
+ getCoworkSession: () => getCoworkSession,
3055
4013
  getGeminiSession: () => getGeminiSession,
3056
4014
  getLatestCodexUsage: () => getLatestCodexUsage,
3057
4015
  getSession: () => getSession,
3058
4016
  invalidateFile: () => invalidateFile,
3059
4017
  isFleetlensRuntimeDir: () => isFleetlensRuntimeDir,
3060
4018
  listAllSessions: () => listAllSessions,
4019
+ listAntigravitySessions: () => listAntigravitySessions,
3061
4020
  listCodexSessions: () => listCodexSessions,
4021
+ listCoworkSessions: () => listCoworkSessions,
3062
4022
  listGeminiSessions: () => listGeminiSessions,
3063
4023
  listProjects: () => listProjects,
3064
4024
  listSessions: () => listSessions,
@@ -3067,20 +4027,21 @@ __export(fs_exports, {
3067
4027
  loadCalibrationSnapshots: () => loadCalibrationSnapshots,
3068
4028
  loadTeamForSession: () => loadTeamForSession,
3069
4029
  loadUsageByDay: () => loadUsageByDay,
4030
+ loadWorkflowAgentDetail: () => loadWorkflowAgentDetail,
3070
4031
  readJsonlFile: () => readJsonlFile,
3071
4032
  readTeamConfig: () => readTeamConfig,
3072
4033
  sessionIdFromFileName: () => sessionIdFromFileName,
3073
4034
  walkJsonlFiles: () => walkJsonlFiles,
3074
4035
  writeTeamConfig: () => writeTeamConfig
3075
4036
  });
3076
- import { promises as fs4 } from "node:fs";
3077
- import os4 from "node:os";
3078
- import path4 from "node:path";
4037
+ import { promises as fs6 } from "node:fs";
4038
+ import os6 from "node:os";
4039
+ import path6 from "node:path";
3079
4040
  function cclensHome() {
3080
- return process.env.CCLENS_HOME || path4.join(os4.homedir(), ".cclens");
4041
+ return process.env.CCLENS_HOME || path6.join(os6.homedir(), ".cclens");
3081
4042
  }
3082
4043
  function cclensPath(...parts) {
3083
- return path4.join(cclensHome(), ...parts);
4044
+ return path6.join(cclensHome(), ...parts);
3084
4045
  }
3085
4046
  function cacheStats() {
3086
4047
  return claudeCodeCacheStats();
@@ -3089,6 +4050,8 @@ function clearCaches() {
3089
4050
  clearClaudeCodeCaches();
3090
4051
  clearCodexCaches();
3091
4052
  clearGeminiCaches();
4053
+ clearAntigravityCaches();
4054
+ clearCoworkCaches();
3092
4055
  }
3093
4056
  function invalidateFile(fullPath) {
3094
4057
  invalidateClaudeCodeFile(fullPath);
@@ -3120,7 +4083,7 @@ function usageLogPath() {
3120
4083
  async function loadUsageByDay(start2, end) {
3121
4084
  let raw;
3122
4085
  try {
3123
- raw = await fs4.readFile(usageLogPath(), "utf8");
4086
+ raw = await fs6.readFile(usageLogPath(), "utf8");
3124
4087
  } catch {
3125
4088
  return { by_day: [] };
3126
4089
  }
@@ -3165,7 +4128,7 @@ async function loadUsageByDay(start2, end) {
3165
4128
  async function loadCalibrationSnapshots() {
3166
4129
  let raw;
3167
4130
  try {
3168
- raw = await fs4.readFile(usageLogPath(), "utf8");
4131
+ raw = await fs6.readFile(usageLogPath(), "utf8");
3169
4132
  } catch {
3170
4133
  return [];
3171
4134
  }
@@ -3323,7 +4286,7 @@ async function loadCalibrationCurve(tier = "pro-max-20x", root = DEFAULT_ROOT, g
3323
4286
  ]);
3324
4287
  return buildCalibrationCurve(events, snapshots, tier, granularityMin);
3325
4288
  }
3326
- var codexSource, geminiSource, agentSources;
4289
+ var codexSource, geminiSource, antigravitySource, coworkSource, agentSources;
3327
4290
  var init_fs = __esm({
3328
4291
  "../parser/dist/fs.js"() {
3329
4292
  "use strict";
@@ -3333,9 +4296,13 @@ var init_fs = __esm({
3333
4296
  init_claude_code();
3334
4297
  init_codex();
3335
4298
  init_gemini();
4299
+ init_antigravity();
4300
+ init_cowork();
3336
4301
  init_claude_code();
3337
4302
  init_codex();
3338
4303
  init_gemini();
4304
+ init_antigravity();
4305
+ init_cowork();
3339
4306
  init_team_config();
3340
4307
  init_team_wire();
3341
4308
  codexSource = {
@@ -3369,7 +4336,33 @@ var init_fs = __esm({
3369
4336
  return getGeminiSession(id);
3370
4337
  }
3371
4338
  };
3372
- agentSources = [claudeCodeSource, codexSource, geminiSource];
4339
+ antigravitySource = {
4340
+ ...ANTIGRAVITY_METADATA,
4341
+ defaultRoot: DEFAULT_ANTIGRAVITY_ROOT,
4342
+ async listSessions(opts) {
4343
+ return listAntigravitySessions(opts);
4344
+ },
4345
+ async getSession(id) {
4346
+ return getAntigravitySession(id);
4347
+ }
4348
+ };
4349
+ coworkSource = {
4350
+ ...COWORK_METADATA,
4351
+ defaultRoot: DEFAULT_COWORK_ROOT,
4352
+ async listSessions(opts) {
4353
+ return listCoworkSessions(opts);
4354
+ },
4355
+ async getSession(id) {
4356
+ return getCoworkSession(id);
4357
+ }
4358
+ };
4359
+ agentSources = [
4360
+ claudeCodeSource,
4361
+ codexSource,
4362
+ geminiSource,
4363
+ antigravitySource,
4364
+ coworkSource
4365
+ ];
3373
4366
  }
3374
4367
  });
3375
4368
 
@@ -3442,11 +4435,11 @@ function stopServer() {
3442
4435
  }
3443
4436
  async function checkPort(port) {
3444
4437
  const { createServer } = await import("node:net");
3445
- return new Promise((resolve) => {
4438
+ return new Promise((resolve3) => {
3446
4439
  const server = createServer();
3447
- server.once("error", () => resolve(true));
4440
+ server.once("error", () => resolve3(true));
3448
4441
  server.once("listening", () => {
3449
- server.close(() => resolve(false));
4442
+ server.close(() => resolve3(false));
3450
4443
  });
3451
4444
  server.listen(port, "localhost");
3452
4445
  });
@@ -3776,9 +4769,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
3776
4769
  console.warn(
3777
4770
  ` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
3778
4771
  );
3779
- } else if (verify.installedVersion !== "0.11.2") {
4772
+ } else if (verify.installedVersion !== "0.12.0") {
3780
4773
  console.log(
3781
- ` \u2192 This process is still running ${"0.11.2"}. Next invocation will use ${verify.installedVersion}.`
4774
+ ` \u2192 This process is still running ${"0.12.0"}. Next invocation will use ${verify.installedVersion}.`
3782
4775
  );
3783
4776
  }
3784
4777
  }
@@ -3813,14 +4806,14 @@ async function getUpdateAvailability() {
3813
4806
  if (isDevMode()) return null;
3814
4807
  const latest = await fetchLatestVersion();
3815
4808
  if (latest === null) return {
3816
- current: "0.11.2",
4809
+ current: "0.12.0",
3817
4810
  latest: null,
3818
4811
  updateAvailable: false
3819
4812
  };
3820
4813
  return {
3821
- current: "0.11.2",
4814
+ current: "0.12.0",
3822
4815
  latest,
3823
- updateAvailable: shouldUpdate("0.11.2", latest)
4816
+ updateAvailable: shouldUpdate("0.12.0", latest)
3824
4817
  };
3825
4818
  }
3826
4819
  async function checkForUpdate() {
@@ -3910,7 +4903,7 @@ async function restartServices(state) {
3910
4903
  }
3911
4904
  async function forceUpdate() {
3912
4905
  const latest = await fetchLatestVersion();
3913
- const current = "0.11.2";
4906
+ const current = "0.12.0";
3914
4907
  if (latest === null) {
3915
4908
  console.error("Could not reach npm registry. Check your network.");
3916
4909
  process.exit(1);
@@ -4088,10 +5081,10 @@ async function web(args2) {
4088
5081
  const open = args2.includes("--open");
4089
5082
  const positional = args2.filter((a) => !a.startsWith("--"));
4090
5083
  const rawPath = positional[0] ?? "";
4091
- const path5 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
5084
+ const path7 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
4092
5085
  const status2 = getServerStatus();
4093
5086
  if (status2.running) {
4094
- const url = `http://localhost:${status2.port}${path5}`;
5087
+ const url = `http://localhost:${status2.port}${path7}`;
4095
5088
  if (open) {
4096
5089
  console.log(`Opening ${url}`);
4097
5090
  openBrowser(url);
@@ -4103,7 +5096,7 @@ async function web(args2) {
4103
5096
  console.log("fleetlens is not running. Starting server...");
4104
5097
  try {
4105
5098
  const result = await startServer({});
4106
- const url = `http://localhost:${result.port}${path5}`;
5099
+ const url = `http://localhost:${result.port}${path7}`;
4107
5100
  console.log(`fleetlens running on ${url} (PID ${result.pid})`);
4108
5101
  if (open) {
4109
5102
  openBrowser(url);
@@ -4165,9 +5158,9 @@ function readFromCredentialsFile() {
4165
5158
  join5(homedir2(), ".claude", ".credentials.json"),
4166
5159
  join5(homedir2(), ".config", "claude", "credentials.json")
4167
5160
  ];
4168
- for (const path5 of candidates) {
5161
+ for (const path7 of candidates) {
4169
5162
  try {
4170
- const blob = readFileSync6(path5, "utf8");
5163
+ const blob = readFileSync6(path7, "utf8");
4171
5164
  const creds = extractCredentials(blob);
4172
5165
  if (creds) return creds;
4173
5166
  } catch {
@@ -5100,8 +6093,10 @@ var init_aggregate = __esm({
5100
6093
  // ../parser/dist/index.js
5101
6094
  var dist_exports = {};
5102
6095
  __export(dist_exports, {
6096
+ ANTIGRAVITY_METADATA: () => ANTIGRAVITY_METADATA,
5103
6097
  CLAUDE_CODE_METADATA: () => CLAUDE_CODE_METADATA,
5104
6098
  CODEX_METADATA: () => CODEX_METADATA,
6099
+ COWORK_METADATA: () => COWORK_METADATA,
5105
6100
  GEMINI_METADATA: () => GEMINI_METADATA,
5106
6101
  PRICES: () => PRICES,
5107
6102
  RATE_PER_PCT: () => RATE_PER_PCT,
@@ -5142,6 +6137,7 @@ __export(dist_exports, {
5142
6137
  predictUtilization: () => predictUtilization,
5143
6138
  priorCalendarMonth: () => priorCalendarMonth,
5144
6139
  priorCalendarWeek: () => priorCalendarWeek,
6140
+ projectRepoName: () => projectRepoName,
5145
6141
  rowPrimaryIndex: () => rowPrimaryIndex,
5146
6142
  sessionAirTimeMs: () => sessionAirTimeMs,
5147
6143
  sessionDay: () => sessionDay,
@@ -5345,7 +6341,7 @@ var init_trivial = __esm({
5345
6341
  });
5346
6342
 
5347
6343
  // ../entries/dist/build.js
5348
- var IDLE_GAP_MS3;
6344
+ var IDLE_GAP_MS4;
5349
6345
  var init_build = __esm({
5350
6346
  "../entries/dist/build.js"() {
5351
6347
  "use strict";
@@ -5353,7 +6349,7 @@ var init_build = __esm({
5353
6349
  init_types();
5354
6350
  init_signals();
5355
6351
  init_trivial();
5356
- IDLE_GAP_MS3 = 3 * 60 * 1e3;
6352
+ IDLE_GAP_MS4 = 3 * 60 * 1e3;
5357
6353
  }
5358
6354
  });
5359
6355
 
@@ -5386,11 +6382,11 @@ function digestsDir() {
5386
6382
  digestsDirCached = envOverride ?? cclensPath("digests");
5387
6383
  return digestsDirCached;
5388
6384
  }
5389
- function __setDigestsDirForTest(path5) {
5390
- digestsDirCached = path5;
5391
- mkdirSync4(join6(path5, "day"), { recursive: true });
5392
- mkdirSync4(join6(path5, "week"), { recursive: true });
5393
- mkdirSync4(join6(path5, "month"), { recursive: true });
6385
+ function __setDigestsDirForTest(path7) {
6386
+ digestsDirCached = path7;
6387
+ mkdirSync4(join6(path7, "day"), { recursive: true });
6388
+ mkdirSync4(join6(path7, "week"), { recursive: true });
6389
+ mkdirSync4(join6(path7, "month"), { recursive: true });
5394
6390
  }
5395
6391
  function dayDigestPath(date) {
5396
6392
  return join6(digestsDir(), "day", `${date}.json`);
@@ -5577,9 +6573,9 @@ function entriesDir() {
5577
6573
  entriesDirCached = envOverride ?? cclensPath("entries");
5578
6574
  return entriesDirCached;
5579
6575
  }
5580
- function __setEntriesDirForTest(path5) {
5581
- entriesDirCached = path5;
5582
- mkdirSync5(path5, { recursive: true });
6576
+ function __setEntriesDirForTest(path7) {
6577
+ entriesDirCached = path7;
6578
+ mkdirSync5(path7, { recursive: true });
5583
6579
  }
5584
6580
  function pathFor(sessionId, localDay) {
5585
6581
  return join7(entriesDir(), `${entryKey(sessionId, localDay)}.json`);
@@ -6115,8 +7111,8 @@ var init_parseUtil = __esm({
6115
7111
  init_errors();
6116
7112
  init_en();
6117
7113
  makeIssue = (params) => {
6118
- const { data, path: path5, errorMaps, issueData } = params;
6119
- const fullPath = [...path5, ...issueData.path || []];
7114
+ const { data, path: path7, errorMaps, issueData } = params;
7115
+ const fullPath = [...path7, ...issueData.path || []];
6120
7116
  const fullIssue = {
6121
7117
  ...issueData,
6122
7118
  path: fullPath
@@ -6424,11 +7420,11 @@ var init_types3 = __esm({
6424
7420
  init_parseUtil();
6425
7421
  init_util();
6426
7422
  ParseInputLazyPath = class {
6427
- constructor(parent, value, path5, key) {
7423
+ constructor(parent, value, path7, key) {
6428
7424
  this._cachedPath = [];
6429
7425
  this.parent = parent;
6430
7426
  this.data = value;
6431
- this._path = path5;
7427
+ this._path = path7;
6432
7428
  this._key = key;
6433
7429
  }
6434
7430
  get path() {
@@ -10086,10 +11082,10 @@ function snapshotAllTranscripts() {
10086
11082
  }
10087
11083
  return out;
10088
11084
  }
10089
- function readJsonlCwd(path5, maxLines = 8) {
11085
+ function readJsonlCwd(path7, maxLines = 8) {
10090
11086
  let raw;
10091
11087
  try {
10092
- raw = readFileSync9(path5, "utf8");
11088
+ raw = readFileSync9(path7, "utf8");
10093
11089
  } catch {
10094
11090
  return void 0;
10095
11091
  }
@@ -10143,10 +11139,10 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
10143
11139
  }
10144
11140
  throw new TmuxRunError(`timeout waiting for claude to create the session transcript (cwd: ${targetCwd})`);
10145
11141
  }
10146
- function parseTranscript2(path5, defaultModel) {
11142
+ function parseTranscript2(path7, defaultModel) {
10147
11143
  let raw;
10148
11144
  try {
10149
- raw = readFileSync9(path5, "utf8");
11145
+ raw = readFileSync9(path7, "utf8");
10150
11146
  } catch {
10151
11147
  return { assembled: "", inputTokens: 0, outputTokens: 0, modelUsed: defaultModel, done: false };
10152
11148
  }
@@ -10275,15 +11271,15 @@ function newRunId(kind) {
10275
11271
  function tracePath(runId) {
10276
11272
  return join9(RUNS_DIR, `${runId}.jsonl`);
10277
11273
  }
10278
- function safeAppend(path5, line) {
11274
+ function safeAppend(path7, line) {
10279
11275
  try {
10280
- appendFileSync2(path5, line);
11276
+ appendFileSync2(path7, line);
10281
11277
  } catch {
10282
11278
  }
10283
11279
  }
10284
- function safeWrite(path5, line) {
11280
+ function safeWrite(path7, line) {
10285
11281
  try {
10286
- writeFileSync6(path5, line);
11282
+ writeFileSync6(path7, line);
10287
11283
  } catch {
10288
11284
  }
10289
11285
  }
@@ -10361,7 +11357,7 @@ async function runClaudeSubprocess(args2) {
10361
11357
  return runClaudePrintMode(args2);
10362
11358
  }
10363
11359
  function runClaudePrintMode(args2) {
10364
- return new Promise((resolve, reject) => {
11360
+ return new Promise((resolve3, reject) => {
10365
11361
  ensureRunsDir();
10366
11362
  const kind = detectKindFromSystemPrompt(args2.systemPrompt);
10367
11363
  const runId = newRunId(kind);
@@ -10492,7 +11488,7 @@ ${args2.reminder}` : args2.userPrompt;
10492
11488
  reject(new Error(`claude exited ${code}: ${stderr.trim().slice(0, 300)}`));
10493
11489
  return;
10494
11490
  }
10495
- resolve({ content: buffer, input_tokens: inputTokens, output_tokens: outputTokens, model: modelUsed });
11491
+ resolve3({ content: buffer, input_tokens: inputTokens, output_tokens: outputTokens, model: modelUsed });
10496
11492
  });
10497
11493
  proc.on("error", (err) => {
10498
11494
  safeAppend(trace, JSON.stringify({ _meta: { type: "spawn_error", run_id: runId, ts: (/* @__PURE__ */ new Date()).toISOString(), error: err.message } }) + "\n");
@@ -10697,8 +11693,8 @@ function spendPath() {
10697
11693
  spendPathCached = cclensPath("llm-spend.jsonl");
10698
11694
  return spendPathCached;
10699
11695
  }
10700
- function __setSpendPathForTest(path5) {
10701
- spendPathCached = path5;
11696
+ function __setSpendPathForTest(path7) {
11697
+ spendPathCached = path7;
10702
11698
  }
10703
11699
  function appendSpend(record) {
10704
11700
  const p = spendPath();
@@ -10748,8 +11744,8 @@ function lockPath() {
10748
11744
  lockPathCached = cclensPath("llm-interactive.lock");
10749
11745
  return lockPathCached;
10750
11746
  }
10751
- function __setInteractiveLockPathForTest(path5) {
10752
- lockPathCached = path5;
11747
+ function __setInteractiveLockPathForTest(path7) {
11748
+ lockPathCached = path7;
10753
11749
  }
10754
11750
  function touchLock() {
10755
11751
  writeFileSync7(lockPath(), String(process.pid), "utf8");
@@ -10832,10 +11828,10 @@ async function runEnrichmentQueue(settings, opts = {}) {
10832
11828
  if (monthToDateSpend() >= budget)
10833
11829
  return { skipped: "budget_cap_reached" };
10834
11830
  const queue = listEntriesWithStatus(["pending", "error"]).filter((e) => (e.enrichment.retry_count ?? 0) < MAX_RETRY_COUNT);
10835
- const todayLocal = toLocalDay2(now());
11831
+ const todayLocal2 = toLocalDay2(now());
10836
11832
  let enriched = 0, errors = 0, skipped = 0;
10837
11833
  for (const entry of queue) {
10838
- if (entry.local_day === todayLocal) {
11834
+ if (entry.local_day === todayLocal2) {
10839
11835
  skipped++;
10840
11836
  continue;
10841
11837
  }
@@ -10906,8 +11902,8 @@ function settingsPath() {
10906
11902
  settingsPathCached = cclensPath("settings.json");
10907
11903
  return settingsPathCached;
10908
11904
  }
10909
- function __setSettingsPathForTest(path5) {
10910
- settingsPathCached = path5;
11905
+ function __setSettingsPathForTest(path7) {
11906
+ settingsPathCached = path7;
10911
11907
  }
10912
11908
  function toDisk(s) {
10913
11909
  return {
@@ -11144,7 +12140,7 @@ function rollupOutcomeDay(entries2) {
11144
12140
  return "exploratory";
11145
12141
  return "trivial";
11146
12142
  }
11147
- function truncate4(s, max) {
12143
+ function truncate5(s, max) {
11148
12144
  if (s.length <= max)
11149
12145
  return s;
11150
12146
  return s.slice(0, max - 1).trimEnd() + "\u2026";
@@ -11200,7 +12196,7 @@ function computeDaySignals(entries2) {
11200
12196
  type,
11201
12197
  count: v.count,
11202
12198
  sample_description: v.sample_description,
11203
- sample_prompt_preview: truncate4(v.sample_prompt_preview, 200)
12199
+ sample_prompt_preview: truncate5(v.sample_prompt_preview, 200)
11204
12200
  }));
11205
12201
  const frameCounts = /* @__PURE__ */ new Map();
11206
12202
  for (const s of signalsList) {
@@ -11219,7 +12215,7 @@ function computeDaySignals(entries2) {
11219
12215
  if ((e.first_user ?? "").length > 0)
11220
12216
  verbosity_distribution[s.verbosity] += 1;
11221
12217
  for (const ref of s.external_refs) {
11222
- external_refs.push({ session_id: e.session_id, kind: ref.kind, preview: truncate4(ref.preview, 200) });
12218
+ external_refs.push({ session_id: e.session_id, kind: ref.kind, preview: truncate5(ref.preview, 200) });
11223
12219
  }
11224
12220
  const ints = e.numbers.interrupts ?? 0;
11225
12221
  interrupts += ints;
@@ -12092,7 +13088,7 @@ function computeInteractionModes(entries2) {
12092
13088
  date: entry.local_day,
12093
13089
  project_display: prettyProject3(entry.project),
12094
13090
  type: sa.type,
12095
- prompt_preview: truncate5(sa.prompt_preview, 200)
13091
+ prompt_preview: truncate6(sa.prompt_preview, 200)
12096
13092
  });
12097
13093
  }
12098
13094
  const skillExamples = [];
@@ -12109,7 +13105,7 @@ function computeInteractionModes(entries2) {
12109
13105
  skillExamples.push({
12110
13106
  date: e.local_day,
12111
13107
  skill,
12112
- first_user_preview: truncate5(e.first_user, 200)
13108
+ first_user_preview: truncate6(e.first_user, 200)
12113
13109
  });
12114
13110
  }
12115
13111
  }
@@ -12126,7 +13122,7 @@ function computeInteractionModes(entries2) {
12126
13122
  project_display: prettyProject3(longestTurnEntry.project),
12127
13123
  active_min: longestTurnEntry.numbers.active_min,
12128
13124
  top_tools: longestTurnEntry.top_tools.slice(0, 5),
12129
- first_user_preview: truncate5(longestTurnEntry.first_user, 200)
13125
+ first_user_preview: truncate6(longestTurnEntry.first_user, 200)
12130
13126
  } : null;
12131
13127
  const tools_per_turn = turn_count > 0 ? tools_total / turn_count : 0;
12132
13128
  const label = tools_per_turn < 5 ? "rapid" : tools_per_turn < 15 ? "mixed" : "batch";
@@ -12157,7 +13153,7 @@ function computeInteractionModes(entries2) {
12157
13153
  }
12158
13154
  };
12159
13155
  }
12160
- function truncate5(s, max) {
13156
+ function truncate6(s, max) {
12161
13157
  if (s.length <= max)
12162
13158
  return s;
12163
13159
  return s.slice(0, max - 1).trimEnd() + "\u2026";
@@ -12204,7 +13200,7 @@ function computeWorkingShapes(dayDigests, entriesByDay = /* @__PURE__ */ new Map
12204
13200
  evidence_subagent = {
12205
13201
  type: sa.type,
12206
13202
  description: sa.description,
12207
- prompt_preview: truncate5(sa.prompt_preview, 200)
13203
+ prompt_preview: truncate6(sa.prompt_preview, 200)
12208
13204
  };
12209
13205
  }
12210
13206
  const project_display = repEntry ? prettyProject3(repEntry.project) : dd.projects[0]?.display_name ?? "";
@@ -12215,7 +13211,7 @@ function computeWorkingShapes(dayDigests, entriesByDay = /* @__PURE__ */ new Map
12215
13211
  outcome: dayOutcome.get(dd.key) ?? null,
12216
13212
  helpfulness: dayHelp.get(dd.key) ?? null,
12217
13213
  evidence_subagent,
12218
- evidence_first_user: repEntry?.first_user ? truncate5(repEntry.first_user, 200) : null,
13214
+ evidence_first_user: repEntry?.first_user ? truncate6(repEntry.first_user, 200) : null,
12219
13215
  day_signature: dd.day_signature ?? null
12220
13216
  };
12221
13217
  });
@@ -12290,7 +13286,7 @@ function computeInteractionGrammar(dayDigests, entriesByDay = /* @__PURE__ */ ne
12290
13286
  date: day2,
12291
13287
  session_id: ref.session_id,
12292
13288
  ref_kind: ref.kind,
12293
- preview: truncate5(ref.preview, 200)
13289
+ preview: truncate6(ref.preview, 200)
12294
13290
  });
12295
13291
  }
12296
13292
  total_interrupts += signals.comm_style.steering.interrupts;
@@ -12362,7 +13358,7 @@ function computeInteractionGrammar(dayDigests, entriesByDay = /* @__PURE__ */ ne
12362
13358
  count: v.count,
12363
13359
  days: [...v.days].sort(),
12364
13360
  sample_description: v.sample_description,
12365
- sample_prompt_preview: truncate5(v.sample_prompt_preview, 200)
13361
+ sample_prompt_preview: truncate6(v.sample_prompt_preview, 200)
12366
13362
  })),
12367
13363
  threads,
12368
13364
  communication_style: {
@@ -13010,7 +14006,7 @@ function activeMs(timestamps) {
13010
14006
  let active = 0;
13011
14007
  for (let i = 1; i < ts.length; i++) {
13012
14008
  const dt = ts[i] - ts[i - 1];
13013
- if (dt < IDLE_GAP_MS4)
14009
+ if (dt < IDLE_GAP_MS5)
13014
14010
  active += dt;
13015
14011
  }
13016
14012
  return active;
@@ -13181,7 +14177,7 @@ function buildSessionSlice(entry, events, daySignature) {
13181
14177
  let intEnd = sortedTs[0];
13182
14178
  for (let i = 1; i < sortedTs.length; i++) {
13183
14179
  const dt = sortedTs[i] - intEnd;
13184
- if (dt >= IDLE_GAP_MS4) {
14180
+ if (dt >= IDLE_GAP_MS5) {
13185
14181
  intervals.push({
13186
14182
  start_min: fmtMin(intStart - sliceStartMs),
13187
14183
  end_min: fmtMin(intEnd - sliceStartMs)
@@ -13578,14 +14574,14 @@ async function generateTopSession(slice, opts = {}) {
13578
14574
  void lastModel;
13579
14575
  }
13580
14576
  }
13581
- var IDLE_GAP_MS4, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE, INTERRUPT_RE2, PR_TITLE_RE, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
14577
+ var IDLE_GAP_MS5, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE, INTERRUPT_RE2, PR_TITLE_RE, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
13582
14578
  var init_top_sessions = __esm({
13583
14579
  "../entries/dist/top-sessions.js"() {
13584
14580
  "use strict";
13585
14581
  init_zod();
13586
14582
  init_signals();
13587
14583
  init_llm_runner();
13588
- IDLE_GAP_MS4 = 3 * 60 * 1e3;
14584
+ IDLE_GAP_MS5 = 3 * 60 * 1e3;
13589
14585
  LONG_AUTONOMOUS_MIN = 20;
13590
14586
  N_USER = 150;
13591
14587
  N_FIRST_AGENT = 150;
@@ -14104,6 +15100,10 @@ async function* runMonthDigestPipeline(yearMonth, opts) {
14104
15100
  } else {
14105
15101
  digest2.is_live = true;
14106
15102
  setCurrentMonthDigestInCache(yearMonth, digest2, now());
15103
+ if (opts.force === true) {
15104
+ writeMonthDigest(digest2);
15105
+ yield { type: "saved", path: `~/.cclens/digests/month/${yearMonth}.json` };
15106
+ }
14107
15107
  }
14108
15108
  yield { type: "digest", digest: digest2 };
14109
15109
  } finally {
@@ -14754,7 +15754,7 @@ function sessionTouchesDay(s, day2) {
14754
15754
  }
14755
15755
  return sessionDay(s) === day2;
14756
15756
  }
14757
- function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects) {
15757
+ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, resolveRepo) {
14758
15758
  const bounds = dayBoundsMs(day2);
14759
15759
  const clippedSessions = [];
14760
15760
  for (const s of daySessions) {
@@ -14764,16 +15764,40 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects) {
14764
15764
  }
14765
15765
  const bursts = computeBurstsFromSessions(clippedSessions);
14766
15766
  const stats = summarizeBursts(bursts);
15767
+ const repoForEntry = (e) => {
15768
+ if (resolveRepo) {
15769
+ const own = resolveRepo(e.project);
15770
+ if (own) return own;
15771
+ const counts = /* @__PURE__ */ new Map();
15772
+ for (const d of e.edited_dirs ?? []) {
15773
+ const r = resolveRepo(d);
15774
+ if (r) counts.set(r, (counts.get(r) ?? 0) + 1);
15775
+ }
15776
+ const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
15777
+ if (dominant) return dominant;
15778
+ }
15779
+ return e.github_repos?.length === 1 ? e.github_repos[0] : null;
15780
+ };
15781
+ const sessionRepo = /* @__PURE__ */ new Map();
15782
+ for (const e of entries2) {
15783
+ if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
15784
+ const r = repoForEntry(e);
15785
+ if (r) sessionRepo.set(e.session_id, r);
15786
+ }
14767
15787
  const projects = /* @__PURE__ */ new Map();
14768
15788
  for (const s of clippedSessions) {
14769
- const name = canonicalProjectName(s.projectName);
14770
- if (privateProjects.has(name)) continue;
15789
+ const canonical = canonicalProjectName(s.projectName);
15790
+ const name = projectRepoName(s.projectName);
15791
+ if (privateProjects.has(name) || privateProjects.has(canonical)) continue;
15792
+ const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
15793
+ const key = repo ?? name;
14771
15794
  const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
14772
- const cur = projects.get(name) ?? { agentTimeMs: 0, sessions: 0 };
15795
+ const cur = projects.get(key) ?? { project: name, agentTimeMs: 0, sessions: 0, ...repo ? { resolved: repo } : {} };
14773
15796
  cur.agentTimeMs += ms;
14774
15797
  cur.sessions += 1;
14775
- projects.set(name, cur);
15798
+ projects.set(key, cur);
14776
15799
  }
15800
+ const projectRepos = /* @__PURE__ */ new Map();
14777
15801
  const workingShapes = /* @__PURE__ */ new Map();
14778
15802
  const skills = /* @__PURE__ */ new Map();
14779
15803
  const subagents = /* @__PURE__ */ new Map();
@@ -14787,12 +15811,12 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects) {
14787
15811
  let longTotalMin = 0;
14788
15812
  let longMaxSingleMin = 0;
14789
15813
  for (const e of entries2) {
14790
- if (privateProjects.has(e.project)) continue;
15814
+ if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
14791
15815
  const shape = e.signals?.working_shape ?? null;
14792
15816
  if (shape) {
14793
15817
  const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
14794
15818
  cur.sessions += 1;
14795
- cur.agentTimeMs += e.numbers.active_min * 6e4;
15819
+ cur.agentTimeMs += Math.round(e.numbers.active_min * 6e4);
14796
15820
  workingShapes.set(shape, cur);
14797
15821
  }
14798
15822
  for (const [name, count] of Object.entries(e.skills)) {
@@ -14807,6 +15831,12 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects) {
14807
15831
  prs += e.numbers.prs;
14808
15832
  commits += e.numbers.commits;
14809
15833
  pushes += e.numbers.pushes;
15834
+ if (e.github_repos?.length) {
15835
+ const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
15836
+ const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
15837
+ for (const r of e.github_repos) set.add(r);
15838
+ projectRepos.set(key, set);
15839
+ }
14810
15840
  if (e.flags.includes("long_autonomous")) {
14811
15841
  longCount += 1;
14812
15842
  longTotalMin += e.numbers.active_min;
@@ -14814,11 +15844,21 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects) {
14814
15844
  }
14815
15845
  }
14816
15846
  return {
14817
- projects: Array.from(projects.entries()).map(([project, v]) => ({ project, ...v })).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
15847
+ projects: Array.from(projects.entries()).map(([key, v]) => {
15848
+ const extras = [...projectRepos.get(key) ?? []].filter((r) => r !== v.resolved);
15849
+ const repos = v.resolved ? [v.resolved, ...extras] : extras;
15850
+ return {
15851
+ project: v.project,
15852
+ agentTimeMs: v.agentTimeMs,
15853
+ sessions: v.sessions,
15854
+ ...repos.length ? { githubRepos: repos.slice(0, 5) } : {}
15855
+ };
15856
+ }).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
14818
15857
  workingShapes: Array.from(workingShapes.entries()).map(([shape, v]) => ({ shape, ...v })).sort((a, b) => b.sessions - a.sessions),
14819
15858
  concurrencyPeak: stats.peakConcurrent,
14820
15859
  parallelMinutes: Math.round(stats.totalParallelMs / 6e4),
14821
- longAutonomous: { count: longCount, totalMin: longTotalMin, maxSingleMin: longMaxSingleMin },
15860
+ // Wire schema requires integer minutes (active_min is a float).
15861
+ longAutonomous: { count: longCount, totalMin: Math.round(longTotalMin), maxSingleMin: Math.round(longMaxSingleMin) },
14822
15862
  toolErrors,
14823
15863
  skillsLoaded: Array.from(skills.entries()).map(([name, sessions]) => ({ name, sessions })).sort((a, b) => b.sessions - a.sessions),
14824
15864
  subagentsDispatched: Array.from(subagents.entries()).map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count),
@@ -14847,10 +15887,10 @@ function buildEnrichedExtras(entries2) {
14847
15887
  }
14848
15888
  return { outcomeMix, helpfulnessMix, goalMix };
14849
15889
  }
14850
- function buildRichBlocksForDay(day2, daySessions, privateProjects, enrichmentOptIn) {
15890
+ function buildRichBlocksForDay(day2, daySessions, privateProjects, enrichmentOptIn, resolveRepo) {
14851
15891
  const entries2 = listEntriesForDay(day2);
14852
15892
  if (entries2.length === 0) return void 0;
14853
- const rich = buildRichRollupBlocks(day2, daySessions, entries2, privateProjects);
15893
+ const rich = buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, resolveRepo);
14854
15894
  const enriched = enrichmentOptIn ? buildEnrichedExtras(entries2) : void 0;
14855
15895
  return enriched ? { rich, enriched } : { rich };
14856
15896
  }
@@ -14859,10 +15899,11 @@ function buildIngestPayload(inputs) {
14859
15899
  return {
14860
15900
  ingestId: randomUUID2(),
14861
15901
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
14862
- ...richRollup || inputs.enrichedExtras ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
15902
+ ...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
14863
15903
  ...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
14864
15904
  ...richRollup ? { richRollup } : {},
14865
15905
  ...inputs.enrichedExtras ? { enrichedExtras: inputs.enrichedExtras } : {},
15906
+ ...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
14866
15907
  ...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
14867
15908
  ...inputs.planTier ? { planTier: inputs.planTier } : {},
14868
15909
  ...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {}
@@ -14894,15 +15935,83 @@ var init_push = __esm({
14894
15935
  }
14895
15936
  });
14896
15937
 
15938
+ // src/team/git-remote.ts
15939
+ import { existsSync as existsSync12, readFileSync as readFileSync13, statSync as statSync3 } from "node:fs";
15940
+ import { dirname as dirname9, isAbsolute, join as join10, resolve } from "node:path";
15941
+ function parseGithubRemote(url) {
15942
+ const m = url.trim().match(GITHUB_URL_RE);
15943
+ return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
15944
+ }
15945
+ function originUrl(configText) {
15946
+ let inOrigin = false;
15947
+ for (const line of configText.split("\n")) {
15948
+ const t = line.trim();
15949
+ if (t.startsWith("[")) {
15950
+ inOrigin = /^\[remote\s+"origin"\]/.test(t);
15951
+ continue;
15952
+ }
15953
+ if (!inOrigin) continue;
15954
+ const m = t.match(/^url\s*=\s*(.+)$/);
15955
+ if (m) return m[1].trim();
15956
+ }
15957
+ return null;
15958
+ }
15959
+ function gitConfigPathFor(dir) {
15960
+ const dotGit = join10(dir, ".git");
15961
+ const st = statSync3(dotGit, { throwIfNoEntry: false });
15962
+ if (!st) return null;
15963
+ if (st.isDirectory()) return join10(dotGit, "config");
15964
+ const m = readFileSync13(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
15965
+ if (!m) return null;
15966
+ const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
15967
+ const commondirFile = join10(gd, "commondir");
15968
+ if (existsSync12(commondirFile)) {
15969
+ const common = readFileSync13(commondirFile, "utf8").trim();
15970
+ return join10(isAbsolute(common) ? common : resolve(gd, common), "config");
15971
+ }
15972
+ return join10(gd, "config");
15973
+ }
15974
+ function createRepoResolver() {
15975
+ const cache = /* @__PURE__ */ new Map();
15976
+ return (dir) => {
15977
+ if (!dir || !isAbsolute(dir)) return null;
15978
+ const hit = cache.get(dir);
15979
+ if (hit !== void 0) return hit;
15980
+ let repo = null;
15981
+ try {
15982
+ for (let d = dir; ; d = dirname9(d)) {
15983
+ const config = gitConfigPathFor(d);
15984
+ if (config && existsSync12(config)) {
15985
+ const url = originUrl(readFileSync13(config, "utf8"));
15986
+ repo = url ? parseGithubRemote(url) : null;
15987
+ break;
15988
+ }
15989
+ if (d === dirname9(d)) break;
15990
+ }
15991
+ } catch {
15992
+ repo = null;
15993
+ }
15994
+ cache.set(dir, repo);
15995
+ return repo;
15996
+ };
15997
+ }
15998
+ var GITHUB_URL_RE;
15999
+ var init_git_remote = __esm({
16000
+ "src/team/git-remote.ts"() {
16001
+ "use strict";
16002
+ GITHUB_URL_RE = /github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?$/;
16003
+ }
16004
+ });
16005
+
14897
16006
  // src/team/queue.ts
14898
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, appendFileSync as appendFileSync4, statSync as statSync3, mkdirSync as mkdirSync10 } from "node:fs";
14899
- import { dirname as dirname9 } from "node:path";
16007
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, appendFileSync as appendFileSync4, statSync as statSync4, mkdirSync as mkdirSync10 } from "node:fs";
16008
+ import { dirname as dirname10 } from "node:path";
14900
16009
  function enqueuePayload(payload, queuePath = QUEUE_PATH) {
14901
- mkdirSync10(dirname9(queuePath), { recursive: true });
16010
+ mkdirSync10(dirname10(queuePath), { recursive: true });
14902
16011
  const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
14903
16012
  appendFileSync4(queuePath, JSON.stringify(entry) + "\n", "utf8");
14904
16013
  try {
14905
- const { size } = statSync3(queuePath);
16014
+ const { size } = statSync4(queuePath);
14906
16015
  if (size > MAX_SIZE_BYTES) {
14907
16016
  pruneOldest(queuePath);
14908
16017
  }
@@ -14912,7 +16021,7 @@ function enqueuePayload(payload, queuePath = QUEUE_PATH) {
14912
16021
  function dequeuePayloads(queuePath = QUEUE_PATH) {
14913
16022
  let lines;
14914
16023
  try {
14915
- lines = readFileSync13(queuePath, "utf8").trim().split("\n").filter(Boolean);
16024
+ lines = readFileSync14(queuePath, "utf8").trim().split("\n").filter(Boolean);
14916
16025
  } catch {
14917
16026
  return [];
14918
16027
  }
@@ -14933,7 +16042,7 @@ function dequeuePayloads(queuePath = QUEUE_PATH) {
14933
16042
  function pruneOldest(queuePath) {
14934
16043
  let lines;
14935
16044
  try {
14936
- lines = readFileSync13(queuePath, "utf8").trim().split("\n").filter(Boolean);
16045
+ lines = readFileSync14(queuePath, "utf8").trim().split("\n").filter(Boolean);
14937
16046
  } catch {
14938
16047
  return;
14939
16048
  }
@@ -14952,8 +16061,8 @@ var init_queue2 = __esm({
14952
16061
  });
14953
16062
 
14954
16063
  // src/usage/profile.ts
14955
- import { existsSync as existsSync12, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
14956
- import { dirname as dirname10 } from "node:path";
16064
+ import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
16065
+ import { dirname as dirname11 } from "node:path";
14957
16066
  function mapRateLimitTier(rateLimitTier) {
14958
16067
  switch (rateLimitTier) {
14959
16068
  case "default_claude_pro":
@@ -14993,9 +16102,9 @@ async function fetchProfile(nowMs = Date.now()) {
14993
16102
  };
14994
16103
  }
14995
16104
  function readCachedProfile(cachePath) {
14996
- if (!existsSync12(cachePath)) return null;
16105
+ if (!existsSync13(cachePath)) return null;
14997
16106
  try {
14998
- const raw = JSON.parse(readFileSync14(cachePath, "utf8"));
16107
+ const raw = JSON.parse(readFileSync15(cachePath, "utf8"));
14999
16108
  if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
15000
16109
  return raw;
15001
16110
  } catch {
@@ -15003,7 +16112,7 @@ function readCachedProfile(cachePath) {
15003
16112
  }
15004
16113
  }
15005
16114
  function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
15006
- mkdirSync11(dirname10(cachePath), { recursive: true });
16115
+ mkdirSync11(dirname11(cachePath), { recursive: true });
15007
16116
  writeFileSync10(
15008
16117
  cachePath,
15009
16118
  JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
@@ -15185,11 +16294,11 @@ var init_backfill = __esm({
15185
16294
 
15186
16295
  // src/team/last-push.ts
15187
16296
  import { writeFileSync as writeFileSync11, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "node:fs";
15188
- import { join as join10 } from "node:path";
16297
+ import { join as join11 } from "node:path";
15189
16298
  function write(record, dir) {
15190
16299
  const d = dir ?? cclensHome();
15191
16300
  mkdirSync12(d, { recursive: true });
15192
- writeFileSync11(join10(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
16301
+ writeFileSync11(join11(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
15193
16302
  }
15194
16303
  function writeLastPushSuccess(payload, dir) {
15195
16304
  write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: true, payload }, dir);
@@ -15200,7 +16309,7 @@ function writeLastPushFailure(payload, error, dir) {
15200
16309
  function clearLastPush(dir) {
15201
16310
  const d = dir ?? cclensHome();
15202
16311
  try {
15203
- unlinkSync5(join10(d, LAST_PUSH_FILE));
16312
+ unlinkSync5(join11(d, LAST_PUSH_FILE));
15204
16313
  } catch {
15205
16314
  }
15206
16315
  }
@@ -15213,6 +16322,194 @@ var init_last_push = __esm({
15213
16322
  }
15214
16323
  });
15215
16324
 
16325
+ // src/perception/file-probe.ts
16326
+ var file_probe_exports = {};
16327
+ __export(file_probe_exports, {
16328
+ pathHash: () => pathHash,
16329
+ probeArtifactSignals: () => probeArtifactSignals
16330
+ });
16331
+ import { createHash } from "node:crypto";
16332
+ import { existsSync as existsSync14, readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
16333
+ import { homedir as homedir4 } from "node:os";
16334
+ import { join as join12, relative, resolve as resolve2, sep } from "node:path";
16335
+ import { execFileSync as execFileSync2 } from "node:child_process";
16336
+ function pathHash(relPath) {
16337
+ return createHash("sha256").update(relPath.split(sep).join("/")).digest("hex").slice(0, 32);
16338
+ }
16339
+ function defaultScanRoots(extraRoots = []) {
16340
+ const roots = [];
16341
+ const home = join12(homedir4(), ".claude");
16342
+ if (existsSync14(home)) roots.push({ root: home, label: "user" });
16343
+ for (const e of extraRoots) {
16344
+ const r = resolve2(e, ".claude");
16345
+ if (existsSync14(r) && r !== home) roots.push({ root: r, label: "project" });
16346
+ }
16347
+ return roots;
16348
+ }
16349
+ function listMarkdownFiles(dir) {
16350
+ if (!existsSync14(dir)) return [];
16351
+ const out = [];
16352
+ let entries2;
16353
+ try {
16354
+ entries2 = readdirSync4(dir);
16355
+ } catch {
16356
+ return [];
16357
+ }
16358
+ for (const name of entries2) {
16359
+ if (name.startsWith(".")) continue;
16360
+ const full = join12(dir, name);
16361
+ let stat;
16362
+ try {
16363
+ stat = statSync5(full);
16364
+ } catch {
16365
+ continue;
16366
+ }
16367
+ if (stat.isDirectory()) {
16368
+ out.push(...listMarkdownFiles(full));
16369
+ } else if (stat.isFile() && name.endsWith(".md")) {
16370
+ out.push(full);
16371
+ }
16372
+ }
16373
+ return out;
16374
+ }
16375
+ function firstSeenDateOf(file) {
16376
+ try {
16377
+ const out = execFileSync2(
16378
+ "git",
16379
+ ["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
16380
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname12(file) }
16381
+ ).trim().split("\n").filter(Boolean);
16382
+ if (out.length > 0) return out[out.length - 1];
16383
+ } catch {
16384
+ }
16385
+ try {
16386
+ const s = statSync5(file);
16387
+ return new Date((s.birthtime || s.mtime).getTime()).toISOString().slice(0, 10);
16388
+ } catch {
16389
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
16390
+ }
16391
+ }
16392
+ function dirname12(p) {
16393
+ const i = p.lastIndexOf(sep);
16394
+ return i < 0 ? "." : p.slice(0, i);
16395
+ }
16396
+ function detectGitEmail(cwd) {
16397
+ try {
16398
+ return execFileSync2("git", ["config", "user.email"], {
16399
+ encoding: "utf8",
16400
+ stdio: ["ignore", "pipe", "ignore"],
16401
+ cwd
16402
+ }).trim() || null;
16403
+ } catch {
16404
+ return null;
16405
+ }
16406
+ }
16407
+ function todayLocal() {
16408
+ const d = /* @__PURE__ */ new Date();
16409
+ const y = d.getFullYear();
16410
+ const m = String(d.getMonth() + 1).padStart(2, "0");
16411
+ const day2 = String(d.getDate()).padStart(2, "0");
16412
+ return `${y}-${m}-${day2}`;
16413
+ }
16414
+ function scanAuthored(roots, subdir, day2) {
16415
+ const out = [];
16416
+ for (const r of roots) {
16417
+ const dir = join12(r.root, subdir);
16418
+ if (!existsSync14(dir)) continue;
16419
+ for (const f of listMarkdownFiles(dir)) {
16420
+ const firstSeen = firstSeenDateOf(f);
16421
+ if (firstSeen !== day2) continue;
16422
+ const rel = relative(dir, f);
16423
+ out.push({ pathHash: pathHash(`${r.label}:${subdir}:${rel}`), firstSeenDate: firstSeen });
16424
+ }
16425
+ }
16426
+ return out;
16427
+ }
16428
+ function scanEdited(roots, day2) {
16429
+ const out = [];
16430
+ for (const r of roots) {
16431
+ const dir = join12(r.root, "skills");
16432
+ if (!existsSync14(dir)) continue;
16433
+ for (const f of listMarkdownFiles(dir)) {
16434
+ const firstSeen = firstSeenDateOf(f);
16435
+ if (firstSeen === day2) continue;
16436
+ let stat;
16437
+ try {
16438
+ stat = statSync5(f);
16439
+ } catch {
16440
+ continue;
16441
+ }
16442
+ const mtimeDay = new Date(stat.mtime.getTime()).toISOString().slice(0, 10);
16443
+ if (mtimeDay !== day2) continue;
16444
+ const rel = relative(dir, f);
16445
+ out.push({ pathHash: pathHash(`${r.label}:skills:${rel}`), lineDelta: 0 });
16446
+ }
16447
+ }
16448
+ return out;
16449
+ }
16450
+ function claudemdLineDelta(extraRoots, authorEmail, day2) {
16451
+ let delta = 0;
16452
+ const candidates = [];
16453
+ for (const e of extraRoots) {
16454
+ const f = resolve2(e, "CLAUDE.md");
16455
+ if (existsSync14(f)) candidates.push(f);
16456
+ }
16457
+ const homeClaudeMd = join12(homedir4(), "CLAUDE.md");
16458
+ if (existsSync14(homeClaudeMd)) candidates.push(homeClaudeMd);
16459
+ for (const file of candidates) {
16460
+ try {
16461
+ const out = execFileSync2(
16462
+ "git",
16463
+ ["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
16464
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname12(file) }
16465
+ );
16466
+ let currentDate = "";
16467
+ let currentEmail = "";
16468
+ for (const line of out.split("\n")) {
16469
+ if (line.startsWith("COMMIT ")) {
16470
+ const parts = line.slice("COMMIT ".length).trim().split(/\s+/);
16471
+ currentDate = parts[0] ?? "";
16472
+ currentEmail = parts.slice(1).join(" ");
16473
+ } else if (currentDate === day2 && line.trim().length > 0) {
16474
+ if (authorEmail && currentEmail !== authorEmail) continue;
16475
+ const parts = line.split(/\s+/);
16476
+ const added = Number(parts[0]);
16477
+ const removed = Number(parts[1]);
16478
+ if (Number.isFinite(added) && Number.isFinite(removed)) {
16479
+ delta += added - removed;
16480
+ }
16481
+ }
16482
+ }
16483
+ } catch {
16484
+ }
16485
+ }
16486
+ return delta;
16487
+ }
16488
+ function probeArtifactSignals(opts = {}) {
16489
+ const day2 = opts.day ?? todayLocal();
16490
+ const roots = defaultScanRoots(opts.extraRoots);
16491
+ const skillsAuthored = scanAuthored(roots, "skills", day2);
16492
+ const subagentsAuthored = scanAuthored(roots, "agents", day2);
16493
+ const slashCommandsAuthored = scanAuthored(roots, "commands", day2);
16494
+ const skillsEdited = scanEdited(roots, day2);
16495
+ const authorEmail = opts.authorEmail ?? detectGitEmail(opts.extraRoots?.[0] ?? process.cwd()) ?? void 0;
16496
+ const claudemd = claudemdLineDelta(opts.extraRoots ?? [], authorEmail, day2);
16497
+ const empty = skillsAuthored.length === 0 && subagentsAuthored.length === 0 && slashCommandsAuthored.length === 0 && skillsEdited.length === 0 && claudemd === 0;
16498
+ if (empty) return null;
16499
+ return {
16500
+ skillsAuthored,
16501
+ skillsEdited,
16502
+ subagentsAuthored,
16503
+ slashCommandsAuthored,
16504
+ claudemdLineDelta: claudemd
16505
+ };
16506
+ }
16507
+ var init_file_probe = __esm({
16508
+ "src/perception/file-probe.ts"() {
16509
+ "use strict";
16510
+ }
16511
+ });
16512
+
15216
16513
  // src/team/commands.ts
15217
16514
  async function dispatchCommand(command2, config, log) {
15218
16515
  switch (command2.type) {
@@ -15230,6 +16527,7 @@ async function dispatchCommand(command2, config, log) {
15230
16527
  async function runActivityBackfill(command2, config, log) {
15231
16528
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
15232
16529
  const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
16530
+ const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
15233
16531
  const days = command2.params.days;
15234
16532
  const todayMs = Date.now();
15235
16533
  const targetDayMs = todayMs - (days - 1) * 24 * 60 * 60 * 1e3;
@@ -15255,9 +16553,24 @@ async function runActivityBackfill(command2, config, log) {
15255
16553
  summary: { pushed: 0, fromDay: targetDay }
15256
16554
  };
15257
16555
  }
16556
+ const privateProjects = new Set(config.privateProjects ?? []);
16557
+ const enrichmentOptIn = !!config.enrichmentOptIn;
16558
+ const resolveRepo = createRepoResolver();
15258
16559
  let pushed = 0;
15259
16560
  for (const rollup of rollups) {
15260
- const payload = buildIngestPayload({ rollup });
16561
+ const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
16562
+ const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, privateProjects, enrichmentOptIn, resolveRepo);
16563
+ let artifactSignals = null;
16564
+ try {
16565
+ artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
16566
+ } catch {
16567
+ }
16568
+ const payload = buildIngestPayload({
16569
+ rollup,
16570
+ richExtras: richBlocks?.rich,
16571
+ enrichedExtras: richBlocks?.enriched,
16572
+ artifactSignals: artifactSignals ?? void 0
16573
+ });
15261
16574
  const result = await pushToTeamServer(config, payload);
15262
16575
  if (!result.ok) {
15263
16576
  return {
@@ -15281,6 +16594,7 @@ var init_commands = __esm({
15281
16594
  "src/team/commands.ts"() {
15282
16595
  "use strict";
15283
16596
  init_push();
16597
+ init_git_remote();
15284
16598
  }
15285
16599
  });
15286
16600
 
@@ -15388,6 +16702,8 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
15388
16702
  let lastPushedDay;
15389
16703
  const privateProjects = new Set(config.privateProjects ?? []);
15390
16704
  const enrichmentOptIn = !!config.enrichmentOptIn;
16705
+ const resolveRepo = createRepoResolver();
16706
+ const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
15391
16707
  for (let i = 0; i < rollups.length; i++) {
15392
16708
  const rollup = rollups[i];
15393
16709
  const isLatest = i === rollups.length - 1;
@@ -15396,12 +16712,22 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
15396
16712
  rollup.day,
15397
16713
  daySessions,
15398
16714
  privateProjects,
15399
- enrichmentOptIn
16715
+ enrichmentOptIn,
16716
+ resolveRepo
15400
16717
  );
16718
+ let artifactSignals = null;
16719
+ try {
16720
+ artifactSignals = probeArtifactSignals2({
16721
+ day: rollup.day,
16722
+ extraRoots: [process.cwd()]
16723
+ });
16724
+ } catch {
16725
+ }
15401
16726
  const payload = buildIngestPayload({
15402
16727
  rollup,
15403
16728
  richExtras: richBlocks?.rich,
15404
16729
  enrichedExtras: richBlocks?.enriched,
16730
+ artifactSignals: artifactSignals ?? void 0,
15405
16731
  usageSnapshot: isLatest ? usageSnapshot : void 0,
15406
16732
  planTier,
15407
16733
  // Same rationale as usageSnapshot — current cycle data only on the
@@ -15522,6 +16848,7 @@ var init_sync = __esm({
15522
16848
  "use strict";
15523
16849
  init_config();
15524
16850
  init_push();
16851
+ init_git_remote();
15525
16852
  init_queue2();
15526
16853
  init_backfill();
15527
16854
  init_last_push();
@@ -15677,11 +17004,11 @@ var logs_exports = {};
15677
17004
  __export(logs_exports, {
15678
17005
  teamLogs: () => teamLogs
15679
17006
  });
15680
- import { readFileSync as readFileSync15 } from "node:fs";
17007
+ import { readFileSync as readFileSync16 } from "node:fs";
15681
17008
  async function teamLogs() {
15682
17009
  const logPath = cclensPath("daemon.log");
15683
17010
  try {
15684
- const lines = readFileSync15(logPath, "utf8").trim().split("\n");
17011
+ const lines = readFileSync16(logPath, "utf8").trim().split("\n");
15685
17012
  const teamLines = lines.filter((l) => l.includes("team "));
15686
17013
  if (teamLines.length === 0) {
15687
17014
  console.log("No team-related log entries found.");
@@ -15787,8 +17114,8 @@ __export(runs_exports, {
15787
17114
  runs: () => runs
15788
17115
  });
15789
17116
  import { execSync as execSync2 } from "node:child_process";
15790
- import { existsSync as existsSync13, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync4 } from "node:fs";
15791
- import { join as join11 } from "node:path";
17117
+ import { existsSync as existsSync15, readFileSync as readFileSync17, readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
17118
+ import { join as join13 } from "node:path";
15792
17119
  async function runs(args2) {
15793
17120
  const json = args2.includes("--json");
15794
17121
  const watch = args2.includes("--watch");
@@ -15872,8 +17199,8 @@ function listActiveRuns() {
15872
17199
  return runs2.sort((a, b) => a.elapsed_s - b.elapsed_s);
15873
17200
  }
15874
17201
  function readSpend(sinceMs) {
15875
- if (!existsSync13(SPEND_PATH)) return [];
15876
- const lines = readFileSync16(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
17202
+ if (!existsSync15(SPEND_PATH)) return [];
17203
+ const lines = readFileSync17(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
15877
17204
  const out = [];
15878
17205
  for (const line of lines) {
15879
17206
  try {
@@ -15994,23 +17321,23 @@ function ageLabel(ms) {
15994
17321
  return `${Math.floor(ms / 864e5)}d`;
15995
17322
  }
15996
17323
  function listTraceFiles() {
15997
- if (!existsSync13(RUNS_DIR2)) return [];
15998
- const entries2 = readdirSync4(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
17324
+ if (!existsSync15(RUNS_DIR2)) return [];
17325
+ const entries2 = readdirSync5(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
15999
17326
  return entries2.map((f) => {
16000
- const path5 = join11(RUNS_DIR2, f);
16001
- const st = statSync4(path5);
16002
- return { runId: f.replace(/\.jsonl$/, ""), path: path5, mtimeMs: st.mtimeMs };
17327
+ const path7 = join13(RUNS_DIR2, f);
17328
+ const st = statSync6(path7);
17329
+ return { runId: f.replace(/\.jsonl$/, ""), path: path7, mtimeMs: st.mtimeMs };
16003
17330
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
16004
17331
  }
16005
17332
  function resolveRunId(arg) {
16006
- if (!existsSync13(RUNS_DIR2)) return null;
17333
+ if (!existsSync15(RUNS_DIR2)) return null;
16007
17334
  if (arg === "latest") {
16008
17335
  const all2 = listTraceFiles();
16009
17336
  if (all2.length === 0) return null;
16010
17337
  return { runId: all2[0].runId, path: all2[0].path };
16011
17338
  }
16012
- const exact = join11(RUNS_DIR2, `${arg}.jsonl`);
16013
- if (existsSync13(exact)) return { runId: arg, path: exact };
17339
+ const exact = join13(RUNS_DIR2, `${arg}.jsonl`);
17340
+ if (existsSync15(exact)) return { runId: arg, path: exact };
16014
17341
  const all = listTraceFiles();
16015
17342
  const matches = all.filter((r) => r.runId.includes(arg));
16016
17343
  if (matches.length === 1) return { runId: matches[0].runId, path: matches[0].path };
@@ -16021,8 +17348,8 @@ function resolveRunId(arg) {
16021
17348
  }
16022
17349
  return null;
16023
17350
  }
16024
- function readTrace(path5) {
16025
- return readFileSync16(path5, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
17351
+ function readTrace(path7) {
17352
+ return readFileSync17(path7, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
16026
17353
  try {
16027
17354
  return JSON.parse(l);
16028
17355
  } catch {
@@ -16034,7 +17361,7 @@ async function inspect(arg, follow, json) {
16034
17361
  const r = resolveRunId(arg);
16035
17362
  if (!r) {
16036
17363
  console.error(`run not found: ${arg}`);
16037
- if (existsSync13(RUNS_DIR2)) {
17364
+ if (existsSync15(RUNS_DIR2)) {
16038
17365
  const all = listTraceFiles().slice(0, 5);
16039
17366
  if (all.length > 0) {
16040
17367
  console.error(`
@@ -16054,13 +17381,13 @@ recent runs:`);
16054
17381
  const events = readTrace(r.path);
16055
17382
  printTrace(r.runId, events);
16056
17383
  if (!follow) return;
16057
- let lastSize = statSync4(r.path).size;
17384
+ let lastSize = statSync6(r.path).size;
16058
17385
  let lastIdx = events.length;
16059
17386
  console.log("\n \u27F6 following \xB7 ^C to quit\n");
16060
17387
  process.on("SIGINT", () => process.exit(0));
16061
17388
  while (true) {
16062
17389
  await new Promise((res) => setTimeout(res, 500));
16063
- const st = statSync4(r.path);
17390
+ const st = statSync6(r.path);
16064
17391
  if (st.size === lastSize) continue;
16065
17392
  lastSize = st.size;
16066
17393
  const all = readTrace(r.path);
@@ -16204,7 +17531,7 @@ async function main() {
16204
17531
  case "version":
16205
17532
  case "--version":
16206
17533
  case "-v":
16207
- console.log(`fleetlens ${"0.11.2"}`);
17534
+ console.log(`fleetlens ${"0.12.0"}`);
16208
17535
  break;
16209
17536
  case "help":
16210
17537
  case "--help":