@phnx-labs/agents-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/LICENSE +21 -0
  3. package/README.md +537 -0
  4. package/dist/commands/__tests__/sessions.test.d.ts +2 -0
  5. package/dist/commands/__tests__/sessions.test.d.ts.map +1 -0
  6. package/dist/commands/__tests__/sessions.test.js +636 -0
  7. package/dist/commands/__tests__/sessions.test.js.map +1 -0
  8. package/dist/commands/cloud.d.ts +3 -0
  9. package/dist/commands/cloud.d.ts.map +1 -0
  10. package/dist/commands/cloud.js +322 -0
  11. package/dist/commands/cloud.js.map +1 -0
  12. package/dist/commands/commands.d.ts +3 -0
  13. package/dist/commands/commands.d.ts.map +1 -0
  14. package/dist/commands/commands.js +628 -0
  15. package/dist/commands/commands.js.map +1 -0
  16. package/dist/commands/daemon.d.ts +3 -0
  17. package/dist/commands/daemon.d.ts.map +1 -0
  18. package/dist/commands/daemon.js +110 -0
  19. package/dist/commands/daemon.js.map +1 -0
  20. package/dist/commands/drive.d.ts +3 -0
  21. package/dist/commands/drive.d.ts.map +1 -0
  22. package/dist/commands/drive.js +166 -0
  23. package/dist/commands/drive.js.map +1 -0
  24. package/dist/commands/exec.d.ts +3 -0
  25. package/dist/commands/exec.d.ts.map +1 -0
  26. package/dist/commands/exec.js +241 -0
  27. package/dist/commands/exec.js.map +1 -0
  28. package/dist/commands/fork.d.ts +3 -0
  29. package/dist/commands/fork.d.ts.map +1 -0
  30. package/dist/commands/fork.js +139 -0
  31. package/dist/commands/fork.js.map +1 -0
  32. package/dist/commands/hooks.d.ts +3 -0
  33. package/dist/commands/hooks.d.ts.map +1 -0
  34. package/dist/commands/hooks.js +689 -0
  35. package/dist/commands/hooks.js.map +1 -0
  36. package/dist/commands/init.d.ts +6 -0
  37. package/dist/commands/init.d.ts.map +1 -0
  38. package/dist/commands/init.js +127 -0
  39. package/dist/commands/init.js.map +1 -0
  40. package/dist/commands/mcp.d.ts +3 -0
  41. package/dist/commands/mcp.d.ts.map +1 -0
  42. package/dist/commands/mcp.js +581 -0
  43. package/dist/commands/mcp.js.map +1 -0
  44. package/dist/commands/models.d.ts +3 -0
  45. package/dist/commands/models.d.ts.map +1 -0
  46. package/dist/commands/models.js +158 -0
  47. package/dist/commands/models.js.map +1 -0
  48. package/dist/commands/packages.d.ts +3 -0
  49. package/dist/commands/packages.d.ts.map +1 -0
  50. package/dist/commands/packages.js +541 -0
  51. package/dist/commands/packages.js.map +1 -0
  52. package/dist/commands/permissions.d.ts +3 -0
  53. package/dist/commands/permissions.d.ts.map +1 -0
  54. package/dist/commands/permissions.js +723 -0
  55. package/dist/commands/permissions.js.map +1 -0
  56. package/dist/commands/plugins.d.ts +3 -0
  57. package/dist/commands/plugins.d.ts.map +1 -0
  58. package/dist/commands/plugins.js +382 -0
  59. package/dist/commands/plugins.js.map +1 -0
  60. package/dist/commands/profiles.d.ts +3 -0
  61. package/dist/commands/profiles.d.ts.map +1 -0
  62. package/dist/commands/profiles.js +242 -0
  63. package/dist/commands/profiles.js.map +1 -0
  64. package/dist/commands/pty.d.ts +20 -0
  65. package/dist/commands/pty.d.ts.map +1 -0
  66. package/dist/commands/pty.js +389 -0
  67. package/dist/commands/pty.js.map +1 -0
  68. package/dist/commands/pull.d.ts +3 -0
  69. package/dist/commands/pull.d.ts.map +1 -0
  70. package/dist/commands/pull.js +448 -0
  71. package/dist/commands/pull.js.map +1 -0
  72. package/dist/commands/push.d.ts +3 -0
  73. package/dist/commands/push.d.ts.map +1 -0
  74. package/dist/commands/push.js +180 -0
  75. package/dist/commands/push.js.map +1 -0
  76. package/dist/commands/refresh-memory.d.ts +9 -0
  77. package/dist/commands/refresh-memory.d.ts.map +1 -0
  78. package/dist/commands/refresh-memory.js +45 -0
  79. package/dist/commands/refresh-memory.js.map +1 -0
  80. package/dist/commands/resource-view.d.ts +31 -0
  81. package/dist/commands/resource-view.d.ts.map +1 -0
  82. package/dist/commands/resource-view.js +183 -0
  83. package/dist/commands/resource-view.js.map +1 -0
  84. package/dist/commands/routines.d.ts +3 -0
  85. package/dist/commands/routines.d.ts.map +1 -0
  86. package/dist/commands/routines.js +579 -0
  87. package/dist/commands/routines.js.map +1 -0
  88. package/dist/commands/rules.d.ts +3 -0
  89. package/dist/commands/rules.d.ts.map +1 -0
  90. package/dist/commands/rules.js +488 -0
  91. package/dist/commands/rules.js.map +1 -0
  92. package/dist/commands/secrets.d.ts +3 -0
  93. package/dist/commands/secrets.d.ts.map +1 -0
  94. package/dist/commands/secrets.js +339 -0
  95. package/dist/commands/secrets.js.map +1 -0
  96. package/dist/commands/sessions-picker.d.ts +16 -0
  97. package/dist/commands/sessions-picker.d.ts.map +1 -0
  98. package/dist/commands/sessions-picker.js +256 -0
  99. package/dist/commands/sessions-picker.js.map +1 -0
  100. package/dist/commands/sessions.d.ts +16 -0
  101. package/dist/commands/sessions.d.ts.map +1 -0
  102. package/dist/commands/sessions.js +1077 -0
  103. package/dist/commands/sessions.js.map +1 -0
  104. package/dist/commands/skills.d.ts +3 -0
  105. package/dist/commands/skills.d.ts.map +1 -0
  106. package/dist/commands/skills.js +716 -0
  107. package/dist/commands/skills.js.map +1 -0
  108. package/dist/commands/status.d.ts +3 -0
  109. package/dist/commands/status.d.ts.map +1 -0
  110. package/dist/commands/status.js +19 -0
  111. package/dist/commands/status.js.map +1 -0
  112. package/dist/commands/subagents.d.ts +3 -0
  113. package/dist/commands/subagents.d.ts.map +1 -0
  114. package/dist/commands/subagents.js +350 -0
  115. package/dist/commands/subagents.js.map +1 -0
  116. package/dist/commands/sync.d.ts +3 -0
  117. package/dist/commands/sync.d.ts.map +1 -0
  118. package/dist/commands/sync.js +62 -0
  119. package/dist/commands/sync.js.map +1 -0
  120. package/dist/commands/teams-picker.d.ts +14 -0
  121. package/dist/commands/teams-picker.d.ts.map +1 -0
  122. package/dist/commands/teams-picker.js +278 -0
  123. package/dist/commands/teams-picker.js.map +1 -0
  124. package/dist/commands/teams.d.ts +3 -0
  125. package/dist/commands/teams.d.ts.map +1 -0
  126. package/dist/commands/teams.js +917 -0
  127. package/dist/commands/teams.js.map +1 -0
  128. package/dist/commands/utils.d.ts +39 -0
  129. package/dist/commands/utils.d.ts.map +1 -0
  130. package/dist/commands/utils.js +100 -0
  131. package/dist/commands/utils.js.map +1 -0
  132. package/dist/commands/versions.d.ts +3 -0
  133. package/dist/commands/versions.d.ts.map +1 -0
  134. package/dist/commands/versions.js +700 -0
  135. package/dist/commands/versions.js.map +1 -0
  136. package/dist/commands/view.d.ts +8 -0
  137. package/dist/commands/view.d.ts.map +1 -0
  138. package/dist/commands/view.js +626 -0
  139. package/dist/commands/view.js.map +1 -0
  140. package/dist/index.d.ts +3 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +484 -0
  143. package/dist/index.js.map +1 -0
  144. package/dist/lib/__tests__/bugfixes.test.d.ts +2 -0
  145. package/dist/lib/__tests__/bugfixes.test.d.ts.map +1 -0
  146. package/dist/lib/__tests__/bugfixes.test.js +192 -0
  147. package/dist/lib/__tests__/bugfixes.test.js.map +1 -0
  148. package/dist/lib/__tests__/exec.test.d.ts +2 -0
  149. package/dist/lib/__tests__/exec.test.d.ts.map +1 -0
  150. package/dist/lib/__tests__/exec.test.js +446 -0
  151. package/dist/lib/__tests__/exec.test.js.map +1 -0
  152. package/dist/lib/__tests__/git-sync.test.d.ts +2 -0
  153. package/dist/lib/__tests__/git-sync.test.d.ts.map +1 -0
  154. package/dist/lib/__tests__/git-sync.test.js +138 -0
  155. package/dist/lib/__tests__/git-sync.test.js.map +1 -0
  156. package/dist/lib/__tests__/hooks.test.d.ts +2 -0
  157. package/dist/lib/__tests__/hooks.test.d.ts.map +1 -0
  158. package/dist/lib/__tests__/hooks.test.js +203 -0
  159. package/dist/lib/__tests__/hooks.test.js.map +1 -0
  160. package/dist/lib/__tests__/memory-compile.test.d.ts +2 -0
  161. package/dist/lib/__tests__/memory-compile.test.d.ts.map +1 -0
  162. package/dist/lib/__tests__/memory-compile.test.js +95 -0
  163. package/dist/lib/__tests__/memory-compile.test.js.map +1 -0
  164. package/dist/lib/__tests__/models.test.d.ts +2 -0
  165. package/dist/lib/__tests__/models.test.d.ts.map +1 -0
  166. package/dist/lib/__tests__/models.test.js +239 -0
  167. package/dist/lib/__tests__/models.test.js.map +1 -0
  168. package/dist/lib/__tests__/rotate.test.d.ts +2 -0
  169. package/dist/lib/__tests__/rotate.test.d.ts.map +1 -0
  170. package/dist/lib/__tests__/rotate.test.js +80 -0
  171. package/dist/lib/__tests__/rotate.test.js.map +1 -0
  172. package/dist/lib/__tests__/secrets-bundles.test.d.ts +2 -0
  173. package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +1 -0
  174. package/dist/lib/__tests__/secrets-bundles.test.js +104 -0
  175. package/dist/lib/__tests__/secrets-bundles.test.js.map +1 -0
  176. package/dist/lib/__tests__/secrets.test.d.ts +2 -0
  177. package/dist/lib/__tests__/secrets.test.d.ts.map +1 -0
  178. package/dist/lib/__tests__/secrets.test.js +90 -0
  179. package/dist/lib/__tests__/secrets.test.js.map +1 -0
  180. package/dist/lib/__tests__/shims.test.d.ts +2 -0
  181. package/dist/lib/__tests__/shims.test.d.ts.map +1 -0
  182. package/dist/lib/__tests__/shims.test.js +39 -0
  183. package/dist/lib/__tests__/shims.test.js.map +1 -0
  184. package/dist/lib/__tests__/usage.test.d.ts +2 -0
  185. package/dist/lib/__tests__/usage.test.d.ts.map +1 -0
  186. package/dist/lib/__tests__/usage.test.js +220 -0
  187. package/dist/lib/__tests__/usage.test.js.map +1 -0
  188. package/dist/lib/__tests__/versions.test.d.ts +2 -0
  189. package/dist/lib/__tests__/versions.test.d.ts.map +1 -0
  190. package/dist/lib/__tests__/versions.test.js +63 -0
  191. package/dist/lib/__tests__/versions.test.js.map +1 -0
  192. package/dist/lib/agents.d.ts +107 -0
  193. package/dist/lib/agents.d.ts.map +1 -0
  194. package/dist/lib/agents.js +1096 -0
  195. package/dist/lib/agents.js.map +1 -0
  196. package/dist/lib/artifact-actions.d.ts +22 -0
  197. package/dist/lib/artifact-actions.d.ts.map +1 -0
  198. package/dist/lib/artifact-actions.js +55 -0
  199. package/dist/lib/artifact-actions.js.map +1 -0
  200. package/dist/lib/cloud/codex.d.ts +19 -0
  201. package/dist/lib/cloud/codex.d.ts.map +1 -0
  202. package/dist/lib/cloud/codex.js +210 -0
  203. package/dist/lib/cloud/codex.js.map +1 -0
  204. package/dist/lib/cloud/factory.d.ts +26 -0
  205. package/dist/lib/cloud/factory.d.ts.map +1 -0
  206. package/dist/lib/cloud/factory.js +37 -0
  207. package/dist/lib/cloud/factory.js.map +1 -0
  208. package/dist/lib/cloud/registry.d.ts +6 -0
  209. package/dist/lib/cloud/registry.d.ts.map +1 -0
  210. package/dist/lib/cloud/registry.js +56 -0
  211. package/dist/lib/cloud/registry.js.map +1 -0
  212. package/dist/lib/cloud/rush.d.ts +15 -0
  213. package/dist/lib/cloud/rush.d.ts.map +1 -0
  214. package/dist/lib/cloud/rush.js +185 -0
  215. package/dist/lib/cloud/rush.js.map +1 -0
  216. package/dist/lib/cloud/store.d.ts +10 -0
  217. package/dist/lib/cloud/store.d.ts.map +1 -0
  218. package/dist/lib/cloud/store.js +96 -0
  219. package/dist/lib/cloud/store.js.map +1 -0
  220. package/dist/lib/cloud/stream.d.ts +18 -0
  221. package/dist/lib/cloud/stream.d.ts.map +1 -0
  222. package/dist/lib/cloud/stream.js +138 -0
  223. package/dist/lib/cloud/stream.js.map +1 -0
  224. package/dist/lib/cloud/types.d.ts +60 -0
  225. package/dist/lib/cloud/types.d.ts.map +1 -0
  226. package/dist/lib/cloud/types.js +2 -0
  227. package/dist/lib/cloud/types.js.map +1 -0
  228. package/dist/lib/commands.d.ts +121 -0
  229. package/dist/lib/commands.d.ts.map +1 -0
  230. package/dist/lib/commands.js +499 -0
  231. package/dist/lib/commands.js.map +1 -0
  232. package/dist/lib/convert.d.ts +11 -0
  233. package/dist/lib/convert.d.ts.map +1 -0
  234. package/dist/lib/convert.js +45 -0
  235. package/dist/lib/convert.js.map +1 -0
  236. package/dist/lib/daemon.d.ts +22 -0
  237. package/dist/lib/daemon.d.ts.map +1 -0
  238. package/dist/lib/daemon.js +311 -0
  239. package/dist/lib/daemon.js.map +1 -0
  240. package/dist/lib/drive-sync.d.ts +28 -0
  241. package/dist/lib/drive-sync.d.ts.map +1 -0
  242. package/dist/lib/drive-sync.js +193 -0
  243. package/dist/lib/drive-sync.js.map +1 -0
  244. package/dist/lib/exec.d.ts +85 -0
  245. package/dist/lib/exec.d.ts.map +1 -0
  246. package/dist/lib/exec.js +423 -0
  247. package/dist/lib/exec.js.map +1 -0
  248. package/dist/lib/factory.d.ts +57 -0
  249. package/dist/lib/factory.d.ts.map +1 -0
  250. package/dist/lib/factory.js +110 -0
  251. package/dist/lib/factory.js.map +1 -0
  252. package/dist/lib/git.d.ts +146 -0
  253. package/dist/lib/git.d.ts.map +1 -0
  254. package/dist/lib/git.js +635 -0
  255. package/dist/lib/git.js.map +1 -0
  256. package/dist/lib/help.d.ts +3 -0
  257. package/dist/lib/help.d.ts.map +1 -0
  258. package/dist/lib/help.js +63 -0
  259. package/dist/lib/help.js.map +1 -0
  260. package/dist/lib/hooks.d.ts +116 -0
  261. package/dist/lib/hooks.d.ts.map +1 -0
  262. package/dist/lib/hooks.js +837 -0
  263. package/dist/lib/hooks.js.map +1 -0
  264. package/dist/lib/manifest.d.ts +8 -0
  265. package/dist/lib/manifest.d.ts.map +1 -0
  266. package/dist/lib/manifest.js +36 -0
  267. package/dist/lib/manifest.js.map +1 -0
  268. package/dist/lib/markdown.d.ts +5 -0
  269. package/dist/lib/markdown.d.ts.map +1 -0
  270. package/dist/lib/markdown.js +11 -0
  271. package/dist/lib/markdown.js.map +1 -0
  272. package/dist/lib/mcp.d.ts +64 -0
  273. package/dist/lib/mcp.d.ts.map +1 -0
  274. package/dist/lib/mcp.js +327 -0
  275. package/dist/lib/mcp.js.map +1 -0
  276. package/dist/lib/memory-compile.d.ts +56 -0
  277. package/dist/lib/memory-compile.d.ts.map +1 -0
  278. package/dist/lib/memory-compile.js +167 -0
  279. package/dist/lib/memory-compile.js.map +1 -0
  280. package/dist/lib/memory.d.ts +56 -0
  281. package/dist/lib/memory.d.ts.map +1 -0
  282. package/dist/lib/memory.js +267 -0
  283. package/dist/lib/memory.js.map +1 -0
  284. package/dist/lib/models.d.ts +91 -0
  285. package/dist/lib/models.d.ts.map +1 -0
  286. package/dist/lib/models.js +706 -0
  287. package/dist/lib/models.js.map +1 -0
  288. package/dist/lib/permissions.d.ts +204 -0
  289. package/dist/lib/permissions.d.ts.map +1 -0
  290. package/dist/lib/permissions.js +1022 -0
  291. package/dist/lib/permissions.js.map +1 -0
  292. package/dist/lib/picker.d.ts +17 -0
  293. package/dist/lib/picker.d.ts.map +1 -0
  294. package/dist/lib/picker.js +95 -0
  295. package/dist/lib/picker.js.map +1 -0
  296. package/dist/lib/plugins.d.ts +73 -0
  297. package/dist/lib/plugins.d.ts.map +1 -0
  298. package/dist/lib/plugins.js +549 -0
  299. package/dist/lib/plugins.js.map +1 -0
  300. package/dist/lib/profiles-keychain.d.ts +3 -0
  301. package/dist/lib/profiles-keychain.d.ts.map +1 -0
  302. package/dist/lib/profiles-keychain.js +10 -0
  303. package/dist/lib/profiles-keychain.js.map +1 -0
  304. package/dist/lib/profiles-presets.d.ts +15 -0
  305. package/dist/lib/profiles-presets.d.ts.map +1 -0
  306. package/dist/lib/profiles-presets.js +95 -0
  307. package/dist/lib/profiles-presets.js.map +1 -0
  308. package/dist/lib/profiles.d.ts +35 -0
  309. package/dist/lib/profiles.d.ts.map +1 -0
  310. package/dist/lib/profiles.js +123 -0
  311. package/dist/lib/profiles.js.map +1 -0
  312. package/dist/lib/pty-client.d.ts +22 -0
  313. package/dist/lib/pty-client.d.ts.map +1 -0
  314. package/dist/lib/pty-client.js +181 -0
  315. package/dist/lib/pty-client.js.map +1 -0
  316. package/dist/lib/pty-server.d.ts +16 -0
  317. package/dist/lib/pty-server.d.ts.map +1 -0
  318. package/dist/lib/pty-server.js +422 -0
  319. package/dist/lib/pty-server.js.map +1 -0
  320. package/dist/lib/registry.d.ts +28 -0
  321. package/dist/lib/registry.d.ts.map +1 -0
  322. package/dist/lib/registry.js +203 -0
  323. package/dist/lib/registry.js.map +1 -0
  324. package/dist/lib/resources.d.ts +50 -0
  325. package/dist/lib/resources.d.ts.map +1 -0
  326. package/dist/lib/resources.js +103 -0
  327. package/dist/lib/resources.js.map +1 -0
  328. package/dist/lib/rotate.d.ts +52 -0
  329. package/dist/lib/rotate.d.ts.map +1 -0
  330. package/dist/lib/rotate.js +87 -0
  331. package/dist/lib/rotate.js.map +1 -0
  332. package/dist/lib/routines.d.ts +70 -0
  333. package/dist/lib/routines.d.ts.map +1 -0
  334. package/dist/lib/routines.js +325 -0
  335. package/dist/lib/routines.js.map +1 -0
  336. package/dist/lib/runner.d.ts +12 -0
  337. package/dist/lib/runner.d.ts.map +1 -0
  338. package/dist/lib/runner.js +311 -0
  339. package/dist/lib/runner.js.map +1 -0
  340. package/dist/lib/sandbox.d.ts +10 -0
  341. package/dist/lib/sandbox.d.ts.map +1 -0
  342. package/dist/lib/sandbox.js +201 -0
  343. package/dist/lib/sandbox.js.map +1 -0
  344. package/dist/lib/scheduler.d.ts +18 -0
  345. package/dist/lib/scheduler.d.ts.map +1 -0
  346. package/dist/lib/scheduler.js +69 -0
  347. package/dist/lib/scheduler.js.map +1 -0
  348. package/dist/lib/secrets-bundles.d.ts +29 -0
  349. package/dist/lib/secrets-bundles.d.ts.map +1 -0
  350. package/dist/lib/secrets-bundles.js +168 -0
  351. package/dist/lib/secrets-bundles.js.map +1 -0
  352. package/dist/lib/secrets.d.ts +27 -0
  353. package/dist/lib/secrets.d.ts.map +1 -0
  354. package/dist/lib/secrets.js +127 -0
  355. package/dist/lib/secrets.js.map +1 -0
  356. package/dist/lib/session/__tests__/db.test.d.ts +2 -0
  357. package/dist/lib/session/__tests__/db.test.d.ts.map +1 -0
  358. package/dist/lib/session/__tests__/db.test.js +54 -0
  359. package/dist/lib/session/__tests__/db.test.js.map +1 -0
  360. package/dist/lib/session/__tests__/discover.test.d.ts +2 -0
  361. package/dist/lib/session/__tests__/discover.test.d.ts.map +1 -0
  362. package/dist/lib/session/__tests__/discover.test.js +63 -0
  363. package/dist/lib/session/__tests__/discover.test.js.map +1 -0
  364. package/dist/lib/session/__tests__/prompt.test.d.ts +2 -0
  365. package/dist/lib/session/__tests__/prompt.test.d.ts.map +1 -0
  366. package/dist/lib/session/__tests__/prompt.test.js +44 -0
  367. package/dist/lib/session/__tests__/prompt.test.js.map +1 -0
  368. package/dist/lib/session/__tests__/render.test.d.ts +2 -0
  369. package/dist/lib/session/__tests__/render.test.d.ts.map +1 -0
  370. package/dist/lib/session/__tests__/render.test.js +602 -0
  371. package/dist/lib/session/__tests__/render.test.js.map +1 -0
  372. package/dist/lib/session/artifacts.d.ts +5 -0
  373. package/dist/lib/session/artifacts.d.ts.map +1 -0
  374. package/dist/lib/session/artifacts.js +75 -0
  375. package/dist/lib/session/artifacts.js.map +1 -0
  376. package/dist/lib/session/db.d.ts +118 -0
  377. package/dist/lib/session/db.d.ts.map +1 -0
  378. package/dist/lib/session/db.js +576 -0
  379. package/dist/lib/session/db.js.map +1 -0
  380. package/dist/lib/session/discover.d.ts +60 -0
  381. package/dist/lib/session/discover.d.ts.map +1 -0
  382. package/dist/lib/session/discover.js +1272 -0
  383. package/dist/lib/session/discover.js.map +1 -0
  384. package/dist/lib/session/parse.d.ts +23 -0
  385. package/dist/lib/session/parse.d.ts.map +1 -0
  386. package/dist/lib/session/parse.js +650 -0
  387. package/dist/lib/session/parse.js.map +1 -0
  388. package/dist/lib/session/prompt.d.ts +4 -0
  389. package/dist/lib/session/prompt.d.ts.map +1 -0
  390. package/dist/lib/session/prompt.js +64 -0
  391. package/dist/lib/session/prompt.js.map +1 -0
  392. package/dist/lib/session/prompt.test.d.ts +2 -0
  393. package/dist/lib/session/prompt.test.d.ts.map +1 -0
  394. package/dist/lib/session/prompt.test.js +57 -0
  395. package/dist/lib/session/prompt.test.js.map +1 -0
  396. package/dist/lib/session/render.d.ts +90 -0
  397. package/dist/lib/session/render.d.ts.map +1 -0
  398. package/dist/lib/session/render.js +778 -0
  399. package/dist/lib/session/render.js.map +1 -0
  400. package/dist/lib/session/team-filter.d.ts +26 -0
  401. package/dist/lib/session/team-filter.d.ts.map +1 -0
  402. package/dist/lib/session/team-filter.js +66 -0
  403. package/dist/lib/session/team-filter.js.map +1 -0
  404. package/dist/lib/session/team-filter.test.d.ts +2 -0
  405. package/dist/lib/session/team-filter.test.d.ts.map +1 -0
  406. package/dist/lib/session/team-filter.test.js +157 -0
  407. package/dist/lib/session/team-filter.test.js.map +1 -0
  408. package/dist/lib/session/types.d.ts +69 -0
  409. package/dist/lib/session/types.d.ts.map +1 -0
  410. package/dist/lib/session/types.js +2 -0
  411. package/dist/lib/session/types.js.map +1 -0
  412. package/dist/lib/shims.d.ts +228 -0
  413. package/dist/lib/shims.d.ts.map +1 -0
  414. package/dist/lib/shims.js +1170 -0
  415. package/dist/lib/shims.js.map +1 -0
  416. package/dist/lib/skills.d.ts +134 -0
  417. package/dist/lib/skills.d.ts.map +1 -0
  418. package/dist/lib/skills.js +783 -0
  419. package/dist/lib/skills.js.map +1 -0
  420. package/dist/lib/state.d.ts +53 -0
  421. package/dist/lib/state.d.ts.map +1 -0
  422. package/dist/lib/state.js +299 -0
  423. package/dist/lib/state.js.map +1 -0
  424. package/dist/lib/subagents.d.ts +75 -0
  425. package/dist/lib/subagents.d.ts.map +1 -0
  426. package/dist/lib/subagents.js +402 -0
  427. package/dist/lib/subagents.js.map +1 -0
  428. package/dist/lib/teams/agents.d.ts +146 -0
  429. package/dist/lib/teams/agents.d.ts.map +1 -0
  430. package/dist/lib/teams/agents.js +1072 -0
  431. package/dist/lib/teams/agents.js.map +1 -0
  432. package/dist/lib/teams/api.d.ts +77 -0
  433. package/dist/lib/teams/api.d.ts.map +1 -0
  434. package/dist/lib/teams/api.js +229 -0
  435. package/dist/lib/teams/api.js.map +1 -0
  436. package/dist/lib/teams/cloud.d.ts +11 -0
  437. package/dist/lib/teams/cloud.d.ts.map +1 -0
  438. package/dist/lib/teams/cloud.js +169 -0
  439. package/dist/lib/teams/cloud.js.map +1 -0
  440. package/dist/lib/teams/debug.d.ts +2 -0
  441. package/dist/lib/teams/debug.d.ts.map +1 -0
  442. package/dist/lib/teams/debug.js +6 -0
  443. package/dist/lib/teams/debug.js.map +1 -0
  444. package/dist/lib/teams/file_ops.d.ts +6 -0
  445. package/dist/lib/teams/file_ops.d.ts.map +1 -0
  446. package/dist/lib/teams/file_ops.js +59 -0
  447. package/dist/lib/teams/file_ops.js.map +1 -0
  448. package/dist/lib/teams/parsers.d.ts +5 -0
  449. package/dist/lib/teams/parsers.d.ts.map +1 -0
  450. package/dist/lib/teams/parsers.js +826 -0
  451. package/dist/lib/teams/parsers.js.map +1 -0
  452. package/dist/lib/teams/persistence.d.ts +28 -0
  453. package/dist/lib/teams/persistence.d.ts.map +1 -0
  454. package/dist/lib/teams/persistence.js +289 -0
  455. package/dist/lib/teams/persistence.js.map +1 -0
  456. package/dist/lib/teams/ralph.d.ts +8 -0
  457. package/dist/lib/teams/ralph.d.ts.map +1 -0
  458. package/dist/lib/teams/ralph.js +59 -0
  459. package/dist/lib/teams/ralph.js.map +1 -0
  460. package/dist/lib/teams/registry.d.ts +11 -0
  461. package/dist/lib/teams/registry.d.ts.map +1 -0
  462. package/dist/lib/teams/registry.js +56 -0
  463. package/dist/lib/teams/registry.js.map +1 -0
  464. package/dist/lib/teams/summarizer.d.ts +58 -0
  465. package/dist/lib/teams/summarizer.d.ts.map +1 -0
  466. package/dist/lib/teams/summarizer.js +766 -0
  467. package/dist/lib/teams/summarizer.js.map +1 -0
  468. package/dist/lib/template.d.ts +24 -0
  469. package/dist/lib/template.d.ts.map +1 -0
  470. package/dist/lib/template.js +57 -0
  471. package/dist/lib/template.js.map +1 -0
  472. package/dist/lib/types.d.ts +282 -0
  473. package/dist/lib/types.d.ts.map +1 -0
  474. package/dist/lib/types.js +18 -0
  475. package/dist/lib/types.js.map +1 -0
  476. package/dist/lib/usage.d.ts +73 -0
  477. package/dist/lib/usage.d.ts.map +1 -0
  478. package/dist/lib/usage.js +623 -0
  479. package/dist/lib/usage.js.map +1 -0
  480. package/dist/lib/versions.d.ts +248 -0
  481. package/dist/lib/versions.d.ts.map +1 -0
  482. package/dist/lib/versions.js +1737 -0
  483. package/dist/lib/versions.js.map +1 -0
  484. package/package.json +82 -0
  485. package/scripts/postinstall.js +72 -0
  486. package/scripts/rebuild-sqlite.sh +46 -0
@@ -0,0 +1,1272 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import * as crypto from 'crypto';
5
+ import * as readline from 'readline';
6
+ import { execSync } from 'child_process';
7
+ import { AGENTS, getCliVersion } from '../agents.js';
8
+ import { getConfigSymlinkVersion } from '../shims.js';
9
+ import { SESSION_AGENTS } from './types.js';
10
+ import { extractSessionTopic } from './prompt.js';
11
+ import { getDB, getScanStampByPath, getScanStampsForPaths, recordScans, syncLabels, upsertSessionsBatch, querySessions, countSessions, ftsSearch, } from './db.js';
12
+ const HOME = os.homedir();
13
+ const AGENTS_DIR = path.join(HOME, '.agents');
14
+ /** How long OpenClaw channel/cron snapshots stay valid before we re-shell-out. */
15
+ const OPENCLAW_TTL_MS = 60_000;
16
+ let cachedOpenClawWorkspaces = null;
17
+ const cachedAgentVersions = new Map();
18
+ /**
19
+ * Discover sessions. Scans only files whose (mtime, size) have changed since
20
+ * the last run; everything else is served from the SQLite cache.
21
+ */
22
+ export async function discoverSessions(options) {
23
+ // Touch the DB so the schema is ready and connection is cached for this run.
24
+ getDB();
25
+ const agents = options?.agent ? [options.agent] : SESSION_AGENTS;
26
+ const onProgress = options?.onProgress;
27
+ // Incrementally re-scan changed files across all selected agents in parallel.
28
+ await Promise.all(agents.map(agent => {
29
+ switch (agent) {
30
+ case 'claude': return scanClaudeIncremental(onProgress);
31
+ case 'codex': return scanCodexIncremental(onProgress);
32
+ case 'gemini': return scanGeminiIncremental(onProgress);
33
+ case 'opencode': return scanOpenCodeIncremental();
34
+ case 'openclaw': return scanOpenClawIncremental();
35
+ }
36
+ }));
37
+ const sessions = querySessions(buildQueryOptions(options, agents, { includeLimit: true }));
38
+ return sessions;
39
+ }
40
+ /**
41
+ * Count sessions in scope without running an incremental scan. Assumes the DB
42
+ * is already fresh (typically true because `discoverSessions` ran first this
43
+ * turn). Uses the exact same filter shape as the discover query.
44
+ */
45
+ export function countSessionsInScope(options) {
46
+ const agents = options.agent ? [options.agent] : SESSION_AGENTS;
47
+ return countSessions(buildQueryOptions(options, agents, { includeLimit: false }));
48
+ }
49
+ function buildQueryOptions(options, agents, opts) {
50
+ const projectQuery = options?.project?.trim();
51
+ const sinceMs = options?.since ? parseTimeFilter(options.since) : undefined;
52
+ const untilMs = options?.until ? new Date(options.until).getTime() : undefined;
53
+ let cwdFilter;
54
+ let cwdPrefixFilter;
55
+ if (options?.cwdPrefix) {
56
+ cwdPrefixFilter = normalizeCwd(options.cwdPrefix);
57
+ }
58
+ else if (!options?.all && !projectQuery) {
59
+ cwdFilter = normalizeCwd(options?.cwd || process.cwd());
60
+ }
61
+ return {
62
+ agent: options?.agent,
63
+ agents: options?.agent ? undefined : agents,
64
+ version: options?.version,
65
+ cwd: cwdFilter,
66
+ cwdPrefix: cwdPrefixFilter,
67
+ project: projectQuery,
68
+ sinceMs,
69
+ untilMs: Number.isFinite(untilMs) ? untilMs : undefined,
70
+ limit: opts.includeLimit ? (options?.limit ?? 50) : undefined,
71
+ excludeTeamOrigin: options?.excludeTeamOrigin,
72
+ onlyTeamOrigin: options?.onlyTeamOrigin,
73
+ };
74
+ }
75
+ function normalizeCwd(cwd) {
76
+ if (!cwd)
77
+ return '';
78
+ const resolved = path.resolve(cwd);
79
+ return safeRealpathSync(resolved) || resolved;
80
+ }
81
+ /**
82
+ * Resolve a session by full or short ID. Accepts a pre-loaded session list
83
+ * (fast path from discoverSessions) and falls back to a DB lookup for the
84
+ * "I only know the id" case.
85
+ */
86
+ export function resolveSessionById(sessions, idQuery) {
87
+ const query = idQuery.toLowerCase();
88
+ const exact = sessions.filter(s => s.id.toLowerCase() === query || s.shortId.toLowerCase() === query);
89
+ if (exact.length > 0)
90
+ return exact;
91
+ return sessions.filter(s => s.id.toLowerCase().startsWith(query) || s.shortId.toLowerCase().startsWith(query));
92
+ }
93
+ // ---------------------------------------------------------------------------
94
+ // Content-index search (FTS5-backed)
95
+ // ---------------------------------------------------------------------------
96
+ /**
97
+ * Run an FTS5 search over the DB and intersect with the given session list,
98
+ * preserving the existing SessionMeta[] contract so sessions.ts is unchanged.
99
+ */
100
+ export function searchContentIndex(sessions, query) {
101
+ if (!query.trim())
102
+ return new Map();
103
+ const hits = ftsSearch(query);
104
+ if (hits.length === 0)
105
+ return new Map();
106
+ const byId = new Map(sessions.map(s => [s.id, s]));
107
+ const result = new Map();
108
+ for (const hit of hits) {
109
+ const session = byId.get(hit.sessionId);
110
+ if (!session)
111
+ continue;
112
+ result.set(hit.sessionId, {
113
+ ...session,
114
+ _matchedTerms: hit.matchedTerms,
115
+ _bm25Score: hit.score,
116
+ });
117
+ }
118
+ return result;
119
+ }
120
+ // ---------------------------------------------------------------------------
121
+ // Incremental scan orchestration
122
+ // ---------------------------------------------------------------------------
123
+ /**
124
+ * For a list of files, stat each, compare to the DB ledger, and return only
125
+ * the ones that need rescanning. One bulk DB query for the whole list.
126
+ */
127
+ function filterChangedFiles(filePaths) {
128
+ const ledger = getScanStampsForPaths(filePaths);
129
+ const out = [];
130
+ for (const filePath of filePaths) {
131
+ const stat = safeStatSync(filePath);
132
+ if (!stat)
133
+ continue;
134
+ const scan = {
135
+ fileMtimeMs: Math.floor(stat.mtimeMs),
136
+ fileSize: stat.size,
137
+ };
138
+ const prev = ledger.get(filePath);
139
+ if (prev && prev.fileMtimeMs === scan.fileMtimeMs && prev.fileSize === scan.fileSize) {
140
+ continue;
141
+ }
142
+ out.push({ filePath, scan });
143
+ }
144
+ return out;
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // Multi-version directory scanning
148
+ // ---------------------------------------------------------------------------
149
+ /**
150
+ * Collect all directories to scan for an agent's sessions. Deduplicates by
151
+ * realpath to avoid double-counting symlinked version homes.
152
+ */
153
+ export function getAgentSessionDirs(agent, subdir) {
154
+ const resolved = new Set();
155
+ const dirs = [];
156
+ function addDir(dir) {
157
+ if (!fs.existsSync(dir))
158
+ return;
159
+ const real = safeRealpathSync(dir);
160
+ const key = real || dir;
161
+ if (resolved.has(key))
162
+ return;
163
+ resolved.add(key);
164
+ dirs.push(dir);
165
+ }
166
+ addDir(path.join(HOME, `.${agent}`, subdir));
167
+ const versionsBase = path.join(AGENTS_DIR, 'versions', agent);
168
+ if (fs.existsSync(versionsBase)) {
169
+ try {
170
+ for (const version of fs.readdirSync(versionsBase)) {
171
+ addDir(path.join(versionsBase, version, 'home', `.${agent}`, subdir));
172
+ }
173
+ }
174
+ catch { /* dir unreadable */ }
175
+ }
176
+ const backupsBase = path.join(AGENTS_DIR, 'backups', agent);
177
+ if (fs.existsSync(backupsBase)) {
178
+ try {
179
+ for (const ts of fs.readdirSync(backupsBase)) {
180
+ addDir(path.join(backupsBase, ts, subdir));
181
+ }
182
+ }
183
+ catch { /* dir unreadable */ }
184
+ }
185
+ return dirs;
186
+ }
187
+ // ---------------------------------------------------------------------------
188
+ // Claude account info
189
+ // ---------------------------------------------------------------------------
190
+ let cachedClaudeAccount;
191
+ function getClaudeAccount() {
192
+ if (cachedClaudeAccount !== undefined)
193
+ return cachedClaudeAccount || undefined;
194
+ // Claude's active config lives at $CLAUDE_CONFIG_DIR/.claude.json; for our shim
195
+ // that's <version>/home/.claude/.claude.json. The home-level .claude.json is a
196
+ // legacy path used when Claude runs without CLAUDE_CONFIG_DIR set.
197
+ const candidates = [
198
+ path.join(HOME, '.claude', '.claude.json'),
199
+ path.join(HOME, '.claude.json'),
200
+ ];
201
+ const versionsBase = path.join(AGENTS_DIR, 'versions', 'claude');
202
+ if (fs.existsSync(versionsBase)) {
203
+ try {
204
+ for (const version of fs.readdirSync(versionsBase)) {
205
+ candidates.push(path.join(versionsBase, version, 'home', '.claude', '.claude.json'));
206
+ candidates.push(path.join(versionsBase, version, 'home', '.claude.json'));
207
+ }
208
+ }
209
+ catch { /* versions dir unreadable */ }
210
+ }
211
+ for (const candidate of candidates) {
212
+ try {
213
+ if (!fs.existsSync(candidate))
214
+ continue;
215
+ const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
216
+ const name = data.oauthAccount?.emailAddress || data.oauthAccount?.displayName;
217
+ if (name) {
218
+ cachedClaudeAccount = name;
219
+ return name;
220
+ }
221
+ }
222
+ catch { /* auth file unreadable or malformed */ }
223
+ }
224
+ cachedClaudeAccount = '';
225
+ return undefined;
226
+ }
227
+ // ---------------------------------------------------------------------------
228
+ // Claude
229
+ // ---------------------------------------------------------------------------
230
+ /**
231
+ * Build a map of Claude sessionId -> user-given label from ~/.claude/sessions/*.json.
232
+ * Each JSON has shape { pid, sessionId, cwd, startedAt, name?, ... }. The
233
+ * `name` field only exists if the user ran /rename in that session.
234
+ * For sessionId collisions (re-resume of the same session), prefer the most
235
+ * recent startedAt.
236
+ */
237
+ function buildClaudeLabelMap() {
238
+ const map = new Map();
239
+ const dir = path.join(HOME, '.claude', 'sessions');
240
+ if (!fs.existsSync(dir))
241
+ return new Map();
242
+ let files;
243
+ try {
244
+ files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
245
+ }
246
+ catch {
247
+ return new Map();
248
+ }
249
+ for (const f of files) {
250
+ try {
251
+ const data = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
252
+ if (typeof data.sessionId !== 'string')
253
+ continue;
254
+ const name = typeof data.name === 'string' && data.name.trim() ? data.name.trim() : null;
255
+ const startedAt = typeof data.startedAt === 'number' ? data.startedAt : 0;
256
+ const existing = map.get(data.sessionId);
257
+ if (!existing || startedAt > existing.startedAt) {
258
+ map.set(data.sessionId, { label: name, startedAt });
259
+ }
260
+ }
261
+ catch { /* unreadable session metadata file */ }
262
+ }
263
+ const out = new Map();
264
+ for (const [sid, { label }] of map)
265
+ out.set(sid, label);
266
+ return out;
267
+ }
268
+ async function scanClaudeIncremental(onProgress) {
269
+ const account = getClaudeAccount();
270
+ const labelMap = buildClaudeLabelMap();
271
+ const filePaths = [];
272
+ const seen = new Set();
273
+ for (const projectsDir of getAgentSessionDirs('claude', 'projects')) {
274
+ let projectDirs;
275
+ try {
276
+ projectDirs = fs.readdirSync(projectsDir);
277
+ }
278
+ catch {
279
+ continue;
280
+ }
281
+ for (const dirName of projectDirs) {
282
+ const dirPath = path.join(projectsDir, dirName);
283
+ const stat = safeStatSync(dirPath);
284
+ if (!stat?.isDirectory())
285
+ continue;
286
+ let files;
287
+ try {
288
+ files = fs.readdirSync(dirPath).filter(f => f.endsWith('.jsonl'));
289
+ }
290
+ catch {
291
+ continue;
292
+ }
293
+ for (const file of files) {
294
+ const sessionId = file.replace('.jsonl', '');
295
+ if (seen.has(sessionId))
296
+ continue;
297
+ seen.add(sessionId);
298
+ filePaths.push(path.join(dirPath, file));
299
+ }
300
+ }
301
+ }
302
+ const changed = filterChangedFiles(filePaths);
303
+ if (changed.length > 0) {
304
+ onProgress?.({ agent: 'claude', parsed: 0, total: changed.length });
305
+ const entries = [];
306
+ const touched = [];
307
+ let parsed = 0;
308
+ for (const { filePath, scan } of changed) {
309
+ try {
310
+ const sessionId = path.basename(filePath).replace('.jsonl', '');
311
+ const label = labelMap.get(sessionId) ?? undefined;
312
+ const result = await readClaudeMeta(filePath, sessionId, account, label);
313
+ if (result) {
314
+ entries.push({ meta: result.meta, content: result.content, scan });
315
+ }
316
+ else {
317
+ touched.push({ filePath, scan });
318
+ }
319
+ }
320
+ catch {
321
+ touched.push({ filePath, scan });
322
+ }
323
+ parsed++;
324
+ onProgress?.({ agent: 'claude', parsed, total: changed.length });
325
+ }
326
+ upsertSessionsBatch(entries);
327
+ recordScans(touched);
328
+ }
329
+ // Pick up /rename changes on sessions whose JSONL didn't change.
330
+ // Only bother for sessions we actually have a Claude row for.
331
+ if (labelMap.size > 0)
332
+ syncLabels(labelMap);
333
+ }
334
+ async function readClaudeMeta(filePath, sessionId, account, label) {
335
+ const scan = await scanClaudeSession(filePath);
336
+ const isTeamOrigin = scan.entrypoint === 'sdk-cli';
337
+ let meta;
338
+ if (scan.timestamp) {
339
+ const cwd = normalizeCwd(scan.cwd || '');
340
+ meta = {
341
+ id: sessionId,
342
+ shortId: sessionId.slice(0, 8),
343
+ agent: 'claude',
344
+ timestamp: scan.timestamp,
345
+ project: cwd ? path.basename(cwd) : undefined,
346
+ cwd,
347
+ filePath,
348
+ gitBranch: scan.gitBranch,
349
+ version: scan.version,
350
+ account,
351
+ topic: scan.topic,
352
+ label,
353
+ messageCount: scan.messageCount,
354
+ tokenCount: scan.tokenCount,
355
+ isTeamOrigin,
356
+ };
357
+ }
358
+ else {
359
+ const stat = safeStatSync(filePath);
360
+ meta = {
361
+ id: sessionId,
362
+ shortId: sessionId.slice(0, 8),
363
+ agent: 'claude',
364
+ timestamp: stat ? stat.mtime.toISOString() : new Date().toISOString(),
365
+ filePath,
366
+ account,
367
+ label,
368
+ messageCount: scan.messageCount,
369
+ tokenCount: scan.tokenCount,
370
+ topic: scan.topic,
371
+ isTeamOrigin,
372
+ };
373
+ }
374
+ return { meta, content: scan.contentText || '' };
375
+ }
376
+ // ---------------------------------------------------------------------------
377
+ // Codex account info
378
+ // ---------------------------------------------------------------------------
379
+ let cachedCodexAccount;
380
+ function getCodexAccount() {
381
+ if (cachedCodexAccount !== undefined)
382
+ return cachedCodexAccount || undefined;
383
+ const candidates = [path.join(HOME, '.codex', 'auth.json')];
384
+ const versionsBase = path.join(AGENTS_DIR, 'versions', 'codex');
385
+ if (fs.existsSync(versionsBase)) {
386
+ try {
387
+ for (const version of fs.readdirSync(versionsBase)) {
388
+ candidates.push(path.join(versionsBase, version, 'home', '.codex', 'auth.json'));
389
+ }
390
+ }
391
+ catch { /* versions dir unreadable */ }
392
+ }
393
+ for (const candidate of candidates) {
394
+ try {
395
+ if (!fs.existsSync(candidate))
396
+ continue;
397
+ const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
398
+ const idToken = data.tokens?.id_token;
399
+ if (idToken) {
400
+ const parts = idToken.split('.');
401
+ if (parts.length >= 2) {
402
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
403
+ if (payload.email) {
404
+ cachedCodexAccount = payload.email;
405
+ return payload.email;
406
+ }
407
+ }
408
+ }
409
+ }
410
+ catch { /* auth file or JWT malformed */ }
411
+ }
412
+ cachedCodexAccount = '';
413
+ return undefined;
414
+ }
415
+ // ---------------------------------------------------------------------------
416
+ // Codex
417
+ // ---------------------------------------------------------------------------
418
+ async function scanCodexIncremental(onProgress) {
419
+ const account = getCodexAccount();
420
+ const currentVersion = await getCurrentAgentVersion('codex');
421
+ const filePaths = [];
422
+ for (const sessionsDir of getAgentSessionDirs('codex', 'sessions')) {
423
+ // High limit: we only stat files here, parsing is gated by ledger match.
424
+ for (const fp of walkForFiles(sessionsDir, '.jsonl', 100_000)) {
425
+ filePaths.push(fp);
426
+ }
427
+ }
428
+ const changed = filterChangedFiles(filePaths);
429
+ if (changed.length === 0)
430
+ return;
431
+ onProgress?.({ agent: 'codex', parsed: 0, total: changed.length });
432
+ const entries = [];
433
+ const touched = [];
434
+ const seen = new Set();
435
+ let parsed = 0;
436
+ for (const { filePath, scan } of changed) {
437
+ try {
438
+ const result = await readCodexMeta(filePath, account, currentVersion);
439
+ if (result && !seen.has(result.meta.id)) {
440
+ seen.add(result.meta.id);
441
+ entries.push({ meta: result.meta, content: result.content, scan });
442
+ }
443
+ else {
444
+ touched.push({ filePath, scan });
445
+ }
446
+ }
447
+ catch {
448
+ touched.push({ filePath, scan });
449
+ }
450
+ parsed++;
451
+ onProgress?.({ agent: 'codex', parsed, total: changed.length });
452
+ }
453
+ upsertSessionsBatch(entries);
454
+ recordScans(touched);
455
+ }
456
+ async function readCodexMeta(filePath, account, currentVersion) {
457
+ const scan = await scanCodexSession(filePath);
458
+ const sessionId = scan.sessionId || '';
459
+ if (!sessionId)
460
+ return null;
461
+ const cwd = normalizeCwd(scan.cwd || '');
462
+ const meta = {
463
+ id: sessionId,
464
+ shortId: sessionId.slice(0, 8),
465
+ agent: 'codex',
466
+ timestamp: scan.timestamp || new Date().toISOString(),
467
+ project: cwd ? path.basename(cwd) : undefined,
468
+ cwd,
469
+ filePath,
470
+ gitBranch: scan.gitBranch,
471
+ version: resolveSessionVersion('codex', filePath, scan.version, currentVersion),
472
+ topic: scan.topic,
473
+ messageCount: scan.messageCount,
474
+ tokenCount: scan.tokenCount,
475
+ account,
476
+ };
477
+ return { meta, content: scan.contentText || '' };
478
+ }
479
+ // ---------------------------------------------------------------------------
480
+ // Gemini
481
+ // ---------------------------------------------------------------------------
482
+ async function scanGeminiIncremental(onProgress) {
483
+ const currentVersion = await getCurrentAgentVersion('gemini');
484
+ const projectMap = buildGeminiProjectMap();
485
+ const filePaths = [];
486
+ for (const tmpDir of getAgentSessionDirs('gemini', 'tmp')) {
487
+ let hashDirs;
488
+ try {
489
+ hashDirs = fs.readdirSync(tmpDir);
490
+ }
491
+ catch {
492
+ continue;
493
+ }
494
+ for (const hashDir of hashDirs) {
495
+ const chatsDir = path.join(tmpDir, hashDir, 'chats');
496
+ if (!fs.existsSync(chatsDir))
497
+ continue;
498
+ let chatFiles;
499
+ try {
500
+ chatFiles = fs.readdirSync(chatsDir).filter(f => f.endsWith('.json'));
501
+ }
502
+ catch {
503
+ continue;
504
+ }
505
+ for (const file of chatFiles) {
506
+ filePaths.push({ filePath: path.join(chatsDir, file), hashDir });
507
+ }
508
+ }
509
+ }
510
+ const changedPaths = filterChangedFiles(filePaths.map(f => f.filePath));
511
+ const changedByPath = new Map(changedPaths.map(c => [c.filePath, c.scan]));
512
+ if (changedByPath.size === 0)
513
+ return;
514
+ onProgress?.({ agent: 'gemini', parsed: 0, total: changedByPath.size });
515
+ const entries = [];
516
+ const touched = [];
517
+ const seen = new Set();
518
+ let parsed = 0;
519
+ for (const { filePath, hashDir } of filePaths) {
520
+ const scan = changedByPath.get(filePath);
521
+ if (!scan)
522
+ continue;
523
+ try {
524
+ const result = readGeminiMeta(filePath, hashDir, projectMap, currentVersion);
525
+ if (result && !seen.has(result.meta.id)) {
526
+ seen.add(result.meta.id);
527
+ entries.push({ meta: result.meta, content: result.content, scan });
528
+ }
529
+ else {
530
+ // Gemini file without a sessionId — record scan so we don't re-parse it next run.
531
+ touched.push({ filePath, scan });
532
+ }
533
+ }
534
+ catch {
535
+ touched.push({ filePath, scan });
536
+ }
537
+ parsed++;
538
+ onProgress?.({ agent: 'gemini', parsed, total: changedByPath.size });
539
+ }
540
+ upsertSessionsBatch(entries);
541
+ recordScans(touched);
542
+ }
543
+ function readGeminiMeta(filePath, hashDir, projectMap, currentVersion) {
544
+ let session;
545
+ try {
546
+ session = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
547
+ }
548
+ catch {
549
+ return null;
550
+ }
551
+ const sessionId = typeof session.sessionId === 'string' ? session.sessionId : '';
552
+ const startTime = typeof session.startTime === 'string' ? session.startTime : '';
553
+ const projectHash = typeof session.projectHash === 'string' ? session.projectHash : '';
554
+ const embeddedVersion = typeof session.version === 'string'
555
+ ? session.version
556
+ : typeof session.cliVersion === 'string'
557
+ ? session.cliVersion
558
+ : undefined;
559
+ if (!sessionId)
560
+ return null;
561
+ const projectInfo = projectMap.get(projectHash || hashDir);
562
+ const project = projectInfo?.name || hashDir.slice(0, 12);
563
+ const cwd = projectInfo?.path ? normalizeCwd(projectInfo.path) : undefined;
564
+ const stat = safeStatSync(filePath);
565
+ const messages = Array.isArray(session.messages) ? session.messages : [];
566
+ let topic;
567
+ let messageCount = 0;
568
+ let tokenCount = 0;
569
+ let sawTokenCount = false;
570
+ const userTexts = [];
571
+ for (const message of messages) {
572
+ if (message.type === 'user') {
573
+ const text = extractGeminiMessageText(message.content);
574
+ if (text) {
575
+ messageCount++;
576
+ userTexts.push(text);
577
+ if (!topic)
578
+ topic = extractSessionTopic(text);
579
+ }
580
+ }
581
+ else if (message.type === 'gemini') {
582
+ if (extractGeminiMessageText(message.content)) {
583
+ messageCount++;
584
+ }
585
+ }
586
+ const total = getGeminiTokenCount(message.tokens);
587
+ if (total !== null) {
588
+ tokenCount += total;
589
+ sawTokenCount = true;
590
+ }
591
+ }
592
+ const meta = {
593
+ id: sessionId,
594
+ shortId: sessionId.slice(0, 8),
595
+ agent: 'gemini',
596
+ timestamp: startTime || (stat ? stat.mtime.toISOString() : new Date().toISOString()),
597
+ project,
598
+ cwd,
599
+ filePath,
600
+ version: resolveSessionVersion('gemini', filePath, embeddedVersion, currentVersion),
601
+ topic,
602
+ messageCount,
603
+ tokenCount: sawTokenCount ? tokenCount : undefined,
604
+ };
605
+ return { meta, content: userTexts.join('\n') };
606
+ }
607
+ function buildGeminiProjectMap() {
608
+ const map = new Map();
609
+ const projectsJsonPath = path.join(HOME, '.gemini', 'projects.json');
610
+ if (fs.existsSync(projectsJsonPath)) {
611
+ try {
612
+ const data = JSON.parse(fs.readFileSync(projectsJsonPath, 'utf-8'));
613
+ const projects = data.projects;
614
+ if (typeof projects === 'object' && projects !== null) {
615
+ if (Array.isArray(projects)) {
616
+ for (const p of projects) {
617
+ if (typeof p === 'string') {
618
+ const hash = sha256(p);
619
+ map.set(hash, { name: path.basename(p), path: p });
620
+ map.set(p, { name: path.basename(p), path: p });
621
+ }
622
+ }
623
+ }
624
+ else {
625
+ for (const [p, name] of Object.entries(projects)) {
626
+ const hash = sha256(p);
627
+ map.set(hash, { name: String(name), path: p });
628
+ }
629
+ }
630
+ }
631
+ }
632
+ catch { /* projects.json missing or malformed */ }
633
+ }
634
+ const historyDir = path.join(HOME, '.gemini', 'history');
635
+ if (fs.existsSync(historyDir)) {
636
+ try {
637
+ for (const name of fs.readdirSync(historyDir)) {
638
+ const rootFile = path.join(historyDir, name, '.project_root');
639
+ if (fs.existsSync(rootFile)) {
640
+ try {
641
+ const projectPath = fs.readFileSync(rootFile, 'utf-8').trim();
642
+ if (projectPath) {
643
+ const hash = sha256(projectPath);
644
+ map.set(hash, { name, path: projectPath });
645
+ }
646
+ }
647
+ catch { /* history entry unreadable */ }
648
+ }
649
+ }
650
+ }
651
+ catch { /* history entry unreadable */ }
652
+ }
653
+ return map;
654
+ }
655
+ // ---------------------------------------------------------------------------
656
+ // OpenCode
657
+ // ---------------------------------------------------------------------------
658
+ const OPENCODE_DB = path.join(HOME, '.local', 'share', 'opencode', 'opencode.db');
659
+ let cachedOpenCodeAccount;
660
+ function getOpenCodeAccount() {
661
+ if (cachedOpenCodeAccount !== undefined)
662
+ return cachedOpenCodeAccount || undefined;
663
+ try {
664
+ if (fs.existsSync(OPENCODE_DB)) {
665
+ const out = execSync(`sqlite3 "${OPENCODE_DB}" "SELECT email FROM control_account WHERE active=1 LIMIT 1;"`, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
666
+ if (out) {
667
+ cachedOpenCodeAccount = out;
668
+ return out;
669
+ }
670
+ }
671
+ }
672
+ catch { /* sqlite3 unavailable or DB locked */ }
673
+ cachedOpenCodeAccount = '';
674
+ return undefined;
675
+ }
676
+ async function scanOpenCodeIncremental() {
677
+ if (!fs.existsSync(OPENCODE_DB))
678
+ return;
679
+ const stat = safeStatSync(OPENCODE_DB);
680
+ if (!stat)
681
+ return;
682
+ // OpenCode is one big DB; we use its mtime/size as the ledger for the
683
+ // entire fleet of OpenCode sessions.
684
+ const currentScan = {
685
+ fileMtimeMs: Math.floor(stat.mtimeMs),
686
+ fileSize: stat.size,
687
+ };
688
+ const prev = getScanStampByPath(OPENCODE_DB);
689
+ if (prev && prev.fileMtimeMs === currentScan.fileMtimeMs && prev.fileSize === currentScan.fileSize) {
690
+ return;
691
+ }
692
+ const account = getOpenCodeAccount();
693
+ const currentVersion = await getCurrentAgentVersion('opencode');
694
+ try {
695
+ const query = `
696
+ SELECT
697
+ s.id,
698
+ s.title,
699
+ s.directory,
700
+ s.version,
701
+ s.time_created,
702
+ COALESCE(stats.message_count, 0),
703
+ stats.token_count,
704
+ COALESCE(stats.has_token_data, 0)
705
+ FROM session s
706
+ LEFT JOIN (
707
+ SELECT
708
+ session_id,
709
+ COUNT(*) AS message_count,
710
+ SUM(
711
+ COALESCE(json_extract(data, '$.tokens.input'), 0) +
712
+ COALESCE(json_extract(data, '$.tokens.output'), 0) +
713
+ COALESCE(json_extract(data, '$.tokens.reasoning'), 0) +
714
+ COALESCE(json_extract(data, '$.tokens.cache.read'), 0) +
715
+ COALESCE(json_extract(data, '$.tokens.cache.write'), 0)
716
+ ) AS token_count,
717
+ MAX(CASE WHEN json_type(data, '$.tokens') IS NOT NULL THEN 1 ELSE 0 END) AS has_token_data
718
+ FROM message
719
+ GROUP BY session_id
720
+ ) stats ON stats.session_id = s.id
721
+ WHERE s.parent_id IS NULL
722
+ ORDER BY time_created DESC
723
+ LIMIT 1000;
724
+ `.replace(/\n/g, ' ');
725
+ const out = execSync(`sqlite3 -separator '|||' "${OPENCODE_DB}"`, { encoding: 'utf-8', input: query, stdio: ['pipe', 'pipe', 'ignore'], timeout: 5000 });
726
+ const entries = [];
727
+ for (const line of out.split('\n')) {
728
+ if (!line.trim())
729
+ continue;
730
+ const [id, title, directory, version, timeCreatedStr, messageCountStr, tokenCountStr, hasTokenDataStr] = line.split('|||');
731
+ if (!id)
732
+ continue;
733
+ const timeCreated = parseInt(timeCreatedStr, 10);
734
+ const messageCount = parseInt(messageCountStr, 10);
735
+ const tokenCount = parseInt(tokenCountStr, 10);
736
+ const hasTokenData = hasTokenDataStr === '1';
737
+ const timestamp = isNaN(timeCreated) ? new Date().toISOString() : new Date(timeCreated).toISOString();
738
+ const topic = title || undefined;
739
+ const meta = {
740
+ id,
741
+ shortId: id.replace(/^ses_/, '').slice(0, 8),
742
+ agent: 'opencode',
743
+ timestamp,
744
+ project: directory ? path.basename(directory) : undefined,
745
+ cwd: directory ? normalizeCwd(directory) : undefined,
746
+ filePath: `${OPENCODE_DB}#${id}`,
747
+ version: resolveSessionVersion('opencode', OPENCODE_DB, version || undefined, currentVersion),
748
+ account,
749
+ topic,
750
+ messageCount: Number.isNaN(messageCount) ? undefined : messageCount,
751
+ tokenCount: hasTokenData && !Number.isNaN(tokenCount) ? tokenCount : undefined,
752
+ };
753
+ entries.push({ meta, content: topic || '', scan: currentScan });
754
+ }
755
+ upsertSessionsBatch(entries);
756
+ // Stamp the OpenCode DB itself so we can short-circuit on the next run.
757
+ recordScans([{ filePath: OPENCODE_DB, scan: currentScan }]);
758
+ }
759
+ catch (err) {
760
+ if (process.stderr.isTTY) {
761
+ console.error(`Warning: Could not query OpenCode sessions: ${err.message}`);
762
+ }
763
+ }
764
+ }
765
+ // ---------------------------------------------------------------------------
766
+ // OpenClaw
767
+ // ---------------------------------------------------------------------------
768
+ async function scanOpenClawIncremental() {
769
+ // Check if openclaw is installed — silently skip if not.
770
+ try {
771
+ execSync('which openclaw', { stdio: 'ignore' });
772
+ }
773
+ catch {
774
+ return;
775
+ }
776
+ // TTL cache: skip subprocess calls if we scanned recently. Stored in the
777
+ // meta table so we skip even when no channels/cron exist to produce rows.
778
+ const db = getDB();
779
+ const row = db.prepare(`SELECT value FROM meta WHERE key = 'openclaw_last_scan_ms'`).get();
780
+ const lastScanMs = row ? parseInt(row.value, 10) : 0;
781
+ if (lastScanMs && Date.now() - lastScanMs < OPENCLAW_TTL_MS) {
782
+ return;
783
+ }
784
+ const currentVersion = await getCurrentAgentVersion('openclaw');
785
+ const now = Date.now();
786
+ const scan = { fileMtimeMs: now, fileSize: 0 };
787
+ const entries = [];
788
+ try {
789
+ const output = execSync('openclaw channels status', {
790
+ encoding: 'utf-8',
791
+ stdio: ['ignore', 'pipe', 'ignore'],
792
+ });
793
+ for (const line of output.split('\n')) {
794
+ const match = line.match(/^-\s+\w+\s+(\S+)\s+\((\w+)\):\s*(.+)/);
795
+ if (!match)
796
+ continue;
797
+ const [, agentId, name, statusStr] = match;
798
+ if (!statusStr.includes('running'))
799
+ continue;
800
+ entries.push({
801
+ meta: {
802
+ id: `openclaw-${agentId}`,
803
+ shortId: agentId.slice(0, 8),
804
+ agent: 'openclaw',
805
+ timestamp: new Date().toISOString(),
806
+ project: name,
807
+ cwd: getOpenClawSessionCwd(agentId),
808
+ version: currentVersion,
809
+ filePath: '',
810
+ },
811
+ content: `${name} ${agentId}`,
812
+ scan,
813
+ });
814
+ }
815
+ }
816
+ catch {
817
+ /* channels command failed */
818
+ }
819
+ try {
820
+ const output = execSync('openclaw cron list', {
821
+ encoding: 'utf-8',
822
+ stdio: ['ignore', 'pipe', 'ignore'],
823
+ });
824
+ const lines = output.split('\n');
825
+ for (let i = 1; i < lines.length; i++) {
826
+ const line = lines[i].trim();
827
+ if (!line)
828
+ continue;
829
+ const headMatch = line.match(/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\s+(\S+)/);
830
+ if (!headMatch)
831
+ continue;
832
+ const jobId = headMatch[1];
833
+ const jobName = headMatch[2];
834
+ const rest = line.slice(headMatch[0].length).trim();
835
+ const cols = rest.split(/\s{2,}/);
836
+ const agentId = cols[4] || '';
837
+ entries.push({
838
+ meta: {
839
+ id: `openclaw-cron-${jobId}`,
840
+ shortId: jobId.slice(0, 8),
841
+ agent: 'openclaw',
842
+ timestamp: new Date().toISOString(),
843
+ project: `${jobName} (${agentId || 'unknown'})`,
844
+ cwd: getOpenClawSessionCwd(agentId),
845
+ version: currentVersion,
846
+ filePath: '',
847
+ },
848
+ content: `${jobName} ${agentId}`,
849
+ scan,
850
+ });
851
+ }
852
+ }
853
+ catch {
854
+ /* cron command failed */
855
+ }
856
+ upsertSessionsBatch(entries);
857
+ db.prepare(`INSERT OR REPLACE INTO meta (key, value) VALUES ('openclaw_last_scan_ms', ?)`).run(String(Date.now()));
858
+ }
859
+ async function scanClaudeSession(filePath) {
860
+ const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
861
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
862
+ let timestamp;
863
+ let cwd;
864
+ let gitBranch;
865
+ let version;
866
+ let topic;
867
+ let entrypoint;
868
+ let messageCount = 0;
869
+ let tokenCount = 0;
870
+ let sawTokenCount = false;
871
+ const seenAssistantIds = new Set();
872
+ const userTexts = [];
873
+ try {
874
+ for await (const line of rl) {
875
+ if (!line.trim())
876
+ continue;
877
+ let parsed;
878
+ try {
879
+ parsed = JSON.parse(line);
880
+ }
881
+ catch {
882
+ continue;
883
+ }
884
+ // entrypoint ships on the first envelope event (attachment/user/assistant)
885
+ // and is the clean structural signal for "was this a team spawn?"
886
+ if (!entrypoint && typeof parsed.entrypoint === 'string') {
887
+ entrypoint = parsed.entrypoint;
888
+ }
889
+ if (!timestamp && (parsed.type === 'user' || parsed.type === 'assistant') && parsed.timestamp) {
890
+ timestamp = parsed.timestamp;
891
+ cwd = parsed.cwd || '';
892
+ gitBranch = parsed.gitBranch || undefined;
893
+ version = parsed.version || undefined;
894
+ }
895
+ if (parsed.type === 'user') {
896
+ const text = extractClaudeUserText(parsed);
897
+ if (text) {
898
+ messageCount++;
899
+ userTexts.push(text);
900
+ if (!topic)
901
+ topic = extractSessionTopic(text);
902
+ }
903
+ continue;
904
+ }
905
+ if (parsed.type !== 'assistant')
906
+ continue;
907
+ const assistantId = typeof parsed.message?.id === 'string'
908
+ ? parsed.message.id
909
+ : typeof parsed.uuid === 'string'
910
+ ? parsed.uuid
911
+ : undefined;
912
+ const logicalId = assistantId || `${parsed.timestamp || ''}:${seenAssistantIds.size}`;
913
+ if (seenAssistantIds.has(logicalId))
914
+ continue;
915
+ seenAssistantIds.add(logicalId);
916
+ messageCount++;
917
+ const usage = getClaudeUsageTotal(parsed.message?.usage || parsed.usage);
918
+ if (usage !== null) {
919
+ tokenCount += usage;
920
+ sawTokenCount = true;
921
+ }
922
+ }
923
+ }
924
+ finally {
925
+ rl.close();
926
+ stream.destroy();
927
+ }
928
+ return {
929
+ timestamp,
930
+ cwd,
931
+ gitBranch,
932
+ version,
933
+ topic,
934
+ entrypoint,
935
+ messageCount,
936
+ tokenCount: sawTokenCount ? tokenCount : undefined,
937
+ contentText: userTexts.length > 0 ? userTexts.join('\n') : undefined,
938
+ };
939
+ }
940
+ async function scanCodexSession(filePath) {
941
+ const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
942
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
943
+ let sessionId;
944
+ let timestamp;
945
+ let cwd;
946
+ let gitBranch;
947
+ let version;
948
+ let topic;
949
+ let messageCount = 0;
950
+ let tokenCount;
951
+ const userTexts = [];
952
+ try {
953
+ for await (const line of rl) {
954
+ if (!line.trim())
955
+ continue;
956
+ let parsed;
957
+ try {
958
+ parsed = JSON.parse(line);
959
+ }
960
+ catch {
961
+ continue;
962
+ }
963
+ if (parsed.type === 'session_meta') {
964
+ const payload = parsed.payload || {};
965
+ sessionId = payload.id || sessionId;
966
+ timestamp = payload.timestamp || parsed.timestamp || timestamp;
967
+ cwd = payload.cwd || cwd;
968
+ gitBranch = payload.git?.branch || gitBranch;
969
+ version = payload.cli_version || payload.version || version;
970
+ continue;
971
+ }
972
+ if (parsed.type === 'response_item' && parsed.payload?.type === 'message') {
973
+ const role = parsed.payload.role === 'user' || parsed.payload.role === 'developer'
974
+ ? 'user'
975
+ : 'assistant';
976
+ const text = extractCodexMessageText(parsed.payload.content, role);
977
+ if (!text)
978
+ continue;
979
+ messageCount++;
980
+ if (role === 'user') {
981
+ userTexts.push(text);
982
+ if (!topic)
983
+ topic = extractSessionTopic(text);
984
+ }
985
+ continue;
986
+ }
987
+ if (parsed.type === 'event_msg' && parsed.payload?.type === 'token_count') {
988
+ const total = getCodexTokenCount(parsed.payload.info?.total_token_usage);
989
+ if (total !== null)
990
+ tokenCount = total;
991
+ }
992
+ }
993
+ }
994
+ finally {
995
+ rl.close();
996
+ stream.destroy();
997
+ }
998
+ return {
999
+ sessionId,
1000
+ timestamp,
1001
+ cwd,
1002
+ gitBranch,
1003
+ version,
1004
+ topic,
1005
+ messageCount,
1006
+ tokenCount,
1007
+ contentText: userTexts.length > 0 ? userTexts.join('\n') : undefined,
1008
+ };
1009
+ }
1010
+ function getOpenClawSessionCwd(agentId) {
1011
+ const workspace = agentId ? getOpenClawWorkspaceMap().get(agentId) : undefined;
1012
+ if (workspace)
1013
+ return workspace;
1014
+ const configDir = AGENTS.openclaw.configDir;
1015
+ return safeRealpathSync(configDir) || configDir;
1016
+ }
1017
+ function getOpenClawWorkspaceMap() {
1018
+ if (cachedOpenClawWorkspaces)
1019
+ return cachedOpenClawWorkspaces;
1020
+ const workspaces = new Map();
1021
+ const configPath = path.join(AGENTS.openclaw.configDir, 'openclaw.json');
1022
+ if (!fs.existsSync(configPath)) {
1023
+ cachedOpenClawWorkspaces = workspaces;
1024
+ return workspaces;
1025
+ }
1026
+ try {
1027
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1028
+ for (const agent of config.agents?.list || []) {
1029
+ if (!agent.id || !agent.workspace)
1030
+ continue;
1031
+ workspaces.set(agent.id, safeRealpathSync(agent.workspace) || agent.workspace);
1032
+ }
1033
+ }
1034
+ catch {
1035
+ // Ignore invalid OpenClaw config and fall back to ~/.openclaw.
1036
+ }
1037
+ cachedOpenClawWorkspaces = workspaces;
1038
+ return workspaces;
1039
+ }
1040
+ // ---------------------------------------------------------------------------
1041
+ // Utilities
1042
+ // ---------------------------------------------------------------------------
1043
+ export function readFirstLines(filePath, maxLines) {
1044
+ return new Promise((resolve) => {
1045
+ const lines = [];
1046
+ const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
1047
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
1048
+ rl.on('line', (line) => {
1049
+ if (line.trim()) {
1050
+ lines.push(line);
1051
+ }
1052
+ if (lines.length >= maxLines) {
1053
+ rl.close();
1054
+ stream.destroy();
1055
+ }
1056
+ });
1057
+ rl.on('close', () => resolve(lines));
1058
+ rl.on('error', () => resolve(lines));
1059
+ });
1060
+ }
1061
+ /**
1062
+ * Walk a directory recursively for files with a given extension.
1063
+ */
1064
+ export function walkForFiles(dir, ext, limit) {
1065
+ const results = [];
1066
+ function walk(d, depth) {
1067
+ if (depth > 5)
1068
+ return;
1069
+ let entries;
1070
+ try {
1071
+ entries = fs.readdirSync(d);
1072
+ }
1073
+ catch {
1074
+ return;
1075
+ }
1076
+ for (const entry of entries) {
1077
+ const full = path.join(d, entry);
1078
+ const stat = safeStatSync(full);
1079
+ if (!stat)
1080
+ continue;
1081
+ if (stat.isDirectory()) {
1082
+ walk(full, depth + 1);
1083
+ }
1084
+ else if (entry.endsWith(ext)) {
1085
+ results.push({ path: full, mtime: stat.mtimeMs });
1086
+ }
1087
+ }
1088
+ }
1089
+ walk(dir, 0);
1090
+ results.sort((a, b) => b.mtime - a.mtime);
1091
+ return results.slice(0, limit).map(r => r.path);
1092
+ }
1093
+ function sha256(input) {
1094
+ return crypto.createHash('sha256').update(input).digest('hex');
1095
+ }
1096
+ function safeStatSync(p) {
1097
+ try {
1098
+ return fs.statSync(p);
1099
+ }
1100
+ catch {
1101
+ return null;
1102
+ }
1103
+ }
1104
+ function safeRealpathSync(p) {
1105
+ try {
1106
+ return fs.realpathSync(p);
1107
+ }
1108
+ catch {
1109
+ return null;
1110
+ }
1111
+ }
1112
+ function extractClaudeUserText(parsed) {
1113
+ if (parsed.isMeta === true)
1114
+ return undefined;
1115
+ const content = parsed.message?.content;
1116
+ if (typeof content === 'string') {
1117
+ const text = content.trim();
1118
+ return isLocalCommandMessage(text) ? undefined : text || undefined;
1119
+ }
1120
+ if (!Array.isArray(content))
1121
+ return undefined;
1122
+ const text = content
1123
+ .filter((block) => block.type === 'text')
1124
+ .map((block) => String(block.text || '').trim())
1125
+ .find((value) => value && !value.startsWith('[Request interrupted'));
1126
+ if (!text || isLocalCommandMessage(text))
1127
+ return undefined;
1128
+ return text;
1129
+ }
1130
+ function isLocalCommandMessage(text) {
1131
+ return /<local-command-caveat>|<bash-(input|stdout|stderr)>/i.test(text);
1132
+ }
1133
+ function getClaudeUsageTotal(usage) {
1134
+ if (!usage || typeof usage !== 'object')
1135
+ return null;
1136
+ return sumKnownNumbers([
1137
+ usage.input_tokens,
1138
+ usage.output_tokens,
1139
+ usage.cache_creation_input_tokens,
1140
+ usage.cache_read_input_tokens,
1141
+ ]);
1142
+ }
1143
+ function extractCodexMessageText(contentBlocks, role) {
1144
+ if (!Array.isArray(contentBlocks))
1145
+ return undefined;
1146
+ const matches = role === 'user'
1147
+ ? contentBlocks.filter((block) => block.type === 'input_text')
1148
+ : contentBlocks.filter((block) => block.type === 'output_text');
1149
+ const text = matches
1150
+ .map((block) => String(block.text || '').trim())
1151
+ .find((value) => {
1152
+ if (!value)
1153
+ return false;
1154
+ if (role === 'user' && (value.length >= 2000 || value.includes('<permissions instructions>') || value.startsWith('# AGENTS.md instructions'))) {
1155
+ return false;
1156
+ }
1157
+ return true;
1158
+ });
1159
+ return text || undefined;
1160
+ }
1161
+ function normalizeVersion(version) {
1162
+ const trimmed = version?.trim();
1163
+ return trimmed ? trimmed : undefined;
1164
+ }
1165
+ function extractVersionFromManagedPath(agent, sourcePath) {
1166
+ if (!sourcePath)
1167
+ return undefined;
1168
+ const candidates = [sourcePath, safeRealpathSync(sourcePath) || ''];
1169
+ const marker = `/.agents/versions/${agent}/`;
1170
+ for (const candidate of candidates) {
1171
+ if (!candidate)
1172
+ continue;
1173
+ const normalized = candidate.split(path.sep).join('/');
1174
+ const start = normalized.indexOf(marker);
1175
+ if (start === -1)
1176
+ continue;
1177
+ const version = normalized.slice(start + marker.length).split('/')[0];
1178
+ if (version)
1179
+ return version;
1180
+ }
1181
+ return undefined;
1182
+ }
1183
+ async function getCurrentAgentVersion(agent) {
1184
+ const cached = cachedAgentVersions.get(agent);
1185
+ if (cached)
1186
+ return cached;
1187
+ const promise = (async () => {
1188
+ const symlinkVersion = normalizeVersion(getConfigSymlinkVersion(agent));
1189
+ if (symlinkVersion)
1190
+ return symlinkVersion;
1191
+ return normalizeVersion(await getCliVersion(agent));
1192
+ })();
1193
+ cachedAgentVersions.set(agent, promise);
1194
+ return promise;
1195
+ }
1196
+ function resolveSessionVersion(agent, sourcePath, embeddedVersion, currentVersion) {
1197
+ return normalizeVersion(embeddedVersion)
1198
+ || extractVersionFromManagedPath(agent, sourcePath)
1199
+ || normalizeVersion(currentVersion);
1200
+ }
1201
+ function getCodexTokenCount(totalTokenUsage) {
1202
+ if (!totalTokenUsage || typeof totalTokenUsage !== 'object')
1203
+ return null;
1204
+ return sumKnownNumbers([
1205
+ totalTokenUsage.input_tokens,
1206
+ totalTokenUsage.cached_input_tokens,
1207
+ totalTokenUsage.output_tokens,
1208
+ totalTokenUsage.reasoning_output_tokens,
1209
+ ]);
1210
+ }
1211
+ function extractGeminiMessageText(content) {
1212
+ if (typeof content === 'string')
1213
+ return content.trim();
1214
+ if (Array.isArray(content)) {
1215
+ return content
1216
+ .map((part) => {
1217
+ if (typeof part === 'string')
1218
+ return part;
1219
+ if (typeof part?.text === 'string')
1220
+ return part.text;
1221
+ return '';
1222
+ })
1223
+ .join('\n')
1224
+ .trim();
1225
+ }
1226
+ return '';
1227
+ }
1228
+ function getGeminiTokenCount(tokens) {
1229
+ if (!tokens || typeof tokens !== 'object')
1230
+ return null;
1231
+ if (typeof tokens.total === 'number')
1232
+ return tokens.total;
1233
+ return sumKnownNumbers([
1234
+ tokens.input,
1235
+ tokens.output,
1236
+ tokens.cached,
1237
+ tokens.thoughts,
1238
+ tokens.tool,
1239
+ ]);
1240
+ }
1241
+ function sumKnownNumbers(values) {
1242
+ let total = 0;
1243
+ let found = false;
1244
+ for (const value of values) {
1245
+ if (typeof value !== 'number' || Number.isNaN(value))
1246
+ continue;
1247
+ total += value;
1248
+ found = true;
1249
+ }
1250
+ return found ? total : null;
1251
+ }
1252
+ // ---------------------------------------------------------------------------
1253
+ // Time range parsing
1254
+ // ---------------------------------------------------------------------------
1255
+ export function parseTimeFilter(input) {
1256
+ const relativeMatch = input.match(/^(\d+)([mhdw])$/i);
1257
+ if (relativeMatch) {
1258
+ const value = parseInt(relativeMatch[1], 10);
1259
+ const unit = relativeMatch[2].toLowerCase();
1260
+ if (unit === 'm')
1261
+ return Date.now() - value * 60_000;
1262
+ if (unit === 'h')
1263
+ return Date.now() - value * 3_600_000;
1264
+ if (unit === 'd')
1265
+ return Date.now() - value * 86_400_000;
1266
+ if (unit === 'w')
1267
+ return Date.now() - value * 7 * 86_400_000;
1268
+ }
1269
+ const ts = new Date(input).getTime();
1270
+ return Number.isNaN(ts) ? 0 : ts;
1271
+ }
1272
+ //# sourceMappingURL=discover.js.map