fleetlens 0.15.2 → 0.15.4

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 (222) 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 +18 -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 +1 -1
  14. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +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.nft.json +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  20. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  21. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  22. package/app/apps/web/.next/server/app/api/menubar/route/app-paths-manifest.json +3 -0
  23. package/app/apps/web/.next/server/app/api/menubar/route/build-manifest.json +9 -0
  24. package/app/apps/web/.next/server/app/api/menubar/route/server-reference-manifest.json +4 -0
  25. package/app/apps/web/.next/server/app/api/menubar/route.js +6 -0
  26. package/app/apps/web/.next/server/app/api/menubar/route.js.map +5 -0
  27. package/app/apps/web/.next/server/app/api/menubar/route.js.nft.json +1 -0
  28. package/app/apps/web/.next/server/app/api/menubar/route_client-reference-manifest.js +3 -0
  29. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +1 -1
  30. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
  31. package/app/apps/web/.next/server/app/api/projects/open-folder/route/app-paths-manifest.json +3 -0
  32. package/app/apps/web/.next/server/app/api/projects/open-folder/route/build-manifest.json +9 -0
  33. package/app/apps/web/.next/server/app/api/projects/open-folder/route/server-reference-manifest.json +4 -0
  34. package/app/apps/web/.next/server/app/api/projects/open-folder/route.js +7 -0
  35. package/app/apps/web/.next/server/app/api/projects/open-folder/route.js.map +5 -0
  36. package/app/apps/web/.next/server/app/api/projects/open-folder/route.js.nft.json +1 -0
  37. package/app/apps/web/.next/server/app/api/projects/open-folder/route_client-reference-manifest.js +3 -0
  38. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  39. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  40. package/app/apps/web/.next/server/app/api/settings/credentials/route/app-paths-manifest.json +3 -0
  41. package/app/apps/web/.next/server/app/api/settings/credentials/route/build-manifest.json +9 -0
  42. package/app/apps/web/.next/server/app/api/settings/credentials/route/server-reference-manifest.json +4 -0
  43. package/app/apps/web/.next/server/app/api/settings/credentials/route.js +10 -0
  44. package/app/apps/web/.next/server/app/api/settings/credentials/route.js.map +5 -0
  45. package/app/apps/web/.next/server/app/api/settings/credentials/route.js.nft.json +1 -0
  46. package/app/apps/web/.next/server/app/api/settings/credentials/route_client-reference-manifest.js +3 -0
  47. package/app/apps/web/.next/server/app/api/settings/route.js +4 -4
  48. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  49. package/app/apps/web/.next/server/app/changelog/page.js +1 -1
  50. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  51. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  52. package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
  53. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  54. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  55. package/app/apps/web/.next/server/app/day/page.js +1 -1
  56. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  57. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  58. package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
  59. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  60. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  61. package/app/apps/web/.next/server/app/digest/page.js +1 -1
  62. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  63. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  64. package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
  65. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  66. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  67. package/app/apps/web/.next/server/app/insights/page.js +1 -1
  68. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  69. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  70. package/app/apps/web/.next/server/app/page.js +1 -1
  71. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  72. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  73. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  74. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  75. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  76. package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
  77. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  78. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  79. package/app/apps/web/.next/server/app/projects/[slug]/page.js +2 -2
  80. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  81. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  82. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  83. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  84. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  85. package/app/apps/web/.next/server/app/runs/page.js +1 -1
  86. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  87. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  88. package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
  89. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  90. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  91. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  92. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  93. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  94. package/app/apps/web/.next/server/app/settings/page.js +2 -2
  95. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  96. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  97. package/app/apps/web/.next/server/app/team/onboarding/page.js +1 -1
  98. package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -1
  99. package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +1 -1
  100. package/app/apps/web/.next/server/app/team/page.js +1 -1
  101. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  102. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  103. package/app/apps/web/.next/server/app/usage/page.js +1 -1
  104. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  105. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  106. package/app/apps/web/.next/server/app-paths-manifest.json +3 -0
  107. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_projects_open-folder_route_actions_0m~419c.js +3 -0
  108. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_settings_credentials_route_actions_04an_i2.js +3 -0
  109. package/app/apps/web/.next/server/chunks/[root-of-the-server]__05z0pcw._.js +3 -0
  110. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0_nle98._.js +3 -0
  111. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b5r~-1._.js +3 -0
  112. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0a0~7d6._.js → [root-of-the-server]__0t7i-hy._.js} +2 -2
  113. package/app/apps/web/.next/server/chunks/[root-of-the-server]__12iid50._.js +3 -0
  114. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_menubar_route_actions_0xep8_i.js +3 -0
  115. package/app/apps/web/.next/server/chunks/{node_modules_next_036my~r._.js → node_modules_next_04~_e52._.js} +5 -5
  116. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +14 -14
  117. package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
  118. package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
  119. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0.zquoa._.js +1 -1
  120. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__03svi~4._.js +8 -0
  121. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04eq6z2._.js +1 -1
  122. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04il6m2._.js +1 -1
  123. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__08l86e-._.js +1 -1
  124. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__09rjz7_._.js +1 -1
  125. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ar_8wr._.js +1 -1
  126. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__10~b4bf._.js → [root-of-the-server]__0blm9gz._.js} +3 -3
  127. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0e.gxk3._.js +1 -1
  128. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0g-tomj._.js +3 -0
  129. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0i.q-1h._.js +1 -1
  130. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0k1x-c9._.js +1 -1
  131. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0lz4a~g._.js +1 -1
  132. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0nr2~y1._.js +3 -0
  133. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0o1_g.~._.js +1 -1
  134. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0p1x6v2._.js +1 -1
  135. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
  136. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0rvzdnx._.js +1 -1
  137. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0stu~~r._.js +1 -1
  138. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0tejjy_._.js +1 -1
  139. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0u39afl._.js +1 -1
  140. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ufi3.w._.js +1 -1
  141. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0xy1qim._.js +1 -1
  142. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +3 -3
  143. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__10srpu0._.js +1 -1
  144. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js +1 -1
  145. package/app/apps/web/.next/server/chunks/ssr/_02r-a1a._.js +8 -0
  146. package/app/apps/web/.next/server/chunks/ssr/_0k62ejd._.js +8 -0
  147. package/app/apps/web/.next/server/chunks/ssr/{_0xu2pvc._.js → _0k8yioi._.js} +2 -2
  148. package/app/apps/web/.next/server/chunks/ssr/{_0wj750k._.js → _0t~mx7w._.js} +2 -2
  149. package/app/apps/web/.next/server/chunks/ssr/{_0kajk31._.js → _0whg~r-._.js} +2 -2
  150. package/app/apps/web/.next/server/chunks/ssr/_13jdm1g._.js +3 -0
  151. package/app/apps/web/.next/server/chunks/ssr/apps_web_0~aj0~u._.js +3 -0
  152. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_sessions-grid_tsx_0bri5x.._.js +1 -1
  153. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0x014hn._.js +16 -0
  154. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  155. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  156. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_12vc-v-._.js +3 -0
  157. package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +1 -1
  158. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
  159. package/app/apps/web/.next/server/functions-config-manifest.json +3 -0
  160. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  161. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  162. package/app/apps/web/.next/server/pages/500.html +1 -1
  163. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  164. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  165. package/app/apps/web/.next/static/chunks/0-z49mtd-zwgv.js +1 -0
  166. package/app/apps/web/.next/static/chunks/043slkg3juwck.js +1 -0
  167. package/app/apps/web/.next/static/chunks/{0~qdw1b2uz._5.js → 044mtrzh7d7va.js} +3 -3
  168. package/app/apps/web/.next/static/chunks/05b0dn.r00hq1.js +1 -0
  169. package/app/apps/web/.next/static/chunks/{14vx702c32kn6.js → 0_0~~ipv90vm3.js} +1 -1
  170. package/app/apps/web/.next/static/chunks/0awkd5df28gvj.js +6 -0
  171. package/app/apps/web/.next/static/chunks/{0m7dd-2zd9vl2.js → 0eoc4yy0s0gpe.js} +1 -1
  172. package/app/apps/web/.next/static/chunks/0g0hv.dt-de9g.js +14 -0
  173. package/app/apps/web/.next/static/chunks/0rsp61c8wb.sx.js +6 -0
  174. package/app/apps/web/.next/static/chunks/{03rd.3cc_t~yf.js → 0s~0-8l~_kqnf.js} +1 -1
  175. package/app/apps/web/.next/static/chunks/0t2on1dhydyb2.css +1 -0
  176. package/app/apps/web/.next/static/chunks/0y3448zrint0k.js +6 -0
  177. package/app/apps/web/app/api/menubar/route.ts +66 -0
  178. package/app/apps/web/app/api/projects/open-folder/route.ts +60 -0
  179. package/app/apps/web/app/api/settings/credentials/route.ts +45 -0
  180. package/app/apps/web/app/layout.tsx +2 -0
  181. package/app/apps/web/app/projects/[slug]/local-folders-button.tsx +406 -0
  182. package/app/apps/web/app/projects/[slug]/page.tsx +155 -20
  183. package/app/apps/web/app/projects/page.tsx +2 -2
  184. package/app/apps/web/app/sessions/[id]/page.tsx +4 -1
  185. package/app/apps/web/app/sessions/[id]/session-view/header-stats.tsx +81 -0
  186. package/app/apps/web/app/sessions/[id]/session-view.tsx +11 -0
  187. package/app/apps/web/app/sessions/sessions-grid.tsx +56 -25
  188. package/app/apps/web/app/settings/page.tsx +26 -0
  189. package/app/apps/web/app/usage/page.tsx +80 -0
  190. package/app/apps/web/components/menubar-install-banner.tsx +117 -0
  191. package/app/apps/web/components/menubar-install.tsx +107 -0
  192. package/app/apps/web/components/zai-credentials-form.tsx +116 -0
  193. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  194. package/app/apps/web/lib/entries-index.ts +4 -4
  195. package/app/apps/web/lib/menubar-data.ts +50 -0
  196. package/app/apps/web/lib/usage-data.ts +4 -0
  197. package/app/apps/web/package.json +1 -1
  198. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  199. package/dist/daemon-worker.js +640 -240
  200. package/dist/index.js +938 -283
  201. package/package.json +3 -2
  202. package/app/apps/web/.next/server/chunks/[root-of-the-server]__065lib8._.js +0 -3
  203. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0a7n44-._.js +0 -3
  204. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0c00i76._.js +0 -3
  205. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0lhwp-k._.js +0 -8
  206. package/app/apps/web/.next/server/chunks/ssr/_0s3n5mz._.js +0 -8
  207. package/app/apps/web/.next/server/chunks/ssr/_12mbgs5._.js +0 -3
  208. package/app/apps/web/.next/server/chunks/ssr/apps_web_0q2x4a~._.js +0 -3
  209. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0b-4-4j._.js +0 -8
  210. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +0 -16
  211. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0cepfi8._.js +0 -3
  212. package/app/apps/web/.next/static/chunks/00a9oyw~h5i1p.js +0 -1
  213. package/app/apps/web/.next/static/chunks/03gy1evbn94qu.js +0 -6
  214. package/app/apps/web/.next/static/chunks/040s42ypidk7s.css +0 -1
  215. package/app/apps/web/.next/static/chunks/0d2x-wfu.m0-z.js +0 -6
  216. package/app/apps/web/.next/static/chunks/0i77er85v.rj..js +0 -1
  217. package/app/apps/web/.next/static/chunks/0nqklmbs5s3ax.js +0 -6
  218. package/app/apps/web/.next/static/chunks/0tirv_vo6q8uc.js +0 -1
  219. package/app/apps/web/.next/static/chunks/0u6gxpqhl0gc7.js +0 -14
  220. /package/app/apps/web/.next/static/{mTj8nKiazosAKJec15dLe → nmAGY0LiBgXuibWTKZ1RD}/_buildManifest.js +0 -0
  221. /package/app/apps/web/.next/static/{mTj8nKiazosAKJec15dLe → nmAGY0LiBgXuibWTKZ1RD}/_clientMiddlewareManifest.js +0 -0
  222. /package/app/apps/web/.next/static/{mTj8nKiazosAKJec15dLe → nmAGY0LiBgXuibWTKZ1RD}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -148,11 +148,22 @@ function worktreeName(projectName) {
148
148
  return ss.workspaceName;
149
149
  return null;
150
150
  }
151
- function projectRepoName(projectName) {
151
+ function projectKey(projectName) {
152
152
  const canonical = canonicalProjectName(projectName);
153
153
  const segs = canonical.replace(/\/+$/, "").split("/").filter(Boolean);
154
154
  return segs[segs.length - 1] || canonical;
155
155
  }
156
+ function sessionProjectKey(s) {
157
+ return s.repoName ?? projectKey(s.projectName);
158
+ }
159
+ function buildProjectKeyResolver(sessions) {
160
+ const alias = /* @__PURE__ */ new Map();
161
+ for (const s of sessions) {
162
+ if (s.repoName)
163
+ alias.set(projectKey(s.projectName), s.repoName);
164
+ }
165
+ return (s) => s.repoName ?? alias.get(projectKey(s.projectName)) ?? projectKey(s.projectName);
166
+ }
156
167
  function makeDay(date) {
157
168
  return {
158
169
  date,
@@ -587,8 +598,9 @@ function summarizeBursts(bursts) {
587
598
  }
588
599
  function groupByProject(sessions) {
589
600
  const map = /* @__PURE__ */ new Map();
601
+ const keyOf = buildProjectKeyResolver(sessions);
590
602
  for (const s of sessions) {
591
- const repo = projectRepoName(s.projectName);
603
+ const repo = keyOf(s);
592
604
  const key = repo;
593
605
  let cur = map.get(key);
594
606
  if (!cur) {
@@ -617,7 +629,7 @@ function groupByProject(sessions) {
617
629
  p.metrics = highLevelMetrics(p.sessions);
618
630
  const wtNames = /* @__PURE__ */ new Set();
619
631
  for (const s of p.sessions) {
620
- const wt = worktreeName(s.projectName);
632
+ const wt = s.worktreeName ?? worktreeName(s.projectName) ?? (s.cwd ? worktreeName(s.cwd) : null);
621
633
  if (wt)
622
634
  wtNames.add(wt);
623
635
  }
@@ -636,7 +648,7 @@ function groupByProject(sessions) {
636
648
  }
637
649
  return Array.from(map.values()).sort((a, b) => (b.lastActiveMs ?? 0) - (a.lastActiveMs ?? 0));
638
650
  }
639
- var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, SUPERSET_MARKER, UUID_SEG_RE, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
651
+ var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, SUPERSET_MARKER, UUID_SEG_RE, projectRepoName, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
640
652
  var init_analytics = __esm({
641
653
  "../parser/dist/analytics.js"() {
642
654
  "use strict";
@@ -649,6 +661,7 @@ var init_analytics = __esm({
649
661
  CONDUCTOR_RE = /\/conductor\/workspaces\/[^/]+\/([^/]+)(?:\/|$)/;
650
662
  SUPERSET_MARKER = "/.superset/worktrees/";
651
663
  UUID_SEG_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
664
+ projectRepoName = projectKey;
652
665
  PR_CREATE_RE = /gh\s+pr\s+create\b/i;
653
666
  PR_NUMBER_RE = /#(\d+)/;
654
667
  TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
@@ -662,7 +675,7 @@ function getAgentMetadata(kind) {
662
675
  function isAgentKind(s) {
663
676
  return typeof s === "string" && KNOWN_AGENT_KINDS.has(s);
664
677
  }
665
- var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, ANTIGRAVITY_METADATA, COWORK_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
678
+ var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, ANTIGRAVITY_METADATA, COWORK_METADATA, ZAI_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
666
679
  var init_agent_metadata = __esm({
667
680
  "../parser/dist/agent-metadata.js"() {
668
681
  "use strict";
@@ -701,12 +714,20 @@ var init_agent_metadata = __esm({
701
714
  accentColor: "rgb(217, 119, 87)",
702
715
  iconChar: "\u2318"
703
716
  };
717
+ ZAI_METADATA = {
718
+ kind: "zai",
719
+ displayName: "Z.ai",
720
+ shortLabel: "Z.ai",
721
+ accentColor: "rgb(59, 130, 246)",
722
+ iconChar: "Z"
723
+ };
704
724
  agentMetadata = [
705
725
  CLAUDE_CODE_METADATA,
706
726
  CODEX_METADATA,
707
727
  GEMINI_METADATA,
708
728
  ANTIGRAVITY_METADATA,
709
- COWORK_METADATA
729
+ COWORK_METADATA,
730
+ ZAI_METADATA
710
731
  ];
711
732
  KNOWN_AGENT_KINDS = new Set(agentMetadata.map((m) => m.kind));
712
733
  }
@@ -1447,6 +1468,187 @@ var init_parser = __esm({
1447
1468
  }
1448
1469
  });
1449
1470
 
1471
+ // ../parser/dist/git-project.js
1472
+ import { existsSync, readFileSync as readFileSync2, realpathSync, statSync } from "node:fs";
1473
+ import { homedir } from "node:os";
1474
+ import { basename, dirname as dirname2, isAbsolute, join, resolve } from "node:path";
1475
+ function resolveGitDir(root, dotGitText) {
1476
+ const m = dotGitText.match(/^gitdir:\s*(.+)\s*$/m);
1477
+ if (!m)
1478
+ return void 0;
1479
+ const gitDir = m[1].trim();
1480
+ return isAbsolute(gitDir) ? gitDir : resolve(root, gitDir);
1481
+ }
1482
+ function resolveCommonDir(gitDir) {
1483
+ const file = join(gitDir, "commondir");
1484
+ if (!existsSync(file))
1485
+ return void 0;
1486
+ const common = readFileSync2(file, "utf8").trim();
1487
+ if (!common)
1488
+ return void 0;
1489
+ return isAbsolute(common) ? common : resolve(gitDir, common);
1490
+ }
1491
+ function dotGitInfoFor(root) {
1492
+ const dotGit = join(root, ".git");
1493
+ const st = statSync(dotGit, { throwIfNoEntry: false });
1494
+ if (!st)
1495
+ return null;
1496
+ if (st.isDirectory())
1497
+ return { root, gitDir: dotGit, commonDir: dotGit };
1498
+ const gitDir = resolveGitDir(root, readFileSync2(dotGit, "utf8"));
1499
+ if (!gitDir)
1500
+ return { root };
1501
+ return { root, gitDir, commonDir: resolveCommonDir(gitDir) };
1502
+ }
1503
+ function walkBoundary(start2) {
1504
+ const home = realpathOrSelf(homedir());
1505
+ return start2 === home || start2.startsWith(`${home}/`) ? home : void 0;
1506
+ }
1507
+ function nearestDotGit(start2) {
1508
+ if (!start2 || !isAbsolute(start2))
1509
+ return null;
1510
+ const boundary = walkBoundary(start2);
1511
+ for (let dir = start2.replace(/\/+$/, "") || start2; ; dir = dirname2(dir)) {
1512
+ if (dir === boundary)
1513
+ return null;
1514
+ const info = dotGitInfoFor(dir);
1515
+ if (info)
1516
+ return info;
1517
+ const parent = dirname2(dir);
1518
+ if (parent === dir)
1519
+ return null;
1520
+ }
1521
+ }
1522
+ function originUrl(configText) {
1523
+ let inOrigin = false;
1524
+ for (const line of configText.split("\n")) {
1525
+ const t = line.trim();
1526
+ if (t.startsWith("[")) {
1527
+ inOrigin = /^\[remote\s+"origin"\]/.test(t);
1528
+ continue;
1529
+ }
1530
+ if (!inOrigin)
1531
+ continue;
1532
+ const m = t.match(/^url\s*=\s*(.+)$/);
1533
+ if (m)
1534
+ return m[1].trim();
1535
+ }
1536
+ return void 0;
1537
+ }
1538
+ function parseRemote(url) {
1539
+ const m = url.match(REMOTE_URL_RE);
1540
+ if (!m)
1541
+ return void 0;
1542
+ const host = m[1];
1543
+ const segs = m[2].split("/").filter(Boolean);
1544
+ if (segs.length < 2)
1545
+ return void 0;
1546
+ const name = segs[segs.length - 1];
1547
+ const owner = segs.slice(0, -1).join("/");
1548
+ return { host, owner, name, url, webUrl: `https://${host}/${owner}/${name}` };
1549
+ }
1550
+ function remoteFor(git) {
1551
+ const dir = git.commonDir ?? git.gitDir;
1552
+ if (!dir)
1553
+ return void 0;
1554
+ const config = join(dir, "config");
1555
+ if (!existsSync(config))
1556
+ return void 0;
1557
+ const url = originUrl(readFileSync2(config, "utf8"));
1558
+ return url ? parseRemote(url) : void 0;
1559
+ }
1560
+ function headBranch(gitDir) {
1561
+ if (!gitDir)
1562
+ return void 0;
1563
+ const head = join(gitDir, "HEAD");
1564
+ if (!existsSync(head))
1565
+ return void 0;
1566
+ const t = readFileSync2(head, "utf8").trim();
1567
+ const m = t.match(/^ref:\s*refs\/heads\/(.+)$/);
1568
+ return m ? m[1] : `detached @ ${t.slice(0, 7)}`;
1569
+ }
1570
+ function defaultBranch(commonDir) {
1571
+ if (!commonDir)
1572
+ return void 0;
1573
+ const head = join(commonDir, "refs", "remotes", "origin", "HEAD");
1574
+ if (existsSync(head)) {
1575
+ const m = readFileSync2(head, "utf8").trim().match(/refs\/remotes\/origin\/(.+)$/);
1576
+ if (m)
1577
+ return m[1];
1578
+ }
1579
+ const packed = join(commonDir, "packed-refs");
1580
+ if (existsSync(packed)) {
1581
+ const m = readFileSync2(packed, "utf8").match(/refs\/remotes\/origin\/(\S+)/);
1582
+ if (m && m[1] !== "HEAD")
1583
+ return m[1];
1584
+ }
1585
+ return void 0;
1586
+ }
1587
+ function readGitFolder(path7) {
1588
+ if (!existsSync(path7))
1589
+ return { exists: false, isWorktree: false };
1590
+ try {
1591
+ const git = nearestDotGit(realpathOrSelf(path7));
1592
+ if (!git)
1593
+ return { exists: true, isWorktree: false };
1594
+ const projectRoot = projectRootFromCommonDir(git.commonDir);
1595
+ const isWorktree = !!projectRoot && realpathOrSelf(projectRoot) !== realpathOrSelf(git.root);
1596
+ return {
1597
+ exists: true,
1598
+ isWorktree,
1599
+ branch: headBranch(git.gitDir),
1600
+ defaultBranch: defaultBranch(git.commonDir),
1601
+ remote: remoteFor(git)
1602
+ };
1603
+ } catch {
1604
+ return { exists: true, isWorktree: false };
1605
+ }
1606
+ }
1607
+ function projectRootFromCommonDir(commonDir) {
1608
+ if (!commonDir)
1609
+ return void 0;
1610
+ return basename(commonDir) === ".git" ? dirname2(commonDir) : void 0;
1611
+ }
1612
+ function realpathOrSelf(p) {
1613
+ try {
1614
+ return realpathSync(p);
1615
+ } catch {
1616
+ return p;
1617
+ }
1618
+ }
1619
+ function resolveProjectIdentity(cwd) {
1620
+ const normalized = realpathOrSelf(cwd.replace(/\/{2,}/g, "/"));
1621
+ try {
1622
+ const git = nearestDotGit(normalized);
1623
+ if (git) {
1624
+ const projectRoot = realpathOrSelf(projectRootFromCommonDir(git.commonDir) ?? git.root);
1625
+ const gitRoot = realpathOrSelf(git.root);
1626
+ const wt2 = projectRoot !== gitRoot ? worktreeName(normalized) ?? basename(git.root) : void 0;
1627
+ const identity = { projectName: projectRoot };
1628
+ if (wt2)
1629
+ identity.worktreeName = wt2;
1630
+ const remote = remoteFor(git);
1631
+ if (remote) {
1632
+ identity.remote = remote;
1633
+ identity.repoName = remote.name;
1634
+ }
1635
+ return identity;
1636
+ }
1637
+ } catch {
1638
+ }
1639
+ const projectName = canonicalProjectName(normalized);
1640
+ const wt = worktreeName(normalized) ?? void 0;
1641
+ return wt ? { projectName, worktreeName: wt } : { projectName };
1642
+ }
1643
+ var REMOTE_URL_RE;
1644
+ var init_git_project = __esm({
1645
+ "../parser/dist/git-project.js"() {
1646
+ "use strict";
1647
+ init_analytics();
1648
+ REMOTE_URL_RE = /^(?:(?:https?|ssh|git):\/\/)?(?:[^@/]+@)?([^/:]+)[:/](.+?)(?:\.git)?\/?$/;
1649
+ }
1650
+ });
1651
+
1450
1652
  // ../parser/dist/team.js
1451
1653
  function groupByTeam(sessions, details) {
1452
1654
  const byTeam = /* @__PURE__ */ new Map();
@@ -1915,12 +2117,16 @@ async function getCachedMeta(f) {
1915
2117
  const sessionId = sessionIdFromFileName(f.fileName);
1916
2118
  const projectDirPath = path.dirname(f.fullPath);
1917
2119
  const wf = await scanWorkflowAggregate(projectDirPath, sessionId);
2120
+ const cwd = meta.cwd ?? decodeProjectName(f.projectDir);
2121
+ const project = resolveProjectIdentity(cwd);
1918
2122
  const full = {
1919
2123
  ...meta,
1920
2124
  id: sessionId,
1921
2125
  filePath: f.fullPath,
1922
2126
  projectDir: f.projectDir,
1923
- projectName: meta.cwd ?? decodeProjectName(f.projectDir),
2127
+ projectName: project.projectName,
2128
+ worktreeName: project.worktreeName,
2129
+ repoName: project.repoName,
1924
2130
  ...wf.workflowCount > 0 ? { workflowCount: wf.workflowCount, spawnedAgentCount: wf.spawnedAgentCount } : {},
1925
2131
  ...foldWorkflowAgentTime(meta.activeSegments, wf.spans)
1926
2132
  };
@@ -1941,12 +2147,16 @@ async function getCachedDetail(f) {
1941
2147
  const sessionId = sessionIdFromFileName(f.fileName);
1942
2148
  await applySubagentUsage(meta, rawLines, f);
1943
2149
  const wf = await scanWorkflowAggregate(path.dirname(f.fullPath), sessionId);
2150
+ const cwd = meta.cwd ?? decodeProjectName(f.projectDir);
2151
+ const project = resolveProjectIdentity(cwd);
1944
2152
  const detail = {
1945
2153
  ...meta,
1946
2154
  id: sessionId,
1947
2155
  filePath: f.fullPath,
1948
2156
  projectDir: f.projectDir,
1949
- projectName: meta.cwd ?? decodeProjectName(f.projectDir),
2157
+ projectName: project.projectName,
2158
+ worktreeName: project.worktreeName,
2159
+ repoName: project.repoName,
1950
2160
  events,
1951
2161
  ...wf.workflowCount > 0 ? { workflowCount: wf.workflowCount, spawnedAgentCount: wf.spawnedAgentCount } : {},
1952
2162
  ...foldWorkflowAgentTime(meta.activeSegments, wf.spans)
@@ -2438,19 +2648,25 @@ async function listProjects(root = DEFAULT_ROOT) {
2438
2648
  cur.lastActiveMs = f.mtimeMs;
2439
2649
  byRawDir.set(f.projectDir, cur);
2440
2650
  }
2441
- const cwdForProject = (dir) => {
2651
+ const cachedIdentityForProject = (dir) => {
2442
2652
  for (const [, entry] of metaCache.entries()) {
2443
- if (entry.meta.projectDir === dir && entry.meta.cwd) {
2444
- return entry.meta.cwd;
2653
+ if (entry.meta.projectDir === dir) {
2654
+ return {
2655
+ cwd: entry.meta.cwd,
2656
+ projectName: entry.meta.projectName,
2657
+ worktreeName: entry.meta.worktreeName
2658
+ };
2445
2659
  }
2446
2660
  }
2447
2661
  return void 0;
2448
2662
  };
2449
2663
  const canonicalMap = /* @__PURE__ */ new Map();
2450
2664
  for (const [rawDir, { count, lastActiveMs }] of byRawDir) {
2451
- const cwdOrDecoded = cwdForProject(rawDir) ?? decodeProjectName(rawDir);
2452
- const repo = projectRepoName(cwdOrDecoded);
2453
- const wt = worktreeName(cwdOrDecoded);
2665
+ const cached = cachedIdentityForProject(rawDir);
2666
+ const cwdOrDecoded = cached?.cwd ?? decodeProjectName(rawDir);
2667
+ const projectName = cached?.projectName ?? canonicalProjectName(cwdOrDecoded);
2668
+ const wt = cached?.worktreeName ?? worktreeName(cwdOrDecoded) ?? void 0;
2669
+ const repo = projectKey(projectName);
2454
2670
  const agg = canonicalMap.get(repo) ?? {
2455
2671
  canonicalName: repo,
2456
2672
  count: 0,
@@ -2516,6 +2732,7 @@ var init_claude_code = __esm({
2516
2732
  "use strict";
2517
2733
  init_parser();
2518
2734
  init_analytics();
2735
+ init_git_project();
2519
2736
  init_team();
2520
2737
  init_agent_metadata();
2521
2738
  init_calibration();
@@ -2822,13 +3039,16 @@ function parseRollout(file, lines) {
2822
3039
  const activeSegments = computeActiveSegments2(tsMs);
2823
3040
  const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
2824
3041
  const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
2825
- const projectName = cwd ? canonicalProjectName(cwd) : "(unknown)";
3042
+ const project = cwd ? resolveProjectIdentity(cwd) : void 0;
3043
+ const projectName = project?.projectName ?? "(unknown)";
2826
3044
  const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : "(unknown)";
2827
3045
  const meta = {
2828
3046
  agent: "codex",
2829
3047
  id: file.sessionId,
2830
3048
  filePath: file.filePath,
2831
3049
  projectName,
3050
+ worktreeName: project?.worktreeName,
3051
+ repoName: project?.repoName,
2832
3052
  projectDir,
2833
3053
  sessionId: file.sessionId,
2834
3054
  firstTimestamp,
@@ -2960,13 +3180,16 @@ async function getLatestCodexUsage(opts = {}) {
2960
3180
  const fiveResetUnix = numberOf(primary?.resets_at);
2961
3181
  const sevenResetUnix = numberOf(secondary?.resets_at);
2962
3182
  const planType = typeof rl.plan_type === "string" ? rl.plan_type : null;
3183
+ const nowMs = Date.now();
3184
+ const fiveExpired = fiveResetUnix !== void 0 && nowMs > fiveResetUnix * 1e3;
3185
+ const sevenExpired = sevenResetUnix !== void 0 && nowMs > sevenResetUnix * 1e3;
2963
3186
  return {
2964
3187
  five_hour: {
2965
- utilization: fivePct ?? null,
3188
+ utilization: fiveExpired ? 0 : fivePct ?? null,
2966
3189
  resets_at: fiveResetUnix !== void 0 ? new Date(fiveResetUnix * 1e3).toISOString() : null
2967
3190
  },
2968
3191
  seven_day: {
2969
- utilization: sevenPct ?? null,
3192
+ utilization: sevenExpired ? 0 : sevenPct ?? null,
2970
3193
  resets_at: sevenResetUnix !== void 0 ? new Date(sevenResetUnix * 1e3).toISOString() : null
2971
3194
  },
2972
3195
  plan_type: planType,
@@ -2987,6 +3210,7 @@ var init_codex = __esm({
2987
3210
  "../parser/dist/codex.js"() {
2988
3211
  "use strict";
2989
3212
  init_analytics();
3213
+ init_git_project();
2990
3214
  init_user_input();
2991
3215
  DEFAULT_CODEX_ROOT = path2.join(os2.homedir(), ".codex", "sessions");
2992
3216
  ROLLOUT_RE = /^rollout-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})-([0-9a-f-]+)\.jsonl$/;
@@ -3366,13 +3590,16 @@ async function parseSession(file) {
3366
3590
  const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
3367
3591
  const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
3368
3592
  const sessionId = metaRec.sessionId ?? file.filenameId;
3369
- const projectName = cwd ? canonicalProjectName(cwd) : file.slug;
3593
+ const project = cwd ? resolveProjectIdentity(cwd) : void 0;
3594
+ const projectName = project?.projectName ?? file.slug;
3370
3595
  const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.slug;
3371
3596
  const meta = {
3372
3597
  agent: "gemini",
3373
3598
  id: sessionId,
3374
3599
  filePath: file.filePath,
3375
3600
  projectName,
3601
+ worktreeName: project?.worktreeName,
3602
+ repoName: project?.repoName,
3376
3603
  projectDir,
3377
3604
  sessionId,
3378
3605
  firstTimestamp,
@@ -3486,6 +3713,7 @@ var init_gemini = __esm({
3486
3713
  "../parser/dist/gemini.js"() {
3487
3714
  "use strict";
3488
3715
  init_analytics();
3716
+ init_git_project();
3489
3717
  init_user_input();
3490
3718
  DEFAULT_GEMINI_ROOT = path3.join(os3.homedir(), ".gemini", "tmp");
3491
3719
  GEMINI_PROJECTS_FILE = path3.join(os3.homedir(), ".gemini", "projects.json");
@@ -3759,13 +3987,16 @@ async function parseSession2(file) {
3759
3987
  const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
3760
3988
  const sessionId = file.conversationId;
3761
3989
  const cwd = file.cwd;
3762
- const projectName = cwd ? canonicalProjectName(cwd) : file.conversationId;
3990
+ const project = cwd ? resolveProjectIdentity(cwd) : void 0;
3991
+ const projectName = project?.projectName ?? file.conversationId;
3763
3992
  const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.conversationId;
3764
3993
  const meta = {
3765
3994
  agent: "antigravity",
3766
3995
  id: sessionId,
3767
3996
  filePath: file.filePath,
3768
3997
  projectName,
3998
+ worktreeName: project?.worktreeName,
3999
+ repoName: project?.repoName,
3769
4000
  projectDir,
3770
4001
  sessionId,
3771
4002
  firstTimestamp,
@@ -3872,6 +4103,7 @@ var init_antigravity = __esm({
3872
4103
  "../parser/dist/antigravity.js"() {
3873
4104
  "use strict";
3874
4105
  init_analytics();
4106
+ init_git_project();
3875
4107
  init_user_input();
3876
4108
  DEFAULT_ANTIGRAVITY_ROOT = path4.join(os4.homedir(), ".gemini", "antigravity-cli");
3877
4109
  IDLE_GAP_MS3 = 3 * 60 * 1e3;
@@ -3991,8 +4223,10 @@ function resolveProject(meta, spaceIdToPath) {
3991
4223
  if (!projectPath) {
3992
4224
  return { projectName: "cowork:unspaced", projectDir: "cowork-unspaced" };
3993
4225
  }
4226
+ const project = resolveProjectIdentity(projectPath);
3994
4227
  return {
3995
- projectName: canonicalProjectName(projectPath),
4228
+ projectName: project.projectName,
4229
+ worktreeName: project.worktreeName,
3996
4230
  projectDir: projectPath.replace(/^\//, "-").replace(/\//g, "-"),
3997
4231
  cwd: projectPath
3998
4232
  };
@@ -4219,8 +4453,8 @@ var DEFAULT_COWORK_ROOT, SESSION_ID_RE, spacesCache, metaCache5, detailCache5;
4219
4453
  var init_cowork = __esm({
4220
4454
  "../parser/dist/cowork.js"() {
4221
4455
  "use strict";
4222
- init_analytics();
4223
4456
  init_claude_code();
4457
+ init_git_project();
4224
4458
  init_parser();
4225
4459
  DEFAULT_COWORK_ROOT = defaultCoworkRoot();
4226
4460
  SESSION_ID_RE = /^local_[0-9a-f-]+$/;
@@ -4231,21 +4465,21 @@ var init_cowork = __esm({
4231
4465
  });
4232
4466
 
4233
4467
  // ../parser/dist/team-config.js
4234
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, renameSync } from "node:fs";
4235
- import { join } from "node:path";
4236
- function shouldSyncProject(repoName, sp) {
4468
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, renameSync } from "node:fs";
4469
+ import { join as join2 } from "node:path";
4470
+ function shouldSyncProject(projectKey2, sp) {
4237
4471
  if (!sp)
4238
4472
  return true;
4239
- if (sp.excluded.includes(repoName))
4473
+ if (sp.excluded.includes(projectKey2))
4240
4474
  return false;
4241
- if (sp.included.includes(repoName))
4475
+ if (sp.included.includes(projectKey2))
4242
4476
  return true;
4243
4477
  return sp.autoIncludeNew;
4244
4478
  }
4245
4479
  function readTeamConfig(dir) {
4246
4480
  const d = dir ?? cclensHome();
4247
4481
  try {
4248
- return JSON.parse(readFileSync2(join(d, CONFIG_FILE), "utf8"));
4482
+ return JSON.parse(readFileSync3(join2(d, CONFIG_FILE), "utf8"));
4249
4483
  } catch {
4250
4484
  return null;
4251
4485
  }
@@ -4253,14 +4487,14 @@ function readTeamConfig(dir) {
4253
4487
  function writeTeamConfig(config, dir) {
4254
4488
  const d = dir ?? cclensHome();
4255
4489
  mkdirSync2(d, { recursive: true });
4256
- const tmp = join(d, `${CONFIG_FILE}.${process.pid}.tmp`);
4490
+ const tmp = join2(d, `${CONFIG_FILE}.${process.pid}.tmp`);
4257
4491
  writeFileSync2(tmp, JSON.stringify(config, null, 2), { mode: 384 });
4258
- renameSync(tmp, join(d, CONFIG_FILE));
4492
+ renameSync(tmp, join2(d, CONFIG_FILE));
4259
4493
  }
4260
4494
  function clearTeamConfig(dir) {
4261
4495
  const d = dir ?? cclensHome();
4262
4496
  try {
4263
- unlinkSync2(join(d, CONFIG_FILE));
4497
+ unlinkSync2(join2(d, CONFIG_FILE));
4264
4498
  } catch {
4265
4499
  }
4266
4500
  }
@@ -4293,6 +4527,7 @@ __export(fs_exports, {
4293
4527
  RICH_ROLLUP_SCHEMA_VERSION: () => RICH_ROLLUP_SCHEMA_VERSION,
4294
4528
  agentSources: () => agentSources,
4295
4529
  antigravitySessionLocalDay: () => antigravitySessionLocalDay,
4530
+ appendUsageSnapshot: () => appendUsageSnapshot,
4296
4531
  buildCalibrationCurve: () => buildCalibrationCurve,
4297
4532
  cacheStats: () => cacheStats,
4298
4533
  cclensHome: () => cclensHome,
@@ -4326,14 +4561,17 @@ __export(fs_exports, {
4326
4561
  loadTeamForSession: () => loadTeamForSession,
4327
4562
  loadUsageByDay: () => loadUsageByDay,
4328
4563
  loadWorkflowAgentDetail: () => loadWorkflowAgentDetail,
4564
+ pruneUsageAgent: () => pruneUsageAgent,
4565
+ readGitFolder: () => readGitFolder,
4329
4566
  readJsonlFile: () => readJsonlFile,
4330
4567
  readTeamConfig: () => readTeamConfig,
4568
+ resolveProjectIdentity: () => resolveProjectIdentity,
4331
4569
  sessionIdFromFileName: () => sessionIdFromFileName,
4332
4570
  shouldSyncProject: () => shouldSyncProject,
4333
4571
  walkJsonlFiles: () => walkJsonlFiles,
4334
4572
  writeTeamConfig: () => writeTeamConfig
4335
4573
  });
4336
- import { promises as fs6 } from "node:fs";
4574
+ import { promises as fs6, existsSync as existsSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
4337
4575
  import os6 from "node:os";
4338
4576
  import path6 from "node:path";
4339
4577
  function cclensHome() {
@@ -4379,6 +4617,27 @@ async function getAnySession(id) {
4379
4617
  function usageLogPath() {
4380
4618
  return cclensPath("usage.jsonl");
4381
4619
  }
4620
+ function appendUsageSnapshot(snapshot) {
4621
+ const p = usageLogPath();
4622
+ mkdirSync3(path6.dirname(p), { recursive: true });
4623
+ appendFileSync(p, JSON.stringify(snapshot) + "\n", "utf8");
4624
+ }
4625
+ function pruneUsageAgent(agent) {
4626
+ const p = usageLogPath();
4627
+ if (!existsSync2(p))
4628
+ return;
4629
+ const kept = readFileSync4(p, "utf8").split("\n").filter((line) => {
4630
+ if (!line.trim())
4631
+ return false;
4632
+ try {
4633
+ return JSON.parse(line).agent !== agent;
4634
+ } catch {
4635
+ return true;
4636
+ }
4637
+ });
4638
+ mkdirSync3(path6.dirname(p), { recursive: true });
4639
+ writeFileSync3(p, kept.join("\n") + (kept.length ? "\n" : ""), "utf8");
4640
+ }
4382
4641
  async function loadUsageByDay(start2, end) {
4383
4642
  let raw;
4384
4643
  try {
@@ -4603,6 +4862,7 @@ var init_fs = __esm({
4603
4862
  init_antigravity();
4604
4863
  init_cowork();
4605
4864
  init_claude_code();
4865
+ init_git_project();
4606
4866
  init_codex();
4607
4867
  init_gemini();
4608
4868
  init_antigravity();
@@ -4681,13 +4941,13 @@ __export(server_exports, {
4681
4941
  stopServer: () => stopServer
4682
4942
  });
4683
4943
  import { spawn } from "node:child_process";
4684
- import { join as join2, dirname as dirname2 } from "node:path";
4944
+ import { join as join3, dirname as dirname3 } from "node:path";
4685
4945
  import { fileURLToPath } from "node:url";
4686
- import { existsSync } from "node:fs";
4687
- import { homedir } from "node:os";
4946
+ import { existsSync as existsSync3 } from "node:fs";
4947
+ import { homedir as homedir2 } from "node:os";
4688
4948
  function appDir() {
4689
- const here = dirname2(fileURLToPath(import.meta.url));
4690
- return join2(here, "..", "app", "apps", "web");
4949
+ const here = dirname3(fileURLToPath(import.meta.url));
4950
+ return join3(here, "..", "app", "apps", "web");
4691
4951
  }
4692
4952
  function getServerStatus() {
4693
4953
  cleanStalePid(PID_FILE, SERVER_MARKERS);
@@ -4698,7 +4958,7 @@ function getServerStatus() {
4698
4958
  return { running: false };
4699
4959
  }
4700
4960
  function isServerStale(status2) {
4701
- return status2.running === true && status2.version !== "0.15.2";
4961
+ return status2.running === true && status2.version !== "0.15.4";
4702
4962
  }
4703
4963
  async function ensureCurrentServer(opts = {}) {
4704
4964
  const status2 = getServerStatus();
@@ -4731,15 +4991,15 @@ async function ensureCurrentServer(opts = {}) {
4731
4991
  }
4732
4992
  async function startServer(opts = {}) {
4733
4993
  const port = opts.port ?? (parseInt(process.env.CCLENS_PORT ?? "", 10) || DEFAULT_PORT);
4734
- const serverJs = join2(appDir(), "server.js");
4735
- if (!existsSync(serverJs)) {
4994
+ const serverJs = join3(appDir(), "server.js");
4995
+ if (!existsSync3(serverJs)) {
4736
4996
  throw new Error(`Server not found at ${serverJs}. Reinstall with: npm install -g fleetlens`);
4737
4997
  }
4738
4998
  const portInUse = await checkPort(port);
4739
4999
  if (portInUse) {
4740
5000
  throw new Error(`Port ${port} is in use. Use --port to specify a different port.`);
4741
5001
  }
4742
- const dataDir = join2(homedir(), ".claude", "projects");
5002
+ const dataDir = join3(homedir2(), ".claude", "projects");
4743
5003
  const child = spawn(process.execPath, [serverJs], {
4744
5004
  detached: true,
4745
5005
  stdio: "ignore",
@@ -4754,7 +5014,7 @@ async function startServer(opts = {}) {
4754
5014
  });
4755
5015
  child.unref();
4756
5016
  const pid = child.pid;
4757
- writePid(PID_FILE, pid, port, "0.15.2");
5017
+ writePid(PID_FILE, pid, port, "0.15.4");
4758
5018
  await waitForHealth(`http://localhost:${port}`, 3e4);
4759
5019
  return { pid, port };
4760
5020
  }
@@ -4773,11 +5033,11 @@ function stopServer() {
4773
5033
  }
4774
5034
  async function checkPort(port) {
4775
5035
  const { createServer } = await import("node:net");
4776
- return new Promise((resolve3) => {
5036
+ return new Promise((resolve4) => {
4777
5037
  const server = createServer();
4778
- server.once("error", () => resolve3(true));
5038
+ server.once("error", () => resolve4(true));
4779
5039
  server.once("listening", () => {
4780
- server.close(() => resolve3(false));
5040
+ server.close(() => resolve4(false));
4781
5041
  });
4782
5042
  server.listen(port, "localhost");
4783
5043
  });
@@ -4823,17 +5083,18 @@ __export(storage_exports, {
4823
5083
  appendSnapshot: () => appendSnapshot,
4824
5084
  latestClaudeCodeSnapshot: () => latestClaudeCodeSnapshot,
4825
5085
  latestSnapshot: () => latestSnapshot,
5086
+ pruneAgent: () => pruneAgent,
4826
5087
  readSnapshots: () => readSnapshots
4827
5088
  });
4828
- import { appendFileSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, existsSync as existsSync2 } from "node:fs";
4829
- import { dirname as dirname3 } from "node:path";
5089
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "node:fs";
5090
+ import { dirname as dirname4 } from "node:path";
4830
5091
  function appendSnapshot(filePath, snapshot) {
4831
- mkdirSync3(dirname3(filePath), { recursive: true });
4832
- appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
5092
+ mkdirSync4(dirname4(filePath), { recursive: true });
5093
+ appendFileSync2(filePath, JSON.stringify(snapshot) + "\n", "utf8");
4833
5094
  }
4834
5095
  function readSnapshots(filePath) {
4835
- if (!existsSync2(filePath)) return [];
4836
- const content = readFileSync3(filePath, "utf8");
5096
+ if (!existsSync4(filePath)) return [];
5097
+ const content = readFileSync5(filePath, "utf8");
4837
5098
  const snapshots = [];
4838
5099
  for (const line of content.split("\n")) {
4839
5100
  if (!line.trim()) continue;
@@ -4848,6 +5109,19 @@ function latestSnapshot(filePath) {
4848
5109
  const all = readSnapshots(filePath);
4849
5110
  return all.length > 0 ? all[all.length - 1] : null;
4850
5111
  }
5112
+ function pruneAgent(filePath, agent) {
5113
+ if (!existsSync4(filePath)) return;
5114
+ const kept = readFileSync5(filePath, "utf8").split("\n").filter((line) => {
5115
+ if (!line.trim()) return false;
5116
+ try {
5117
+ return JSON.parse(line).agent !== agent;
5118
+ } catch {
5119
+ return true;
5120
+ }
5121
+ });
5122
+ mkdirSync4(dirname4(filePath), { recursive: true });
5123
+ writeFileSync4(filePath, kept.join("\n") + (kept.length ? "\n" : ""), "utf8");
5124
+ }
4851
5125
  function latestClaudeCodeSnapshot(filePath) {
4852
5126
  const all = readSnapshots(filePath);
4853
5127
  for (let i = all.length - 1; i >= 0; i--) {
@@ -4871,9 +5145,9 @@ __export(daemon_exports, {
4871
5145
  stopDaemonSilent: () => stopDaemonSilent
4872
5146
  });
4873
5147
  import { spawn as spawn2 } from "node:child_process";
4874
- import { join as join3, dirname as dirname4 } from "node:path";
5148
+ import { join as join4, dirname as dirname5 } from "node:path";
4875
5149
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4876
- import { existsSync as existsSync3, statSync, readFileSync as readFileSync4 } from "node:fs";
5150
+ import { existsSync as existsSync5, statSync as statSync2, readFileSync as readFileSync6 } from "node:fs";
4877
5151
  function startDaemonSilent() {
4878
5152
  cleanStalePid(DAEMON_PID, DAEMON_MARKERS);
4879
5153
  const existing = readPid(DAEMON_PID);
@@ -4881,7 +5155,7 @@ function startDaemonSilent() {
4881
5155
  return { started: false, pid: existing.pid, alreadyRunning: true };
4882
5156
  }
4883
5157
  const script = workerPath();
4884
- if (!existsSync3(script)) {
5158
+ if (!existsSync5(script)) {
4885
5159
  return {
4886
5160
  started: false,
4887
5161
  pid: null,
@@ -4915,7 +5189,7 @@ function getDaemonStatusInfo() {
4915
5189
  const running = entry !== null && isProcessAlive(entry.pid, DAEMON_MARKERS);
4916
5190
  const latest = latestSnapshot(USAGE_LOG);
4917
5191
  const lastSnapshotAgeMs = latest ? Date.now() - new Date(latest.captured_at).getTime() : null;
4918
- const usageLogSize = existsSync3(USAGE_LOG) ? statSync(USAGE_LOG).size : null;
5192
+ const usageLogSize = existsSync5(USAGE_LOG) ? statSync2(USAGE_LOG).size : null;
4919
5193
  return {
4920
5194
  running,
4921
5195
  pid: running ? entry.pid : null,
@@ -4925,8 +5199,8 @@ function getDaemonStatusInfo() {
4925
5199
  };
4926
5200
  }
4927
5201
  function workerPath() {
4928
- const here = dirname4(fileURLToPath2(import.meta.url));
4929
- return join3(here, "daemon-worker.js");
5202
+ const here = dirname5(fileURLToPath2(import.meta.url));
5203
+ return join4(here, "daemon-worker.js");
4930
5204
  }
4931
5205
  async function daemon(args2) {
4932
5206
  const sub = args2[0] ?? "status";
@@ -4991,11 +5265,11 @@ function daemonStatus() {
4991
5265
  }
4992
5266
  }
4993
5267
  function daemonLogs() {
4994
- if (!existsSync3(DAEMON_LOG)) {
5268
+ if (!existsSync5(DAEMON_LOG)) {
4995
5269
  console.log("No daemon logs yet.");
4996
5270
  return;
4997
5271
  }
4998
- const content = readFileSync4(DAEMON_LOG, "utf8");
5272
+ const content = readFileSync6(DAEMON_LOG, "utf8");
4999
5273
  const lines = content.trim().split("\n").slice(-20);
5000
5274
  for (const line of lines) process.stdout.write(line + "\n");
5001
5275
  }
@@ -5015,8 +5289,8 @@ var init_daemon = __esm({
5015
5289
 
5016
5290
  // src/updater.ts
5017
5291
  import { execSync, spawnSync } from "node:child_process";
5018
- import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync } from "node:fs";
5019
- import { dirname as dirname5, join as join4 } from "node:path";
5292
+ import { existsSync as existsSync6, readFileSync as readFileSync7, realpathSync as realpathSync2 } from "node:fs";
5293
+ import { dirname as dirname6, join as join5 } from "node:path";
5020
5294
  function shouldUpdate(local, remote) {
5021
5295
  const l = local.split(".").map(Number);
5022
5296
  const r = remote.split(".").map(Number);
@@ -5058,15 +5332,15 @@ function verifyInstalledVersion() {
5058
5332
  encoding: "utf8",
5059
5333
  timeout: 5e3
5060
5334
  }).trim();
5061
- const pkgPath = join4(globalRoot, PACKAGE_NAME, "package.json");
5062
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
5335
+ const pkgPath = join5(globalRoot, PACKAGE_NAME, "package.json");
5336
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
5063
5337
  if (!pkg.version) {
5064
5338
  return { ok: false, reason: `no version field in ${pkgPath}` };
5065
5339
  }
5066
5340
  return {
5067
5341
  ok: true,
5068
5342
  installedVersion: pkg.version,
5069
- installedPath: join4(globalRoot, PACKAGE_NAME)
5343
+ installedPath: join5(globalRoot, PACKAGE_NAME)
5070
5344
  };
5071
5345
  } catch (err) {
5072
5346
  return { ok: false, reason: err.message };
@@ -5123,28 +5397,28 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
5123
5397
  console.warn(
5124
5398
  ` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
5125
5399
  );
5126
- } else if (verify.installedVersion !== "0.15.2") {
5400
+ } else if (verify.installedVersion !== "0.15.4") {
5127
5401
  console.log(
5128
- ` \u2192 This process is still running ${"0.15.2"}. Next invocation will use ${verify.installedVersion}.`
5402
+ ` \u2192 This process is still running ${"0.15.4"}. Next invocation will use ${verify.installedVersion}.`
5129
5403
  );
5130
5404
  }
5131
5405
  }
5132
5406
  function resolvesIntoPackage(binPath, packagePath) {
5133
5407
  try {
5134
- const real = realpathSync(binPath);
5408
+ const real = realpathSync2(binPath);
5135
5409
  if (real.startsWith(packagePath)) return true;
5136
5410
  } catch {
5137
5411
  }
5138
- const binPrefix = dirname5(dirname5(binPath));
5139
- const installPrefix = dirname5(dirname5(dirname5(packagePath)));
5412
+ const binPrefix = dirname6(dirname6(binPath));
5413
+ const installPrefix = dirname6(dirname6(dirname6(packagePath)));
5140
5414
  return binPrefix === installPrefix;
5141
5415
  }
5142
5416
  function reExec() {
5143
5417
  const verify = verifyInstalledVersion();
5144
5418
  let script = process.argv[1] ?? "";
5145
5419
  if (verify.ok) {
5146
- const candidate = join4(verify.installedPath, "dist", "index.js");
5147
- if (existsSync4(candidate)) script = candidate;
5420
+ const candidate = join5(verify.installedPath, "dist", "index.js");
5421
+ if (existsSync6(candidate)) script = candidate;
5148
5422
  }
5149
5423
  const result = spawnSync(process.argv[0], [script, ...process.argv.slice(2)], {
5150
5424
  stdio: "inherit",
@@ -5160,14 +5434,14 @@ async function getUpdateAvailability() {
5160
5434
  if (isDevMode()) return null;
5161
5435
  const latest = await fetchLatestVersion();
5162
5436
  if (latest === null) return {
5163
- current: "0.15.2",
5437
+ current: "0.15.4",
5164
5438
  latest: null,
5165
5439
  updateAvailable: false
5166
5440
  };
5167
5441
  return {
5168
- current: "0.15.2",
5442
+ current: "0.15.4",
5169
5443
  latest,
5170
- updateAvailable: shouldUpdate("0.15.2", latest)
5444
+ updateAvailable: shouldUpdate("0.15.4", latest)
5171
5445
  };
5172
5446
  }
5173
5447
  async function checkForUpdate() {
@@ -5257,7 +5531,7 @@ async function restartServices(state) {
5257
5531
  }
5258
5532
  async function forceUpdate() {
5259
5533
  const latest = await fetchLatestVersion();
5260
- const current = "0.15.2";
5534
+ const current = "0.15.4";
5261
5535
  if (latest === null) {
5262
5536
  console.error("Could not reach npm registry. Check your network.");
5263
5537
  process.exit(1);
@@ -5309,27 +5583,27 @@ __export(autostart_exports, {
5309
5583
  maybePromptAutostart: () => maybePromptAutostart,
5310
5584
  recordOptOut: () => recordOptOut
5311
5585
  });
5312
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync, readFileSync as readFileSync6, realpathSync as realpathSync2 } from "node:fs";
5313
- import { homedir as homedir2 } from "node:os";
5314
- import { join as join5, dirname as dirname6 } from "node:path";
5586
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, rmSync, readFileSync as readFileSync8, realpathSync as realpathSync3 } from "node:fs";
5587
+ import { homedir as homedir3 } from "node:os";
5588
+ import { join as join6, dirname as dirname7 } from "node:path";
5315
5589
  import { execFile } from "node:child_process";
5316
5590
  import { promisify } from "node:util";
5317
5591
  import { createInterface } from "node:readline";
5318
5592
  function plistPath() {
5319
- return join5(homedir2(), "Library", "LaunchAgents", `${LABEL}.plist`);
5593
+ return join6(homedir3(), "Library", "LaunchAgents", `${LABEL}.plist`);
5320
5594
  }
5321
5595
  function isMac() {
5322
5596
  return process.platform === "darwin";
5323
5597
  }
5324
5598
  function isAutostartInstalled() {
5325
- return isMac() && existsSync5(plistPath());
5599
+ return isMac() && existsSync7(plistPath());
5326
5600
  }
5327
5601
  function isStalePlistContent(content) {
5328
5602
  return content.includes("<string>daemon</string>");
5329
5603
  }
5330
5604
  function installedPlistIsStale() {
5331
5605
  try {
5332
- return isStalePlistContent(readFileSync6(plistPath(), "utf8"));
5606
+ return isStalePlistContent(readFileSync8(plistPath(), "utf8"));
5333
5607
  } catch {
5334
5608
  return false;
5335
5609
  }
@@ -5337,7 +5611,7 @@ function installedPlistIsStale() {
5337
5611
  function resolveScriptPath() {
5338
5612
  const argv1 = process.argv[1] ?? "";
5339
5613
  try {
5340
- return realpathSync2(argv1);
5614
+ return realpathSync3(argv1);
5341
5615
  } catch {
5342
5616
  return argv1;
5343
5617
  }
@@ -5380,8 +5654,8 @@ async function installAutostart() {
5380
5654
  const scriptPath = resolveScriptPath();
5381
5655
  const logPath = cclensPath("daemon-autostart.log");
5382
5656
  const p = plistPath();
5383
- mkdirSync4(dirname6(p), { recursive: true });
5384
- writeFileSync3(p, buildPlist({ nodePath, scriptPath, logPath }), "utf8");
5657
+ mkdirSync5(dirname7(p), { recursive: true });
5658
+ writeFileSync5(p, buildPlist({ nodePath, scriptPath, logPath }), "utf8");
5385
5659
  await runLaunchctl(["unload", p]).catch(() => {
5386
5660
  });
5387
5661
  await runLaunchctl(["load", "-w", p]);
@@ -5401,7 +5675,7 @@ async function uninstallAutostart() {
5401
5675
  }
5402
5676
  const p = plistPath();
5403
5677
  recordOptOut();
5404
- if (!existsSync5(p)) {
5678
+ if (!existsSync7(p)) {
5405
5679
  console.log("Autostart is not installed \u2014 nothing to remove. Noted your opt-out; team join won't enable it.");
5406
5680
  return;
5407
5681
  }
@@ -5434,7 +5708,7 @@ async function statusAutostart() {
5434
5708
  return;
5435
5709
  }
5436
5710
  const p = plistPath();
5437
- const present = existsSync5(p);
5711
+ const present = existsSync7(p);
5438
5712
  let loaded = false;
5439
5713
  try {
5440
5714
  const { stdout } = await execFileAsync("launchctl", ["list"]);
@@ -5449,7 +5723,7 @@ function promptFlagPath() {
5449
5723
  }
5450
5724
  function readFlags() {
5451
5725
  try {
5452
- return JSON.parse(readFileSync6(promptFlagPath(), "utf8"));
5726
+ return JSON.parse(readFileSync8(promptFlagPath(), "utf8"));
5453
5727
  } catch {
5454
5728
  return {};
5455
5729
  }
@@ -5457,8 +5731,8 @@ function readFlags() {
5457
5731
  function writeFlags(patch) {
5458
5732
  const p = promptFlagPath();
5459
5733
  try {
5460
- mkdirSync4(dirname6(p), { recursive: true });
5461
- writeFileSync3(p, JSON.stringify({ ...readFlags(), ...patch }, null, 2), "utf8");
5734
+ mkdirSync5(dirname7(p), { recursive: true });
5735
+ writeFileSync5(p, JSON.stringify({ ...readFlags(), ...patch }, null, 2), "utf8");
5462
5736
  } catch {
5463
5737
  }
5464
5738
  }
@@ -5475,13 +5749,13 @@ function recordOptOut() {
5475
5749
  writeFlags({ optedOut: true });
5476
5750
  }
5477
5751
  function promptYesNo(question, defaultYes) {
5478
- return new Promise((resolve3) => {
5752
+ return new Promise((resolve4) => {
5479
5753
  const rl = createInterface({ input: process.stdin, output: process.stdout });
5480
5754
  rl.question(question, (answer) => {
5481
5755
  rl.close();
5482
5756
  const a = answer.trim().toLowerCase();
5483
- if (a === "") return resolve3(defaultYes);
5484
- resolve3(a === "y" || a === "yes");
5757
+ if (a === "") return resolve4(defaultYes);
5758
+ resolve4(a === "y" || a === "yes");
5485
5759
  });
5486
5760
  });
5487
5761
  }
@@ -5670,7 +5944,7 @@ async function status() {
5670
5944
  console.log(`Server: running on ${url} (PID ${server.pid}, v${ver})`);
5671
5945
  if (isServerStale(server)) {
5672
5946
  console.log(
5673
- ` \u26A0 serving stale ${ver} (CLI is ${"0.15.2"}) \u2014 run 'fleetlens start' to restart on the current version`
5947
+ ` \u26A0 serving stale ${ver} (CLI is ${"0.15.4"}) \u2014 run 'fleetlens start' to restart on the current version`
5674
5948
  );
5675
5949
  }
5676
5950
  } else {
@@ -5752,9 +6026,9 @@ var init_update = __esm({
5752
6026
 
5753
6027
  // src/usage/token.ts
5754
6028
  import { execFileSync as execFileSync2 } from "node:child_process";
5755
- import { readFileSync as readFileSync7 } from "node:fs";
5756
- import { homedir as homedir3, platform } from "node:os";
5757
- import { join as join6 } from "node:path";
6029
+ import { readFileSync as readFileSync9 } from "node:fs";
6030
+ import { homedir as homedir4, platform } from "node:os";
6031
+ import { join as join7 } from "node:path";
5758
6032
  function readOAuthCredentials() {
5759
6033
  if (platform() === "darwin") {
5760
6034
  return readFromMacKeychain() ?? readFromCredentialsFile();
@@ -5775,12 +6049,12 @@ function readFromMacKeychain() {
5775
6049
  }
5776
6050
  function readFromCredentialsFile() {
5777
6051
  const candidates = [
5778
- join6(homedir3(), ".claude", ".credentials.json"),
5779
- join6(homedir3(), ".config", "claude", "credentials.json")
6052
+ join7(homedir4(), ".claude", ".credentials.json"),
6053
+ join7(homedir4(), ".config", "claude", "credentials.json")
5780
6054
  ];
5781
6055
  for (const path7 of candidates) {
5782
6056
  try {
5783
- const blob = readFileSync7(path7, "utf8");
6057
+ const blob = readFileSync9(path7, "utf8");
5784
6058
  const creds = extractCredentials(blob);
5785
6059
  if (creds) return creds;
5786
6060
  } catch {
@@ -6775,11 +7049,13 @@ __export(dist_exports, {
6775
7049
  RATE_PER_PCT: () => RATE_PER_PCT,
6776
7050
  RATE_PER_PCT_5H: () => RATE_PER_PCT_5H,
6777
7051
  RATE_PER_PCT_7D: () => RATE_PER_PCT_7D,
7052
+ ZAI_METADATA: () => ZAI_METADATA,
6778
7053
  agentMetadata: () => agentMetadata,
6779
7054
  aggregateConcurrency: () => aggregateConcurrency,
6780
7055
  buildGanttDay: () => buildGanttDay,
6781
7056
  buildMegaRows: () => buildMegaRows,
6782
7057
  buildPresentation: () => buildPresentation,
7058
+ buildProjectKeyResolver: () => buildProjectKeyResolver,
6783
7059
  buildSpendIndex: () => buildSpendIndex,
6784
7060
  calendarMonth: () => calendarMonth,
6785
7061
  calendarWeek: () => calendarWeek,
@@ -6808,10 +7084,12 @@ __export(dist_exports, {
6808
7084
  predictAnchored: () => predictAnchored,
6809
7085
  priorCalendarMonth: () => priorCalendarMonth,
6810
7086
  priorCalendarWeek: () => priorCalendarWeek,
7087
+ projectKey: () => projectKey,
6811
7088
  projectRepoName: () => projectRepoName,
6812
7089
  rowPrimaryIndex: () => rowPrimaryIndex,
6813
7090
  sessionAirTimeMs: () => sessionAirTimeMs,
6814
7091
  sessionDay: () => sessionDay,
7092
+ sessionProjectKey: () => sessionProjectKey,
6815
7093
  stripFrameworkBoilerplate: () => stripFrameworkBoilerplate,
6816
7094
  summarizeBursts: () => summarizeBursts,
6817
7095
  toLocalDay: () => toLocalDay,
@@ -7426,6 +7704,7 @@ function buildEntries(sessionDetail) {
7426
7704
  const entries2 = [];
7427
7705
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
7428
7706
  const sessionFallbackProject = canonicalProjectName(sessionDetail.projectName ?? "");
7707
+ const trustSessionProject = !!sessionDetail.worktreeName;
7429
7708
  for (const [local_day, dayEvents] of byDay) {
7430
7709
  if (dayEvents.length === 0)
7431
7710
  continue;
@@ -7490,7 +7769,7 @@ function buildEntries(sessionDetail) {
7490
7769
  const finalAgentRaw = agg.finalAgent ?? "";
7491
7770
  const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
7492
7771
  let project = sessionFallbackProject;
7493
- if (agg.cwdCounts.size > 0) {
7772
+ if (!trustSessionProject && agg.cwdCounts.size > 0) {
7494
7773
  const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
7495
7774
  project = canonicalProjectName(best[0]);
7496
7775
  }
@@ -7600,11 +7879,11 @@ var init_dist2 = __esm({
7600
7879
  });
7601
7880
 
7602
7881
  // ../entries/dist/digest-fs.js
7603
- import { readFileSync as readFileSync8, readdirSync, writeFileSync as writeFileSync4, renameSync as renameSync2, chmodSync, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "node:fs";
7604
- import { dirname as dirname7, join as join7 } from "node:path";
7882
+ import { readFileSync as readFileSync10, readdirSync, writeFileSync as writeFileSync6, renameSync as renameSync2, chmodSync, mkdirSync as mkdirSync6, existsSync as existsSync8 } from "node:fs";
7883
+ import { dirname as dirname8, join as join8 } from "node:path";
7605
7884
  function parseDigestFile(path7, expectedVersion) {
7606
7885
  try {
7607
- const parsed = JSON.parse(readFileSync8(path7, "utf8"));
7886
+ const parsed = JSON.parse(readFileSync10(path7, "utf8"));
7608
7887
  if (!parsed || typeof parsed !== "object")
7609
7888
  return null;
7610
7889
  if (parsed.version !== expectedVersion)
@@ -7625,24 +7904,24 @@ function digestsDir() {
7625
7904
  }
7626
7905
  function __setDigestsDirForTest(path7) {
7627
7906
  digestsDirCached = path7;
7628
- mkdirSync5(join7(path7, "day"), { recursive: true });
7629
- mkdirSync5(join7(path7, "week"), { recursive: true });
7630
- mkdirSync5(join7(path7, "month"), { recursive: true });
7907
+ mkdirSync6(join8(path7, "day"), { recursive: true });
7908
+ mkdirSync6(join8(path7, "week"), { recursive: true });
7909
+ mkdirSync6(join8(path7, "month"), { recursive: true });
7631
7910
  }
7632
7911
  function dayDigestPath(date) {
7633
- return join7(digestsDir(), "day", `${date}.json`);
7912
+ return join8(digestsDir(), "day", `${date}.json`);
7634
7913
  }
7635
7914
  function weekDigestPath(monday) {
7636
- return join7(digestsDir(), "week", `${monday}.json`);
7915
+ return join8(digestsDir(), "week", `${monday}.json`);
7637
7916
  }
7638
7917
  function monthDigestPath(yearMonth) {
7639
- return join7(digestsDir(), "month", `${yearMonth}.json`);
7918
+ return join8(digestsDir(), "month", `${yearMonth}.json`);
7640
7919
  }
7641
7920
  function writeDayDigest(digest2) {
7642
7921
  const final = dayDigestPath(digest2.key);
7643
- mkdirSync5(dirname7(final), { recursive: true });
7922
+ mkdirSync6(dirname8(final), { recursive: true });
7644
7923
  const tmp = `${final}.tmp`;
7645
- writeFileSync4(tmp, JSON.stringify(digest2, null, 2), "utf8");
7924
+ writeFileSync6(tmp, JSON.stringify(digest2, null, 2), "utf8");
7646
7925
  if (process.platform !== "win32") {
7647
7926
  try {
7648
7927
  chmodSync(tmp, 384);
@@ -7653,7 +7932,7 @@ function writeDayDigest(digest2) {
7653
7932
  }
7654
7933
  function readDayDigest(date) {
7655
7934
  const p = dayDigestPath(date);
7656
- if (!existsSync6(p))
7935
+ if (!existsSync8(p))
7657
7936
  return null;
7658
7937
  return parseDigestFile(p, CURRENT_DAY_DIGEST_SCHEMA_VERSION);
7659
7938
  }
@@ -7676,9 +7955,9 @@ function __clearTodayCacheForTest() {
7676
7955
  }
7677
7956
  function writeWeekDigest(digest2) {
7678
7957
  const final = weekDigestPath(digest2.key);
7679
- mkdirSync5(dirname7(final), { recursive: true });
7958
+ mkdirSync6(dirname8(final), { recursive: true });
7680
7959
  const tmp = `${final}.tmp`;
7681
- writeFileSync4(tmp, JSON.stringify(digest2, null, 2), "utf8");
7960
+ writeFileSync6(tmp, JSON.stringify(digest2, null, 2), "utf8");
7682
7961
  if (process.platform !== "win32") {
7683
7962
  try {
7684
7963
  chmodSync(tmp, 384);
@@ -7689,13 +7968,13 @@ function writeWeekDigest(digest2) {
7689
7968
  }
7690
7969
  function readWeekDigest(monday) {
7691
7970
  const p = weekDigestPath(monday);
7692
- if (!existsSync6(p))
7971
+ if (!existsSync8(p))
7693
7972
  return null;
7694
7973
  return parseDigestFile(p, CURRENT_WEEK_DIGEST_SCHEMA_VERSION);
7695
7974
  }
7696
7975
  function listWeekDigestKeys() {
7697
- const dir = join7(digestsDir(), "week");
7698
- if (!existsSync6(dir))
7976
+ const dir = join8(digestsDir(), "week");
7977
+ if (!existsSync8(dir))
7699
7978
  return [];
7700
7979
  return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length)).filter((k) => /^\d{4}-\d{2}-\d{2}$/.test(k));
7701
7980
  }
@@ -7713,9 +7992,9 @@ function setCurrentWeekDigestInCache(monday, digest2, nowMs) {
7713
7992
  }
7714
7993
  function writeMonthDigest(digest2) {
7715
7994
  const final = monthDigestPath(digest2.key);
7716
- mkdirSync5(dirname7(final), { recursive: true });
7995
+ mkdirSync6(dirname8(final), { recursive: true });
7717
7996
  const tmp = `${final}.tmp`;
7718
- writeFileSync4(tmp, JSON.stringify(digest2, null, 2), "utf8");
7997
+ writeFileSync6(tmp, JSON.stringify(digest2, null, 2), "utf8");
7719
7998
  if (process.platform !== "win32") {
7720
7999
  try {
7721
8000
  chmodSync(tmp, 384);
@@ -7726,13 +8005,13 @@ function writeMonthDigest(digest2) {
7726
8005
  }
7727
8006
  function readMonthDigest(yearMonth) {
7728
8007
  const p = monthDigestPath(yearMonth);
7729
- if (!existsSync6(p))
8008
+ if (!existsSync8(p))
7730
8009
  return null;
7731
8010
  return parseDigestFile(p, CURRENT_MONTH_DIGEST_SCHEMA_VERSION);
7732
8011
  }
7733
8012
  function listMonthDigestKeys() {
7734
- const dir = join7(digestsDir(), "month");
7735
- if (!existsSync6(dir))
8013
+ const dir = join8(digestsDir(), "month");
8014
+ if (!existsSync8(dir))
7736
8015
  return [];
7737
8016
  return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length)).filter((k) => /^\d{4}-\d{2}$/.test(k));
7738
8017
  }
@@ -7794,9 +8073,9 @@ __export(fs_exports2, {
7794
8073
  writeMonthDigest: () => writeMonthDigest,
7795
8074
  writeWeekDigest: () => writeWeekDigest
7796
8075
  });
7797
- import { readFileSync as readFileSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, renameSync as renameSync3, existsSync as existsSync7 } from "node:fs";
8076
+ import { readFileSync as readFileSync11, readdirSync as readdirSync2, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, renameSync as renameSync3, existsSync as existsSync9 } from "node:fs";
7798
8077
  import { randomBytes } from "node:crypto";
7799
- import { join as join8 } from "node:path";
8078
+ import { join as join9 } from "node:path";
7800
8079
  function entriesDir() {
7801
8080
  if (entriesDirCached)
7802
8081
  return entriesDirCached;
@@ -7806,21 +8085,21 @@ function entriesDir() {
7806
8085
  }
7807
8086
  function __setEntriesDirForTest(path7) {
7808
8087
  entriesDirCached = path7;
7809
- mkdirSync6(path7, { recursive: true });
8088
+ mkdirSync7(path7, { recursive: true });
7810
8089
  }
7811
8090
  function pathFor(sessionId, localDay2) {
7812
- return join8(entriesDir(), `${entryKey(sessionId, localDay2)}.json`);
8091
+ return join9(entriesDir(), `${entryKey(sessionId, localDay2)}.json`);
7813
8092
  }
7814
8093
  function tmpPathFor(final) {
7815
8094
  return `${final}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
7816
8095
  }
7817
8096
  function writeEntry(entry) {
7818
8097
  const dir = entriesDir();
7819
- mkdirSync6(dir, { recursive: true });
8098
+ mkdirSync7(dir, { recursive: true });
7820
8099
  const final = pathFor(entry.session_id, entry.local_day);
7821
8100
  const tmp = tmpPathFor(final);
7822
8101
  const json = JSON.stringify(entry, null, 2);
7823
- writeFileSync5(tmp, json, { encoding: "utf8" });
8102
+ writeFileSync7(tmp, json, { encoding: "utf8" });
7824
8103
  renameSync3(tmp, final);
7825
8104
  }
7826
8105
  function enrichmentInputHash(e) {
@@ -7840,7 +8119,7 @@ function enrichmentInputHash(e) {
7840
8119
  }
7841
8120
  function writeEntryPreservingEnrichment(fresh) {
7842
8121
  const dir = entriesDir();
7843
- mkdirSync6(dir, { recursive: true });
8122
+ mkdirSync7(dir, { recursive: true });
7844
8123
  const final = pathFor(fresh.session_id, fresh.local_day);
7845
8124
  const existing = readEntry(fresh.session_id, fresh.local_day);
7846
8125
  let toWrite = fresh;
@@ -7848,22 +8127,22 @@ function writeEntryPreservingEnrichment(fresh) {
7848
8127
  toWrite = { ...fresh, enrichment: existing.enrichment };
7849
8128
  }
7850
8129
  const tmp = tmpPathFor(final);
7851
- writeFileSync5(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
8130
+ writeFileSync7(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
7852
8131
  renameSync3(tmp, final);
7853
8132
  }
7854
8133
  function readEntry(sessionId, localDay2) {
7855
8134
  const p = pathFor(sessionId, localDay2);
7856
- if (!existsSync7(p))
8135
+ if (!existsSync9(p))
7857
8136
  return null;
7858
- const raw = readFileSync9(p, "utf8");
8137
+ const raw = readFileSync11(p, "utf8");
7859
8138
  return JSON.parse(raw);
7860
8139
  }
7861
8140
  function entryExists(sessionId, localDay2) {
7862
- return existsSync7(pathFor(sessionId, localDay2));
8141
+ return existsSync9(pathFor(sessionId, localDay2));
7863
8142
  }
7864
8143
  function listEntryKeys() {
7865
8144
  const dir = entriesDir();
7866
- if (!existsSync7(dir))
8145
+ if (!existsSync9(dir))
7867
8146
  return [];
7868
8147
  return readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length));
7869
8148
  }
@@ -12142,9 +12421,9 @@ RESPOND WITH ONLY VALID JSON (no prose, no code fence):
12142
12421
  });
12143
12422
 
12144
12423
  // ../entries/dist/tmux-runner.js
12145
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6, existsSync as existsSync8, readdirSync as readdirSync3, realpathSync as realpathSync3, readFileSync as readFileSync10, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
12146
- import { join as join9 } from "node:path";
12147
- import { homedir as homedir4 } from "node:os";
12424
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8, existsSync as existsSync10, readdirSync as readdirSync3, realpathSync as realpathSync4, readFileSync as readFileSync12, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
12425
+ import { join as join10 } from "node:path";
12426
+ import { homedir as homedir5 } from "node:os";
12148
12427
  import { spawnSync as spawnSync2 } from "node:child_process";
12149
12428
  function which(bin) {
12150
12429
  const r = spawnSync2("/usr/bin/env", ["which", bin], { encoding: "utf8" });
@@ -12160,16 +12439,16 @@ function tmuxRunnerAvailable() {
12160
12439
  return { ok: true };
12161
12440
  }
12162
12441
  function runtimeCwd() {
12163
- const dir = join9(homedir4(), ".cclens", "runtime");
12164
- if (!existsSync8(dir))
12165
- mkdirSync7(dir, { recursive: true });
12166
- return realpathSync3(dir);
12442
+ const dir = join10(homedir5(), ".cclens", "runtime");
12443
+ if (!existsSync10(dir))
12444
+ mkdirSync8(dir, { recursive: true });
12445
+ return realpathSync4(dir);
12167
12446
  }
12168
12447
  function ensureRuntimeCwdTrusted(cwd) {
12169
- const cfgPath = join9(homedir4(), ".claude.json");
12448
+ const cfgPath = join10(homedir5(), ".claude.json");
12170
12449
  let cfg;
12171
12450
  try {
12172
- cfg = JSON.parse(readFileSync10(cfgPath, "utf8"));
12451
+ cfg = JSON.parse(readFileSync12(cfgPath, "utf8"));
12173
12452
  } catch {
12174
12453
  return;
12175
12454
  }
@@ -12184,7 +12463,7 @@ function ensureRuntimeCwdTrusted(cwd) {
12184
12463
  cfg.projects = projects;
12185
12464
  const tmp = `${cfgPath}.cclens-tmp-${process.pid}`;
12186
12465
  try {
12187
- writeFileSync6(tmp, JSON.stringify(cfg, null, 2));
12466
+ writeFileSync8(tmp, JSON.stringify(cfg, null, 2));
12188
12467
  renameSync4(tmp, cfgPath);
12189
12468
  } catch {
12190
12469
  try {
@@ -12211,16 +12490,16 @@ async function runClaudeUnderTmuxImpl(args2) {
12211
12490
  ensureRuntimeCwdTrusted(cwd);
12212
12491
  const runId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
12213
12492
  const sessionName = `cclens-${runId}`;
12214
- const systemFile = join9(cwd, `system-${runId}.txt`);
12215
- const userFile = join9(cwd, `user-${runId}.txt`);
12216
- const wrapper = join9(cwd, `run-${runId}.sh`);
12217
- writeFileSync6(systemFile, args2.systemPrompt);
12218
- writeFileSync6(userFile, args2.reminder ? `${args2.userPrompt}
12493
+ const systemFile = join10(cwd, `system-${runId}.txt`);
12494
+ const userFile = join10(cwd, `user-${runId}.txt`);
12495
+ const wrapper = join10(cwd, `run-${runId}.sh`);
12496
+ writeFileSync8(systemFile, args2.systemPrompt);
12497
+ writeFileSync8(userFile, args2.reminder ? `${args2.userPrompt}
12219
12498
 
12220
12499
  ---
12221
12500
 
12222
12501
  ${args2.reminder}` : args2.userPrompt);
12223
- writeFileSync6(wrapper, `#!/bin/bash
12502
+ writeFileSync8(wrapper, `#!/bin/bash
12224
12503
  SYS="$(cat ${shellEscape(systemFile)})"
12225
12504
  USR="$(cat ${shellEscape(userFile)})"
12226
12505
  exec ${shellEscape(claudeBin)} \\
@@ -12302,7 +12581,7 @@ function snapshotAllTranscripts() {
12302
12581
  return out;
12303
12582
  }
12304
12583
  for (const sub of subs) {
12305
- const dir = join9(root, sub);
12584
+ const dir = join10(root, sub);
12306
12585
  let files;
12307
12586
  try {
12308
12587
  files = readdirSync3(dir);
@@ -12311,7 +12590,7 @@ function snapshotAllTranscripts() {
12311
12590
  }
12312
12591
  for (const f of files) {
12313
12592
  if (f.endsWith(".jsonl"))
12314
- out.add(join9(dir, f));
12593
+ out.add(join10(dir, f));
12315
12594
  }
12316
12595
  }
12317
12596
  return out;
@@ -12319,7 +12598,7 @@ function snapshotAllTranscripts() {
12319
12598
  function readJsonlCwd(path7, maxLines = 8) {
12320
12599
  let raw;
12321
12600
  try {
12322
- raw = readFileSync10(path7, "utf8");
12601
+ raw = readFileSync12(path7, "utf8");
12323
12602
  } catch {
12324
12603
  return void 0;
12325
12604
  }
@@ -12352,7 +12631,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
12352
12631
  subs = [];
12353
12632
  }
12354
12633
  for (const sub of subs) {
12355
- const dir = join9(root, sub);
12634
+ const dir = join10(root, sub);
12356
12635
  let files;
12357
12636
  try {
12358
12637
  files = readdirSync3(dir);
@@ -12362,7 +12641,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
12362
12641
  for (const f of files) {
12363
12642
  if (!f.endsWith(".jsonl"))
12364
12643
  continue;
12365
- const full = join9(dir, f);
12644
+ const full = join10(dir, f);
12366
12645
  if (existingBefore.has(full))
12367
12646
  continue;
12368
12647
  if (readJsonlCwd(full) === targetCwd)
@@ -12376,7 +12655,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
12376
12655
  function parseTranscript2(path7, defaultModel) {
12377
12656
  let raw;
12378
12657
  try {
12379
- raw = readFileSync10(path7, "utf8");
12658
+ raw = readFileSync12(path7, "utf8");
12380
12659
  } catch {
12381
12660
  return { assembled: "", inputTokens: 0, outputTokens: 0, modelUsed: defaultModel, done: false };
12382
12661
  }
@@ -12436,7 +12715,7 @@ function cleanup(cwd, runId, tmuxBin, sessionName, transcriptPath) {
12436
12715
  }
12437
12716
  for (const f of [`system-${runId}.txt`, `user-${runId}.txt`, `run-${runId}.sh`]) {
12438
12717
  try {
12439
- unlinkSync3(join9(cwd, f));
12718
+ unlinkSync3(join10(cwd, f));
12440
12719
  } catch {
12441
12720
  }
12442
12721
  }
@@ -12464,19 +12743,19 @@ var init_tmux_runner = __esm({
12464
12743
  }
12465
12744
  };
12466
12745
  mutex = Promise.resolve();
12467
- PROJECTS_ROOT = () => join9(homedir4(), ".claude", "projects");
12746
+ PROJECTS_ROOT = () => join10(homedir5(), ".claude", "projects");
12468
12747
  }
12469
12748
  });
12470
12749
 
12471
12750
  // ../entries/dist/llm-runner.js
12472
12751
  import { spawn as spawn3 } from "node:child_process";
12473
- import { mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "node:fs";
12474
- import { join as join10 } from "node:path";
12752
+ import { mkdirSync as mkdirSync9, appendFileSync as appendFileSync3, writeFileSync as writeFileSync9, existsSync as existsSync11 } from "node:fs";
12753
+ import { join as join11 } from "node:path";
12475
12754
  import { randomUUID } from "node:crypto";
12476
12755
  function ensureRunsDir() {
12477
- if (!existsSync9(RUNS_DIR)) {
12756
+ if (!existsSync11(RUNS_DIR)) {
12478
12757
  try {
12479
- mkdirSync8(RUNS_DIR, { recursive: true });
12758
+ mkdirSync9(RUNS_DIR, { recursive: true });
12480
12759
  } catch {
12481
12760
  }
12482
12761
  }
@@ -12503,17 +12782,17 @@ function newRunId(kind) {
12503
12782
  return `${ts}_${kind}_${short}`;
12504
12783
  }
12505
12784
  function tracePath(runId) {
12506
- return join10(RUNS_DIR, `${runId}.jsonl`);
12785
+ return join11(RUNS_DIR, `${runId}.jsonl`);
12507
12786
  }
12508
12787
  function safeAppend(path7, line) {
12509
12788
  try {
12510
- appendFileSync2(path7, line);
12789
+ appendFileSync3(path7, line);
12511
12790
  } catch {
12512
12791
  }
12513
12792
  }
12514
12793
  function safeWrite(path7, line) {
12515
12794
  try {
12516
- writeFileSync7(path7, line);
12795
+ writeFileSync9(path7, line);
12517
12796
  } catch {
12518
12797
  }
12519
12798
  }
@@ -12591,7 +12870,7 @@ async function runClaudeSubprocess(args2) {
12591
12870
  return runClaudePrintMode(args2);
12592
12871
  }
12593
12872
  function runClaudePrintMode(args2) {
12594
- return new Promise((resolve3, reject) => {
12873
+ return new Promise((resolve4, reject) => {
12595
12874
  ensureRunsDir();
12596
12875
  const kind = detectKindFromSystemPrompt(args2.systemPrompt);
12597
12876
  const runId = newRunId(kind);
@@ -12722,7 +13001,7 @@ ${args2.reminder}` : args2.userPrompt;
12722
13001
  reject(new Error(`claude exited ${code}: ${stderr.trim().slice(0, 300)}`));
12723
13002
  return;
12724
13003
  }
12725
- resolve3({ content: buffer, input_tokens: inputTokens, output_tokens: outputTokens, model: modelUsed });
13004
+ resolve4({ content: buffer, input_tokens: inputTokens, output_tokens: outputTokens, model: modelUsed });
12726
13005
  });
12727
13006
  proc.on("error", (err) => {
12728
13007
  safeAppend(trace, JSON.stringify({ _meta: { type: "spawn_error", run_id: runId, ts: (/* @__PURE__ */ new Date()).toISOString(), error: err.message } }) + "\n");
@@ -12919,8 +13198,8 @@ var init_enrich2 = __esm({
12919
13198
  });
12920
13199
 
12921
13200
  // ../entries/dist/budget.js
12922
- import { appendFileSync as appendFileSync3, readFileSync as readFileSync11, existsSync as existsSync10, mkdirSync as mkdirSync9 } from "node:fs";
12923
- import { dirname as dirname8 } from "node:path";
13201
+ import { appendFileSync as appendFileSync4, readFileSync as readFileSync13, existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
13202
+ import { dirname as dirname9 } from "node:path";
12924
13203
  function spendPath() {
12925
13204
  if (spendPathCached)
12926
13205
  return spendPathCached;
@@ -12932,17 +13211,17 @@ function __setSpendPathForTest(path7) {
12932
13211
  }
12933
13212
  function appendSpend(record) {
12934
13213
  const p = spendPath();
12935
- mkdirSync9(dirname8(p), { recursive: true });
12936
- appendFileSync3(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
13214
+ mkdirSync10(dirname9(p), { recursive: true });
13215
+ appendFileSync4(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
12937
13216
  }
12938
13217
  function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
12939
13218
  const p = spendPath();
12940
- if (!existsSync10(p))
13219
+ if (!existsSync12(p))
12941
13220
  return 0;
12942
13221
  const year = now.getUTCFullYear();
12943
13222
  const month2 = now.getUTCMonth();
12944
13223
  let total = 0;
12945
- const raw = readFileSync11(p, "utf8");
13224
+ const raw = readFileSync13(p, "utf8");
12946
13225
  for (const line of raw.split("\n")) {
12947
13226
  if (!line)
12948
13227
  continue;
@@ -12971,7 +13250,7 @@ var init_budget = __esm({
12971
13250
  });
12972
13251
 
12973
13252
  // ../entries/dist/pipeline-lock.js
12974
- import { writeFileSync as writeFileSync8, unlinkSync as unlinkSync4, existsSync as existsSync11, readFileSync as readFileSync12, statSync as statSync2 } from "node:fs";
13253
+ import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync13, readFileSync as readFileSync14, statSync as statSync3 } from "node:fs";
12975
13254
  function lockPath() {
12976
13255
  if (lockPathCached)
12977
13256
  return lockPathCached;
@@ -12982,7 +13261,7 @@ function __setInteractiveLockPathForTest(path7) {
12982
13261
  lockPathCached = path7;
12983
13262
  }
12984
13263
  function touchLock() {
12985
- writeFileSync8(lockPath(), String(process.pid), "utf8");
13264
+ writeFileSync10(lockPath(), String(process.pid), "utf8");
12986
13265
  }
12987
13266
  function writeInteractiveLock() {
12988
13267
  touchLock();
@@ -13016,13 +13295,13 @@ function pidAlive(pid) {
13016
13295
  }
13017
13296
  function interactiveLockFresh(nowMs) {
13018
13297
  const p = lockPath();
13019
- if (!existsSync11(p))
13298
+ if (!existsSync13(p))
13020
13299
  return false;
13021
13300
  try {
13022
- const mtime = statSync2(p).mtimeMs;
13301
+ const mtime = statSync3(p).mtimeMs;
13023
13302
  if (nowMs - mtime > STALE_MS)
13024
13303
  return false;
13025
- const pid = Number(readFileSync12(p, "utf8").trim());
13304
+ const pid = Number(readFileSync14(p, "utf8").trim());
13026
13305
  if (!Number.isFinite(pid) || pid <= 0)
13027
13306
  return false;
13028
13307
  if (!pidAlive(pid))
@@ -13128,8 +13407,8 @@ var init_queue = __esm({
13128
13407
  });
13129
13408
 
13130
13409
  // ../entries/dist/settings.js
13131
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, renameSync as renameSync5, chmodSync as chmodSync2, mkdirSync as mkdirSync10, existsSync as existsSync12 } from "node:fs";
13132
- import { dirname as dirname9 } from "node:path";
13410
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, renameSync as renameSync5, chmodSync as chmodSync2, mkdirSync as mkdirSync11, existsSync as existsSync14 } from "node:fs";
13411
+ import { dirname as dirname10 } from "node:path";
13133
13412
  function settingsPath() {
13134
13413
  if (settingsPathCached)
13135
13414
  return settingsPathCached;
@@ -13164,10 +13443,10 @@ function fromDisk(d) {
13164
13443
  }
13165
13444
  function readSettings() {
13166
13445
  const p = settingsPath();
13167
- if (!existsSync12(p))
13446
+ if (!existsSync14(p))
13168
13447
  return DEFAULT_SETTINGS;
13169
13448
  try {
13170
- const raw = readFileSync13(p, "utf8");
13449
+ const raw = readFileSync15(p, "utf8");
13171
13450
  return fromDisk(JSON.parse(raw));
13172
13451
  } catch {
13173
13452
  return DEFAULT_SETTINGS;
@@ -13175,9 +13454,9 @@ function readSettings() {
13175
13454
  }
13176
13455
  function writeSettings(s) {
13177
13456
  const p = settingsPath();
13178
- mkdirSync10(dirname9(p), { recursive: true });
13457
+ mkdirSync11(dirname10(p), { recursive: true });
13179
13458
  const tmp = `${p}.tmp`;
13180
- writeFileSync9(tmp, JSON.stringify(toDisk(s), null, 2), { encoding: "utf8" });
13459
+ writeFileSync11(tmp, JSON.stringify(toDisk(s), null, 2), { encoding: "utf8" });
13181
13460
  if (process.platform !== "win32") {
13182
13461
  chmodSync2(tmp, 384);
13183
13462
  }
@@ -16356,6 +16635,142 @@ var init_digest_month_pipeline = __esm({
16356
16635
  }
16357
16636
  });
16358
16637
 
16638
+ // ../entries/dist/credentials.js
16639
+ import { writeFileSync as writeFileSync12, readFileSync as readFileSync16, renameSync as renameSync6, chmodSync as chmodSync3, mkdirSync as mkdirSync12, existsSync as existsSync15, unlinkSync as unlinkSync5 } from "node:fs";
16640
+ import { dirname as dirname11 } from "node:path";
16641
+ async function fetchZaiUsage(candidate) {
16642
+ const key = candidate.trim();
16643
+ if (!key)
16644
+ throw new Error("empty key");
16645
+ const res = await fetch(ZAI_QUOTA_ENDPOINT, {
16646
+ headers: { Authorization: key, "Accept-Language": "en-US,en" }
16647
+ });
16648
+ if (!res.ok) {
16649
+ throw new Error(`Z.ai rejected the key (${res.status} ${res.statusText})`);
16650
+ }
16651
+ const body = await res.json().catch(() => {
16652
+ throw new Error("Z.ai returned an unparseable response");
16653
+ });
16654
+ if (body.code !== void 0 && body.code !== 200) {
16655
+ throw new Error(`Z.ai rejected the key (code ${body.code})`);
16656
+ }
16657
+ if (body.success === false) {
16658
+ throw new Error("Z.ai rejected the key");
16659
+ }
16660
+ const limits = body.data?.limits ?? [];
16661
+ let fiveHour = null;
16662
+ let sevenDay = null;
16663
+ let webSearch = null;
16664
+ for (const limit of limits) {
16665
+ const type = limit.type;
16666
+ if (type === "TIME_LIMIT") {
16667
+ const pct2 = typeof limit.percentage === "number" ? limit.percentage : null;
16668
+ webSearch = { used: pct2, limit: pct2 !== null ? 100 : null };
16669
+ continue;
16670
+ }
16671
+ if (type !== "TOKENS_LIMIT")
16672
+ continue;
16673
+ const unit = limit.unit;
16674
+ const pct = typeof limit.percentage === "number" ? limit.percentage : null;
16675
+ const resetsAt = typeof limit.nextResetTime === "number" ? new Date(limit.nextResetTime).toISOString() : null;
16676
+ const window = { utilization: pct, resets_at: resetsAt };
16677
+ if (unit === 6)
16678
+ sevenDay = sevenDay ?? window;
16679
+ else
16680
+ fiveHour = fiveHour ?? window;
16681
+ }
16682
+ return {
16683
+ captured_at: (/* @__PURE__ */ new Date()).toISOString(),
16684
+ agent: "zai",
16685
+ five_hour: fiveHour ?? { utilization: null, resets_at: null },
16686
+ seven_day: sevenDay ?? { utilization: null, resets_at: null },
16687
+ seven_day_opus: null,
16688
+ seven_day_sonnet: null,
16689
+ seven_day_oauth_apps: null,
16690
+ seven_day_cowork: null,
16691
+ extra_usage: null,
16692
+ plan_type: body.data?.level ?? null,
16693
+ web_search_quota: webSearch ?? null
16694
+ };
16695
+ }
16696
+ async function validateAndSnapshotZaiKey(key) {
16697
+ const snap = await fetchZaiUsage(key);
16698
+ appendUsageSnapshot(snap);
16699
+ return snap;
16700
+ }
16701
+ function deleteZaiKeyAndPrune() {
16702
+ deleteZaiKey();
16703
+ pruneUsageAgent("zai");
16704
+ }
16705
+ function credentialsPath() {
16706
+ return cclensPath("credentials.json");
16707
+ }
16708
+ function readCredentials() {
16709
+ const p = credentialsPath();
16710
+ if (!existsSync15(p))
16711
+ return {};
16712
+ try {
16713
+ const raw = readFileSync16(p, "utf8");
16714
+ return JSON.parse(raw);
16715
+ } catch {
16716
+ return {};
16717
+ }
16718
+ }
16719
+ function readCredentialsMasked() {
16720
+ const store = readCredentials();
16721
+ if (!store.zai?.apiKey)
16722
+ return {};
16723
+ const key = store.zai.apiKey;
16724
+ const hint = key.length > 12 ? `${key.slice(0, 6)}\u2026${key.slice(-6)}` : `${key.slice(0, 6)}\u2026`;
16725
+ return { zai: { configured: true, hint } };
16726
+ }
16727
+ function writeZaiKey(apiKey) {
16728
+ apiKey = apiKey.trim();
16729
+ if (!apiKey) {
16730
+ deleteZaiKey();
16731
+ return;
16732
+ }
16733
+ const store = readCredentials();
16734
+ store.zai = { ...store.zai ?? {}, apiKey };
16735
+ const p = credentialsPath();
16736
+ mkdirSync12(dirname11(p), { recursive: true });
16737
+ const tmp = `${p}.tmp`;
16738
+ writeFileSync12(tmp, JSON.stringify(store, null, 2), { encoding: "utf8" });
16739
+ if (process.platform !== "win32") {
16740
+ chmodSync3(tmp, 384);
16741
+ }
16742
+ renameSync6(tmp, p);
16743
+ }
16744
+ function deleteZaiKey() {
16745
+ const store = readCredentials();
16746
+ delete store.zai;
16747
+ const p = credentialsPath();
16748
+ if (Object.keys(store).length === 0) {
16749
+ try {
16750
+ unlinkSync5(p);
16751
+ } catch {
16752
+ }
16753
+ return;
16754
+ }
16755
+ const tmp = `${p}.tmp`;
16756
+ writeFileSync12(tmp, JSON.stringify(store, null, 2), { encoding: "utf8" });
16757
+ if (process.platform !== "win32") {
16758
+ chmodSync3(tmp, 384);
16759
+ }
16760
+ renameSync6(tmp, p);
16761
+ }
16762
+ function hasZaiKey() {
16763
+ return !!readCredentials().zai?.apiKey;
16764
+ }
16765
+ var ZAI_QUOTA_ENDPOINT;
16766
+ var init_credentials = __esm({
16767
+ "../entries/dist/credentials.js"() {
16768
+ "use strict";
16769
+ init_fs();
16770
+ ZAI_QUOTA_ENDPOINT = "https://api.z.ai/api/monitor/usage/quota/limit";
16771
+ }
16772
+ });
16773
+
16359
16774
  // ../entries/dist/node.js
16360
16775
  var node_exports = {};
16361
16776
  __export(node_exports, {
@@ -16386,19 +16801,25 @@ __export(node_exports, {
16386
16801
  computeInteractionGrammar: () => computeInteractionGrammar,
16387
16802
  computeInteractionModes: () => computeInteractionModes,
16388
16803
  computeWorkingShapes: () => computeWorkingShapes,
16804
+ deleteZaiKey: () => deleteZaiKey,
16805
+ deleteZaiKeyAndPrune: () => deleteZaiKeyAndPrune,
16389
16806
  enrichEntry: () => enrichEntry,
16807
+ fetchZaiUsage: () => fetchZaiUsage,
16390
16808
  generateDayDigest: () => generateDayDigest,
16391
16809
  generateMonthDigest: () => generateMonthDigest,
16392
16810
  generateWeekDigest: () => generateWeekDigest,
16393
16811
  getCurrentMonthDigestFromCache: () => getCurrentMonthDigestFromCache,
16394
16812
  getCurrentWeekDigestFromCache: () => getCurrentWeekDigestFromCache,
16395
16813
  getTodayDigestFromCache: () => getTodayDigestFromCache,
16814
+ hasZaiKey: () => hasZaiKey,
16396
16815
  interactiveLockFresh: () => interactiveLockFresh,
16397
16816
  listMonthDigestKeys: () => listMonthDigestKeys,
16398
16817
  listWeekDigestKeys: () => listWeekDigestKeys,
16399
16818
  mondayFor: () => mondayFor,
16400
16819
  mondaysInMonth: () => mondaysInMonth,
16401
16820
  monthToDateSpend: () => monthToDateSpend,
16821
+ readCredentials: () => readCredentials,
16822
+ readCredentialsMasked: () => readCredentialsMasked,
16402
16823
  readDayDigest: () => readDayDigest,
16403
16824
  readMonthDigest: () => readMonthDigest,
16404
16825
  readSettings: () => readSettings,
@@ -16413,12 +16834,14 @@ __export(node_exports, {
16413
16834
  setCurrentWeekDigestInCache: () => setCurrentWeekDigestInCache,
16414
16835
  setTodayDigestInCache: () => setTodayDigestInCache,
16415
16836
  tmuxRunnerAvailable: () => tmuxRunnerAvailable,
16837
+ validateAndSnapshotZaiKey: () => validateAndSnapshotZaiKey,
16416
16838
  weekDates: () => weekDates,
16417
16839
  writeDayDigest: () => writeDayDigest,
16418
16840
  writeInteractiveLock: () => writeInteractiveLock,
16419
16841
  writeMonthDigest: () => writeMonthDigest,
16420
16842
  writeSettings: () => writeSettings,
16421
- writeWeekDigest: () => writeWeekDigest
16843
+ writeWeekDigest: () => writeWeekDigest,
16844
+ writeZaiKey: () => writeZaiKey
16422
16845
  });
16423
16846
  var init_node = __esm({
16424
16847
  "../entries/dist/node.js"() {
@@ -16440,6 +16863,7 @@ var init_node = __esm({
16440
16863
  init_digest_month_pipeline();
16441
16864
  init_tmux_runner();
16442
16865
  init_digest_fs();
16866
+ init_credentials();
16443
16867
  }
16444
16868
  });
16445
16869
 
@@ -16909,6 +17333,230 @@ var init_digest = __esm({
16909
17333
  }
16910
17334
  });
16911
17335
 
17336
+ // src/commands/menubar.ts
17337
+ var menubar_exports = {};
17338
+ __export(menubar_exports, {
17339
+ menubar: () => menubar
17340
+ });
17341
+ import { existsSync as existsSync16, mkdirSync as mkdirSync13, cpSync, rmSync as rmSync2, readdirSync as readdirSync4, writeFileSync as writeFileSync13, realpathSync as realpathSync5 } from "node:fs";
17342
+ import { homedir as homedir6 } from "node:os";
17343
+ import { join as join12, dirname as dirname12 } from "node:path";
17344
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
17345
+ import { execFile as execFile2 } from "node:child_process";
17346
+ import { promisify as promisify2 } from "node:util";
17347
+ function isMac2() {
17348
+ return process.platform === "darwin";
17349
+ }
17350
+ function bundledAppPath() {
17351
+ const here = dirname12(fileURLToPath3(import.meta.url));
17352
+ return join12(here, "..", "menubar", APP_BUNDLE);
17353
+ }
17354
+ function resolveScriptPath2() {
17355
+ const argv1 = process.argv[1] ?? "";
17356
+ try {
17357
+ return realpathSync5(argv1);
17358
+ } catch {
17359
+ return argv1;
17360
+ }
17361
+ }
17362
+ function writeCliLaunchFile() {
17363
+ try {
17364
+ writeFileSync13(
17365
+ cclensPath("cli-launch.json"),
17366
+ JSON.stringify({ node: process.execPath, script: resolveScriptPath2() }),
17367
+ "utf8"
17368
+ );
17369
+ } catch {
17370
+ }
17371
+ }
17372
+ function installedAppPath() {
17373
+ return join12(homedir6(), "Applications", APP_BUNDLE);
17374
+ }
17375
+ async function isRunning() {
17376
+ try {
17377
+ const { stdout } = await execFileAsync2("pgrep", ["-x", BUNDLE_EXEC]);
17378
+ return stdout.trim().length > 0;
17379
+ } catch {
17380
+ return false;
17381
+ }
17382
+ }
17383
+ async function launchApp(path7) {
17384
+ await execFileAsync2("open", ["-a", path7]);
17385
+ }
17386
+ async function quitApp() {
17387
+ await execFileAsync2("osascript", ["-e", `tell application "${BUNDLE_EXEC}" to quit`]).catch(
17388
+ () => {
17389
+ }
17390
+ );
17391
+ await execFileAsync2("pkill", ["-x", BUNDLE_EXEC]).catch(() => {
17392
+ });
17393
+ }
17394
+ async function setLoginItem(enabled, appPath) {
17395
+ const script = enabled ? `tell application "System Events" to make login item at end with properties {path:"${appPath}", hidden:false}` : `tell application "System Events" to delete login item "${APP_BUNDLE.replace(".app", "")}"`;
17396
+ await execFileAsync2("osascript", ["-e", script]).catch(() => {
17397
+ });
17398
+ }
17399
+ async function installMenubar(opts) {
17400
+ if (!isMac2()) {
17401
+ console.log(NON_MAC_MSG2);
17402
+ return;
17403
+ }
17404
+ const src = bundledAppPath();
17405
+ if (!existsSync16(src)) {
17406
+ console.error(
17407
+ `Bundled widget not found at ${src}.
17408
+ This install was probably built without the menubar artifact. Run \`node apps/menubar/build-app.sh && cp -R apps/menubar/dist/FleetlensMenubar.app packages/cli/menubar/\` and rebuild.`
17409
+ );
17410
+ process.exit(1);
17411
+ }
17412
+ const destDir = join12(homedir6(), "Applications");
17413
+ mkdirSync13(destDir, { recursive: true });
17414
+ const dest = installedAppPath();
17415
+ if (await isRunning()) {
17416
+ console.log("Stopping the running widget\u2026");
17417
+ await quitApp();
17418
+ await new Promise((r) => setTimeout(r, 400));
17419
+ }
17420
+ rmSync2(dest, { recursive: true, force: true });
17421
+ cpSync(src, dest, { recursive: true });
17422
+ writeCliLaunchFile();
17423
+ console.log(`Installed \u2192 ${dest}`);
17424
+ if (opts.login) {
17425
+ await setLoginItem(true, dest);
17426
+ console.log("Login item enabled (launches at login).");
17427
+ }
17428
+ if (opts.open) {
17429
+ await launchApp(dest);
17430
+ console.log("Opened \u2014 check your menu bar (gauge icon on the right side).");
17431
+ } else {
17432
+ console.log(`Launch with: open -a "${dest}"`);
17433
+ }
17434
+ }
17435
+ async function uninstallMenubar() {
17436
+ if (!isMac2()) {
17437
+ console.log(NON_MAC_MSG2);
17438
+ return;
17439
+ }
17440
+ await setLoginItem(false, installedAppPath());
17441
+ if (await isRunning()) {
17442
+ console.log("Quitting the running widget\u2026");
17443
+ await quitApp();
17444
+ await new Promise((r) => setTimeout(r, 300));
17445
+ }
17446
+ const dest = installedAppPath();
17447
+ if (existsSync16(dest)) {
17448
+ rmSync2(dest, { recursive: true, force: true });
17449
+ console.log(`Removed ${dest}`);
17450
+ } else {
17451
+ console.log("Widget is not installed \u2014 nothing to remove.");
17452
+ }
17453
+ console.log("Login item cleared (if it was set).");
17454
+ }
17455
+ async function openMenubar() {
17456
+ if (!isMac2()) {
17457
+ console.log(NON_MAC_MSG2);
17458
+ return;
17459
+ }
17460
+ const dest = installedAppPath();
17461
+ if (!existsSync16(dest)) {
17462
+ console.log("Widget not installed yet. Run `fleetlens menubar install` first.");
17463
+ return;
17464
+ }
17465
+ if (await isRunning()) {
17466
+ console.log("Widget is already running.");
17467
+ return;
17468
+ }
17469
+ await launchApp(dest);
17470
+ console.log("Opened \u2014 check your menu bar.");
17471
+ }
17472
+ async function statusMenubar() {
17473
+ if (!isMac2()) {
17474
+ console.log(`Menu bar widget: macOS-only (not available on ${process.platform}).`);
17475
+ return;
17476
+ }
17477
+ const dest = installedAppPath();
17478
+ const installed = existsSync16(dest);
17479
+ const running = await isRunning();
17480
+ const bundled = existsSync16(bundledAppPath());
17481
+ console.log(`Menu bar widget: ${installed ? "installed" : "not installed"}${installed ? ` (${dest})` : ""}`);
17482
+ console.log(`Running: ${running ? "yes" : "no"}`);
17483
+ console.log(`Bundled in CLI: ${bundled ? "yes" : "no \u2014 this build ships without the widget"}`);
17484
+ if (installed) {
17485
+ try {
17486
+ const apps = readdirSync4(join12(homedir6(), "Applications"));
17487
+ const ours = apps.find((a) => a === APP_BUNDLE);
17488
+ console.log(`App bundle: ${ours ? "present in ~/Applications" : "missing"}`);
17489
+ } catch {
17490
+ }
17491
+ }
17492
+ }
17493
+ async function menubar(args2) {
17494
+ const sub = args2[0] ?? "status";
17495
+ const flags = args2.slice(1);
17496
+ const noOpen = flags.includes("--no-open");
17497
+ const noLogin = flags.includes("--no-login");
17498
+ switch (sub) {
17499
+ case "install": {
17500
+ await installMenubar({ open: !noOpen, login: !noLogin });
17501
+ break;
17502
+ }
17503
+ case "uninstall":
17504
+ case "remove":
17505
+ await uninstallMenubar();
17506
+ break;
17507
+ case "open":
17508
+ case "start":
17509
+ await openMenubar();
17510
+ break;
17511
+ case "stop":
17512
+ case "quit":
17513
+ if (await isRunning()) {
17514
+ await quitApp();
17515
+ console.log("Stopped.");
17516
+ } else {
17517
+ console.log("Widget is not running.");
17518
+ }
17519
+ break;
17520
+ case "status":
17521
+ await statusMenubar();
17522
+ break;
17523
+ case "help":
17524
+ case "--help":
17525
+ case "-h":
17526
+ console.log(`Usage: fleetlens menubar <install|uninstall|open|stop|status>
17527
+
17528
+ install [--no-open] [--no-login] Copy the native macOS menu bar widget to
17529
+ ~/Applications and launch it. Registers a
17530
+ login item by default.
17531
+ uninstall Quit + remove the widget, clear login item.
17532
+ open Launch the installed widget.
17533
+ stop Quit the running widget.
17534
+ status Show install/running/bundled state.
17535
+
17536
+ The widget tails ~/.cclens/usage.jsonl and shows live Claude Code / Codex
17537
+ plan utilization in the menu bar. Click it for a popover, or "Dashboard" to
17538
+ open http://localhost:3321.`);
17539
+ break;
17540
+ default:
17541
+ console.error(
17542
+ `Unknown menubar subcommand: ${sub}
17543
+ Usage: fleetlens menubar <install|uninstall|open|stop|status>`
17544
+ );
17545
+ process.exit(1);
17546
+ }
17547
+ }
17548
+ var execFileAsync2, APP_BUNDLE, BUNDLE_EXEC, NON_MAC_MSG2;
17549
+ var init_menubar = __esm({
17550
+ "src/commands/menubar.ts"() {
17551
+ "use strict";
17552
+ init_fs();
17553
+ execFileAsync2 = promisify2(execFile2);
17554
+ APP_BUNDLE = "FleetlensMenubar.app";
17555
+ BUNDLE_EXEC = "FleetlensMenubar";
17556
+ NON_MAC_MSG2 = "The Fleetlens menu bar widget is a native macOS app and is macOS-only. On Linux/Windows, keep using the dashboard at the printed URL.";
17557
+ }
17558
+ });
17559
+
16912
17560
  // src/team/config.ts
16913
17561
  var init_config = __esm({
16914
17562
  "src/team/config.ts"() {
@@ -16918,8 +17566,8 @@ var init_config = __esm({
16918
17566
  });
16919
17567
 
16920
17568
  // src/perception/build-entries.ts
16921
- import { statSync as statSync3, readFileSync as readFileSync14 } from "node:fs";
16922
- import { basename, dirname as dirname10 } from "node:path";
17569
+ import { statSync as statSync4, readFileSync as readFileSync17 } from "node:fs";
17570
+ import { basename as basename2, dirname as dirname13 } from "node:path";
16923
17571
  function decodeProjectDirName(projectDir) {
16924
17572
  if (projectDir.startsWith("-")) {
16925
17573
  return "/" + projectDir.slice(1).replace(/-/g, "/");
@@ -16927,8 +17575,8 @@ function decodeProjectDirName(projectDir) {
16927
17575
  return projectDir.replace(/-/g, "/");
16928
17576
  }
16929
17577
  function buildEntriesForFile(filePath) {
16930
- const stat = statSync3(filePath);
16931
- const raw = readFileSync14(filePath, "utf8");
17578
+ const stat = statSync4(filePath);
17579
+ const raw = readFileSync17(filePath, "utf8");
16932
17580
  const rawLines = raw.split("\n").filter(Boolean).map((l) => {
16933
17581
  try {
16934
17582
  return JSON.parse(l);
@@ -16938,8 +17586,8 @@ function buildEntriesForFile(filePath) {
16938
17586
  }).filter((x) => x !== null);
16939
17587
  if (rawLines.length === 0) return null;
16940
17588
  const { meta, events } = parseTranscript(rawLines);
16941
- const fileName = basename(filePath);
16942
- const projectDir = basename(dirname10(filePath));
17589
+ const fileName = basename2(filePath);
17590
+ const projectDir = basename2(dirname13(filePath));
16943
17591
  const sessionId = fileName.replace(/\.jsonl$/, "");
16944
17592
  const sd = {
16945
17593
  ...meta,
@@ -17025,7 +17673,7 @@ function clipSegmentsToDay(segments, bounds) {
17025
17673
  }
17026
17674
  function filterSyncedSessions(sessions, syncProjects) {
17027
17675
  if (!syncProjects) return sessions;
17028
- return sessions.filter((s) => shouldSyncProject(projectRepoName(s.projectName), syncProjects));
17676
+ return sessions.filter((s) => shouldSyncProject(projectKey(s.projectName), syncProjects));
17029
17677
  }
17030
17678
  function sessionTouchesDay(s, day2) {
17031
17679
  const bounds = dayBoundsMs(day2);
@@ -17066,7 +17714,7 @@ function buildRichRollupBlocks(day2, daySessions, entries2, resolveRepo) {
17066
17714
  const projects = /* @__PURE__ */ new Map();
17067
17715
  for (const s of clippedSessions) {
17068
17716
  const canonical = canonicalProjectName(s.projectName);
17069
- const name = projectRepoName(s.projectName);
17717
+ const name = projectKey(s.projectName);
17070
17718
  const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
17071
17719
  const key = repo ?? name;
17072
17720
  const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
@@ -17109,7 +17757,7 @@ function buildRichRollupBlocks(day2, daySessions, entries2, resolveRepo) {
17109
17757
  commits += e.numbers.commits;
17110
17758
  pushes += e.numbers.pushes;
17111
17759
  if (e.github_repos?.length) {
17112
- const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
17760
+ const key = sessionRepo.get(e.session_id) ?? projectKey(e.project);
17113
17761
  const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
17114
17762
  for (const r of e.github_repos) set.add(r);
17115
17763
  projectRepos.set(key, set);
@@ -17193,7 +17841,7 @@ function buildIngestPayload(inputs) {
17193
17841
  return {
17194
17842
  ingestId: randomUUID2(),
17195
17843
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
17196
- cliVersion: "0.15.2",
17844
+ cliVersion: "0.15.4",
17197
17845
  ...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
17198
17846
  ...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
17199
17847
  ...richRollup ? { richRollup } : {},
@@ -17234,13 +17882,13 @@ var init_push = __esm({
17234
17882
  });
17235
17883
 
17236
17884
  // src/team/git-remote.ts
17237
- import { existsSync as existsSync13, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
17238
- import { dirname as dirname11, isAbsolute, join as join11, resolve } from "node:path";
17885
+ import { existsSync as existsSync17, readFileSync as readFileSync18, statSync as statSync5 } from "node:fs";
17886
+ import { dirname as dirname14, isAbsolute as isAbsolute2, join as join13, resolve as resolve2 } from "node:path";
17239
17887
  function parseGithubRemote(url) {
17240
17888
  const m = url.trim().match(GITHUB_URL_RE);
17241
17889
  return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
17242
17890
  }
17243
- function originUrl(configText) {
17891
+ function originUrl2(configText) {
17244
17892
  let inOrigin = false;
17245
17893
  for (const line of configText.split("\n")) {
17246
17894
  const t = line.trim();
@@ -17255,36 +17903,36 @@ function originUrl(configText) {
17255
17903
  return null;
17256
17904
  }
17257
17905
  function gitConfigPathFor(dir) {
17258
- const dotGit = join11(dir, ".git");
17259
- const st = statSync4(dotGit, { throwIfNoEntry: false });
17906
+ const dotGit = join13(dir, ".git");
17907
+ const st = statSync5(dotGit, { throwIfNoEntry: false });
17260
17908
  if (!st) return null;
17261
- if (st.isDirectory()) return join11(dotGit, "config");
17262
- const m = readFileSync15(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
17909
+ if (st.isDirectory()) return join13(dotGit, "config");
17910
+ const m = readFileSync18(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
17263
17911
  if (!m) return null;
17264
- const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
17265
- const commondirFile = join11(gd, "commondir");
17266
- if (existsSync13(commondirFile)) {
17267
- const common = readFileSync15(commondirFile, "utf8").trim();
17268
- return join11(isAbsolute(common) ? common : resolve(gd, common), "config");
17912
+ const gd = isAbsolute2(m[1]) ? m[1] : resolve2(dir, m[1]);
17913
+ const commondirFile = join13(gd, "commondir");
17914
+ if (existsSync17(commondirFile)) {
17915
+ const common = readFileSync18(commondirFile, "utf8").trim();
17916
+ return join13(isAbsolute2(common) ? common : resolve2(gd, common), "config");
17269
17917
  }
17270
- return join11(gd, "config");
17918
+ return join13(gd, "config");
17271
17919
  }
17272
17920
  function createRepoResolver() {
17273
17921
  const cache = /* @__PURE__ */ new Map();
17274
17922
  return (dir) => {
17275
- if (!dir || !isAbsolute(dir)) return null;
17923
+ if (!dir || !isAbsolute2(dir)) return null;
17276
17924
  const hit = cache.get(dir);
17277
17925
  if (hit !== void 0) return hit;
17278
17926
  let repo = null;
17279
17927
  try {
17280
- for (let d = dir; ; d = dirname11(d)) {
17928
+ for (let d = dir; ; d = dirname14(d)) {
17281
17929
  const config = gitConfigPathFor(d);
17282
- if (config && existsSync13(config)) {
17283
- const url = originUrl(readFileSync15(config, "utf8"));
17930
+ if (config && existsSync17(config)) {
17931
+ const url = originUrl2(readFileSync18(config, "utf8"));
17284
17932
  repo = url ? parseGithubRemote(url) : null;
17285
17933
  break;
17286
17934
  }
17287
- if (d === dirname11(d)) break;
17935
+ if (d === dirname14(d)) break;
17288
17936
  }
17289
17937
  } catch {
17290
17938
  repo = null;
@@ -17302,14 +17950,14 @@ var init_git_remote = __esm({
17302
17950
  });
17303
17951
 
17304
17952
  // src/team/queue.ts
17305
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, appendFileSync as appendFileSync4, statSync as statSync5, mkdirSync as mkdirSync11 } from "node:fs";
17306
- import { dirname as dirname12 } from "node:path";
17953
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync14, appendFileSync as appendFileSync5, statSync as statSync6, mkdirSync as mkdirSync14 } from "node:fs";
17954
+ import { dirname as dirname15 } from "node:path";
17307
17955
  function enqueuePayload(payload, queuePath = QUEUE_PATH) {
17308
- mkdirSync11(dirname12(queuePath), { recursive: true });
17956
+ mkdirSync14(dirname15(queuePath), { recursive: true });
17309
17957
  const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
17310
- appendFileSync4(queuePath, JSON.stringify(entry) + "\n", "utf8");
17958
+ appendFileSync5(queuePath, JSON.stringify(entry) + "\n", "utf8");
17311
17959
  try {
17312
- const { size } = statSync5(queuePath);
17960
+ const { size } = statSync6(queuePath);
17313
17961
  if (size > MAX_SIZE_BYTES) {
17314
17962
  pruneOldest(queuePath);
17315
17963
  }
@@ -17319,7 +17967,7 @@ function enqueuePayload(payload, queuePath = QUEUE_PATH) {
17319
17967
  function dequeuePayloads(queuePath = QUEUE_PATH) {
17320
17968
  let lines;
17321
17969
  try {
17322
- lines = readFileSync16(queuePath, "utf8").trim().split("\n").filter(Boolean);
17970
+ lines = readFileSync19(queuePath, "utf8").trim().split("\n").filter(Boolean);
17323
17971
  } catch {
17324
17972
  return [];
17325
17973
  }
@@ -17334,18 +17982,18 @@ function dequeuePayloads(queuePath = QUEUE_PATH) {
17334
17982
  } catch {
17335
17983
  }
17336
17984
  }
17337
- writeFileSync10(queuePath, "", "utf8");
17985
+ writeFileSync14(queuePath, "", "utf8");
17338
17986
  return valid.map((e) => e.payload);
17339
17987
  }
17340
17988
  function pruneOldest(queuePath) {
17341
17989
  let lines;
17342
17990
  try {
17343
- lines = readFileSync16(queuePath, "utf8").trim().split("\n").filter(Boolean);
17991
+ lines = readFileSync19(queuePath, "utf8").trim().split("\n").filter(Boolean);
17344
17992
  } catch {
17345
17993
  return;
17346
17994
  }
17347
17995
  const keep = lines.slice(Math.floor(lines.length / 2));
17348
- writeFileSync10(queuePath, keep.join("\n") + "\n", "utf8");
17996
+ writeFileSync14(queuePath, keep.join("\n") + "\n", "utf8");
17349
17997
  }
17350
17998
  var QUEUE_PATH, MAX_SIZE_BYTES, MAX_AGE_MS;
17351
17999
  var init_queue2 = __esm({
@@ -17359,8 +18007,8 @@ var init_queue2 = __esm({
17359
18007
  });
17360
18008
 
17361
18009
  // src/usage/profile.ts
17362
- import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
17363
- import { dirname as dirname13 } from "node:path";
18010
+ import { existsSync as existsSync18, mkdirSync as mkdirSync15, readFileSync as readFileSync20, writeFileSync as writeFileSync15 } from "node:fs";
18011
+ import { dirname as dirname16 } from "node:path";
17364
18012
  function mapRateLimitTier(rateLimitTier) {
17365
18013
  switch (rateLimitTier) {
17366
18014
  case "default_claude_pro":
@@ -17400,9 +18048,9 @@ async function fetchProfile(nowMs = Date.now()) {
17400
18048
  };
17401
18049
  }
17402
18050
  function readCachedProfile(cachePath) {
17403
- if (!existsSync14(cachePath)) return null;
18051
+ if (!existsSync18(cachePath)) return null;
17404
18052
  try {
17405
- const raw = JSON.parse(readFileSync17(cachePath, "utf8"));
18053
+ const raw = JSON.parse(readFileSync20(cachePath, "utf8"));
17406
18054
  if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
17407
18055
  return raw;
17408
18056
  } catch {
@@ -17410,8 +18058,8 @@ function readCachedProfile(cachePath) {
17410
18058
  }
17411
18059
  }
17412
18060
  function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
17413
- mkdirSync12(dirname13(cachePath), { recursive: true });
17414
- writeFileSync11(
18061
+ mkdirSync15(dirname16(cachePath), { recursive: true });
18062
+ writeFileSync15(
17415
18063
  cachePath,
17416
18064
  JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
17417
18065
  { mode: 384 }
@@ -17591,11 +18239,11 @@ var init_backfill = __esm({
17591
18239
  });
17592
18240
 
17593
18241
  // src/team/sync-log.ts
17594
- import { readFileSync as readFileSync18 } from "node:fs";
18242
+ import { readFileSync as readFileSync21 } from "node:fs";
17595
18243
  function readPendingSyncLog(watermark) {
17596
18244
  let raw;
17597
18245
  try {
17598
- raw = readFileSync18(cclensPath("daemon.log"), "utf8");
18246
+ raw = readFileSync21(cclensPath("daemon.log"), "utf8");
17599
18247
  } catch {
17600
18248
  return { lines: [] };
17601
18249
  }
@@ -17623,12 +18271,12 @@ var init_sync_log = __esm({
17623
18271
  });
17624
18272
 
17625
18273
  // src/team/last-push.ts
17626
- import { writeFileSync as writeFileSync12, unlinkSync as unlinkSync5, mkdirSync as mkdirSync13 } from "node:fs";
17627
- import { join as join12 } from "node:path";
18274
+ import { writeFileSync as writeFileSync16, unlinkSync as unlinkSync6, mkdirSync as mkdirSync16 } from "node:fs";
18275
+ import { join as join14 } from "node:path";
17628
18276
  function write(record, dir) {
17629
18277
  const d = dir ?? cclensHome();
17630
- mkdirSync13(d, { recursive: true });
17631
- writeFileSync12(join12(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
18278
+ mkdirSync16(d, { recursive: true });
18279
+ writeFileSync16(join14(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
17632
18280
  }
17633
18281
  function writeLastPushSuccess(payload, dir) {
17634
18282
  write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: true, payload }, dir);
@@ -17639,7 +18287,7 @@ function writeLastPushFailure(payload, error, dir) {
17639
18287
  function clearLastPush(dir) {
17640
18288
  const d = dir ?? cclensHome();
17641
18289
  try {
17642
- unlinkSync5(join12(d, LAST_PUSH_FILE));
18290
+ unlinkSync6(join14(d, LAST_PUSH_FILE));
17643
18291
  } catch {
17644
18292
  }
17645
18293
  }
@@ -17659,38 +18307,38 @@ __export(file_probe_exports, {
17659
18307
  probeArtifactSignals: () => probeArtifactSignals
17660
18308
  });
17661
18309
  import { createHash } from "node:crypto";
17662
- import { existsSync as existsSync15, readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
17663
- import { homedir as homedir5 } from "node:os";
17664
- import { join as join13, relative, resolve as resolve2, sep } from "node:path";
18310
+ import { existsSync as existsSync19, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
18311
+ import { homedir as homedir7 } from "node:os";
18312
+ import { join as join15, relative, resolve as resolve3, sep } from "node:path";
17665
18313
  import { execFileSync as execFileSync3 } from "node:child_process";
17666
18314
  function pathHash(relPath) {
17667
18315
  return createHash("sha256").update(relPath.split(sep).join("/")).digest("hex").slice(0, 32);
17668
18316
  }
17669
18317
  function defaultScanRoots(extraRoots = []) {
17670
18318
  const roots = [];
17671
- const home = join13(homedir5(), ".claude");
17672
- if (existsSync15(home)) roots.push({ root: home, label: "user" });
18319
+ const home = join15(homedir7(), ".claude");
18320
+ if (existsSync19(home)) roots.push({ root: home, label: "user" });
17673
18321
  for (const e of extraRoots) {
17674
- const r = resolve2(e, ".claude");
17675
- if (existsSync15(r) && r !== home) roots.push({ root: r, label: "project" });
18322
+ const r = resolve3(e, ".claude");
18323
+ if (existsSync19(r) && r !== home) roots.push({ root: r, label: "project" });
17676
18324
  }
17677
18325
  return roots;
17678
18326
  }
17679
18327
  function listMarkdownFiles(dir) {
17680
- if (!existsSync15(dir)) return [];
18328
+ if (!existsSync19(dir)) return [];
17681
18329
  const out = [];
17682
18330
  let entries2;
17683
18331
  try {
17684
- entries2 = readdirSync4(dir);
18332
+ entries2 = readdirSync5(dir);
17685
18333
  } catch {
17686
18334
  return [];
17687
18335
  }
17688
18336
  for (const name of entries2) {
17689
18337
  if (name.startsWith(".")) continue;
17690
- const full = join13(dir, name);
18338
+ const full = join15(dir, name);
17691
18339
  let stat;
17692
18340
  try {
17693
- stat = statSync6(full);
18341
+ stat = statSync7(full);
17694
18342
  } catch {
17695
18343
  continue;
17696
18344
  }
@@ -17707,19 +18355,19 @@ function firstSeenDateOf(file) {
17707
18355
  const out = execFileSync3(
17708
18356
  "git",
17709
18357
  ["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
17710
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname14(file) }
18358
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname17(file) }
17711
18359
  ).trim().split("\n").filter(Boolean);
17712
18360
  if (out.length > 0) return out[out.length - 1];
17713
18361
  } catch {
17714
18362
  }
17715
18363
  try {
17716
- const s = statSync6(file);
18364
+ const s = statSync7(file);
17717
18365
  return localDay(s.birthtime || s.mtime);
17718
18366
  } catch {
17719
18367
  return todayLocal();
17720
18368
  }
17721
18369
  }
17722
- function dirname14(p) {
18370
+ function dirname17(p) {
17723
18371
  const i = p.lastIndexOf(sep);
17724
18372
  return i < 0 ? "." : p.slice(0, i);
17725
18373
  }
@@ -17746,8 +18394,8 @@ function todayLocal() {
17746
18394
  function scanAuthored(roots, subdir, day2) {
17747
18395
  const out = [];
17748
18396
  for (const r of roots) {
17749
- const dir = join13(r.root, subdir);
17750
- if (!existsSync15(dir)) continue;
18397
+ const dir = join15(r.root, subdir);
18398
+ if (!existsSync19(dir)) continue;
17751
18399
  for (const f of listMarkdownFiles(dir)) {
17752
18400
  const firstSeen = firstSeenDateOf(f);
17753
18401
  if (firstSeen !== day2) continue;
@@ -17760,14 +18408,14 @@ function scanAuthored(roots, subdir, day2) {
17760
18408
  function scanEdited(roots, day2) {
17761
18409
  const out = [];
17762
18410
  for (const r of roots) {
17763
- const dir = join13(r.root, "skills");
17764
- if (!existsSync15(dir)) continue;
18411
+ const dir = join15(r.root, "skills");
18412
+ if (!existsSync19(dir)) continue;
17765
18413
  for (const f of listMarkdownFiles(dir)) {
17766
18414
  const firstSeen = firstSeenDateOf(f);
17767
18415
  if (firstSeen === day2) continue;
17768
18416
  let stat;
17769
18417
  try {
17770
- stat = statSync6(f);
18418
+ stat = statSync7(f);
17771
18419
  } catch {
17772
18420
  continue;
17773
18421
  }
@@ -17783,17 +18431,17 @@ function claudemdLineDelta(extraRoots, authorEmail, day2) {
17783
18431
  let delta = 0;
17784
18432
  const candidates = [];
17785
18433
  for (const e of extraRoots) {
17786
- const f = resolve2(e, "CLAUDE.md");
17787
- if (existsSync15(f)) candidates.push(f);
18434
+ const f = resolve3(e, "CLAUDE.md");
18435
+ if (existsSync19(f)) candidates.push(f);
17788
18436
  }
17789
- const homeClaudeMd = join13(homedir5(), "CLAUDE.md");
17790
- if (existsSync15(homeClaudeMd)) candidates.push(homeClaudeMd);
18437
+ const homeClaudeMd = join15(homedir7(), "CLAUDE.md");
18438
+ if (existsSync19(homeClaudeMd)) candidates.push(homeClaudeMd);
17791
18439
  for (const file of candidates) {
17792
18440
  try {
17793
18441
  const out = execFileSync3(
17794
18442
  "git",
17795
18443
  ["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
17796
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname14(file) }
18444
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname17(file) }
17797
18445
  );
17798
18446
  let currentDate = "";
17799
18447
  let currentEmail = "";
@@ -17857,8 +18505,8 @@ async function dispatchCommand(command2, config, log) {
17857
18505
  }
17858
18506
  }
17859
18507
  async function runActivityBackfill(command2, config, log) {
17860
- const { listSessions: listSessions2, shouldSyncProject: shouldSyncProject2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
17861
- const { toLocalDay: toLocalDay4, projectRepoName: projectRepoName2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
18508
+ const { listSessions: listSessions2, resolveProjectIdentity: resolveProjectIdentity2, shouldSyncProject: shouldSyncProject2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
18509
+ const { toLocalDay: toLocalDay4, projectKey: projectKey2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
17862
18510
  const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
17863
18511
  const days = command2.params.days;
17864
18512
  const todayMs = Date.now();
@@ -17892,7 +18540,7 @@ async function runActivityBackfill(command2, config, log) {
17892
18540
  const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
17893
18541
  let artifactSignals = null;
17894
18542
  try {
17895
- const cwdAllowed = shouldSyncProject2(projectRepoName2(process.cwd()), config.syncProjects);
18543
+ const cwdAllowed = shouldSyncProject2(projectKey2(resolveProjectIdentity2(process.cwd()).projectName), config.syncProjects);
17896
18544
  artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: cwdAllowed ? [process.cwd()] : [] });
17897
18545
  } catch {
17898
18546
  }
@@ -18044,7 +18692,7 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
18044
18692
  try {
18045
18693
  resetEnsuredSessions();
18046
18694
  const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
18047
- const { toLocalDay: toLocalDay4, projectRepoName: projectRepoName2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
18695
+ const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
18048
18696
  const today = toLocalDay4(Date.now());
18049
18697
  let patches = {};
18050
18698
  const persistConfig = (patch) => {
@@ -18231,7 +18879,7 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
18231
18879
  } : void 0;
18232
18880
  let artifactSignals = null;
18233
18881
  try {
18234
- const cwdAllowed = shouldSyncProject(projectRepoName2(process.cwd()), config.syncProjects);
18882
+ const cwdAllowed = shouldSyncProject(projectKey(resolveProjectIdentity(process.cwd()).projectName), config.syncProjects);
18235
18883
  artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: cwdAllowed ? [process.cwd()] : [] });
18236
18884
  } catch {
18237
18885
  }
@@ -18429,6 +19077,7 @@ var init_sync = __esm({
18429
19077
  init_last_push();
18430
19078
  init_commands();
18431
19079
  init_profile();
19080
+ init_dist();
18432
19081
  init_fs();
18433
19082
  inFlightCommands = /* @__PURE__ */ new Set();
18434
19083
  USAGE_LOG4 = cclensPath("usage.jsonl");
@@ -18443,10 +19092,10 @@ var daemon_log_exports = {};
18443
19092
  __export(daemon_log_exports, {
18444
19093
  appendDaemonLogLine: () => appendDaemonLogLine
18445
19094
  });
18446
- import { appendFileSync as appendFileSync5 } from "node:fs";
19095
+ import { appendFileSync as appendFileSync6 } from "node:fs";
18447
19096
  function appendDaemonLogLine(level, msg) {
18448
19097
  try {
18449
- appendFileSync5(
19098
+ appendFileSync6(
18450
19099
  cclensPath("daemon.log"),
18451
19100
  `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${msg}
18452
19101
  `,
@@ -18659,11 +19308,11 @@ var logs_exports = {};
18659
19308
  __export(logs_exports, {
18660
19309
  teamLogs: () => teamLogs
18661
19310
  });
18662
- import { readFileSync as readFileSync19 } from "node:fs";
19311
+ import { readFileSync as readFileSync22 } from "node:fs";
18663
19312
  async function teamLogs() {
18664
19313
  const logPath = cclensPath("daemon.log");
18665
19314
  try {
18666
- const lines = readFileSync19(logPath, "utf8").trim().split("\n");
19315
+ const lines = readFileSync22(logPath, "utf8").trim().split("\n");
18667
19316
  const teamLines = lines.filter((l) => l.includes("[sync] ") || l.includes("team "));
18668
19317
  if (teamLines.length === 0) {
18669
19318
  console.log("No team-related log entries found.");
@@ -18791,8 +19440,8 @@ __export(runs_exports, {
18791
19440
  runs: () => runs
18792
19441
  });
18793
19442
  import { execSync as execSync2 } from "node:child_process";
18794
- import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
18795
- import { join as join14 } from "node:path";
19443
+ import { existsSync as existsSync20, readFileSync as readFileSync23, readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
19444
+ import { join as join16 } from "node:path";
18796
19445
  async function runs(args2) {
18797
19446
  const json = args2.includes("--json");
18798
19447
  const watch = args2.includes("--watch");
@@ -18876,8 +19525,8 @@ function listActiveRuns() {
18876
19525
  return runs2.sort((a, b) => a.elapsed_s - b.elapsed_s);
18877
19526
  }
18878
19527
  function readSpend(sinceMs) {
18879
- if (!existsSync16(SPEND_PATH)) return [];
18880
- const lines = readFileSync20(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
19528
+ if (!existsSync20(SPEND_PATH)) return [];
19529
+ const lines = readFileSync23(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
18881
19530
  const out = [];
18882
19531
  for (const line of lines) {
18883
19532
  try {
@@ -18998,23 +19647,23 @@ function ageLabel(ms) {
18998
19647
  return `${Math.floor(ms / 864e5)}d`;
18999
19648
  }
19000
19649
  function listTraceFiles() {
19001
- if (!existsSync16(RUNS_DIR2)) return [];
19002
- const entries2 = readdirSync5(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
19650
+ if (!existsSync20(RUNS_DIR2)) return [];
19651
+ const entries2 = readdirSync6(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
19003
19652
  return entries2.map((f) => {
19004
- const path7 = join14(RUNS_DIR2, f);
19005
- const st = statSync7(path7);
19653
+ const path7 = join16(RUNS_DIR2, f);
19654
+ const st = statSync8(path7);
19006
19655
  return { runId: f.replace(/\.jsonl$/, ""), path: path7, mtimeMs: st.mtimeMs };
19007
19656
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
19008
19657
  }
19009
19658
  function resolveRunId(arg) {
19010
- if (!existsSync16(RUNS_DIR2)) return null;
19659
+ if (!existsSync20(RUNS_DIR2)) return null;
19011
19660
  if (arg === "latest") {
19012
19661
  const all2 = listTraceFiles();
19013
19662
  if (all2.length === 0) return null;
19014
19663
  return { runId: all2[0].runId, path: all2[0].path };
19015
19664
  }
19016
- const exact = join14(RUNS_DIR2, `${arg}.jsonl`);
19017
- if (existsSync16(exact)) return { runId: arg, path: exact };
19665
+ const exact = join16(RUNS_DIR2, `${arg}.jsonl`);
19666
+ if (existsSync20(exact)) return { runId: arg, path: exact };
19018
19667
  const all = listTraceFiles();
19019
19668
  const matches = all.filter((r) => r.runId.includes(arg));
19020
19669
  if (matches.length === 1) return { runId: matches[0].runId, path: matches[0].path };
@@ -19026,7 +19675,7 @@ function resolveRunId(arg) {
19026
19675
  return null;
19027
19676
  }
19028
19677
  function readTrace(path7) {
19029
- return readFileSync20(path7, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
19678
+ return readFileSync23(path7, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
19030
19679
  try {
19031
19680
  return JSON.parse(l);
19032
19681
  } catch {
@@ -19038,7 +19687,7 @@ async function inspect(arg, follow, json) {
19038
19687
  const r = resolveRunId(arg);
19039
19688
  if (!r) {
19040
19689
  console.error(`run not found: ${arg}`);
19041
- if (existsSync16(RUNS_DIR2)) {
19690
+ if (existsSync20(RUNS_DIR2)) {
19042
19691
  const all = listTraceFiles().slice(0, 5);
19043
19692
  if (all.length > 0) {
19044
19693
  console.error(`
@@ -19058,13 +19707,13 @@ recent runs:`);
19058
19707
  const events = readTrace(r.path);
19059
19708
  printTrace(r.runId, events);
19060
19709
  if (!follow) return;
19061
- let lastSize = statSync7(r.path).size;
19710
+ let lastSize = statSync8(r.path).size;
19062
19711
  let lastIdx = events.length;
19063
19712
  console.log("\n \u27F6 following \xB7 ^C to quit\n");
19064
19713
  process.on("SIGINT", () => process.exit(0));
19065
19714
  while (true) {
19066
19715
  await new Promise((res) => setTimeout(res, 500));
19067
- const st = statSync7(r.path);
19716
+ const st = statSync8(r.path);
19068
19717
  if (st.size === lastSize) continue;
19069
19718
  lastSize = st.size;
19070
19719
  const all = readTrace(r.path);
@@ -19200,6 +19849,11 @@ async function main() {
19200
19849
  await autostart2(args.slice(1));
19201
19850
  break;
19202
19851
  }
19852
+ case "menubar": {
19853
+ const { menubar: menubar2 } = await Promise.resolve().then(() => (init_menubar(), menubar_exports));
19854
+ await menubar2(args.slice(1));
19855
+ break;
19856
+ }
19203
19857
  case "team": {
19204
19858
  const { team: team2 } = await Promise.resolve().then(() => (init_team2(), team_exports));
19205
19859
  await team2(args.slice(1));
@@ -19213,7 +19867,7 @@ async function main() {
19213
19867
  case "version":
19214
19868
  case "--version":
19215
19869
  case "-v":
19216
- console.log(`fleetlens ${"0.15.2"}`);
19870
+ console.log(`fleetlens ${"0.15.4"}`);
19217
19871
  break;
19218
19872
  case "help":
19219
19873
  case "--help":
@@ -19240,6 +19894,7 @@ Advanced:
19240
19894
  start --no-daemon Start only the web server (no daemon)
19241
19895
  daemon <start|stop|status|logs> Manage the usage daemon by itself
19242
19896
  autostart <install|uninstall|status> Run the usage daemon at login (macOS launchd)
19897
+ menubar <install|uninstall|open|status> Native macOS menu bar widget (Claude/Codex/Z.ai usage)
19243
19898
 
19244
19899
  Team:
19245
19900
  team join <url> <token> Pair with a team server (browser setup)